? sys/cscope.out
? sys/mbuf.diff.latest
? sys/netinet/.in.h.swp
? sys/netinet/.sctp_usrreq.c.swo
? sys/netinet/cscope.out
Index: sys/conf/files
===================================================================
RCS file: /home/dcvs/src/sys/conf/files,v
retrieving revision 1.93
diff -u -p -r1.93 files
--- sys/conf/files	11 Jun 2005 09:03:48 -0000	1.93
+++ sys/conf/files	18 Jun 2005 16:05:46 -0000
@@ -1019,6 +1019,19 @@ netinet6/in6.c			optional inet6
 netinet6/in6_cksum.c		optional inet6
 netinet6/in6_gif.c		optional gif inet6
 netinet6/ip6_forward.c		optional inet6
+netinet/sctp_asconf.c		optional inet sctp
+netinet/sctp_crc32.c		optional inet sctp
+netinet/sctp_hashdriver.c	optional inet sctp
+netinet/sctp_indata.c		optional inet sctp
+netinet/sctp_input.c		optional inet sctp
+netinet/sctp_output.c		optional inet sctp
+netinet/sctp_pcb.c		optional inet sctp
+netinet/sctp_peeloff.c		optional inet sctp
+netinet/sctp_sha1.c		optional inet sctp
+netinet/sctp_timer.c		optional inet sctp
+netinet/sctp_usrreq.c		optional inet sctp
+netinet/sctputil.c		optional inet sctp
+netinet6/sctp6_usrreq.c		optional inet6 sctp
 net/ip6fw/ip6_fw.c		optional inet6 ipv6firewall
 netinet6/in6_ifattach.c		optional inet6
 netinet6/ip6_input.c		optional inet6
Index: sys/conf/options
===================================================================
RCS file: /home/dcvs/src/sys/conf/options,v
retrieving revision 1.31
diff -u -p -r1.31 options
--- sys/conf/options	1 Mar 2005 00:43:02 -0000	1.31
+++ sys/conf/options	18 Jun 2005 16:05:46 -0000
@@ -543,3 +543,16 @@ DCONS_FORCE_GDB		opt_dcons.h
 
 # depricated drivers and options
 I_WANT_DEPRECATED_STUFF	opt_deprecated.h
+
+# SCTP
+SCTP			opt_sctp.h
+SCTP_DEBUG		opt_sctp.h
+SCTP_TCP_MODEL_SUPPORT	opt_sctp.h
+SCTP_USE_ADLER32	opt_sctp.h
+SCTP_HIGH_SPEED		opt_sctp.h
+SCTP_STAT_LOGGIN	opt_sctp.h
+SCTP_CWND_LOGGING	opt_sctp.h
+SCTP_BLK_LOGGING	opt_sctp.h
+SCTP_STR_LOGGING	opt_sctp.h
+SCTP_FR_LOGGING		opt_sctp.h
+SCTP_MAP_LOGGING	opt_sctp.h
Index: sys/kern/init_sysent.c
===================================================================
RCS file: /home/dcvs/src/sys/kern/init_sysent.c,v
retrieving revision 1.28
diff -u -p -r1.28 init_sysent.c
--- sys/kern/init_sysent.c	8 Apr 2005 08:31:45 -0000	1.28
+++ sys/kern/init_sysent.c	18 Jun 2005 16:05:47 -0000
@@ -2,7 +2,7 @@
  * System call switch table.
  *
  * DO NOT EDIT-- this file is automatically generated.
- * $DragonFly: src/sys/kern/init_sysent.c,v 1.28 2005/04/08 08:31:45 joerg Exp $
+ * $DragonFly$
  * created from DragonFly: src/sys/kern/syscalls.master,v 1.22 2005/04/08 08:31:06 joerg Exp 
  */
 
@@ -386,7 +386,7 @@ struct sysent sysent[] = {
 	{ AS(getresgid_args), (sy_call_t *)getresgid },	/* 361 = getresgid */
 	{ 0, (sy_call_t *)kqueue },			/* 362 = kqueue */
 	{ AS(kevent_args), (sy_call_t *)kevent },	/* 363 = kevent */
-	{ 0, (sy_call_t *)nosys },			/* 364 = nosys */
+	{ AS(sctp_peeloff_args), (sy_call_t *)sctp_peeloff },	/* 364 = sctp_peeloff */
 	{ 0, (sy_call_t *)nosys },			/* 365 = nosys */
 	{ 0, (sy_call_t *)nosys },			/* 366 = nosys */
 	{ 0, (sy_call_t *)nosys },			/* 367 = nosys */
Index: sys/kern/kern_prot.c
===================================================================
RCS file: /home/dcvs/src/sys/kern/kern_prot.c,v
retrieving revision 1.21
diff -u -p -r1.21 kern_prot.c
--- sys/kern/kern_prot.c	20 Apr 2005 16:37:09 -0000	1.21
+++ sys/kern/kern_prot.c	18 Jun 2005 16:05:47 -0000
@@ -834,7 +834,7 @@ suser_cred(struct ucred *cred, int flag)
 		if (flag & NULL_CRED_OKAY)
 			return (0);
 		else
-			return (EPERM);
+			return (EPERM); /* Unreachable? */
 	}
 	if (cred->cr_uid != 0) 
 		return (EPERM);
Index: sys/kern/syscalls.c
===================================================================
RCS file: /home/dcvs/src/sys/kern/syscalls.c,v
retrieving revision 1.27
diff -u -p -r1.27 syscalls.c
--- sys/kern/syscalls.c	8 Apr 2005 08:31:45 -0000	1.27
+++ sys/kern/syscalls.c	18 Jun 2005 16:05:47 -0000
@@ -2,7 +2,7 @@
  * System call names.
  *
  * DO NOT EDIT-- this file is automatically generated.
- * $DragonFly: src/sys/kern/syscalls.c,v 1.27 2005/04/08 08:31:45 joerg Exp $
+ * $DragonFly$
  * created from DragonFly: src/sys/kern/syscalls.master,v 1.22 2005/04/08 08:31:06 joerg Exp 
  */
 
@@ -371,7 +371,7 @@ char *syscallnames[] = {
 	"getresgid",			/* 361 = getresgid */
 	"kqueue",			/* 362 = kqueue */
 	"kevent",			/* 363 = kevent */
-	"#364",			/* 364 = nosys */
+	"sctp_peeloff",			/* 364 = sctp_peeloff */
 	"#365",			/* 365 = nosys */
 	"#366",			/* 366 = nosys */
 	"#367",			/* 367 = nosys */
Index: sys/kern/syscalls.master
===================================================================
RCS file: /home/dcvs/src/sys/kern/syscalls.master,v
retrieving revision 1.22
diff -u -p -r1.22 syscalls.master
--- sys/kern/syscalls.master	8 Apr 2005 08:31:06 -0000	1.22
+++ sys/kern/syscalls.master	18 Jun 2005 16:05:47 -0000
@@ -522,9 +522,8 @@
 			    const struct kevent *changelist, int nchanges, \
 			    struct kevent *eventlist, int nevents, \
 			    const struct timespec *timeout); }
-
-; 364-392 used by FreeBSD-current
-364	UNIMPL	NOHIDE	nosys
+364	STD	BSD	{ int sctp_peeloff(int sd, caddr_t name ); }
+; 365-392 used by FreeBSD-current
 365	UNIMPL	NOHIDE	nosys
 366	UNIMPL	NOHIDE	nosys
 367	UNIMPL	NOHIDE	nosys
Index: sys/kern/uipc_msg.c
===================================================================
RCS file: /home/dcvs/src/sys/kern/uipc_msg.c,v
retrieving revision 1.13
diff -u -p -r1.13 uipc_msg.c
--- sys/kern/uipc_msg.c	8 Jul 2004 22:07:34 -0000	1.13
+++ sys/kern/uipc_msg.c	18 Jun 2005 16:05:47 -0000
@@ -71,6 +71,7 @@ so_pru_abort(struct socket *so)
 	struct netmsg_pru_abort msg;
 	lwkt_port_t port;
 
+	KKASSERT((so->so_proto->pr_mport != NULL));
 	port = so->so_proto->pr_mport(so, NULL, PRU_ABORT);
 	lwkt_initmsg(&msg.nm_lmsg, &curthread->td_msgport, 0,
 		lwkt_cmd_func(netmsg_pru_abort), lwkt_cmd_op_none);
Index: sys/kern/uipc_socket.c
===================================================================
RCS file: /home/dcvs/src/sys/kern/uipc_socket.c,v
retrieving revision 1.34
diff -u -p -r1.34 uipc_socket.c
--- sys/kern/uipc_socket.c	7 Jun 2005 19:08:55 -0000	1.34
+++ sys/kern/uipc_socket.c	18 Jun 2005 16:05:47 -0000
@@ -86,6 +86,7 @@
  */
 
 #include "opt_inet.h"
+#include "opt_sctp.h"
 
 #include <sys/param.h>
 #include <sys/systm.h>
@@ -190,7 +191,7 @@ socreate(int dom, struct socket **aso, i
 	else
 		prp = pffindtype(dom, type);
 
-	if (prp == 0 || prp->pr_usrreqs->pru_attach == 0)
+	if (prp == NULL || prp->pr_usrreqs->pru_attach == 0)
 		return (EPROTONOSUPPORT);
 
 	if (p->p_ucred->cr_prison && jail_socket_unixiproute_only &&
@@ -202,8 +203,8 @@ socreate(int dom, struct socket **aso, i
 
 	if (prp->pr_type != type)
 		return (EPROTOTYPE);
-	so = soalloc(p != 0);
-	if (so == 0)
+	so = soalloc(p != NULL);
+	if (so == NULL)
 		return (ENOBUFS);
 
 	TAILQ_INIT(&so->so_incomp);
@@ -259,23 +260,40 @@ int
 solisten(struct socket *so, int backlog, struct thread *td)
 {
 	int error;
+#ifdef SCTP
+	short oldopt, oldqlimit;
+#endif /* SCTP */
 
 	crit_enter();
- 	if (so->so_state & (SS_ISCONNECTED | SS_ISCONNECTING)) {
+	if (so->so_state & (SS_ISCONNECTED | SS_ISCONNECTING)) {
 		crit_exit();
- 		return (EINVAL);
- 	}
-
-	error = so_pru_listen(so, td);
-	if (error) {
-		crit_exit();
-		return (error);
+		return (EINVAL);
 	}
+
+#ifdef SCTP
+	oldopt = so->so_options;
+	oldqlimit = so->so_qlimit;
+#endif /* SCTP */
+
 	if (TAILQ_EMPTY(&so->so_comp))
 		so->so_options |= SO_ACCEPTCONN;
 	if (backlog < 0 || backlog > somaxconn)
 		backlog = somaxconn;
 	so->so_qlimit = backlog;
+	/* SCTP needs to look at tweak both the inbound backlog parameter AND
+	 * the so_options (UDP model both connect's and gets inbound
+	 * connections .. implicitly).
+	 */
+	error = so_pru_listen(so, td);
+	if (error) {
+#ifdef SCTP
+		/* Restore the params */
+		so->so_options = oldopt;
+		so->so_qlimit = oldqlimit;
+#endif /* SCTP */
+		crit_exit();
+		return (error);
+	}
 	crit_exit();
 	return (0);
 }
@@ -891,6 +909,26 @@ dontblock:
 			m = so->so_rcv.sb_mb;
 		}
 	}
+#ifdef SCTP
+	if (pr->pr_flags & PR_ADDR_OPT) {
+		/*
+		 * For SCTP we may be getting a
+		 * whole message OR a partial delivery.
+		 */
+		if (m->m_type == MT_SONAME) {
+			orig_resid = 0;
+			if (psa)
+				*psa = dup_sockaddr(mtod(m, struct sockaddr *));
+			if (flags & MSG_PEEK) {
+				m = m->m_next;
+			} else {
+				sbfree(&so->so_rcv, m);
+				so->so_rcv.sb_mb = m_free(m);
+				m = so->so_rcv.sb_mb;
+			}
+		}
+	}
+#endif /* SCTP */
 	while (m && m->m_type == MT_CONTROL && error == 0) {
 		if (flags & MSG_PEEK) {
 			if (controlp)
@@ -961,6 +999,10 @@ dontblock:
 		if (len == m->m_len - moff) {
 			if (m->m_flags & M_EOR)
 				flags |= MSG_EOR;
+#ifdef SCTP
+			if (m->m_flags & M_NOTIFICATION)
+				flags |= MSG_NOTIFICATION;
+#endif /* SCTP */
 			if (flags & MSG_PEEK) {
 				m = m->m_next;
 				moff = 0;
Index: sys/kern/uipc_socket2.c
===================================================================
RCS file: /home/dcvs/src/sys/kern/uipc_socket2.c,v
retrieving revision 1.21
diff -u -p -r1.21 uipc_socket2.c
--- sys/kern/uipc_socket2.c	7 Jun 2005 19:08:55 -0000	1.21
+++ sys/kern/uipc_socket2.c	18 Jun 2005 16:05:47 -0000
@@ -812,10 +812,13 @@ sbdrop(sb, len)
 	int len;
 {
 	struct mbuf *m;
-	struct mbuf *nextpkt;
+	struct mbuf *nextpkt = NULL;
 
 	m = sb->sb_mb;
-	nextpkt = (m != NULL) ? m->m_nextpkt : NULL;
+	if (m != NULL) {
+		nextpkt = m->m_nextpkt;
+		m->m_nextpkt = NULL;
+	}
 	while (len > 0) {
 		if (m == NULL) {
 			if (nextpkt == NULL)
@@ -844,7 +847,7 @@ sbdrop(sb, len)
 		m->m_nextpkt = nextpkt;
 	} else {
 		sb->sb_mb = nextpkt;
-		sb->sb_lastmbuf = NULL;		/* invalidate hint */
+		sb->sb_lastmbuf = NULL;
 	}
 }
 
Index: sys/kern/uipc_syscalls.c
===================================================================
RCS file: /home/dcvs/src/sys/kern/uipc_syscalls.c,v
retrieving revision 1.53
diff -u -p -r1.53 uipc_syscalls.c
--- sys/kern/uipc_syscalls.c	31 May 2005 14:11:43 -0000	1.53
+++ sys/kern/uipc_syscalls.c	18 Jun 2005 16:05:47 -0000
@@ -39,6 +39,7 @@
  */
 
 #include "opt_ktrace.h"
+#include "opt_sctp.h"
 
 #include <sys/param.h>
 #include <sys/systm.h>
@@ -62,6 +63,7 @@
 #include <sys/vnode.h>
 #include <sys/lock.h>
 #include <sys/mount.h>
+#include <sys/thread2.h>
 #ifdef KTRACE
 #include <sys/ktrace.h>
 #endif
@@ -77,6 +79,10 @@
 #include <sys/thread2.h>
 #include <sys/msgport2.h>
 
+#ifdef SCTP
+#include <netinet/sctp_peeloff.h>
+#endif /* SCTP */
+
 struct sfbuf_mref {
 	struct sf_buf	*sf;
 	int		mref_count;
@@ -1703,3 +1709,95 @@ done0:
 		m_freem(mheader);
 	return (error);
 }
+
+int
+sctp_peeloff(struct sctp_peeloff_args *uap)
+{
+#ifdef SCTP
+	struct thread *td = curthread;
+	struct proc *p = td->td_proc;
+	struct filedesc *fdp = p->p_fd;
+	struct file *lfp = NULL;
+	struct file *nfp = NULL;
+	int error;
+	struct socket *head, *so;
+	caddr_t assoc_id;
+	int fd;
+	short fflag;		/* type must match fp->f_flag */
+
+	assoc_id = uap->name;
+	error = holdsock(fdp, uap->sd, &lfp);
+	if (error) {
+		return (error);
+	}
+	crit_enter();
+	head = (struct socket *)lfp->f_data;
+	error = sctp_can_peel_off(head, assoc_id);
+	if (error) {
+		crit_exit();
+		goto done;
+	}
+	/*
+	 * At this point we know we do have a assoc to pull
+	 * we proceed to get the fd setup. This may block
+	 * but that is ok.
+	 */
+
+	fflag = lfp->f_flag;
+	error = falloc(p, &nfp, &fd);
+	if (error) {
+		/*
+		 * Probably ran out of file descriptors. Put the
+		 * unaccepted connection back onto the queue and
+		 * do another wakeup so some other process might
+		 * have a chance at it.
+		 */
+		crit_exit();
+		goto done;
+	}
+	fhold(nfp);
+	uap->sysmsg_result = fd;
+
+	so = sctp_get_peeloff(head, assoc_id, &error);
+	if (so == NULL) {
+		/*
+		 * Either someone else peeled it off OR
+		 * we can't get a socket.
+		 */
+		goto noconnection;
+	}
+	so->so_state &= ~SS_COMP;
+	so->so_state &= ~SS_NOFDREF;
+	so->so_head = NULL;
+	if (head->so_sigio != NULL)
+		fsetown(fgetown(head->so_sigio), &so->so_sigio);
+
+	nfp->f_data = (caddr_t)so;
+	nfp->f_flag = fflag;
+	nfp->f_ops = &socketops;
+	nfp->f_type = DTYPE_SOCKET;
+
+noconnection:
+	/*
+	 * close the new descriptor, assuming someone hasn't ripped it
+	 * out from under us.
+	 */
+	if (error) {
+		if (fdp->fd_ofiles[fd] == nfp) {
+			fdp->fd_ofiles[fd] = NULL;
+			fdrop(nfp, td);
+		}
+	}
+	crit_exit();
+	/*
+	 * Release explicitly held references before returning.
+	 */
+done:
+	if (nfp != NULL)
+		fdrop(nfp, td);
+	fdrop(lfp, td);
+	return (error);
+#else /* SCTP */
+	return(EOPNOTSUPP);
+#endif /* SCTP */
+}
Index: sys/net/rtsock.c
===================================================================
RCS file: /home/dcvs/src/sys/net/rtsock.c,v
retrieving revision 1.28
diff -u -p -r1.28 rtsock.c
--- sys/net/rtsock.c	14 Jun 2005 19:47:30 -0000	1.28
+++ sys/net/rtsock.c	18 Jun 2005 16:05:47 -0000
@@ -85,6 +85,8 @@
  * $DragonFly: src/sys/net/rtsock.c,v 1.28 2005/06/14 19:47:30 joerg Exp $
  */
 
+#include "opt_sctp.h"
+
 #include <sys/param.h>
 #include <sys/systm.h>
 #include <sys/kernel.h>
@@ -102,6 +104,11 @@
 #include <net/route.h>
 #include <net/raw_cb.h>
 
+#ifdef SCTP
+extern void sctp_add_ip_address(struct ifaddr *ifa);
+extern void sctp_delete_ip_address(struct ifaddr *ifa);
+#endif /* SCTP */
+
 MALLOC_DEFINE(M_RTABLE, "routetbl", "routing tables");
 
 static struct route_cb {
@@ -936,6 +943,19 @@ rt_rtmsg(int cmd, struct rtentry *rt, st
 void
 rt_newaddrmsg(int cmd, struct ifaddr *ifa, int error, struct rtentry *rt)
 {
+#ifdef SCTP
+	/*
+	 * notify the SCTP stack
+	 * this will only get called when an address is added/deleted
+	 * XXX pass the ifaddr struct instead if ifa->ifa_addr...
+	 * YYY it is done, remove XXX ?
+	 */
+	if (cmd == RTM_ADD)
+		sctp_add_ip_address(ifa);
+	else if (cmd == RTM_DELETE)
+		sctp_delete_ip_address(ifa);
+#endif /* SCTP */
+
 	if (route_cb.any_count == 0)
 		return;
 
Index: sys/netinet/in.h
===================================================================
RCS file: /home/dcvs/src/sys/netinet/in.h,v
retrieving revision 1.10
diff -u -p -r1.10 in.h
--- sys/netinet/in.h	21 Dec 2004 02:54:15 -0000	1.10
+++ sys/netinet/in.h	18 Jun 2005 16:05:47 -0000
@@ -168,6 +168,7 @@
 /* 101-254: Partly Unassigned */
 #define	IPPROTO_PIM		103		/* Protocol Independent Mcast */
 #define	IPPROTO_PGM		113		/* PGM */
+#define	IPPROTO_SCTP		132		/* SCTP */
 #define	IPPROTO_PFSYNC		240		/* PFSYNC */
 /* 255: Reserved */
 /* BSD Private, local use, namespace incursion */
Index: sys/netinet/in_proto.c
===================================================================
RCS file: /home/dcvs/src/sys/netinet/in_proto.c,v
retrieving revision 1.12
diff -u -p -r1.12 in_proto.c
--- sys/netinet/in_proto.c	4 Mar 2005 02:21:48 -0000	1.12
+++ sys/netinet/in_proto.c	18 Jun 2005 16:05:47 -0000
@@ -40,6 +40,7 @@
 #include "opt_mrouting.h"
 #include "opt_ipsec.h"
 #include "opt_inet6.h"
+#include "opt_sctp.h"
 
 #include <sys/param.h>
 #include <sys/kernel.h>
@@ -95,6 +96,13 @@
 #include <netns/ns_if.h>
 #endif
 
+#ifdef SCTP
+#include <netinet/in_pcb.h>
+#include <netinet/sctp_pcb.h>
+#include <netinet/sctp.h>
+#include <netinet/sctp_var.h>
+#endif /* SCTP */
+
 #include <net/netisr.h>		/* for cpu0_soport */
 
 extern	struct domain inetdomain;
@@ -120,6 +128,33 @@ struct protosw inetsw[] = {
   tcp_init,	0,		tcp_slowtimo,	tcp_drain,
   &tcp_usrreqs
 },
+#ifdef SCTP
+/*
+ * Order is very important here, we add the good one in
+ * in this postion so it maps to the right ip_protox[]
+ * postion for SCTP. Don't move the one above below
+ * this one or IPv6/4 compatability will break
+ */
+{ SOCK_DGRAM,	&inetdomain,	IPPROTO_SCTP,	PR_ADDR_OPT|PR_WANTRCVD,
+  sctp_input,	0,		sctp_ctlinput,	sctp_ctloutput,
+  cpu0_soport,
+  sctp_init,	0,		0,		sctp_drain,
+  &sctp_usrreqs
+},
+{ SOCK_SEQPACKET,&inetdomain,	IPPROTO_SCTP,	PR_ADDR_OPT|PR_WANTRCVD,
+  sctp_input,	0,		sctp_ctlinput,	sctp_ctloutput,
+  cpu0_soport,
+  0,		0,		0,		sctp_drain,
+  &sctp_usrreqs
+},
+
+{ SOCK_STREAM,	&inetdomain,	IPPROTO_SCTP,	PR_CONNREQUIRED|PR_ADDR_OPT|PR_WANTRCVD,
+  sctp_input,	0,		sctp_ctlinput,	sctp_ctloutput,
+  cpu0_soport,
+  0,		0,		0,		sctp_drain,
+  &sctp_usrreqs
+},
+#endif /* SCTP */
 { SOCK_RAW,	&inetdomain,	IPPROTO_RAW,	PR_ATOMIC|PR_ADDR,
   rip_input,	0,		rip_ctlinput,	rip_ctloutput,
   cpu0_soport,
Index: sys/netinet/sctp.h
===================================================================
RCS file: sys/netinet/sctp.h
diff -N sys/netinet/sctp.h
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ sys/netinet/sctp.h	18 Jun 2005 16:05:47 -0000
@@ -0,0 +1,300 @@
+/*	$KAME: sctp.h,v 1.17 2004/08/17 04:06:15 itojun Exp $	*/
+
+#ifndef _NETINET_SCTP_H_
+#define _NETINET_SCTP_H_
+
+/*
+ * Copyright (c) 2001, 2002, 2003, 2004 Cisco Systems, Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *      This product includes software developed by Cisco Systems, Inc.
+ * 4. Neither the name of the project nor the names of its contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY CISCO SYSTEMS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL CISCO SYSTEMS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+#include <sys/types.h>
+
+/*
+ * SCTP protocol - RFC2960.
+ */
+
+struct sctphdr {
+	u_int16_t src_port;		/* source port */
+	u_int16_t dest_port;		/* destination port */
+	u_int32_t v_tag;		/* verification tag of packet */
+	u_int32_t checksum;		/* Adler32 C-Sum */
+	/* chunks follow... */
+};
+
+/*
+ * SCTP Chunks
+ */
+struct sctp_chunkhdr {
+	u_int8_t  chunk_type;		/* chunk type */
+	u_int8_t  chunk_flags;		/* chunk flags */
+	u_int16_t chunk_length;		/* chunk length */
+	/* optional params follow */
+};
+
+/*
+ * SCTP chunk parameters
+ */
+struct sctp_paramhdr {
+	u_int16_t param_type;		/* parameter type */
+	u_int16_t param_length;		/* parameter length */
+};
+
+/*
+ * user socket options
+ */
+/* read-write options */
+#define SCTP_NODELAY			0x00000001
+#define SCTP_MAXSEG			0x00000002
+#define SCTP_ASSOCINFO			0x00000003
+
+#define SCTP_INITMSG			0x00000004
+#define SCTP_AUTOCLOSE			0x00000005
+#define SCTP_SET_PEER_PRIMARY_ADDR	0x00000006
+#define SCTP_PRIMARY_ADDR		0x00000007
+
+/* read-only options */
+#define SCTP_STATUS			0x00000008
+#define SCTP_PCB_STATUS			0x00000009
+
+/* ancillary data/notification interest options */
+#define SCTP_EVENTS			0x0000000a
+/* sctp_opt_info params */
+#define SCTP_PEER_ADDR_PARAMS 		0x0000000b
+#define SCTP_GET_PEER_ADDR_INFO		0x0000000c
+/* Hidden socket option that gets the addresses */
+#define SCTP_GET_PEER_ADDRESSES		0x0000000d
+#define SCTP_GET_LOCAL_ADDRESSES	0x0000000e
+/*
+ * Blocking I/O is enabled on any TCP type socket by default.
+ * For the UDP model if this is turned on then the socket buffer is
+ * shared for send resources amongst all associations. The default
+ * for the UDP model is that is SS_NBIO is set. Which means all associations
+ * have a seperate send limit BUT they will NOT ever BLOCK instead
+ * you will get an error back EAGAIN if you try to send to much. If
+ * you want the blocking symantics you set this option at the cost
+ * of sharing one socket send buffer size amongst all associations.
+ * Peeled off sockets turn this option off and block... but since both TCP and
+ * peeled off sockets have only one assoc per socket this is fine.
+ * It probably does NOT make sense to set this  on SS_NBIO on a TCP model OR
+ * peeled off UDP model, but we do allow you to do so. You just use
+ * the normal syscall to toggle SS_NBIO the way you want.
+ */
+/* Blocking I/O is controled by the SS_NBIO flag on the
+ * socket state so_state field.
+ */
+#define SCTP_GET_SNDBUF_USE		0x0000000f
+/* latter added read/write */
+#define SCTP_ADAPTION_LAYER		0x00000010
+#define SCTP_DISABLE_FRAGMENTS		0x00000011
+/* sctp_bindx() flags as socket options */
+#define SCTP_BINDX_ADD_ADDR		0x00000012
+#define SCTP_BINDX_REM_ADDR		0x00000013
+/* return the total count in bytes needed to hold all local addresses bound */
+#define SCTP_GET_LOCAL_ADDR_SIZE	0x00000014
+/* Without this applied we will give V4 and V6 addresses on a V6 socket */
+#define SCTP_I_WANT_MAPPED_V4_ADDR	0x00000015
+/* Return the total count in bytes needed to hold the remote address */
+#define SCTP_GET_REMOTE_ADDR_SIZE	0x00000016
+#define SCTP_GET_PEGS			0x00000017
+#define SCTP_DEFAULT_SEND_PARAM		0x00000018
+#define SCTP_SET_DEBUG_LEVEL		0x00000019
+#define SCTP_RTOINFO			0x0000001a
+#define SCTP_AUTO_ASCONF		0x0000001b
+#define SCTP_MAXBURST			0x0000001c
+#define SCTP_GET_STAT_LOG		0x0000001d
+#define SCTP_CONNECT_X			0x0000001e	/* hidden opt for connectx */
+#define SCTP_RESET_STREAMS		0x0000001f
+#define SCTP_CONNECT_X_DELAYED		0x00000020	/* hidden opt for connectx_delayed
+							 * part of sctp_sendx()
+							 */
+#define SCTP_CONNECT_X_COMPLETE         0x00000021
+#define SCTP_GET_ASOC_ID_LIST           0x00000022
+
+/* Other BSD items */
+#define SCTP_GET_NONCE_VALUES           0x00000023
+#define SCTP_DELAYED_ACK_TIME           0x00000024
+
+/* Things for the AUTH draft possibly */
+#define SCTP_PEER_PUBLIC_KEY            0x00000100 /* get the peers public key */
+#define SCTP_MY_PUBLIC_KEY              0x00000101 /* get/set my endpoints public key */
+#define SCTP_SET_AUTH_SECRET            0x00000102 /* get/set my shared secret */
+#define SCTP_SET_AUTH_CHUNKS            0x00000103/* specify what chunks you want
+						    * the system may have additional requirments
+						     * as well. I.e. probably ASCONF/ASCONF-ACK no matter
+						     * if you want it or not.
+						     */
+/* Debug things that need to be purged */
+#define SCTP_SET_INITIAL_DBG_SEQ	0x00001f00
+#define SCTP_RESET_PEGS                 0x00002000
+#define SCTP_CLR_STAT_LOG               0x00002100
+
+/*
+ * user state values
+ */
+#define SCTP_CLOSED			0x0000
+#define SCTP_BOUND			0x1000
+#define SCTP_LISTEN			0x2000
+#define SCTP_COOKIE_WAIT		0x0002
+#define SCTP_COOKIE_ECHOED		0x0004
+#define SCTP_ESTABLISHED		0x0008
+#define SCTP_SHUTDOWN_SENT		0x0010
+#define SCTP_SHUTDOWN_RECEIVED		0x0020
+#define SCTP_SHUTDOWN_ACK_SENT		0x0040
+#define SCTP_SHUTDOWN_PENDING		0x0080
+
+/*
+ * SCTP operational error codes (user visible)
+ */
+#define SCTP_ERROR_NO_ERROR		0x0000
+#define SCTP_ERROR_INVALID_STREAM	0x0001
+#define SCTP_ERROR_MISSING_PARAM	0x0002
+#define SCTP_ERROR_STALE_COOKIE		0x0003
+#define SCTP_ERROR_OUT_OF_RESOURCES	0x0004
+#define SCTP_ERROR_UNRESOLVABLE_ADDR	0x0005
+#define SCTP_ERROR_UNRECOG_CHUNK	0x0006
+#define SCTP_ERROR_INVALID_PARAM	0x0007
+#define SCTP_ERROR_UNRECOG_PARAM	0x0008
+#define SCTP_ERROR_NO_USER_DATA		0x0009
+#define SCTP_ERROR_COOKIE_IN_SHUTDOWN	0x000a
+/* draft-ietf-tsvwg-sctpimpguide */
+#define SCTP_ERROR_RESTART_NEWADDRS	0x000b
+/* draft-ietf-tsvwg-addip-sctp */
+#define SCTP_ERROR_DELETE_LAST_ADDR	0x0100
+#define SCTP_ERROR_RESOURCE_SHORTAGE	0x0101
+#define SCTP_ERROR_DELETE_SOURCE_ADDR	0x0102
+#define SCTP_ERROR_ILLEGAL_ASCONF_ACK	0x0103
+
+/*
+ * error cause parameters (user visisble)
+ */
+struct sctp_error_cause {
+	u_int16_t code;
+	u_int16_t length;
+	/* optional cause-specific info may follow */
+};
+
+struct sctp_error_invalid_stream {
+	struct sctp_error_cause cause;	/* code=SCTP_ERROR_INVALID_STREAM */
+	u_int16_t stream_id;		/* stream id of the DATA in error */
+	u_int16_t reserved;
+};
+
+struct sctp_error_missing_param {
+	struct sctp_error_cause cause;	/* code=SCTP_ERROR_MISSING_PARAM */
+	u_int32_t num_missing_params;	/* number of missing parameters */
+	/* u_int16_t param_type's follow */
+};
+
+struct sctp_error_stale_cookie {
+	struct sctp_error_cause cause;	/* code=SCTP_ERROR_STALE_COOKIE */
+	u_int32_t stale_time;		/* time in usec of staleness */
+};
+
+struct sctp_error_out_of_resource {
+	struct sctp_error_cause cause;	/* code=SCTP_ERROR_OUT_OF_RESOURCES */
+};
+
+struct sctp_error_unresolv_addr {
+	struct sctp_error_cause cause;	/* code=SCTP_ERROR_UNRESOLVABLE_ADDR */
+
+};
+
+struct sctp_error_unrecognized_chunk {
+	struct sctp_error_cause cause;	/* code=SCTP_ERROR_UNRECOG_CHUNK */
+	struct sctp_chunkhdr ch;	/* header from chunk in error */
+};
+
+#define HAVE_SCTP			1
+#define HAVE_KERNEL_SCTP		1
+#define HAVE_SCTP_PRSCTP		1
+#define HAVE_SCTP_ADDIP			1
+#define HAVE_SCTP_CANSET_PRIMARY	1
+#define HAVE_SCTP_SAT_NETWORK_CAPABILITY1
+#define HAVE_SCTP_MULTIBUF              1
+#define HAVE_SCTP_NOCONNECT             0
+#define HAVE_SCTP_ECN_NONCE             1  /* ECN Nonce option */
+
+/* Main SCTP chunk types, we place
+ * these here since that way natd and f/w's
+ * in user land can find them.
+ */
+#define SCTP_DATA		0x00
+#define SCTP_INITIATION		0x01
+#define SCTP_INITIATION_ACK	0x02
+#define SCTP_SELECTIVE_ACK	0x03
+#define SCTP_HEARTBEAT_REQUEST	0x04
+#define SCTP_HEARTBEAT_ACK	0x05
+#define SCTP_ABORT_ASSOCIATION	0x06
+#define SCTP_SHUTDOWN		0x07
+#define SCTP_SHUTDOWN_ACK	0x08
+#define SCTP_OPERATION_ERROR	0x09
+#define SCTP_COOKIE_ECHO	0x0a
+#define SCTP_COOKIE_ACK		0x0b
+#define SCTP_ECN_ECHO		0x0c
+#define SCTP_ECN_CWR		0x0d
+#define SCTP_SHUTDOWN_COMPLETE	0x0e
+
+/* draft-ietf-tsvwg-addip-sctp */
+#define SCTP_ASCONF		0xc1
+#define	SCTP_ASCONF_ACK		0x80
+
+/* draft-ietf-stewart-prsctp */
+#define SCTP_FORWARD_CUM_TSN	0xc0
+
+/* draft-ietf-stewart-pktdrpsctp */
+#define SCTP_PACKET_DROPPED	0x81
+
+/* draft-ietf-stewart-strreset-xxx */
+#define SCTP_STREAM_RESET       0x82
+
+/* ABORT and SHUTDOWN COMPLETE FLAG */
+#define SCTP_HAD_NO_TCB		0x01
+
+/* Packet dropped flags */
+#define SCTP_FROM_MIDDLE_BOX	SCTP_HAD_NO_TCB
+#define SCTP_BADCRC		0x02
+#define SCTP_PACKET_TRUNCATED	0x04
+
+#define SCTP_SAT_NETWORK_MIN	     400	/* min ms for RTT to set satellite time */
+#define SCTP_SAT_NETWORK_BURST_INCR  2		/* how many times to multiply maxburst in sat */
+/* Data Chuck Specific Flags */
+#define SCTP_DATA_FRAG_MASK	0x03
+#define SCTP_DATA_MIDDLE_FRAG	0x00
+#define SCTP_DATA_LAST_FRAG	0x01
+#define SCTP_DATA_FIRST_FRAG	0x02
+#define SCTP_DATA_NOT_FRAG	0x03
+#define SCTP_DATA_UNORDERED	0x04
+
+/* ECN Nonce: SACK Chunk Specific Flags */
+#define SCTP_SACK_NONCE_SUM     0x01
+
+#include <netinet/sctp_uio.h>
+
+#endif /* !_NETINET_SCTP_H_ */
Index: sys/netinet/sctp_asconf.c
===================================================================
RCS file: sys/netinet/sctp_asconf.c
diff -N sys/netinet/sctp_asconf.c
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ sys/netinet/sctp_asconf.c	18 Jun 2005 16:05:47 -0000
@@ -0,0 +1,2907 @@
+/*	$KAME: sctp_asconf.c,v 1.23 2004/08/17 06:28:01 t-momose Exp $	*/
+
+/*
+ * Copyright (c) 2001, 2002, 2003, 2004 Cisco Systems, Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the project nor the names of its contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY CISCO SYSTEMS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL CISCO SYSTEMS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+#if !(defined(__OpenBSD__) || defined(__APPLE__))
+#include "opt_ipsec.h"
+#endif
+#if defined(__FreeBSD__)
+#include "opt_compat.h"
+#include "opt_inet6.h"
+#include "opt_inet.h"
+#endif
+#if defined(__NetBSD__)
+#include "opt_inet.h"
+#endif
+
+#ifdef __APPLE__
+#include <sctp.h>
+#elif !defined(__OpenBSD__)
+#include "opt_sctp.h"
+#endif
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/malloc.h>
+#include <sys/mbuf.h>
+#include <sys/socket.h>
+#include <sys/socketvar.h>
+#include <sys/kernel.h>
+#include <sys/sysctl.h>
+#include <sys/thread2.h>
+
+#include <net/if.h>
+#include <net/if_types.h>
+#include <net/route.h>
+
+#include <netinet/in.h>
+#include <netinet/in_systm.h>
+#include <netinet/ip.h>
+#include <netinet/in_pcb.h>
+#include <netinet/in_var.h>
+#include <netinet/ip_var.h>
+
+#ifdef INET6
+#include <netinet/ip6.h>
+#include <netinet6/ip6_var.h>
+#if defined(__FreeBSD__) || (__NetBSD__)
+#include <netinet6/in6_pcb.h>
+#elif defined(__OpenBSD__)
+#include <netinet/in_pcb.h>
+#endif
+#include <netinet/icmp6.h>
+#include <netinet6/nd6.h>
+#endif /* INET6 */
+
+#include <netinet/in_pcb.h>
+
+#include <netinet/sctp_var.h>
+#include <netinet/sctp_pcb.h>
+#include <netinet/sctp_header.h>
+#include <netinet/sctputil.h>
+#include <netinet/sctp_output.h>
+#include <netinet/sctp_asconf.h>
+
+/*
+ * debug flags:
+ *   SCTP_DEBUG_ASCONF1: protocol info, general info and errors
+ *   SCTP_DEBUG_ASCONF2: detailed info
+ */
+#ifdef SCTP_DEBUG
+extern u_int32_t sctp_debug_on;
+
+#if defined(SCTP_BASE_FREEBSD) || defined(__APPLE__)
+#define strlcpy strncpy
+#endif
+#endif /* SCTP_DEBUG */
+
+/*
+ * draft-ietf-tsvwg-addip-sctp
+ *
+ * Address management only currently supported
+ * For the bound all case:
+ *	the asoc local addr list is always a "DO NOT USE" list
+ * For the subset bound case:
+ *	If ASCONFs are allowed:
+ *		the endpoint local addr list is the usable address list
+ *		the asoc local addr list is the "DO NOT USE" list
+ *	If ASCONFs are not allowed:
+ *		the endpoint local addr list is the default usable list
+ *		the asoc local addr list is the usable address list
+ *
+ * An ASCONF parameter queue exists per asoc which holds the pending
+ * address operations.  Lists are updated upon receipt of ASCONF-ACK.
+ *
+ * Deleted addresses are always immediately removed from the lists as
+ * they will (shortly) no longer exist in the kernel.  We send ASCONFs
+ * as a courtesy, only if allowed.
+ */
+
+/*
+ * ASCONF parameter processing
+ * response_required: set if a reply is required (eg. SUCCESS_REPORT)
+ * returns a mbuf to an "error" response parameter or NULL/"success" if ok
+ * FIX: allocating this many mbufs on the fly is pretty inefficient...
+ */
+
+static struct mbuf *
+sctp_asconf_success_response(uint32_t id)
+{
+	struct mbuf *m_reply = NULL;
+	struct sctp_asconf_paramhdr *aph;
+
+	MGET(m_reply, MB_DONTWAIT, MT_DATA);
+	if (m_reply == NULL) {
+#ifdef SCTP_DEBUG
+		if (sctp_debug_on & SCTP_DEBUG_ASCONF1) {
+			printf("asconf_success_response: couldn't get mbuf!\n");
+		}
+#endif /* SCTP_DEBUG */
+		return NULL;
+	}
+	aph = mtod(m_reply, struct sctp_asconf_paramhdr *);
+	aph->correlation_id = id;
+	aph->ph.param_type = htons(SCTP_SUCCESS_REPORT);
+	aph->ph.param_length = sizeof(struct sctp_asconf_paramhdr);
+	m_reply->m_len = aph->ph.param_length;
+	aph->ph.param_length = htons(aph->ph.param_length);
+
+	return m_reply;
+}
+
+static struct mbuf *
+sctp_asconf_error_response(uint32_t id, uint16_t cause, uint8_t *error_tlv,
+    uint16_t tlv_length)
+{
+	struct mbuf *m_reply = NULL;
+	struct sctp_asconf_paramhdr *aph;
+	struct sctp_error_cause *error;
+	uint8_t *tlv;
+
+	MGET(m_reply, MB_DONTWAIT, MT_DATA);
+	if (m_reply == NULL) {
+#ifdef SCTP_DEBUG
+		if (sctp_debug_on & SCTP_DEBUG_ASCONF1) {
+			printf("asconf_error_response: couldn't get mbuf!\n");
+		}
+#endif /* SCTP_DEBUG */
+		return NULL;
+	}
+	aph = mtod(m_reply, struct sctp_asconf_paramhdr *);
+	error = (struct sctp_error_cause *)(aph + 1);
+
+	aph->correlation_id = id;
+	aph->ph.param_type = htons(SCTP_ERROR_CAUSE_IND);
+	error->code = htons(cause);
+	error->length = tlv_length + sizeof(struct sctp_error_cause);
+	aph->ph.param_length = error->length +
+	    sizeof(struct sctp_asconf_paramhdr);
+
+	if (aph->ph.param_length > MLEN) {
+#ifdef SCTP_DEBUG
+		if (sctp_debug_on & SCTP_DEBUG_ASCONF1) {
+			printf("asconf_error_response: tlv_length (%xh) too big\n",
+			    tlv_length);
+		}
+#endif /* SCTP_DEBUG */
+		sctp_m_freem(m_reply);	/* discard */
+		return NULL;
+	}
+
+	if (error_tlv != NULL) {
+		tlv = (uint8_t *)(error + 1);
+		memcpy(tlv, error_tlv, tlv_length);
+	}
+
+	m_reply->m_len = aph->ph.param_length;
+	error->length = htons(error->length);
+	aph->ph.param_length = htons(aph->ph.param_length);
+
+	return m_reply;
+}
+
+static struct mbuf *
+sctp_process_asconf_add_ip(struct sctp_asconf_paramhdr *aph,
+    struct sctp_tcb *stcb, int response_required)
+{
+	struct mbuf *m_reply = NULL;
+	struct sockaddr_storage sa_store;
+	struct sctp_ipv4addr_param *v4addr;
+	uint16_t param_type, param_length, aparam_length;
+	struct sockaddr *sa;
+	struct sockaddr_in *sin;
+#ifdef INET6
+	struct sockaddr_in6 *sin6;
+	struct sctp_ipv6addr_param *v6addr;
+#endif /* INET6 */
+
+	aparam_length = ntohs(aph->ph.param_length);
+	v4addr = (struct sctp_ipv4addr_param *)(aph + 1);
+#ifdef INET6
+	v6addr = (struct sctp_ipv6addr_param *)(aph + 1);
+#endif /* INET6 */
+	param_type = ntohs(v4addr->ph.param_type);
+	param_length = ntohs(v4addr->ph.param_length);
+
+	sa = (struct sockaddr *)&sa_store;
+	switch (param_type) {
+	case SCTP_IPV4_ADDRESS:
+		if (param_length != sizeof(struct sctp_ipv4addr_param)) {
+			/* invalid param size */
+			return NULL;
+		}
+		sin = (struct sockaddr_in *)&sa_store;
+		bzero(sin, sizeof(*sin));
+		sin->sin_family = AF_INET;
+		sin->sin_len = sizeof(struct sockaddr_in);
+		sin->sin_port = stcb->rport;
+		sin->sin_addr.s_addr = v4addr->addr;
+#ifdef SCTP_DEBUG
+		if (sctp_debug_on & SCTP_DEBUG_ASCONF1) {
+			printf("process_asconf_add_ip: adding ");
+			sctp_print_address(sa);
+		}
+#endif /* SCTP_DEBUG */
+		break;
+	case SCTP_IPV6_ADDRESS:
+#ifdef INET6
+		if (param_length != sizeof(struct sctp_ipv6addr_param)) {
+			/* invalid param size */
+			return NULL;
+		}
+		sin6 = (struct sockaddr_in6 *)&sa_store;
+		bzero(sin6, sizeof(*sin6));
+		sin6->sin6_family = AF_INET6;
+		sin6->sin6_len = sizeof(struct sockaddr_in6);
+		sin6->sin6_port = stcb->rport;
+		memcpy((caddr_t)&sin6->sin6_addr, v6addr->addr,
+		    sizeof(struct in6_addr));
+#ifdef SCTP_DEBUG
+		if (sctp_debug_on & SCTP_DEBUG_ASCONF1) {
+			printf("process_asconf_add_ip: adding ");
+			sctp_print_address(sa);
+		}
+#endif /* SCTP_DEBUG */
+#else
+		/* IPv6 not enabled! */
+		/* FIX ME: currently sends back an invalid param error */
+		m_reply = sctp_asconf_error_response(aph->correlation_id,
+		    SCTP_ERROR_INVALID_PARAM, (uint8_t *)aph, aparam_length);
+#ifdef SCTP_DEBUG
+		if (sctp_debug_on & SCTP_DEBUG_ASCONF1) {
+			printf("process_asconf_add_ip: v6 disabled- skipping ");
+			sctp_print_address(sa);
+		}
+#endif /* SCTP_DEBUG */
+		return m_reply;
+#endif /* INET6 */
+		break;
+	default:
+		m_reply = sctp_asconf_error_response(aph->correlation_id,
+		    SCTP_ERROR_UNRESOLVABLE_ADDR, (uint8_t *)aph,
+		    aparam_length);
+		return m_reply;
+	} /* end switch */
+
+	/* add the address */
+	if (sctp_add_remote_addr(stcb, sa, 0, 6) != 0) {
+#ifdef SCTP_DEBUG
+		if (sctp_debug_on & SCTP_DEBUG_ASCONF1) {
+			printf("process_asconf_add_ip: error adding address\n");
+		}
+#endif /* SCTP_DEBUG */
+		m_reply = sctp_asconf_error_response(aph->correlation_id,
+		    SCTP_ERROR_RESOURCE_SHORTAGE, (uint8_t *)aph,
+		    aparam_length);
+	} else {
+		/* notify upper layer */
+		sctp_ulp_notify(SCTP_NOTIFY_ASCONF_ADD_IP, stcb, 0, sa);
+		if (response_required) {
+			m_reply =
+			    sctp_asconf_success_response(aph->correlation_id);
+		}
+	}
+
+	return m_reply;
+}
+
+static struct mbuf *
+sctp_process_asconf_delete_ip(struct mbuf *m, struct sctp_asconf_paramhdr *aph,
+    struct sctp_tcb *stcb, int response_required)
+{
+	struct mbuf *m_reply = NULL;
+	struct sockaddr_storage sa_store, sa_source;
+	struct sctp_ipv4addr_param *v4addr;
+	uint16_t param_type, param_length, aparam_length;
+	struct sockaddr *sa;
+	struct sockaddr_in *sin;
+	struct ip *iph;
+	int result;
+#ifdef INET6
+	struct sockaddr_in6 *sin6;
+	struct sctp_ipv6addr_param *v6addr;
+#endif /* INET6 */
+
+	aparam_length = ntohs(aph->ph.param_length);
+	v4addr = (struct sctp_ipv4addr_param *)(aph + 1);
+#ifdef INET6
+	v6addr = (struct sctp_ipv6addr_param *)(aph + 1);
+#endif /* INET6 */
+	param_type = ntohs(v4addr->ph.param_type);
+	param_length = ntohs(v4addr->ph.param_length);
+
+	/* get the source IP address for deletion check */
+	iph = mtod(m, struct ip *);
+	if (iph->ip_v == IPVERSION) {
+		/* IPv4 source */
+		sin = (struct sockaddr_in *)&sa_source;
+		bzero(sin, sizeof(*sin));
+		sin->sin_family = AF_INET;
+		sin->sin_len = sizeof(struct sockaddr_in);
+		sin->sin_port = stcb->rport;
+		sin->sin_addr.s_addr = iph->ip_src.s_addr;
+	}
+#ifdef INET6
+	else if (iph->ip_v == (IPV6_VERSION >> 4)) {
+		/* IPv6 source */
+		struct ip6_hdr *ip6;
+
+		sin6 = (struct sockaddr_in6 *)&sa_source;
+		bzero(sin6, sizeof(*sin6));
+		sin6->sin6_family = AF_INET6;
+		sin6->sin6_len = sizeof(struct sockaddr_in6);
+		sin6->sin6_port = stcb->rport;
+		ip6 = mtod(m, struct ip6_hdr *);
+		sin6->sin6_addr = ip6->ip6_src;
+	}
+#endif /* INET6 */
+	else
+		return NULL;
+
+	sa = (struct sockaddr *)&sa_store;
+	switch (param_type) {
+	case SCTP_IPV4_ADDRESS:
+		if (param_length != sizeof(struct sctp_ipv4addr_param)) {
+			/* invalid param size */
+			return NULL;
+		}
+		sin = (struct sockaddr_in *)&sa_store;
+		bzero(sin, sizeof(*sin));
+		sin->sin_family = AF_INET;
+		sin->sin_len = sizeof(struct sockaddr_in);
+		sin->sin_port = stcb->rport;
+		sin->sin_addr.s_addr = v4addr->addr;
+#ifdef SCTP_DEBUG
+		if (sctp_debug_on & SCTP_DEBUG_ASCONF1) {
+			printf("process_asconf_delete_ip: deleting ");
+			sctp_print_address(sa);
+		}
+#endif /* SCTP_DEBUG */
+		break;
+	case SCTP_IPV6_ADDRESS:
+		if (param_length != sizeof(struct sctp_ipv6addr_param)) {
+			/* invalid param size */
+			return NULL;
+		}
+#ifdef INET6
+		sin6 = (struct sockaddr_in6 *)&sa_store;
+		bzero(sin6, sizeof(*sin6));
+		sin6->sin6_family = AF_INET6;
+		sin6->sin6_len = sizeof(struct sockaddr_in6);
+		sin6->sin6_port = stcb->rport;
+		memcpy(&sin6->sin6_addr, v6addr->addr,
+		    sizeof(struct in6_addr));
+#ifdef SCTP_DEBUG
+		if (sctp_debug_on & SCTP_DEBUG_ASCONF1) {
+			printf("process_asconf_delete_ip: deleting ");
+			sctp_print_address(sa);
+		}
+#endif /* SCTP_DEBUG */
+#else
+		/* IPv6 not enabled!  No "action" needed; just ack it */
+#ifdef SCTP_DEBUG
+		if (sctp_debug_on & SCTP_DEBUG_ASCONF1) {
+			printf("process_asconf_delete_ip: v6 disabled- ignoring: ");
+			sctp_print_address(sa);
+		}
+#endif /* SCTP_DEBUG */
+		/* just respond with a "success" ASCONF-ACK */
+		return NULL;
+#endif /* INET6 */
+		break;
+	default:
+		m_reply = sctp_asconf_error_response(aph->correlation_id,
+		    SCTP_ERROR_UNRESOLVABLE_ADDR, (uint8_t *)aph,
+		    aparam_length);
+		return m_reply;
+	} /* end switch */
+
+	/* make sure the source address is not being deleted */
+	if ((memcmp(sa, &sa_source, sizeof(struct sockaddr_in)) == 0)
+#ifdef INET6
+	    || (memcmp(sa, &sa_source, sizeof(struct sockaddr_in6)) == 0)
+#endif /* INET6 */
+		) {
+		/* trying to delete the source address! */
+#ifdef SCTP_DEBUG
+		if (sctp_debug_on & SCTP_DEBUG_ASCONF1) {
+			printf("process_asconf_delete_ip: tried to delete source addr\n");
+		}
+#endif /* SCTP_DEBUG */
+		m_reply = sctp_asconf_error_response(aph->correlation_id,
+		    SCTP_ERROR_DELETE_SOURCE_ADDR, (uint8_t *)aph,
+		    aparam_length);
+		return m_reply;
+	}
+
+	/* delete the address */
+	result = sctp_del_remote_addr(stcb, sa);
+	/*
+	 * note if result == -2, the address doesn't exist in the asoc
+	 * but since it's being deleted anyways, we just ack the delete
+	 * -- but this probably means something has already gone awry
+	 */
+	if (result == -1) {
+		/* only one address in the asoc */
+#ifdef SCTP_DEBUG
+		if (sctp_debug_on & SCTP_DEBUG_ASCONF1) {
+			printf("process_asconf_delete_ip: tried to delete last IP addr!\n");
+		}
+#endif /* SCTP_DEBUG */
+		m_reply = sctp_asconf_error_response(aph->correlation_id,
+		    SCTP_ERROR_DELETE_LAST_ADDR, (uint8_t *)aph,
+		    aparam_length);
+	} else {
+		/* notify upper layer */
+		sctp_ulp_notify(SCTP_NOTIFY_ASCONF_DELETE_IP, stcb, 0, sa);
+	}
+
+	if (response_required) {
+		m_reply = sctp_asconf_success_response(aph->correlation_id);
+	}
+	return m_reply;
+}
+
+static struct mbuf *
+sctp_process_asconf_set_primary(struct sctp_asconf_paramhdr *aph,
+    struct sctp_tcb *stcb, int response_required)
+{
+	struct mbuf *m_reply = NULL;
+	struct sockaddr_storage sa_store;
+	struct sctp_ipv4addr_param *v4addr;
+	uint16_t param_type, param_length, aparam_length;
+	struct sockaddr *sa;
+	struct sockaddr_in *sin;
+#ifdef INET6
+	struct sockaddr_in6 *sin6;
+	struct sctp_ipv6addr_param *v6addr;
+#endif /* INET6 */
+
+	aparam_length = ntohs(aph->ph.param_length);
+	v4addr = (struct sctp_ipv4addr_param *)(aph + 1);
+#ifdef INET6
+	v6addr = (struct sctp_ipv6addr_param *)(aph + 1);
+#endif /* INET6 */
+	param_type = ntohs(v4addr->ph.param_type);
+	param_length = ntohs(v4addr->ph.param_length);
+
+	sa = (struct sockaddr *)&sa_store;
+	switch (param_type) {
+	case SCTP_IPV4_ADDRESS:
+		if (param_length != sizeof(struct sctp_ipv4addr_param)) {
+			/* invalid param size */
+			return NULL;
+		}
+		sin = (struct sockaddr_in *)&sa_store;
+		bzero(sin, sizeof(*sin));
+		sin->sin_family = AF_INET;
+		sin->sin_len = sizeof(struct sockaddr_in);
+		sin->sin_addr.s_addr = v4addr->addr;
+#ifdef SCTP_DEBUG
+		if (sctp_debug_on & SCTP_DEBUG_ASCONF1) {
+			printf("process_asconf_set_primary: ");
+			sctp_print_address(sa);
+		}
+#endif /* SCTP_DEBUG */
+		break;
+	case SCTP_IPV6_ADDRESS:
+		if (param_length != sizeof(struct sctp_ipv6addr_param)) {
+			/* invalid param size */
+			return NULL;
+		}
+#ifdef INET6
+		sin6 = (struct sockaddr_in6 *)&sa_store;
+		bzero(sin6, sizeof(*sin6));
+		sin6->sin6_family = AF_INET6;
+		sin6->sin6_len = sizeof(struct sockaddr_in6);
+		memcpy((caddr_t)&sin6->sin6_addr, v6addr->addr,
+		    sizeof(struct in6_addr));
+#ifdef SCTP_DEBUG
+		if (sctp_debug_on & SCTP_DEBUG_ASCONF1) {
+			printf("process_asconf_set_primary: ");
+			sctp_print_address(sa);
+		}
+#endif /* SCTP_DEBUG */
+#else
+		/* IPv6 not enabled!  No "action" needed; just ack it */
+#ifdef SCTP_DEBUG
+		if (sctp_debug_on & SCTP_DEBUG_ASCONF1) {
+			printf("process_asconf_set_primary: v6 disabled- ignoring: ");
+			sctp_print_address(sa);
+		}
+#endif /* SCTP_DEBUG */
+		/* just respond with a "success" ASCONF-ACK */
+		return NULL;
+#endif /* INET6 */
+		break;
+	default:
+		m_reply = sctp_asconf_error_response(aph->correlation_id,
+		    SCTP_ERROR_UNRESOLVABLE_ADDR, (uint8_t *)aph,
+		    aparam_length);
+		return m_reply;
+	} /* end switch */
+
+	/* set the primary address */
+	if (sctp_set_primary_addr(stcb, sa, NULL) == 0) {
+#ifdef SCTP_DEBUG
+		if (sctp_debug_on & SCTP_DEBUG_ASCONF1) {
+			printf("process_asconf_set_primary: primary address set\n");
+		}
+#endif /* SCTP_DEBUG */
+		/* notify upper layer */
+		sctp_ulp_notify(SCTP_NOTIFY_ASCONF_SET_PRIMARY, stcb, 0, sa);
+
+		if (response_required) {
+			m_reply = sctp_asconf_success_response(aph->correlation_id);
+		}
+	} else {
+		/* couldn't set the requested primary address! */
+#ifdef SCTP_DEBUG
+		if (sctp_debug_on & SCTP_DEBUG_ASCONF1) {
+			printf("process_asconf_set_primary: set primary failed!\n");
+		}
+#endif /* SCTP_DEBUG */
+		/* must have been an invalid address, so report */
+		m_reply = sctp_asconf_error_response(aph->correlation_id,
+		    SCTP_ERROR_UNRESOLVABLE_ADDR, (uint8_t *)aph,
+		    aparam_length);
+	}
+
+	return m_reply;
+}
+
+/*
+ * handles an ASCONF chunk
+ * if all parameters are processed ok, send a plain (empty) ASCONF-ACK
+ */
+void
+sctp_handle_asconf(struct mbuf *m, unsigned int offset, struct sctp_asconf_chunk *cp,
+    struct sctp_tcb *stcb, struct sctp_nets *net)
+{
+	struct sctp_association *asoc;
+	uint32_t serial_num;
+	struct mbuf *m_ack, *m_result, *m_tail;
+	struct sctp_asconf_ack_chunk *ack_cp;
+	struct sctp_asconf_paramhdr *aph, *ack_aph;
+	struct sctp_ipv6addr_param *p_addr;
+	unsigned int asconf_limit;
+	int error = 0;		/* did an error occur? */
+	/* asconf param buffer */
+	static u_int8_t aparam_buf[DEFAULT_PARAM_BUFFER];
+
+	/* verify minimum length */
+	if (ntohs(cp->ch.chunk_length) < sizeof(struct sctp_asconf_chunk)) {
+#ifdef SCTP_DEBUG
+		if (sctp_debug_on & SCTP_DEBUG_ASCONF1) {
+			printf("handle_asconf: chunk too small = %xh\n",
+			    ntohs(cp->ch.chunk_length));
+		}
+#endif /* SCTP_DEBUG */
+		return;
+	}
+
+	asoc = &stcb->asoc;
+	serial_num = ntohl(cp->serial_number);
+
+	if (serial_num == asoc->asconf_seq_in) {
+		/* got a duplicate ASCONF */
+#ifdef SCTP_DEBUG
+		if (sctp_debug_on & SCTP_DEBUG_ASCONF1) {
+			printf("handle_asconf: got duplicate serial number = %xh\n",
+			       serial_num);
+		}
+#endif /* SCTP_DEBUG */
+		/* resend last ASCONF-ACK... */
+		sctp_send_asconf_ack(stcb, 1);
+		return;
+	} else if (serial_num != (asoc->asconf_seq_in + 1)) {
+#ifdef SCTP_DEBUG
+		if (sctp_debug_on & SCTP_DEBUG_ASCONF1) {
+			printf("handle_asconf: incorrect serial number = %xh (expected next = %xh)\n",
+			    serial_num, asoc->asconf_seq_in+1);
+		}
+#endif /* SCTP_DEBUG */
+		return;
+	}
+
+	/* it's the expected "next" sequence number, so process it */
+	asoc->asconf_seq_in = serial_num;	/* update sequence */
+	/* get length of all the param's in the ASCONF */
+	asconf_limit = offset + ntohs(cp->ch.chunk_length);
+#ifdef SCTP_DEBUG
+	if (sctp_debug_on & SCTP_DEBUG_ASCONF1) {
+		printf("handle_asconf: asconf_limit=%u, sequence=%xh\n",
+		    asconf_limit, serial_num);
+	}
+#endif /* SCTP_DEBUG */
+	if (asoc->last_asconf_ack_sent != NULL) {
+		/* free last ASCONF-ACK message sent */
+		sctp_m_freem(asoc->last_asconf_ack_sent);
+		asoc->last_asconf_ack_sent = NULL;
+	}
+	MGETHDR(m_ack, MB_DONTWAIT, MT_DATA);
+	if (m_ack == NULL) {
+#ifdef SCTP_DEBUG
+		if (sctp_debug_on & SCTP_DEBUG_ASCONF1) {
+			printf("handle_asconf: couldn't get mbuf!\n");
+		}
+#endif /* SCTP_DEBUG */
+		return;
+	}
+	m_tail = m_ack;		/* current reply chain's tail */
+
+	/* fill in ASCONF-ACK header */
+	ack_cp = mtod(m_ack, struct sctp_asconf_ack_chunk *);
+	ack_cp->ch.chunk_type = SCTP_ASCONF_ACK;
+	ack_cp->ch.chunk_flags = 0;
+	ack_cp->serial_number = htonl(serial_num);
+	/* set initial lengths (eg. just an ASCONF-ACK), ntohx at the end! */
+	m_ack->m_len = sizeof(struct sctp_asconf_ack_chunk);
+	ack_cp->ch.chunk_length = sizeof(struct sctp_asconf_ack_chunk);
+	m_ack->m_pkthdr.len = sizeof(struct sctp_asconf_ack_chunk);
+
+	/* skip the lookup address parameter */
+	offset += sizeof(struct sctp_asconf_chunk);
+	p_addr = (struct sctp_ipv6addr_param *)sctp_m_getptr(m, offset, sizeof(struct sctp_paramhdr), (uint8_t *)&aparam_buf);
+	if (p_addr == NULL) {
+#ifdef SCTP_DEBUG
+		if (sctp_debug_on & SCTP_DEBUG_ASCONF1) {
+			printf("handle_asconf: couldn't get lookup addr!\n");
+		}
+#endif /* SCTP_DEBUG */
+
+		/* respond with a missing/invalid mandatory parameter error */
+		return;
+	}
+	/* param_length is already validated in process_control... */
+	offset += ntohs(p_addr->ph.param_length);   /* skip lookup addr */
+
+	/* get pointer to first asconf param in ASCONF */
+	aph = (struct sctp_asconf_paramhdr *)sctp_m_getptr(m, offset, sizeof(struct sctp_asconf_paramhdr), (uint8_t *)&aparam_buf);
+	/* get pointer to first asconf param in ASCONF-ACK */
+	if (aph == NULL) {
+		printf("Gak in asconf\n");
+		return;
+	}
+	ack_aph = (struct sctp_asconf_paramhdr *)(mtod(m_ack, caddr_t) + sizeof(struct sctp_asconf_ack_chunk));
+	if (ack_aph == NULL) {
+		printf("Gak in asconf2\n");
+		return;
+	}
+
+	/* process through all parameters */
+	while (aph != NULL) {
+		unsigned int param_length, param_type;
+
+		param_type = ntohs(aph->ph.param_type);
+		param_length = ntohs(aph->ph.param_length);
+		if (offset + param_length > asconf_limit) {
+			/* parameter goes beyond end of chunk! */
+			sctp_m_freem(m_ack);
+			return;
+		}
+		m_result = NULL;
+
+		if (param_length > sizeof(aparam_buf)) {
+#ifdef SCTP_DEBUG
+			if (sctp_debug_on & SCTP_DEBUG_ASCONF1) {
+				printf("handle_asconf: param length (%u) larger than buffer size!\n", param_length);
+			}
+#endif /* SCTP_DEBUG */
+			sctp_m_freem(m_ack);
+			return;
+		}
+		if (param_length <= sizeof(struct sctp_paramhdr)) {
+#ifdef SCTP_DEBUG
+			if (sctp_debug_on & SCTP_DEBUG_ASCONF1) {
+				printf("handle_asconf: param length (%u) too short\n", param_length);
+			}
+#endif /* SCTP_DEBUG */
+			sctp_m_freem(m_ack);
+		}
+
+		/* get the entire parameter */
+		aph = (struct sctp_asconf_paramhdr *)sctp_m_getptr(m, offset, param_length, aparam_buf);
+		if (aph == NULL) {
+			printf("Gag\n");
+			sctp_m_freem(m_ack);
+			return;
+		}
+		switch (param_type) {
+		case SCTP_ADD_IP_ADDRESS:
+			asoc->peer_supports_asconf = 1;
+			m_result = sctp_process_asconf_add_ip(aph, stcb, error);
+			break;
+		case SCTP_DEL_IP_ADDRESS:
+			asoc->peer_supports_asconf = 1;
+			m_result = sctp_process_asconf_delete_ip(m, aph, stcb,
+			    error);
+			break;
+		case SCTP_ERROR_CAUSE_IND:
+			/* not valid in an ASCONF chunk */
+			break;
+		case SCTP_SET_PRIM_ADDR:
+			asoc->peer_supports_asconf_setprim = 1;
+			m_result = sctp_process_asconf_set_primary(aph, stcb,
+			    error);
+			break;
+		case SCTP_SUCCESS_REPORT:
+			/* not valid in an ASCONF chunk */
+			break;
+		case SCTP_ULP_ADAPTION:
+			/* FIX */
+			break;
+		default:
+			if ((param_type & 0x8000) == 0) {
+				/* Been told to STOP at this param */
+				asconf_limit = offset;
+				/* FIX FIX - We need to call sctp_arethere_unrecognized_parameters()
+				 * to get a operr and send it for any param's with the
+				 * 0x4000 bit set OR do it here ourselves... note we still
+				 * must STOP if the 0x8000 bit is clear.
+				 */
+			}
+			/* unknown/invalid param type */
+			break;
+		} /* switch */
+
+		/* add any (error) result to the reply mbuf chain */
+		if (m_result != NULL) {
+#ifdef SCTP_DEBUG
+			if (sctp_debug_on & SCTP_DEBUG_ASCONF1) {
+				printf("handle_asconf: adding reply...\n");
+			}
+#endif /* SCTP_DEBUG */
+			m_tail->m_next = m_result;
+			m_tail = m_result;
+			/* update lengths, make sure it's aligned too */
+			m_result->m_len = SCTP_SIZE32(m_result->m_len);
+			m_ack->m_pkthdr.len += m_result->m_len;
+			ack_cp->ch.chunk_length += m_result->m_len;
+			/* set flag to force success reports */
+			error = 1;
+		}
+
+		offset += SCTP_SIZE32(param_length);
+		/* update remaining ASCONF message length to process */
+		if (offset >= asconf_limit) {
+			/* no more data in the mbuf chain */
+			break;
+		}
+		/* get pointer to next asconf param */
+		aph = (struct sctp_asconf_paramhdr *)sctp_m_getptr(m, offset,
+		    sizeof(struct sctp_asconf_paramhdr),
+		    (uint8_t *)&aparam_buf);
+		if (aph == NULL) {
+			/* can't get an asconf paramhdr */
+#ifdef SCTP_DEBUG
+			if (sctp_debug_on & SCTP_DEBUG_ASCONF1) {
+				printf("handle_asconf: can't get asconf param hdr!\n");
+			}
+#endif /* SCTP_DEBUG */
+			/* FIX ME - add error here... */
+		}
+	} /* while */
+
+	ack_cp->ch.chunk_length = htons(ack_cp->ch.chunk_length);
+	/* save the ASCONF-ACK reply */
+	asoc->last_asconf_ack_sent = m_ack;
+	/* and send (a new one) it out... */
+	sctp_send_asconf_ack(stcb, 0);
+}
+
+/*
+ * does the address match?
+ * returns 0 if not, 1 if so
+ */
+static uint32_t
+sctp_asconf_addr_match(struct sctp_asconf_addr *aa, struct sockaddr *sa)
+{
+#ifdef INET6
+	if (sa->sa_family == AF_INET6) {
+		/* IPv6 sa address */
+		/* XXX scopeid */
+		struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *)sa;
+		if ((aa->ap.addrp.ph.param_type == SCTP_IPV6_ADDRESS) &&
+		    (memcmp(&aa->ap.addrp.addr, &sin6->sin6_addr,
+			    sizeof(struct in6_addr)) == 0)) {
+			return (1);
+		}
+	} else
+#endif /* INET6 */
+	if (sa->sa_family == AF_INET) {
+		/* IPv4 sa address */
+		struct sockaddr_in *sin = (struct sockaddr_in *)sa;
+		if ((aa->ap.addrp.ph.param_type == SCTP_IPV4_ADDRESS) &&
+		    (memcmp(&aa->ap.addrp.addr, &sin->sin_addr,
+			    sizeof(struct in_addr)) == 0)) {
+			return (1);
+		}
+	}
+	return (0);
+}
+
+/*
+ * Cleanup for non-responded/OP ERR'd ASCONF
+ */
+void
+sctp_asconf_cleanup(struct sctp_tcb *stcb, struct sctp_nets *net)
+{
+#ifdef SCTP_DEBUG
+	if (sctp_debug_on & SCTP_DEBUG_ASCONF1) {
+		printf("asconf_cleanup: marking peer ASCONF incapable and cleaning up\n");
+	}
+#endif /* SCTP_DEBUG */
+	/* mark peer as ASCONF incapable */
+	stcb->asoc.peer_supports_asconf = 0;
+	stcb->asoc.peer_supports_asconf_setprim = 0;
+	/*
+	 * clear out any existing asconfs going out
+	 */
+	sctp_timer_stop(SCTP_TIMER_TYPE_ASCONF, stcb->sctp_ep, stcb, net);
+	stcb->asoc.asconf_seq_out++;
+	/* remove the old ASCONF on our outbound queue */
+	sctp_toss_old_asconf(stcb);
+}
+
+/*
+ * process an ADD/DELETE IP ack from peer
+ * ifa:  corresponding ifaddr to the address being added/deleted
+ * type: SCTP_ADD_IP_ADDRESS or SCTP_DEL_IP_ADDRESS
+ * flag: 1=success, 0=failure
+ */
+static void
+sctp_asconf_addr_mgmt_ack(struct sctp_tcb *stcb, struct ifaddr *addr,
+    uint16_t type, uint32_t flag)
+{
+
+	/*
+	 * do the necessary asoc list work-
+	 * if we get a failure indication, leave the address on the
+	 *   "do not use" asoc list
+	 * if we get a success indication, remove the address from
+	 *   the list
+	 */
+	/*
+	 * Note: this will only occur for ADD_IP_ADDRESS, since
+	 * DEL_IP_ADDRESS is never actually added to the list...
+	 */
+	if (flag) {
+		/* success case, so remove from the list */
+		sctp_del_local_addr_assoc(stcb, addr);
+	}
+	/* else, leave it on the list */
+}
+
+/*
+ * add an asconf add/delete IP address parameter to the queue
+ * type = SCTP_ADD_IP_ADDRESS, SCTP_DEL_IP_ADDRESS, SCTP_SET_PRIM_ADDR
+ * returns 0 if completed, non-zero if not completed
+ * NOTE: if adding, but delete already scheduled (and not yet
+ * 	sent out), simply remove from queue.  Same for deleting
+ *	an address already scheduled for add.  If a duplicate
+ *	operation is found, ignore the new one.
+ */
+static uint32_t
+sctp_asconf_queue_add(struct sctp_tcb *stcb, struct ifaddr *ifa, uint16_t type)
+{
+	struct sctp_asconf_addr *aa, *aa_next;
+#ifdef SCTP_DEBUG
+	char buf[128];	/* for address in string format */
+#endif /* SCTP_DEBUG */
+
+	/* see if peer supports ASCONF */
+	if (stcb->asoc.peer_supports_asconf == 0) {
+#ifdef SCTP_DEBUG
+		if (sctp_debug_on & SCTP_DEBUG_ASCONF1) {
+			printf("asconf_queue_add: peer doesn't support ASCONF\n");
+		}
+#endif /* SCTP_DEBUG */
+		return (-1);
+	}
+
+	/* make sure the request isn't already in the queue */
+	for (aa=TAILQ_FIRST(&stcb->asoc.asconf_queue); aa!=NULL; aa=aa_next) {
+		aa_next = TAILQ_NEXT(aa, next);
+		/* address match? */
+		if (sctp_asconf_addr_match(aa, ifa->ifa_addr) == 0)
+			continue;
+		/* is the request already in queue (sent or not) */
+		if (aa->ap.aph.ph.param_type == type) {
+#ifdef SCTP_DEBUG
+			if (sctp_debug_on & SCTP_DEBUG_ASCONF1) {
+				printf("asconf_queue_add: request already exists\n");
+			}
+#endif /* SCTP_DEBUG */
+			return (-1);
+		}
+		/* is the negative request already in queue, and not sent */
+		if (aa->sent == 0 &&
+		    /* add requested, delete already queued */
+		    ((type == SCTP_ADD_IP_ADDRESS &&
+		      aa->ap.aph.ph.param_type == SCTP_DEL_IP_ADDRESS) ||
+		     /* delete requested, add already queued */
+		     (type == SCTP_DEL_IP_ADDRESS &&
+		      aa->ap.aph.ph.param_type == SCTP_ADD_IP_ADDRESS))) {
+			/* delete the existing entry in the queue */
+			TAILQ_REMOVE(&stcb->asoc.asconf_queue, aa, next);
+			/* take the entry off the appropriate list */
+			sctp_asconf_addr_mgmt_ack(stcb, aa->ifa, type, 1);
+			/* free the entry */
+			FREE(aa, M_PCB);
+
+#ifdef SCTP_DEBUG
+			if (sctp_debug_on & SCTP_DEBUG_ASCONF1) {
+				printf("asconf_queue_add: removing 'opposite' queued request\n");
+			}
+#endif /* SCTP_DEBUG */
+			return (-1);
+		}
+	} /* for each aa */
+
+	/* adding new request to the queue */
+	MALLOC(aa, struct sctp_asconf_addr *, sizeof(*aa), M_PCB, M_NOWAIT);
+	if (aa == NULL) {
+		/* didn't get memory */
+#ifdef SCTP_DEBUG
+		if (sctp_debug_on & SCTP_DEBUG_ASCONF1) {
+			printf("asconf_queue_add: failed to get memory!\n");
+		}
+#endif /* SCTP_DEBUG */
+		return (-1);
+	}
+	/* fill in asconf address parameter fields */
+	/* top level elements are "networked" during send */
+	aa->ap.aph.ph.param_type = type;
+	aa->ifa = ifa;
+	/* correlation_id filled in during send routine later... */
+	if (ifa->ifa_addr->sa_family == AF_INET6) {
+		/* IPv6 address */
+		struct sockaddr_in6 *sin6;
+
+		sin6 = (struct sockaddr_in6 *)ifa->ifa_addr;
+		aa->ap.addrp.ph.param_type = SCTP_IPV6_ADDRESS;
+		aa->ap.addrp.ph.param_length = (sizeof(struct sctp_ipv6addr_param));
+		aa->ap.aph.ph.param_length =
+		    sizeof(struct sctp_asconf_paramhdr) +
+		    sizeof(struct sctp_ipv6addr_param);
+		memcpy(&aa->ap.addrp.addr, &sin6->sin6_addr,
+		    sizeof(struct in6_addr));
+#ifdef SCTP_DEBUG
+		strlcpy(buf, ip6_sprintf(&sin6->sin6_addr), sizeof(buf));
+#endif /* SCTP_DEBUG */
+
+	} else if (ifa->ifa_addr->sa_family == AF_INET) {
+		/* IPv4 address */
+		struct sockaddr_in *sin = (struct sockaddr_in *)ifa->ifa_addr;
+		aa->ap.addrp.ph.param_type = SCTP_IPV4_ADDRESS;
+		aa->ap.addrp.ph.param_length = (sizeof(struct sctp_ipv4addr_param));
+		aa->ap.aph.ph.param_length =
+		    sizeof(struct sctp_asconf_paramhdr) +
+		    sizeof(struct sctp_ipv4addr_param);
+		memcpy(&aa->ap.addrp.addr, &sin->sin_addr,
+		    sizeof(struct in_addr));
+#ifdef SCTP_DEBUG
+		strlcpy(buf, inet_ntoa(sin->sin_addr), sizeof(buf));
+#endif /* SCTP_DEBUG */
+	} else {
+		/* invalid family! */
+		return (-1);
+	}
+	aa->sent = 0;			/* clear sent flag */
+
+	/*
+	 * if we are deleting an address it should go out last
+	 * otherwise, add it to front of the pending queue
+	 */
+	if (type == SCTP_ADD_IP_ADDRESS) {
+		/* add goes to the front of the queue */
+		TAILQ_INSERT_HEAD(&stcb->asoc.asconf_queue, aa, next);
+#ifdef SCTP_DEBUG
+		if (sctp_debug_on & SCTP_DEBUG_ASCONF1) {
+			printf("asconf_queue_add: appended asconf ADD_IP_ADDRESS: %s\n", buf);
+		}
+#endif /* SCTP_DEBUG */
+	} else {
+		/* delete and set primary goes to the back of the queue */
+		TAILQ_INSERT_TAIL(&stcb->asoc.asconf_queue, aa, next);
+#ifdef SCTP_DEBUG
+		if (sctp_debug_on & SCTP_DEBUG_ASCONF1) {
+			if (type == SCTP_DEL_IP_ADDRESS) {
+				printf("asconf_queue_add: inserted asconf DEL_IP_ADDRESS: %s\n", buf);
+			} else {
+				printf("asconf_queue_add: inserted asconf SET_PRIM_ADDR: %s\n", buf);
+			}
+		}
+#endif /* SCTP_DEBUG */
+	}
+
+	return (0);
+}
+
+/*
+ * add an asconf add/delete IP address parameter to the queue by addr
+ * type = SCTP_ADD_IP_ADDRESS, SCTP_DEL_IP_ADDRESS, SCTP_SET_PRIM_ADDR
+ * returns 0 if completed, non-zero if not completed
+ * NOTE: if adding, but delete already scheduled (and not yet
+ * 	sent out), simply remove from queue.  Same for deleting
+ *	an address already scheduled for add.  If a duplicate
+ *	operation is found, ignore the new one.
+ */
+static uint32_t
+sctp_asconf_queue_add_sa(struct sctp_tcb *stcb, struct sockaddr *sa,
+    uint16_t type)
+{
+	struct sctp_asconf_addr *aa, *aa_next;
+
+	/* see if peer supports ASCONF */
+	if (stcb->asoc.peer_supports_asconf == 0) {
+#ifdef SCTP_DEBUG
+		if (sctp_debug_on & SCTP_DEBUG_ASCONF1) {
+			printf("asconf_queue_add_sa: peer doesn't support ASCONF\n");
+		}
+#endif /* SCTP_DEBUG */
+		return (-1);
+	}
+
+	/* make sure the request isn't already in the queue */
+	for (aa = TAILQ_FIRST(&stcb->asoc.asconf_queue); aa != NULL;
+	    aa = aa_next) {
+		aa_next = TAILQ_NEXT(aa, next);
+		/* address match? */
+		if (sctp_asconf_addr_match(aa, sa) == 0)
+			continue;
+		/* is the request already in queue (sent or not) */
+		if (aa->ap.aph.ph.param_type == type) {
+#ifdef SCTP_DEBUG
+			if (sctp_debug_on & SCTP_DEBUG_ASCONF1) {
+				printf("asconf_queue_add_sa: request already exists\n");
+			}
+#endif /* SCTP_DEBUG */
+			return (-1);
+		}
+
+		/* is the negative request already in queue, and not sent */
+		if (aa->sent == 1)
+			continue;
+		if (type == SCTP_ADD_IP_ADDRESS &&
+		    aa->ap.aph.ph.param_type == SCTP_DEL_IP_ADDRESS) {
+			/* add requested, delete already queued */
+
+			/* delete the existing entry in the queue */
+			TAILQ_REMOVE(&stcb->asoc.asconf_queue, aa, next);
+			/* free the entry */
+			FREE(aa, M_PCB);
+#ifdef SCTP_DEBUG
+			if (sctp_debug_on & SCTP_DEBUG_ASCONF1) {
+				printf("asconf_queue_add_sa: removing queued delete request\n");
+			}
+#endif /* SCTP_DEBUG */
+			return (-1);
+		} else if (type == SCTP_DEL_IP_ADDRESS &&
+			   aa->ap.aph.ph.param_type == SCTP_ADD_IP_ADDRESS) {
+			/* delete requested, add already queued */
+
+			/* delete the existing entry in the queue */
+			TAILQ_REMOVE(&stcb->asoc.asconf_queue, aa, next);
+			/* take the entry off the appropriate list */
+			sctp_asconf_addr_mgmt_ack(stcb, aa->ifa, type, 1);
+			/* free the entry */
+			FREE(aa, M_PCB);
+#ifdef SCTP_DEBUG
+			if (sctp_debug_on & SCTP_DEBUG_ASCONF1) {
+				printf("asconf_queue_add_sa: removing queued add request\n");
+			}
+#endif /* SCTP_DEBUG */
+			return (-1);
+		}
+	} /* for each aa */
+
+	/* adding new request to the queue */
+	MALLOC(aa, struct sctp_asconf_addr *, sizeof(*aa), M_PCB, M_NOWAIT);
+	if (aa == NULL) {
+		/* didn't get memory */
+#ifdef SCTP_DEBUG
+		if (sctp_debug_on & SCTP_DEBUG_ASCONF1) {
+			printf("asconf_queue_add_sa: failed to get memory!\n");
+		}
+#endif /* SCTP_DEBUG */
+		return (-1);
+	}
+	/* fill in asconf address parameter fields */
+	/* top level elements are "networked" during send */
+	aa->ap.aph.ph.param_type = type;
+	aa->ifa = sctp_find_ifa_by_addr(sa);
+	/* correlation_id filled in during send routine later... */
+	if (sa->sa_family == AF_INET6) {
+		/* IPv6 address */
+		struct sockaddr_in6 *sin6;
+
+		sin6 = (struct sockaddr_in6 *)sa;
+		aa->ap.addrp.ph.param_type = SCTP_IPV6_ADDRESS;
+		aa->ap.addrp.ph.param_length = (sizeof(struct sctp_ipv6addr_param));
+		aa->ap.aph.ph.param_length = sizeof(struct sctp_asconf_paramhdr) + sizeof(struct sctp_ipv6addr_param);
+		memcpy(&aa->ap.addrp.addr, &sin6->sin6_addr,
+		    sizeof(struct in6_addr));
+	} else if (sa->sa_family == AF_INET) {
+		/* IPv4 address */
+		struct sockaddr_in *sin = (struct sockaddr_in *)sa;
+		aa->ap.addrp.ph.param_type = SCTP_IPV4_ADDRESS;
+		aa->ap.addrp.ph.param_length = (sizeof(struct sctp_ipv4addr_param));
+		aa->ap.aph.ph.param_length = sizeof(struct sctp_asconf_paramhdr) + sizeof(struct sctp_ipv4addr_param);
+		memcpy(&aa->ap.addrp.addr, &sin->sin_addr,
+		    sizeof(struct in_addr));
+	} else {
+		/* invalid family! */
+		return (-1);
+	}
+	aa->sent = 0;			/* clear sent flag */
+
+	/*
+	 * if we are deleting an address it should go out last
+	 * otherwise, add it to front of the pending queue
+	 */
+	if (type == SCTP_ADD_IP_ADDRESS) {
+		/* add goes to the front of the queue */
+		TAILQ_INSERT_HEAD(&stcb->asoc.asconf_queue, aa, next);
+#ifdef SCTP_DEBUG
+		if (sctp_debug_on & SCTP_DEBUG_ASCONF1) {
+			printf("asconf_queue_add_sa: appended asconf ADD_IP_ADDRESS\n");
+		}
+#endif /* SCTP_DEBUG */
+	} else {
+		/* delete and set primary goes to the back of the queue */
+		TAILQ_INSERT_TAIL(&stcb->asoc.asconf_queue, aa, next);
+#ifdef SCTP_DEBUG
+		if (sctp_debug_on & SCTP_DEBUG_ASCONF1) {
+			if (type == SCTP_DEL_IP_ADDRESS) {
+				printf("asconf_queue_add_sa: inserted asconf DEL_IP_ADDRESS\n");
+			} else {
+				printf("asconf_queue_add_sa: inserted asconf SET_PRIM_ADDR\n");
+			}
+		}
+#endif /* SCTP_DEBUG */
+	}
+
+	return (0);
+}
+
+/*
+ * find a specific asconf param on our "sent" queue
+ */
+static struct sctp_asconf_addr *
+sctp_asconf_find_param(struct sctp_tcb *stcb, uint32_t correlation_id)
+{
+	struct sctp_asconf_addr *aa;
+
+	TAILQ_FOREACH(aa, &stcb->asoc.asconf_queue, next) {
+		if (aa->ap.aph.correlation_id == correlation_id &&
+		    aa->sent == 1) {
+			/* found it */
+			return (aa);
+		}
+	}
+	/* didn't find it */
+	return (NULL);
+}
+
+/*
+ * process an SCTP_ERROR_CAUSE_IND for a ASCONF-ACK parameter
+ * and do notifications based on the error response
+ */
+static void
+sctp_asconf_process_error(struct sctp_tcb *stcb,
+    struct sctp_asconf_paramhdr *aph)
+{
+	struct sctp_error_cause *eh;
+	struct sctp_paramhdr *ph;
+	uint16_t param_type;
+	uint16_t error_code;
+
+	eh = (struct sctp_error_cause *)(aph + 1);
+	ph = (struct sctp_paramhdr *)(eh + 1);
+	/* validate lengths */
+	if (htons(eh->length) + sizeof(struct sctp_error_cause) >
+	    htons(aph->ph.param_length)) {
+		/* invalid error cause length */
+#ifdef SCTP_DEBUG
+		if (sctp_debug_on & SCTP_DEBUG_ASCONF1) {
+			printf("asconf_process_error: cause element too long\n");
+		}
+#endif /* SCTP_DEBUG */
+		return;
+	}
+	if (htons(ph->param_length) + sizeof(struct sctp_paramhdr) >
+	    htons(eh->length)) {
+		/* invalid included TLV length */
+#ifdef SCTP_DEBUG
+		if (sctp_debug_on & SCTP_DEBUG_ASCONF1) {
+			printf("asconf_process_error: included TLV too long\n");
+		}
+#endif /* SCTP_DEBUG */
+		return;
+	}
+
+	/* which error code ? */
+	error_code = ntohs(eh->code);
+	param_type = ntohs(aph->ph.param_type);
+	/* FIX: this should go back up the REMOTE_ERROR ULP notify */
+	switch (error_code) {
+	case SCTP_ERROR_RESOURCE_SHORTAGE:
+		/* we allow ourselves to "try again" for this error */
+		break;
+	default:
+		/* peer can't handle it... */
+		switch (param_type) {
+		case SCTP_ADD_IP_ADDRESS:
+		case SCTP_DEL_IP_ADDRESS:
+			stcb->asoc.peer_supports_asconf = 0;
+			break;
+		case SCTP_SET_PRIM_ADDR:
+			stcb->asoc.peer_supports_asconf_setprim = 0;
+			break;
+		default:
+			break;
+		}
+	}
+}
+
+/*
+ * process an asconf queue param
+ * aparam: parameter to process, will be removed from the queue
+ * flag: 1=success, 0=failure
+ */
+static void
+sctp_asconf_process_param_ack(struct sctp_tcb *stcb,
+    struct sctp_asconf_addr *aparam, uint32_t flag)
+{
+	uint16_t param_type;
+
+	/* process this param */
+	param_type = aparam->ap.aph.ph.param_type;
+#ifdef SCTP_DEBUG
+	if (sctp_debug_on & SCTP_DEBUG_ASCONF1) {
+		printf("process_param_ack: handling asconf parameter type=%xh\n", param_type);
+	}
+#endif /* SCTP_DEBUG */
+	switch (param_type) {
+	case SCTP_ADD_IP_ADDRESS:
+#ifdef SCTP_DEBUG
+		if (sctp_debug_on & SCTP_DEBUG_ASCONF1) {
+			printf("process_param_ack: added IP address\n");
+		}
+#endif /* SCTP_DEBUG */
+		sctp_asconf_addr_mgmt_ack(stcb, aparam->ifa, param_type, flag);
+		break;
+	case SCTP_DEL_IP_ADDRESS:
+#ifdef SCTP_DEBUG
+		if (sctp_debug_on & SCTP_DEBUG_ASCONF1) {
+			printf("process_param_ack: deleted IP address\n");
+		}
+#endif /* SCTP_DEBUG */
+		/* nothing really to do... lists already updated */
+		break;
+	case SCTP_SET_PRIM_ADDR:
+		/* nothing to do... peer may start using this addr */
+		if (flag == 0)
+			stcb->asoc.peer_supports_asconf_setprim = 0;
+		break;
+	default:
+		/* should NEVER happen */
+		break;
+	} /* switch */
+
+	/* remove the param and free it */
+	TAILQ_REMOVE(&stcb->asoc.asconf_queue, aparam, next);
+	FREE(aparam, M_PCB);
+}
+
+/*
+ * cleanup from a bad asconf ack parameter
+ */
+static void
+sctp_asconf_ack_clear(struct sctp_tcb *stcb)
+{
+	/* assume peer doesn't really know how to do asconfs */
+	stcb->asoc.peer_supports_asconf = 0;
+	stcb->asoc.peer_supports_asconf_setprim = 0;
+	/* XXX we could free the pending queue here */
+}
+
+void
+sctp_handle_asconf_ack(struct mbuf *m, int offset,
+    struct sctp_asconf_ack_chunk *cp, struct sctp_tcb *stcb,
+    struct sctp_nets *net)
+{
+	struct sctp_association *asoc;
+	uint32_t serial_num;
+	uint16_t ack_length;
+	struct sctp_asconf_paramhdr *aph;
+	struct sctp_asconf_addr *aa, *aa_next;
+	uint32_t last_error_id = 0;		/* last error correlation id */
+	uint32_t id;
+	struct sctp_asconf_addr *ap;
+	/* asconf param buffer */
+	static u_int8_t aparam_buf[DEFAULT_PARAM_BUFFER];
+
+	/* verify minimum length */
+	if (ntohs(cp->ch.chunk_length) < sizeof(struct sctp_asconf_ack_chunk)) {
+#ifdef SCTP_DEBUG
+		if (sctp_debug_on & SCTP_DEBUG_ASCONF1) {
+			printf("handle_asconf_ack: chunk too small = %xh\n",
+			       ntohs(cp->ch.chunk_length));
+		}
+#endif /* SCTP_DEBUG */
+		return;
+	}
+
+	asoc = &stcb->asoc;
+	serial_num = ntohl(cp->serial_number);
+
+	/*
+	 * NOTE: we may want to handle this differently- currently, we
+	 * will abort when we get an ack for the expected serial number + 1
+	 * (eg. we didn't send it), process an ack normally if it is the
+	 * expected serial number, and re-send the previous ack for *ALL*
+	 * other serial numbers
+	 */
+
+	/*
+	 * if the serial number is the next expected, but I didn't send it,
+	 * abort the asoc, since someone probably just hijacked us...
+	 */
+	if (serial_num == (asoc->asconf_seq_out + 1)) {
+		sctp_abort_an_association(stcb->sctp_ep, stcb,
+		    SCTP_ERROR_ILLEGAL_ASCONF_ACK, NULL);
+#ifdef SCTP_DEBUG
+		if (sctp_debug_on & SCTP_DEBUG_ASCONF1) {
+			printf("handle_asconf_ack: got unexpected next serial number! Aborting asoc!\n");
+		}
+#endif /* SCTP_DEBUG */
+		return;
+	}
+
+	if (serial_num != asoc->asconf_seq_out) {
+		/* got a duplicate/unexpected ASCONF-ACK */
+#ifdef SCTP_DEBUG
+		if (sctp_debug_on & SCTP_DEBUG_ASCONF1) {
+			printf("handle_asconf_ack: got duplicate/unexpected serial number = %xh (expected = %xh)\n", serial_num, asoc->asconf_seq_out);
+		}
+#endif /* SCTP_DEBUG */
+		return;
+	}
+	/* stop our timer */
+	sctp_timer_stop(SCTP_TIMER_TYPE_ASCONF, stcb->sctp_ep, stcb, net);
+
+	/* process the ASCONF-ACK contents */
+	ack_length = ntohs(cp->ch.chunk_length) -
+	    sizeof(struct sctp_asconf_ack_chunk);
+	offset += sizeof(struct sctp_asconf_ack_chunk);
+	/* process through all parameters */
+	while (ack_length >= sizeof(struct sctp_asconf_paramhdr)) {
+		unsigned int param_length, param_type;
+
+		/* get pointer to next asconf parameter */
+		aph = (struct sctp_asconf_paramhdr *)sctp_m_getptr(m, offset,
+		    sizeof(struct sctp_asconf_paramhdr), aparam_buf);
+		if (aph == NULL) {
+			/* can't get an asconf paramhdr */
+			sctp_asconf_ack_clear(stcb);
+			return;
+		}
+		param_type = ntohs(aph->ph.param_type);
+		param_length = ntohs(aph->ph.param_length);
+		if (param_length > ack_length) {
+			sctp_asconf_ack_clear(stcb);
+			return;
+		}
+		if (param_length < sizeof(struct sctp_paramhdr)) {
+			sctp_asconf_ack_clear(stcb);
+			return;
+		}
+
+		/* get the complete parameter... */
+		if (param_length > sizeof(aparam_buf)) {
+#ifdef SCTP_DEBUG
+			if (sctp_debug_on & SCTP_DEBUG_ASCONF1) {
+				printf("param length (%u) larger than buffer size!\n", param_length);
+			}
+#endif /* SCTP_DEBUG */
+			sctp_asconf_ack_clear(stcb);
+			return;
+		}
+		aph = (struct sctp_asconf_paramhdr *)sctp_m_getptr(m, offset, param_length, aparam_buf);
+		if (aph == NULL) {
+			sctp_asconf_ack_clear(stcb);
+			return;
+		}
+		/* correlation_id is transparent to peer, no ntohl needed */
+		id = aph->correlation_id;
+
+		switch (param_type) {
+		case SCTP_ERROR_CAUSE_IND:
+			last_error_id = id;
+			/* find the corresponding asconf param in our queue */
+			ap = sctp_asconf_find_param(stcb, id);
+			if (ap == NULL) {
+				/* hmm... can't find this in our queue! */
+				break;
+			}
+			/* process the parameter, failed flag */
+			sctp_asconf_process_param_ack(stcb, ap, 0);
+			/* process the error response */
+			sctp_asconf_process_error(stcb, aph);
+			break;
+		case SCTP_SUCCESS_REPORT:
+			/* find the corresponding asconf param in our queue */
+			ap = sctp_asconf_find_param(stcb, id);
+			if (ap == NULL) {
+				/* hmm... can't find this in our queue! */
+				break;
+			}
+			/* process the parameter, success flag */
+			sctp_asconf_process_param_ack(stcb, ap, 1);
+			break;
+		default:
+			break;
+		} /* switch */
+
+		/* update remaining ASCONF-ACK message length to process */
+		ack_length -= SCTP_SIZE32(param_length);
+		if (ack_length <= 0) {
+			/* no more data in the mbuf chain */
+			break;
+		}
+		offset += SCTP_SIZE32(param_length);
+	} /* while */
+
+	/*
+	 * if there are any "sent" params still on the queue, these are
+	 * implicitly "success", or "failed" (if we got an error back)
+	 * ... so process these appropriately
+	 *
+	 * we assume that the correlation_id's are monotonically increasing
+	 * beginning from 1 and that we don't have *that* many outstanding
+	 * at any given time
+	 */
+	if (last_error_id == 0)
+		last_error_id--;	/* set to "max" value */
+	for (aa = TAILQ_FIRST(&stcb->asoc.asconf_queue); aa != NULL;
+	    aa = aa_next) {
+		aa_next = TAILQ_NEXT(aa, next);
+		if (aa->sent == 1) {
+			/*
+			 * implicitly successful or failed
+			 * if correlation_id < last_error_id, then success
+			 * else, failure
+			 */
+			if (aa->ap.aph.correlation_id < last_error_id)
+				sctp_asconf_process_param_ack(stcb, aa,
+				    SCTP_SUCCESS_REPORT);
+			else
+				sctp_asconf_process_param_ack(stcb, aa,
+				    SCTP_ERROR_CAUSE_IND);
+		} else {
+			/*
+			 * since we always process in order (FIFO queue)
+			 * if we reach one that hasn't been sent, the
+			 * rest should not have been sent either.
+			 * so, we're done...
+			 */
+			break;
+		}
+	}
+
+	/* update the next sequence number to use */
+	asoc->asconf_seq_out++;
+	/* remove the old ASCONF on our outbound queue */
+	sctp_toss_old_asconf(stcb);
+	/* clear the sent flag to allow new ASCONFs */
+	asoc->asconf_sent = 0;
+	if (!TAILQ_EMPTY(&stcb->asoc.asconf_queue)) {
+		/* we have more params, so restart our timer */
+		sctp_timer_start(SCTP_TIMER_TYPE_ASCONF, stcb->sctp_ep,
+		    stcb, net);
+	}
+}
+
+/* is this an interface that we care about at all? */
+static uint32_t
+sctp_is_desired_interface_type(struct ifaddr *ifa)
+{
+	int result;
+
+	/* check the interface type to see if it's one we care about */
+	switch (ifa->ifa_ifp->if_type) {
+	case IFT_ETHER:
+	case IFT_ISO88023:
+	case IFT_ISO88025:
+	case IFT_STARLAN:
+	case IFT_P10:
+	case IFT_P80:
+	case IFT_HY:
+	case IFT_FDDI:
+	case IFT_PPP:
+	case IFT_XETHER:
+	case IFT_SLIP:
+	case IFT_GIF:
+		result = 1;
+		break;
+	default:
+#ifdef SCTP_DEBUG
+		if (sctp_debug_on & SCTP_DEBUG_ASCONF1) {
+			printf("ignoring interface type = %u\n",
+			       ifa->ifa_ifp->if_type);
+		}
+#endif /* SCTP_DEBUG */
+		result = 0;
+	} /* end switch */
+
+	return (result);
+}
+
+static uint32_t
+sctp_is_scopeid_in_nets(struct sctp_tcb *stcb, struct sockaddr *sa)
+{
+	struct sockaddr_in6 *sin6, *net6;
+	struct sctp_nets *net;
+
+	if (sa->sa_family != AF_INET6) {
+		/* wrong family */
+		return (0);
+	}
+
+	sin6 = (struct sockaddr_in6 *)sa;
+	if (IN6_IS_ADDR_LINKLOCAL(&sin6->sin6_addr) == 0) {
+		/* not link local address */
+		return (0);
+	}
+
+	/* hunt through our destination nets list for this scope_id */
+	TAILQ_FOREACH(net, &stcb->asoc.nets, sctp_next) {
+		if (((struct sockaddr *)(&net->ro._l_addr))->sa_family !=
+		    AF_INET6)
+			continue;
+		net6 = (struct sockaddr_in6 *)&net->ro._l_addr;
+		if (IN6_IS_ADDR_LINKLOCAL(&net6->sin6_addr) == 0)
+			continue;
+		if (sctp_is_same_scope(sin6, net6)) {
+			/* found one */
+			return (1);
+		}
+	}
+	/* didn't find one */
+	return (0);
+}
+
+/*
+ * address management functions
+ */
+static void
+sctp_addr_mgmt_assoc(struct sctp_inpcb *inp, struct sctp_tcb *stcb,
+    struct ifaddr *ifa, uint16_t type)
+{
+	int status;
+#ifdef SCTP_DEBUG
+	char buf[128];	/* for address in string format */
+#endif /* SCTP_DEBUG */
+
+	if ((inp->sctp_flags & SCTP_PCB_FLAGS_BOUNDALL) == 0 &&
+	    (inp->sctp_flags & SCTP_PCB_FLAGS_DO_ASCONF) == 0) {
+		/* subset bound, no ASCONF allowed case, so ignore */
+		return;
+	}
+
+	/*
+	 * note: we know this is not the subset bound, no ASCONF case
+	 *	eg. this is boundall or subset bound w/ASCONF allowed
+	 */
+
+	/* first, make sure it's a good address family */
+	if (ifa->ifa_addr->sa_family != AF_INET6 &&
+	    ifa->ifa_addr->sa_family != AF_INET) {
+		return;
+	}
+
+	/* make sure we're "allowed" to add this type of addr */
+	if (ifa->ifa_addr->sa_family == AF_INET6) {
+		struct in6_ifaddr *ifa6;
+
+		/* invalid if we're not a v6 endpoint */
+		if ((inp->sctp_flags & SCTP_PCB_FLAGS_BOUND_V6) == 0)
+			return;
+		/* is the v6 addr really valid ? */
+		ifa6 = (struct in6_ifaddr *)ifa;
+		if (IFA6_IS_DEPRECATED(ifa6) ||
+		    (ifa6->ia6_flags &
+		     (IN6_IFF_DETACHED | IN6_IFF_ANYCAST | IN6_IFF_NOTREADY))) {
+			/* can't use an invalid address */
+			return;
+		}
+	}
+
+	/* put this address on the "pending/do not use yet" list */
+	/*
+	 * Note: we do this primarily for the subset bind case
+	 * We don't have scoping flags at the EP level, so we must
+	 * add link local/site local addresses to the EP, then need
+	 * to "negate" them here.  Recall that this routine is only
+	 * called for the subset bound w/ASCONF allowed case.
+	 */
+
+	/*
+	 * do a scope_id check against any link local addresses
+	 * in the destination nets list to see if we should put
+	 * this local address on the pending list or not
+	 * eg. don't put on the list if we have a link local
+	 * destination with the same scope_id
+	 */
+	if (type == SCTP_ADD_IP_ADDRESS) {
+		if (sctp_is_scopeid_in_nets(stcb, ifa->ifa_addr) == 0) {
+			sctp_add_local_addr_assoc(stcb, ifa);
+#ifdef SCTP_DEBUG
+			if (sctp_debug_on & SCTP_DEBUG_ASCONF1) {
+				printf("addr_mgmt_assoc: added to pending list ");
+				sctp_print_address(ifa->ifa_addr);
+			}
+#endif /* SCTP_DEBUG */
+		}
+	}
+	/*
+	 * check address scope
+	 * if address is out of scope, don't queue anything...
+	 * note: this would leave the address on both inp and asoc lists
+	 */
+	if (ifa->ifa_addr->sa_family == AF_INET6) {
+		struct sockaddr_in6 *sin6;
+
+		sin6 = (struct sockaddr_in6 *)ifa->ifa_addr;
+#ifdef SCTP_DEBUG
+		strlcpy(buf, ip6_sprintf(&sin6->sin6_addr), sizeof(buf));
+#endif /* SCTP_DEBUG */
+		if (IN6_IS_ADDR_UNSPECIFIED(&sin6->sin6_addr)) {
+			/* we skip unspecifed addresses */
+#ifdef SCTP_DEBUG
+			if (sctp_debug_on & SCTP_DEBUG_ASCONF1) {
+				printf("addr_mgmt_assoc: unspecified IPv6 addr\n");
+			}
+#endif /* SCTP_DEBUG */
+			return;
+		}
+		if (IN6_IS_ADDR_LINKLOCAL(&sin6->sin6_addr)) {
+			if (stcb->asoc.local_scope == 0) {
+#ifdef SCTP_DEBUG
+				if (sctp_debug_on & SCTP_DEBUG_ASCONF1) {
+					printf("addr_mgmt_assoc: skipping link local IPv6 addr: %s\n", buf);
+				}
+#endif /* SCTP_DEBUG */
+				return;
+			}
+			/* is it the right link local scope? */
+			if (sctp_is_scopeid_in_nets(stcb, ifa->ifa_addr) == 0) {
+#ifdef SCTP_DEBUG
+				if (sctp_debug_on & SCTP_DEBUG_ASCONF1) {
+					printf("addr_mgmt_assoc: skipping link local IPv6 addr: %s, wrong scope_id\n", buf);
+				}
+#endif /* SCTP_DEBUG */
+				return;
+			}
+		}
+		if (stcb->asoc.site_scope == 0 &&
+		    IN6_IS_ADDR_SITELOCAL(&sin6->sin6_addr)) {
+#ifdef SCTP_DEBUG
+			if (sctp_debug_on & SCTP_DEBUG_ASCONF1) {
+				printf("addr_mgmt_assoc: skipping site local IPv6 addr: %s\n", buf);
+			}
+#endif /* SCTP_DEBUG */
+			return;
+		}
+	} else if (ifa->ifa_addr->sa_family == AF_INET) {
+		struct sockaddr_in *sin;
+		struct in6pcb *inp6;
+
+		inp6 = (struct in6pcb *)&inp->ip_inp.inp;
+		/* invalid if we are a v6 only endpoint */
+		if ((inp->sctp_flags & SCTP_PCB_FLAGS_BOUND_V6) &&
+#if defined(__OpenBSD__)
+		    (0) /* we always do dual bind */
+#elif defined (__NetBSD__)
+		    (inp6->in6p_flags & IN6P_IPV6_V6ONLY)
+#else
+		    (inp6->inp_flags & IN6P_IPV6_V6ONLY)
+#endif
+			)
+			return;
+
+		sin = (struct sockaddr_in *)ifa->ifa_addr;
+#ifdef SCTP_DEBUG
+		strlcpy(buf, inet_ntoa(sin->sin_addr), sizeof(buf));
+#endif /* SCTP_DEBUG */
+		if (sin->sin_addr.s_addr == 0) {
+			/* we skip unspecifed addresses */
+#ifdef SCTP_DEBUG
+			if (sctp_debug_on & SCTP_DEBUG_ASCONF1) {
+				printf("addr_mgmt_assoc: unspecified IPv4 addr\n");
+			}
+#endif /* SCTP_DEBUG */
+			return;
+		}
+		if (stcb->asoc.ipv4_local_scope == 0 &&
+		    IN4_ISPRIVATE_ADDRESS(&sin->sin_addr)) {
+#ifdef SCTP_DEBUG
+			if (sctp_debug_on & SCTP_DEBUG_ASCONF1) {
+				printf("addr_mgmt_assoc: skipping private IPv4 addr: %s\n", buf);
+			}
+#endif /* SCTP_DEBUG */
+			return;
+		}
+	} else {
+		/* else, not AF_INET or AF_INET6, so skip */
+#ifdef SCTP_DEBUG
+		if (sctp_debug_on & SCTP_DEBUG_ASCONF1) {
+			printf("addr_mgmt_assoc: not AF_INET or AF_INET6\n");
+		}
+#endif /* SCTP_DEBUG */
+		return;
+	}
+
+	/* queue an asconf for this address add/delete */
+	if (inp->sctp_flags & SCTP_PCB_FLAGS_DO_ASCONF) {
+		/* does the peer do asconf? */
+		if (stcb->asoc.peer_supports_asconf) {
+			/* queue an asconf for this addr */
+			status = sctp_asconf_queue_add(stcb, ifa, type);
+			/*
+			 * if queued ok, and in correct state, set the
+			 * ASCONF timer
+			 * if in non-open state, we will set this timer
+			 * when the state does go open and do all the
+			 * asconf's
+			 */
+			if (status == 0 &&
+			    SCTP_GET_STATE(&stcb->asoc) == SCTP_STATE_OPEN) {
+				sctp_timer_start(SCTP_TIMER_TYPE_ASCONF, inp,
+				    stcb, stcb->asoc.primary_destination);
+			}
+		}
+	} else {
+		/* this is the boundall, no ASCONF case */
+#if 0 /* assume kernel will delete this very shortly; add done above */
+		if (type == SCTP_DEL_IP_ADDRESS) {
+			/* if deleting, add this addr to the do not use list */
+			sctp_add_local_addr_assoc(stcb, ifa);
+		}
+#endif
+	}
+}
+
+static void
+sctp_addr_mgmt_ep(struct sctp_inpcb *inp, struct ifaddr *ifa, uint16_t type)
+{
+	struct sctp_tcb *stcb;
+
+	SCTP_INP_WLOCK(inp);
+	/* make sure we're "allowed" to add this type of addr */
+	if (ifa->ifa_addr->sa_family == AF_INET6) {
+		struct in6_ifaddr *ifa6;
+
+		/* invalid if we're not a v6 endpoint */
+		if ((inp->sctp_flags & SCTP_PCB_FLAGS_BOUND_V6) == 0) {
+			SCTP_INP_WUNLOCK(inp);
+			return;
+		}
+		/* is the v6 addr really valid ? */
+		ifa6 = (struct in6_ifaddr *)ifa;
+		if (IFA6_IS_DEPRECATED(ifa6) ||
+		    (ifa6->ia6_flags &
+		     (IN6_IFF_DETACHED | IN6_IFF_ANYCAST | IN6_IFF_NOTREADY))) {
+			/* can't use an invalid address */
+			SCTP_INP_WUNLOCK(inp);
+			return;
+		}
+	} else if (ifa->ifa_addr->sa_family == AF_INET) {
+		/* invalid if we are a v6 only endpoint */
+		struct in6pcb *inp6;
+		inp6 = (struct in6pcb *)&inp->ip_inp.inp;
+
+		if ((inp->sctp_flags & SCTP_PCB_FLAGS_BOUND_V6) &&
+#if defined(__OpenBSD__)
+		    (0) /* we always do dual bind */
+#elif defined (__NetBSD__)
+		    (inp6->in6p_flags & IN6P_IPV6_V6ONLY)
+#else
+		    (inp6->inp_flags & IN6P_IPV6_V6ONLY)
+#endif
+			) {
+			SCTP_INP_WUNLOCK(inp);
+			return;
+		}
+	} else {
+		/* invalid address family */
+		SCTP_INP_WUNLOCK(inp);
+		return;
+	}
+	/* is this endpoint subset bound ? */
+	if ((inp->sctp_flags & SCTP_PCB_FLAGS_BOUNDALL) == 0) {
+		/* subset bound endpoint */
+		if ((inp->sctp_flags & SCTP_PCB_FLAGS_DO_ASCONF) == 0) {
+			/*
+			 * subset bound, but ASCONFs not allowed...
+			 * if adding, nothing to do, since not allowed
+			 * if deleting, remove address from endpoint
+			 *	peer will have to "timeout" this addr
+			 */
+			if (type == SCTP_DEL_IP_ADDRESS) {
+				sctp_del_local_addr_ep(inp, ifa);
+			}
+			/* no asconfs to queue for this inp... */
+			SCTP_INP_WUNLOCK(inp);
+			return;
+		} else {
+			/*
+			 * subset bound, ASCONFs allowed...
+			 * if adding, add address to endpoint list
+			 * if deleting, remove address from endpoint
+			 */
+			if (type == SCTP_ADD_IP_ADDRESS) {
+				sctp_add_local_addr_ep(inp, ifa);
+			} else {
+				sctp_del_local_addr_ep(inp, ifa);
+			}
+			/* drop through and notify all asocs */
+		}
+	}
+
+	crit_enter();
+	/* process for all associations for this endpoint */
+	LIST_FOREACH(stcb, &inp->sctp_asoc_list, sctp_tcblist) {
+		SCTP_TCB_LOCK(stcb);
+		sctp_addr_mgmt_assoc(inp, stcb, ifa, type);
+		SCTP_TCB_UNLOCK(stcb);
+	} /* for each stcb */
+	crit_exit();
+	SCTP_INP_WUNLOCK(inp);
+}
+
+/*
+ * restrict the use of this address
+ */
+static void
+sctp_addr_mgmt_restrict_ep(struct sctp_inpcb *inp, struct ifaddr *ifa)
+{
+	struct sctp_tcb *stcb;
+
+	/* is this endpoint bound to all? */
+	if ((inp->sctp_flags & SCTP_PCB_FLAGS_BOUNDALL) == 0) {
+		/*
+		 * Nothing to do for subset bound case.
+		 * Allow sctp_bindx() to manage the address lists
+		 */
+		return;
+	}
+
+	crit_enter();
+	SCTP_INP_RLOCK(inp);
+	/* process for all associations for this endpoint */
+	LIST_FOREACH(stcb, &inp->sctp_asoc_list, sctp_tcblist) {
+		/* put this address on the "pending/do not use yet" list */
+		SCTP_TCB_LOCK(stcb);
+		sctp_add_local_addr_assoc(stcb, ifa);
+		SCTP_TCB_UNLOCK(stcb);
+#ifdef SCTP_DEBUG
+		if (sctp_debug_on & SCTP_DEBUG_ASCONF1) {
+			printf("restrict_ep: added addr to unusable list\n");
+		}
+#endif /* SCTP_DEBUG */
+	} /* for each stcb */
+	crit_exit();
+	SCTP_INP_RUNLOCK(inp);
+}
+
+/*
+ * this is only called for kernel initiated address changes
+ * eg. it will check the PCB_FLAGS_AUTO_ASCONF flag
+ */
+static void
+sctp_addr_mgmt(struct ifaddr *ifa, uint16_t type) {
+	struct sockaddr *sa;
+	struct sctp_inpcb *inp;
+
+	/* make sure we care about this interface... */
+	if (!sctp_is_desired_interface_type(ifa)) {
+#ifdef SCTP_DEBUG
+		if (sctp_debug_on & SCTP_DEBUG_ASCONF1) {
+			printf("sctp_addr_mgmt: ignoring this interface\n");
+		}
+#endif /* SCTP_DEBUG */
+		return;
+	}
+
+	sa = ifa->ifa_addr;
+	if (sa->sa_family != AF_INET && sa->sa_family != AF_INET6)
+		return;
+
+#ifdef SCTP_DEBUG
+	if (sctp_debug_on & SCTP_DEBUG_ASCONF1) {
+		if (type == SCTP_ADD_IP_ADDRESS)
+			printf("sctp_addr_mgmt: kernel adds ");
+		else
+			printf("sctp_addr_mgmt: kernel deletes ");
+		sctp_print_address(sa);
+	}
+#endif /* SCTP_DEBUG */
+
+	/* go through all our PCB's */
+	LIST_FOREACH(inp, &sctppcbinfo.listhead, sctp_list) {
+		if (inp->sctp_flags & SCTP_PCB_FLAGS_AUTO_ASCONF) {
+			sctp_addr_mgmt_ep(inp, ifa, type);
+		} else {
+			/* this address is going away anyways... */
+			if (type == SCTP_DEL_IP_ADDRESS)
+				return;
+			/* (temporarily) restrict this address */
+			sctp_addr_mgmt_restrict_ep(inp, ifa);
+		}
+		/* else, not allowing automatic asconf's, so ignore */
+	} /* for each inp */
+}
+
+/*
+ * add/delete IP address requests from kernel (via routing change)
+ * assumed that the address is non-broadcast, non-multicast
+ * all addresses are passed from any type of interface-- need to filter
+ * duplicate addresses may get requested
+ */
+
+void
+sctp_add_ip_address(struct ifaddr *ifa)
+{
+	sctp_addr_mgmt(ifa, SCTP_ADD_IP_ADDRESS);
+}
+
+void
+sctp_delete_ip_address(struct ifaddr *ifa)
+{
+	struct sctp_inpcb *inp;
+
+	/* process the delete */
+	sctp_addr_mgmt(ifa, SCTP_DEL_IP_ADDRESS);
+
+	/*
+	 * need to remove this ifaddr from any cached routes
+	 * and also any from any assoc "restricted/pending" lists
+	 */
+	/* make sure we care about this interface... */
+	if (!sctp_is_desired_interface_type(ifa)) {
+#ifdef SCTP_DEBUG
+		if (sctp_debug_on & SCTP_DEBUG_ASCONF1) {
+			printf("sctp_delete_ip_address: ignoring this interface\n");
+		}
+#endif /* SCTP_DEBUG */
+		return;
+	}
+
+	/* go through all our PCB's */
+	SCTP_INP_INFO_RLOCK();
+	LIST_FOREACH(inp, &sctppcbinfo.listhead, sctp_list) {
+		struct sctp_tcb *stcb;
+		struct sctp_laddr *laddr, *laddr_next;
+
+		/* process for all associations for this endpoint */
+		SCTP_INP_RLOCK(inp);
+		LIST_FOREACH(stcb, &inp->sctp_asoc_list, sctp_tcblist) {
+			struct sctp_nets *net;
+
+			/* process through the nets list */
+			TAILQ_FOREACH(net, &stcb->asoc.nets, sctp_next) {
+				struct rtentry *rt;
+				/* delete this address if cached */
+				rt = net->ro.ro_rt;
+				if (rt != NULL && rt->rt_ifa == ifa) {
+/*					RTFREE(rt);*/
+					net->ro.ro_rt = NULL;
+				}
+			} /* for each net */
+			/* process through the asoc "pending" list */
+			laddr = LIST_FIRST(&stcb->asoc.sctp_local_addr_list);
+			while (laddr != NULL) {
+				laddr_next = LIST_NEXT(laddr, sctp_nxt_addr);
+				/* remove if in use */
+				if (laddr->ifa == ifa) {
+					sctp_remove_laddr(laddr);
+				}
+				laddr = laddr_next;
+			} /* while */
+		} /* for each stcb */
+		/* process through the inp bound addr list */
+		laddr = LIST_FIRST(&inp->sctp_addr_list);
+		while (laddr != NULL) {
+			laddr_next = LIST_NEXT(laddr, sctp_nxt_addr);
+			/* remove if in use */
+			if (laddr->ifa == ifa) {
+				sctp_remove_laddr(laddr);
+			}
+			laddr = laddr_next;
+		} /* while */
+		SCTP_INP_RUNLOCK(inp);
+	} /* for each inp */
+	SCTP_INP_INFO_RUNLOCK();
+}
+
+/*
+ * sa is the sockaddr to ask the peer to set primary to
+ * returns: 0 = completed, -1 = error
+ */
+int32_t
+sctp_set_primary_ip_address_sa(struct sctp_tcb *stcb, struct sockaddr *sa)
+{
+	/* NOTE: we currently don't check the validity of the address! */
+
+	/* queue an ASCONF:SET_PRIM_ADDR to be sent */
+	if (!sctp_asconf_queue_add_sa(stcb, sa, SCTP_SET_PRIM_ADDR)) {
+		/* set primary queuing succeeded */
+		if (SCTP_GET_STATE(&stcb->asoc) == SCTP_STATE_OPEN) {
+			sctp_timer_start(SCTP_TIMER_TYPE_ASCONF,
+			    stcb->sctp_ep, stcb,
+			    stcb->asoc.primary_destination);
+		}
+#ifdef SCTP_DEBUG
+		if (sctp_debug_on & SCTP_DEBUG_ASCONF1) {
+			printf("set_primary_ip_address_sa: queued on tcb=%p, ",
+			    stcb);
+			sctp_print_address(sa);
+		}
+#endif /* SCTP_DEBUG */
+	} else {
+#ifdef SCTP_DEBUG
+		if (sctp_debug_on & SCTP_DEBUG_ASCONF1) {
+			printf("set_primary_ip_address_sa: failed to add to queue on tcb=%p, ",
+			    stcb);
+			sctp_print_address(sa);
+		}
+#endif /* SCTP_DEBUG */
+		return (-1);
+	}
+	return (0);
+}
+
+void
+sctp_set_primary_ip_address(struct ifaddr *ifa)
+{
+	struct sctp_inpcb *inp;
+
+	/* make sure we care about this interface... */
+	if (!sctp_is_desired_interface_type(ifa)) {
+#ifdef SCTP_DEBUG
+		if (sctp_debug_on & SCTP_DEBUG_ASCONF1) {
+			printf("set_primary_ip_address: ignoring this interface\n");
+		}
+#endif /* SCTP_DEBUG */
+		return;
+	}
+
+	/* go through all our PCB's */
+	LIST_FOREACH(inp, &sctppcbinfo.listhead, sctp_list) {
+		struct sctp_tcb *stcb;
+
+		/* process for all associations for this endpoint */
+		LIST_FOREACH(stcb, &inp->sctp_asoc_list, sctp_tcblist) {
+			/* queue an ASCONF:SET_PRIM_ADDR to be sent */
+			if (!sctp_asconf_queue_add(stcb, ifa,
+			    SCTP_SET_PRIM_ADDR)) {
+				/* set primary queuing succeeded */
+				if (SCTP_GET_STATE(&stcb->asoc) ==
+				    SCTP_STATE_OPEN) {
+					sctp_timer_start(SCTP_TIMER_TYPE_ASCONF,
+					    stcb->sctp_ep, stcb,
+					    stcb->asoc.primary_destination);
+				}
+#ifdef SCTP_DEBUG
+				if (sctp_debug_on & SCTP_DEBUG_ASCONF1) {
+					printf("set_primary_ip_address: queued on stcb=%p, ",
+					    stcb);
+					sctp_print_address(ifa->ifa_addr);
+				}
+#endif /* SCTP_DEBUG */
+			} else {
+#ifdef SCTP_DEBUG
+				if (sctp_debug_on & SCTP_DEBUG_ASCONF1) {
+					printf("set_primary_ip_address: failed to add to queue, ");
+					sctp_print_address(ifa->ifa_addr);
+				}
+#endif /* SCTP_DEBUG */
+			}
+		} /* for each stcb */
+	} /* for each inp */
+}
+
+static struct sockaddr *
+sctp_find_valid_localaddr(struct sctp_tcb *stcb)
+{
+	struct ifnet *ifn;
+	struct ifaddr *ifa;
+
+	TAILQ_FOREACH(ifn, &ifnet, if_list) {
+		if (stcb->asoc.loopback_scope == 0 && ifn->if_type == IFT_LOOP) {
+			/* Skip if loopback_scope not set */
+			continue;
+		}
+		TAILQ_FOREACH(ifa, &ifn->if_addrlist, ifa_list) {
+			if (ifa->ifa_addr->sa_family == AF_INET &&
+			    stcb->asoc.ipv4_addr_legal) {
+				struct sockaddr_in *sin;
+
+				sin = (struct sockaddr_in *)ifa->ifa_addr;
+				if (sin->sin_addr.s_addr == 0) {
+					/* skip unspecifed addresses */
+					continue;
+				}
+				if (stcb->asoc.ipv4_local_scope == 0 &&
+				    IN4_ISPRIVATE_ADDRESS(&sin->sin_addr))
+					continue;
+
+				if (sctp_is_addr_restricted(stcb,
+				    ifa->ifa_addr))
+					continue;
+				/* found a valid local v4 address to use */
+				return (ifa->ifa_addr);
+			} else if (ifa->ifa_addr->sa_family == AF_INET6 &&
+			    stcb->asoc.ipv6_addr_legal) {
+				struct sockaddr_in6 *sin6;
+				struct in6_ifaddr *ifa6;
+
+				ifa6 = (struct in6_ifaddr *)ifa;
+				if (IFA6_IS_DEPRECATED(ifa6) ||
+				    (ifa6->ia6_flags & (IN6_IFF_DETACHED |
+				     IN6_IFF_ANYCAST | IN6_IFF_NOTREADY)))
+					continue;
+
+				sin6 = (struct sockaddr_in6 *)ifa->ifa_addr;
+				if (IN6_IS_ADDR_UNSPECIFIED(&sin6->sin6_addr)) {
+					/* we skip unspecifed addresses */
+					continue;
+				}
+				if (stcb->asoc.local_scope == 0 &&
+				    IN6_IS_ADDR_LINKLOCAL(&sin6->sin6_addr))
+					continue;
+				if (stcb->asoc.site_scope == 0 &&
+				    IN6_IS_ADDR_SITELOCAL(&sin6->sin6_addr))
+					continue;
+
+				/* found a valid local v6 address to use */
+				return (ifa->ifa_addr);
+			}
+		}
+	}
+	/* no valid addresses found */
+	return (NULL);
+}
+
+static struct sockaddr *
+sctp_find_valid_localaddr_ep(struct sctp_tcb *stcb)
+{
+	struct sctp_laddr *laddr;
+
+	LIST_FOREACH(laddr, &stcb->sctp_ep->sctp_addr_list, sctp_nxt_addr) {
+		if (laddr->ifa == NULL) {
+#ifdef SCTP_DEBUG
+			if (sctp_debug_on & SCTP_DEBUG_ASCONF1) {
+				printf("find_valid_localaddr_ep: laddr error\n");
+			}
+#endif /* SCTP_DEBUG */
+			continue;
+		}
+		if (laddr->ifa->ifa_addr == NULL) {
+#ifdef SCTP_DEBUG
+			if (sctp_debug_on & SCTP_DEBUG_ASCONF1) {
+				printf("find_valid_localaddr_ep: laddr->ifa error\n");
+			}
+#endif /* SCTP_DEBUG */
+			continue;
+		}
+		/* is the address restricted ? */
+		if (sctp_is_addr_restricted(stcb, laddr->ifa->ifa_addr))
+			continue;
+
+		/* found a valid local address to use */
+		return (laddr->ifa->ifa_addr);
+	}
+	/* no valid addresses found */
+	return (NULL);
+}
+
+/*
+ * builds an ASCONF chunk from queued ASCONF params
+ * returns NULL on error (no mbuf, no ASCONF params queued, etc)
+ */
+struct mbuf *
+sctp_compose_asconf(struct sctp_tcb *stcb)
+{
+	struct mbuf *m_asconf, *m_asconf_chk;
+	struct sctp_asconf_addr *aa;
+	struct sctp_asconf_chunk *acp;
+	struct sctp_asconf_paramhdr *aph;
+	struct sctp_asconf_addr_param *aap;
+	uint32_t p_length;
+	uint32_t correlation_id = 1;		/* 0 is reserved... */
+	caddr_t ptr, lookup_ptr;
+	uint8_t lookup_used = 0;
+
+	/* are there any asconf params to send? */
+	if (TAILQ_EMPTY(&stcb->asoc.asconf_queue)) {
+		return (NULL);
+	}
+
+	/*
+	 * get a chunk header mbuf and a cluster for the asconf params
+	 * since it's simpler to fill in the asconf chunk header lookup
+	 * address on the fly
+	 */
+	m_asconf_chk = NULL;
+	MGETHDR(m_asconf_chk, MB_DONTWAIT, MT_DATA);
+	if (m_asconf_chk == NULL) {
+		/* no mbuf's */
+#ifdef SCTP_DEBUG
+		if (sctp_debug_on & SCTP_DEBUG_ASCONF1)
+			printf("compose_asconf: couldn't get chunk mbuf!\n");
+#endif /* SCTP_DEBUG */
+		return (NULL);
+	}
+	m_asconf = NULL;
+	MGETHDR(m_asconf, MB_DONTWAIT, MT_HEADER);
+	if (m_asconf == NULL) {
+		/* no mbuf's */
+#ifdef SCTP_DEBUG
+		if (sctp_debug_on & SCTP_DEBUG_ASCONF1)
+			printf("compose_asconf: couldn't get mbuf!\n");
+#endif /* SCTP_DEBUG */
+		sctp_m_freem(m_asconf_chk);
+		return (NULL);
+	}
+	MCLGET(m_asconf, MB_DONTWAIT);
+	if ((m_asconf->m_flags & M_EXT) != M_EXT) {
+		/* failed to get cluster buffer */
+#ifdef SCTP_DEBUG
+		if (sctp_debug_on & SCTP_DEBUG_ASCONF1)
+			printf("compose_asconf: couldn't get cluster!\n");
+#endif /* SCTP_DEBUG */
+		sctp_m_freem(m_asconf_chk);
+		sctp_m_freem(m_asconf);
+		return (NULL);
+	}
+
+	m_asconf_chk->m_len = sizeof(struct sctp_asconf_chunk);
+	m_asconf->m_len = 0;
+	acp = mtod(m_asconf_chk, struct sctp_asconf_chunk *);
+	bzero(acp, sizeof(struct sctp_asconf_chunk));
+	/* save pointers to lookup address and asconf params */
+	lookup_ptr = (caddr_t)(acp + 1);	/* after the header */
+	ptr = mtod(m_asconf, caddr_t);		/* beginning of cluster */
+
+	/* fill in chunk header info */
+	acp->ch.chunk_type = SCTP_ASCONF;
+	acp->ch.chunk_flags = 0;
+	acp->serial_number = htonl(stcb->asoc.asconf_seq_out);
+
+	/* add parameters... up to smallest MTU allowed */
+	TAILQ_FOREACH(aa, &stcb->asoc.asconf_queue, next) {
+		/* get the parameter length */
+		p_length = SCTP_SIZE32(aa->ap.aph.ph.param_length);
+		/* will it fit in current chunk? */
+		if (m_asconf->m_len + p_length > stcb->asoc.smallest_mtu) {
+			/* won't fit, so we're done with this chunk */
+			break;
+		}
+		/* assign (and store) a correlation id */
+		aa->ap.aph.correlation_id = correlation_id++;
+
+		/*
+		 * fill in address if we're doing a delete
+		 * this is a simple way for us to fill in the correlation
+		 * address, which should only be used by the peer if we're
+		 * deleting our source address and adding a new address
+		 * (e.g. renumbering case)
+		 */
+		if (lookup_used == 0 &&
+		    aa->ap.aph.ph.param_type == SCTP_DEL_IP_ADDRESS) {
+			struct sctp_ipv6addr_param *lookup;
+			uint16_t p_size, addr_size;
+
+			lookup = (struct sctp_ipv6addr_param *)lookup_ptr;
+			lookup->ph.param_type =
+			    htons(aa->ap.addrp.ph.param_type);
+			if (aa->ap.addrp.ph.param_type == SCTP_IPV6_ADDRESS) {
+				/* copy IPv6 address */
+				p_size = sizeof(struct sctp_ipv6addr_param);
+				addr_size = sizeof(struct in6_addr);
+			} else {
+				/* copy IPv4 address */
+				p_size = sizeof(struct sctp_ipv4addr_param);
+				addr_size = sizeof(struct in_addr);
+			}
+			lookup->ph.param_length = htons(SCTP_SIZE32(p_size));
+			memcpy(lookup->addr, &aa->ap.addrp.addr, addr_size);
+			m_asconf_chk->m_len += SCTP_SIZE32(p_size);
+			lookup_used = 1;
+		}
+
+		/* copy into current space */
+		memcpy(ptr, &aa->ap, p_length);
+
+		/* network elements and update lengths */
+		aph = (struct sctp_asconf_paramhdr *) ptr;
+		aap = (struct sctp_asconf_addr_param *) ptr;
+		/* correlation_id is transparent to peer, no htonl needed */
+		aph->ph.param_type = htons(aph->ph.param_type);
+		aph->ph.param_length = htons(aph->ph.param_length);
+		aap->addrp.ph.param_type = htons(aap->addrp.ph.param_type);
+		aap->addrp.ph.param_length = htons(aap->addrp.ph.param_length);
+
+		m_asconf->m_len += SCTP_SIZE32(p_length);
+		ptr += SCTP_SIZE32(p_length);
+
+		/*
+		 * these params are removed off the pending list upon
+		 * getting an ASCONF-ACK back from the peer, just set flag
+		 */
+		aa->sent = 1;
+	}
+	/* check to see if the lookup addr has been populated yet */
+	if (lookup_used == 0) {
+		/* NOTE: if the address param is optional, can skip this... */
+		/* add any valid (existing) address... */
+		struct sctp_ipv6addr_param *lookup;
+		uint16_t p_size, addr_size;
+		struct sockaddr *found_addr;
+		caddr_t addr_ptr;
+
+		if (stcb->sctp_ep->sctp_flags & SCTP_PCB_FLAGS_BOUNDALL)
+			found_addr = sctp_find_valid_localaddr(stcb);
+		else
+			found_addr = sctp_find_valid_localaddr_ep(stcb);
+
+		lookup = (struct sctp_ipv6addr_param *)lookup_ptr;
+		if (found_addr != NULL) {
+			if (found_addr->sa_family == AF_INET6) {
+				/* copy IPv6 address */
+				lookup->ph.param_type =
+				    htons(SCTP_IPV6_ADDRESS);
+				p_size = sizeof(struct sctp_ipv6addr_param);
+				addr_size = sizeof(struct in6_addr);
+				addr_ptr = (caddr_t)&((struct sockaddr_in6 *)
+				    found_addr)->sin6_addr;
+			} else {
+				/* copy IPv4 address */
+				lookup->ph.param_type =
+				    htons(SCTP_IPV4_ADDRESS);
+				p_size = sizeof(struct sctp_ipv4addr_param);
+				addr_size = sizeof(struct in_addr);
+				addr_ptr = (caddr_t)&((struct sockaddr_in *)
+				    found_addr)->sin_addr;
+			}
+			lookup->ph.param_length = htons(SCTP_SIZE32(p_size));
+			memcpy(lookup->addr, addr_ptr, addr_size);
+			m_asconf_chk->m_len += SCTP_SIZE32(p_size);
+			lookup_used = 1;
+		} else {
+			/* uh oh... don't have any address?? */
+#ifdef SCTP_DEBUG
+			if (sctp_debug_on & SCTP_DEBUG_ASCONF1)
+				printf("compose_asconf: no lookup addr!\n");
+#endif /* SCTP_DEBUG */
+			/* for now, we send a IPv4 address of 0.0.0.0 */
+			lookup->ph.param_type = htons(SCTP_IPV4_ADDRESS);
+			lookup->ph.param_length = htons(SCTP_SIZE32(sizeof(struct sctp_ipv4addr_param)));
+			bzero(lookup->addr, sizeof(struct in_addr));
+			m_asconf_chk->m_len += SCTP_SIZE32(sizeof(struct sctp_ipv4addr_param));
+			lookup_used = 1;
+		}
+	}
+
+	/* chain it all together */
+	m_asconf_chk->m_next = m_asconf;
+	m_asconf_chk->m_pkthdr.len = m_asconf_chk->m_len + m_asconf->m_len;
+	acp->ch.chunk_length = ntohs(m_asconf_chk->m_pkthdr.len);
+
+	/* update "sent" flag */
+	stcb->asoc.asconf_sent++;
+
+	return (m_asconf_chk);
+}
+
+/*
+ * section to handle address changes before an association is up
+ * eg. changes during INIT/INIT-ACK/COOKIE-ECHO handshake
+ */
+
+/*
+ * processes the (local) addresses in the INIT-ACK chunk
+ */
+static void
+sctp_process_initack_addresses(struct sctp_tcb *stcb, struct mbuf *m,
+    unsigned int offset, unsigned int length)
+{
+	struct sctp_paramhdr tmp_param, *ph;
+	uint16_t plen, ptype;
+	struct sctp_ipv6addr_param addr_store;
+	struct sockaddr_in6 sin6;
+	struct sockaddr_in sin;
+	struct sockaddr *sa;
+	struct ifaddr *ifa;
+
+#ifdef SCTP_DEBUG
+	if (sctp_debug_on & SCTP_DEBUG_ASCONF2) {
+		printf("processing init-ack addresses\n");
+	}
+#endif /* SCTP_DEBUG */
+
+	/* convert to upper bound */
+	length += offset;
+
+	if ((offset + sizeof(struct sctp_paramhdr)) > length) {
+#ifdef SCTP_DEBUG
+		if (sctp_debug_on & SCTP_DEBUG_ASCONF1) {
+			printf("process_initack_addrs: invalid offset?\n");
+		}
+#endif /* SCTP_DEBUG */
+		return;
+	}
+
+	/* init the addresses */
+	bzero(&sin6, sizeof(sin6));
+	sin6.sin6_family = AF_INET6;
+	sin6.sin6_len = sizeof(sin6);
+	sin6.sin6_port = stcb->rport;
+
+	bzero(&sin, sizeof(sin));
+	sin.sin_len = sizeof(sin);
+	sin.sin_family = AF_INET;
+	sin.sin_port = stcb->rport;
+
+	/* go through the addresses in the init-ack */
+	ph = (struct sctp_paramhdr *)sctp_m_getptr(m, offset,
+	    sizeof(struct sctp_paramhdr), (uint8_t *)&tmp_param);
+	while (ph != NULL) {
+		ptype = ntohs(ph->param_type);
+		plen = ntohs(ph->param_length);
+		if (ptype == SCTP_IPV6_ADDRESS) {
+			struct sctp_ipv6addr_param *a6p;
+			/* get the entire IPv6 address param */
+#ifdef SCTP_DEBUG
+			if (sctp_debug_on & SCTP_DEBUG_ASCONF2) {
+				printf("process_initack_addrs: checking IPv6 param\n");
+			}
+#endif /* SCTP_DEBUG */
+			a6p = (struct sctp_ipv6addr_param *)
+				sctp_m_getptr(m, offset,
+				    sizeof(struct sctp_ipv6addr_param),
+				    (uint8_t *)&addr_store);
+			if (plen != sizeof(struct sctp_ipv6addr_param) ||
+			    a6p == NULL) {
+#ifdef SCTP_DEBUG
+				if (sctp_debug_on & SCTP_DEBUG_ASCONF1) {
+					printf("process_initack_addrs: invalid IPv6 param length\n");
+				}
+#endif /* SCTP_DEBUG */
+				return;
+			}
+			memcpy(&sin6.sin6_addr, a6p->addr,
+			    sizeof(struct in6_addr));
+			sa = (struct sockaddr *)&sin6;
+		} else if (ptype == SCTP_IPV4_ADDRESS) {
+			struct sctp_ipv4addr_param *a4p;
+			/* get the entire IPv4 address param */
+#ifdef SCTP_DEBUG
+			if (sctp_debug_on & SCTP_DEBUG_ASCONF2) {
+				printf("process_initack_addrs: checking IPv4 param\n");
+			}
+#endif /* SCTP_DEBUG */
+			a4p = (struct sctp_ipv4addr_param *)sctp_m_getptr(m, offset, sizeof(struct sctp_ipv4addr_param), (uint8_t *)&addr_store);
+			if (plen != sizeof(struct sctp_ipv4addr_param) ||
+			    a4p == NULL) {
+#ifdef SCTP_DEBUG
+				if (sctp_debug_on & SCTP_DEBUG_ASCONF1) {
+					printf("process_initack_addrs: invalid IPv4 param length\n");
+				}
+#endif /* SCTP_DEBUG */
+				return;
+			}
+			sin.sin_addr.s_addr = a4p->addr;
+			sa = (struct sockaddr *)&sin;
+		} else {
+#ifdef SCTP_DEBUG
+			if (sctp_debug_on & SCTP_DEBUG_ASCONF2) {
+				printf("process_initack_addrs: skipping param type=%xh\n", ptype);
+			}
+#endif /* SCTP_DEBUG */
+			goto next_addr;
+		}
+
+		/* see if this address really (still) exists */
+		ifa = sctp_find_ifa_by_addr(sa);
+		if (ifa == NULL) {
+			/* address doesn't exist anymore */
+			int status;
+			/* are ASCONFs allowed ? */
+			if ((stcb->sctp_ep->sctp_flags & SCTP_PCB_FLAGS_DO_ASCONF) &&
+			    stcb->asoc.peer_supports_asconf) {
+				/* queue an ASCONF DEL_IP_ADDRESS */
+				status = sctp_asconf_queue_add_sa(stcb, sa,
+				    SCTP_DEL_IP_ADDRESS);
+				/*
+				 * if queued ok, and in correct state,
+				 * set the ASCONF timer
+				 */
+				if (status == 0 &&
+				    SCTP_GET_STATE(&stcb->asoc) ==
+				    SCTP_STATE_OPEN) {
+					sctp_timer_start(SCTP_TIMER_TYPE_ASCONF,
+					    stcb->sctp_ep, stcb,
+					    stcb->asoc.primary_destination);
+				}
+			}
+		} else {
+			/* address still exists */
+			/*
+			 * if subset bound, ep addr's managed by default
+			 * if not doing ASCONF, add the address to the assoc
+			 */
+			if ((stcb->sctp_ep->sctp_flags &
+			     SCTP_PCB_FLAGS_BOUNDALL) == 0 &&
+			    (stcb->sctp_ep->sctp_flags &
+			     SCTP_PCB_FLAGS_DO_ASCONF) == 0) {
+#ifdef SCTP_DEBUG
+				if (sctp_debug_on & SCTP_DEBUG_ASCONF2) {
+					printf("process_initack_addrs: adding local addr to asoc\n");
+				}
+#endif /* SCTP_DEBUG */
+				sctp_add_local_addr_assoc(stcb, ifa);
+			}
+		}
+
+	next_addr:
+		/* get next parameter */
+		offset += SCTP_SIZE32(plen);
+		if ((offset + sizeof(struct sctp_paramhdr)) > length)
+			return;
+		ph = (struct sctp_paramhdr *)sctp_m_getptr(m, offset,
+		    sizeof(struct sctp_paramhdr), (uint8_t *)&tmp_param);
+	} /* while */
+}
+
+/* FIX ME: need to verify return result for v6 address type if v6 disabled */
+/*
+ * checks to see if a specific address is in the initack address list
+ * returns 1 if found, 0 if not
+ */
+static uint32_t
+sctp_addr_in_initack(struct sctp_tcb *stcb, struct mbuf *m, unsigned int offset,
+    unsigned int length, struct sockaddr *sa)
+{
+	struct sctp_paramhdr tmp_param, *ph;
+	uint16_t plen, ptype;
+	struct sctp_ipv6addr_param addr_store;
+	struct sockaddr_in *sin;
+	struct sctp_ipv4addr_param *a4p;
+#ifdef INET6
+	struct sockaddr_in6 *sin6, sin6_tmp;
+	struct sctp_ipv6addr_param *a6p;
+#endif /* INET6 */
+
+	if (
+#ifdef INET6
+		(sa->sa_family != AF_INET6) &&
+#endif /* INET6 */
+		(sa->sa_family != AF_INET))
+		return (0);
+
+#ifdef SCTP_DEBUG
+	if (sctp_debug_on & SCTP_DEBUG_ASCONF2) {
+		printf("find_initack_addr: starting search for ");
+		sctp_print_address(sa);
+	}
+#endif /* SCTP_DEBUG */
+	/* convert to upper bound */
+	length += offset;
+
+	if ((offset + sizeof(struct sctp_paramhdr)) > length) {
+#ifdef SCTP_DEBUG
+		if (sctp_debug_on & SCTP_DEBUG_ASCONF1) {
+			printf("find_initack_addr: invalid offset?\n");
+		}
+#endif /* SCTP_DEBUG */
+		return (0);
+	}
+
+	/* go through the addresses in the init-ack */
+	ph = (struct sctp_paramhdr *)sctp_m_getptr(m, offset,
+	    sizeof(struct sctp_paramhdr), (uint8_t *)&tmp_param);
+	while (ph != NULL) {
+		ptype = ntohs(ph->param_type);
+		plen = ntohs(ph->param_length);
+#ifdef INET6
+		if (ptype == SCTP_IPV6_ADDRESS && sa->sa_family == AF_INET6) {
+			/* get the entire IPv6 address param */
+#ifdef SCTP_DEBUG
+			if (sctp_debug_on & SCTP_DEBUG_ASCONF2) {
+				printf("addr_in_initack: checking IPv6 param\n");
+			}
+#endif /* SCTP_DEBUG */
+			a6p = (struct sctp_ipv6addr_param *)
+				sctp_m_getptr(m, offset,
+				    sizeof(struct sctp_ipv6addr_param),
+				    (uint8_t *)&addr_store);
+			if (plen != sizeof(struct sctp_ipv6addr_param) ||
+			    ph == NULL) {
+#ifdef SCTP_DEBUG
+				if (sctp_debug_on & SCTP_DEBUG_ASCONF2) {
+					printf("addr_in_initack: invalid IPv6 param length\n");
+				}
+#endif /* SCTP_DEBUG */
+				return (0);
+			}
+			sin6 = (struct sockaddr_in6 *)sa;
+			if (IN6_IS_SCOPE_LINKLOCAL(&sin6->sin6_addr)) {
+				/* create a copy and clear scope */
+				memcpy(&sin6_tmp, sin6,
+				    sizeof(struct sockaddr_in6));
+				sin6 = &sin6_tmp;
+				in6_clearscope(&sin6->sin6_addr);
+			}
+			if (memcmp(&sin6->sin6_addr, a6p->addr,
+			    sizeof(struct in6_addr)) == 0) {
+				/* found it */
+#ifdef SCTP_DEBUG
+				if (sctp_debug_on & SCTP_DEBUG_ASCONF2) {
+					printf("addr_in_initack: found IPv6 addr\n");
+				}
+#endif /* SCTP_DEBUG */
+				return (1);
+			}
+		} else
+#endif /* INET6 */
+
+		if (ptype == SCTP_IPV4_ADDRESS &&
+		    sa->sa_family == AF_INET) {
+			/* get the entire IPv4 address param */
+#ifdef SCTP_DEBUG
+			if (sctp_debug_on & SCTP_DEBUG_ASCONF2) {
+				printf("addr_in_initack: checking IPv4 param\n");
+			}
+#endif /* SCTP_DEBUG */
+			a4p = (struct sctp_ipv4addr_param *)sctp_m_getptr(m,
+			    offset, sizeof(struct sctp_ipv4addr_param),
+			    (uint8_t *)&addr_store);
+			if (plen != sizeof(struct sctp_ipv4addr_param) ||
+			    ph == NULL) {
+#ifdef SCTP_DEBUG
+				if (sctp_debug_on & SCTP_DEBUG_ASCONF2) {
+					printf("addr_in_initack: invalid IPv4 param length\n");
+				}
+#endif /* SCTP_DEBUG */
+				return (0);
+			}
+			sin = (struct sockaddr_in *)sa;
+			if (sin->sin_addr.s_addr == a4p->addr) {
+				/* found it */
+#ifdef SCTP_DEBUG
+				if (sctp_debug_on & SCTP_DEBUG_ASCONF2) {
+					printf("addr_in_initack: found IPv4 addr\n");
+				}
+#endif /* SCTP_DEBUG */
+				return (1);
+			}
+		} else {
+#ifdef SCTP_DEBUG
+			if (sctp_debug_on & SCTP_DEBUG_ASCONF2) {
+				printf("addr_in_initack: skipping param type=%xh\n", ptype);
+			}
+#endif /* SCTP_DEBUG */
+		}
+		/* get next parameter */
+		offset += SCTP_SIZE32(plen);
+		if (offset + sizeof(struct sctp_paramhdr) > length)
+			return (0);
+		ph = (struct sctp_paramhdr *)
+			sctp_m_getptr(m, offset, sizeof(struct sctp_paramhdr),
+			    (uint8_t *)&tmp_param);
+	} /* while */
+	/* not found! */
+#ifdef SCTP_DEBUG
+	if (sctp_debug_on & SCTP_DEBUG_ASCONF1) {
+		printf("addr_in_initack: not found!\n");
+	}
+#endif /* SCTP_DEBUG */
+	return (0);
+}
+
+/*
+ * makes sure that the current endpoint local addr list is consistent
+ * with the new association (eg. subset bound, asconf allowed)
+ * adds addresses as necessary
+ */
+static void
+sctp_check_address_list_ep(struct sctp_tcb *stcb, struct mbuf *m, int offset,
+    int length, struct sockaddr *init_addr)
+{
+	struct sctp_laddr *laddr;
+
+	/* go through the endpoint list */
+	LIST_FOREACH(laddr, &stcb->sctp_ep->sctp_addr_list, sctp_nxt_addr) {
+		/* be paranoid and validate the laddr */
+		if (laddr->ifa == NULL) {
+#ifdef SCTP_DEBUG
+			if (sctp_debug_on & SCTP_DEBUG_ASCONF1) {
+				printf("check_addr_list_ep: laddr->ifa is NULL");
+			}
+#endif
+			continue;
+		}
+		if (laddr->ifa->ifa_addr == NULL) {
+#ifdef SCTP_DEBUG
+			if (sctp_debug_on & SCTP_DEBUG_ASCONF1) {
+				printf("check_addr_list_ep: laddr->ifa->ifa_addr is NULL");
+			}
+#endif
+			continue;
+		}
+		/* do i have it implicitly? */
+		if (sctp_cmpaddr(laddr->ifa->ifa_addr, init_addr)) {
+#ifdef SCTP_DEBUG
+			if (sctp_debug_on & SCTP_DEBUG_ASCONF2) {
+				printf("check_address_list_all: skipping ");
+				sctp_print_address(laddr->ifa->ifa_addr);
+			}
+#endif /* SCTP_DEBUG */
+			continue;
+		}
+		/* check to see if in the init-ack */
+		if (!sctp_addr_in_initack(stcb, m, offset, length,
+					  laddr->ifa->ifa_addr)) {
+			/* try to add it */
+			sctp_addr_mgmt_assoc(stcb->sctp_ep, stcb, laddr->ifa,
+			    SCTP_ADD_IP_ADDRESS);
+		}
+	}
+}
+
+/*
+ * makes sure that the current kernel address list is consistent
+ * with the new association (with all addrs bound)
+ * adds addresses as necessary
+ */
+static void
+sctp_check_address_list_all(struct sctp_tcb *stcb, struct mbuf *m, int offset,
+    int length, struct sockaddr *init_addr, uint16_t local_scope,
+    uint16_t site_scope, uint16_t ipv4_scope, uint16_t loopback_scope)
+{
+	struct ifnet *ifn;
+	struct ifaddr *ifa;
+
+	/* go through all our known interfaces */
+	TAILQ_FOREACH(ifn, &ifnet, if_list) {
+		if (loopback_scope == 0 && ifn->if_type == IFT_LOOP) {
+			/* skip loopback interface */
+			continue;
+		}
+
+		/* go through each interface address */
+		TAILQ_FOREACH(ifa, &ifn->if_addrlist, ifa_list) {
+			/* do i have it implicitly? */
+			if (sctp_cmpaddr(ifa->ifa_addr, init_addr)) {
+#ifdef SCTP_DEBUG
+				if (sctp_debug_on & SCTP_DEBUG_ASCONF2) {
+					printf("check_address_list_all: skipping ");
+					sctp_print_address(ifa->ifa_addr);
+				}
+#endif /* SCTP_DEBUG */
+				continue;
+			}
+			/* check to see if in the init-ack */
+			if (!sctp_addr_in_initack(stcb, m, offset, length,
+			    ifa->ifa_addr)) {
+				/* try to add it */
+				sctp_addr_mgmt_assoc(stcb->sctp_ep, stcb,
+				    ifa, SCTP_ADD_IP_ADDRESS);
+			}
+		} /* end foreach ifa */
+	} /* end foreach ifn */
+}
+
+/*
+ * validates an init-ack chunk (from a cookie-echo) with current addresses
+ * adds addresses from the init-ack into our local address list, if needed
+ * queues asconf adds/deletes addresses as needed and makes appropriate
+ * list changes for source address selection
+ * m, offset: points to the start of the address list in an init-ack chunk
+ * length: total length of the address params only
+ * init_addr: address where my INIT-ACK was sent from
+ */
+void
+sctp_check_address_list(struct sctp_tcb *stcb, struct mbuf *m, int offset,
+    int length, struct sockaddr *init_addr, uint16_t local_scope,
+    uint16_t site_scope, uint16_t ipv4_scope, uint16_t loopback_scope)
+{
+
+	/* process the local addresses in the initack */
+	sctp_process_initack_addresses(stcb, m, offset, length);
+
+	if (stcb->sctp_ep->sctp_flags & SCTP_PCB_FLAGS_BOUNDALL) {
+		/* bound all case */
+		sctp_check_address_list_all(stcb, m, offset, length, init_addr,
+		    local_scope, site_scope, ipv4_scope, loopback_scope);
+	} else {
+		/* subset bound case */
+	 	if (stcb->sctp_ep->sctp_flags & SCTP_PCB_FLAGS_DO_ASCONF) {
+			/* asconf's allowed */
+			sctp_check_address_list_ep(stcb, m, offset, length,
+			    init_addr);
+		}
+		/* else, no asconfs allowed, so what we sent is what we get */
+	}
+}
+
+/*
+ * sctp_bindx() support
+ */
+uint32_t
+sctp_addr_mgmt_ep_sa(struct sctp_inpcb *inp, struct sockaddr *sa, uint16_t type)
+{
+	struct ifaddr *ifa;
+
+	if (sa->sa_len == 0)
+		return (EINVAL);
+
+	ifa = sctp_find_ifa_by_addr(sa);
+	if (ifa != NULL) {
+#ifdef INET6
+		if (ifa->ifa_addr->sa_family == AF_INET6) {
+			struct in6_ifaddr *ifa6;
+			ifa6 = (struct in6_ifaddr *)ifa;
+			if (IFA6_IS_DEPRECATED(ifa6) ||
+			    (ifa6->ia6_flags & (IN6_IFF_DETACHED |
+			     IN6_IFF_ANYCAST | IN6_IFF_NOTREADY))) {
+				/* Can't bind a non-existent addr. */
+				return (EINVAL);
+			}
+		}
+#endif /* INET6 */
+		/* add this address */
+		sctp_addr_mgmt_ep(inp, ifa, type);
+	} else {
+		/* invalid address! */
+#ifdef SCTP_DEBUG
+		if (sctp_debug_on & SCTP_DEBUG_ASCONF1) {
+			printf("addr_mgmt_ep_sa: got invalid address!\n");
+		}
+#endif /* SCTP_DEBUG */
+		return (EADDRNOTAVAIL);
+	}
+	return (0);
+}
Index: sys/netinet/sctp_asconf.h
===================================================================
RCS file: sys/netinet/sctp_asconf.h
diff -N sys/netinet/sctp_asconf.h
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ sys/netinet/sctp_asconf.h	18 Jun 2005 16:05:47 -0000
@@ -0,0 +1,65 @@
+/*	$KAME: sctp_asconf.h,v 1.7 2004/08/17 04:06:16 itojun Exp $	*/
+
+#ifndef _NETINET_SCTP_ASCONF_H_
+#define _NETINET_SCTP_ASCONF_H_
+
+/*
+ * Copyright (c) 2001, 2002, 2003, 2004 Cisco Systems, Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the project nor the names of its contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY CISCO SYSTEMS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL CISCO SYSTEMS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+#include <sys/malloc.h>
+
+#if defined(_KERNEL) || (defined(__APPLE__) && defined(KERNEL))
+
+extern void sctp_asconf_cleanup(struct sctp_tcb *, struct sctp_nets *);
+
+extern struct mbuf *sctp_compose_asconf(struct sctp_tcb *);
+
+extern void sctp_handle_asconf(struct mbuf *, unsigned int, struct sctp_asconf_chunk *,
+	struct sctp_tcb *, struct sctp_nets *);
+
+extern void sctp_handle_asconf_ack(struct mbuf *, int,
+	struct sctp_asconf_ack_chunk *, struct sctp_tcb *, struct sctp_nets *);
+
+extern uint32_t sctp_addr_mgmt_ep_sa(struct sctp_inpcb *, struct sockaddr *,
+	uint16_t);
+
+extern void sctp_add_ip_address(struct ifaddr *);
+
+extern void sctp_delete_ip_address(struct ifaddr *);
+
+extern int32_t sctp_set_primary_ip_address_sa(struct sctp_tcb *,
+	struct sockaddr *);
+
+extern void sctp_set_primary_ip_address(struct ifaddr *);
+
+extern void sctp_check_address_list(struct sctp_tcb *, struct mbuf *, int, int,
+	struct sockaddr *, uint16_t, uint16_t, uint16_t, uint16_t);
+
+#endif /* _KERNEL */
+
+#endif /* !_NETINET_SCTP_ASCONF_H_ */
Index: sys/netinet/sctp_callout.h
===================================================================
RCS file: sys/netinet/sctp_callout.h
diff -N sys/netinet/sctp_callout.h
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ sys/netinet/sctp_callout.h	18 Jun 2005 16:05:47 -0000
@@ -0,0 +1,66 @@
+/*	$KAME: sctp_callout.h,v 1.8 2005/01/25 07:35:42 itojun Exp $	*/
+
+#ifndef __SCTP_CALLOUT__
+#define __SCTP_CALLOUT__
+
+/*
+ * Copyright (C) 2002, 2003, 2004 Cisco Systems Inc,
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the project nor the names of its contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#define _SCTP_NEEDS_CALLOUT_ 1
+
+#ifndef __NetBSD__
+struct callout {
+	TAILQ_ENTRY(callout) tqe;
+	int	c_time;				/* ticks to the event */
+	void	*c_arg;				/* function argument */
+	void	(*c_func) __P((void *));	/* function to call */
+	int	c_flags;			/* state of this entry */
+};
+#endif
+#define SCTP_TICKS_PER_FASTTIMO 20		/* we get called about */
+                                                /* every 20ms */
+
+TAILQ_HEAD(calloutlist, callout);
+
+#define	CALLOUT_ACTIVE		0x0002 /* callout is currently active */
+#ifndef __NetBSD__
+#define	CALLOUT_PENDING		0x0004 /* callout is waiting for timeout */
+#define CALLOUT_FIRED		0x0008 /* it expired */
+#endif
+
+#define	callout_active(c)	((c)->c_flags & CALLOUT_ACTIVE)
+#define	callout_deactivate(c)	((c)->c_flags &= ~CALLOUT_ACTIVE)
+void	callout_init __P((struct callout *));
+#define	callout_pending(c)	((c)->c_flags & CALLOUT_PENDING)
+
+void	callout_reset __P((struct callout *, int, void (*)(void *), void *));
+#ifndef __NetBSD__
+int	callout_stop __P((struct callout *));
+#endif
+#endif
Index: sys/netinet/sctp_constants.h
===================================================================
RCS file: sys/netinet/sctp_constants.h
diff -N sys/netinet/sctp_constants.h
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ sys/netinet/sctp_constants.h	18 Jun 2005 16:05:47 -0000
@@ -0,0 +1,831 @@
+/*	$KAME: sctp_constants.h,v 1.16 2004/08/17 04:06:16 itojun Exp $	*/
+
+#ifndef __sctp_constants_h__
+#define __sctp_constants_h__
+
+/*
+ * Copyright (c) 2001, 2002, 2003, 2004 Cisco Systems, Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *      This product includes software developed by Cisco Systems, Inc.
+ * 4. Neither the name of the project nor the names of its contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY CISCO SYSTEMS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL CISCO SYSTEMS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+#define SCTP_VERSION_STRING "KAME-BSD 1.1"
+/*#define SCTP_AUDITING_ENABLED 1 used for debug/auditing */
+#define SCTP_AUDIT_SIZE 256
+#define SCTP_STAT_LOG_SIZE 80000
+
+/* Places that CWND log can happen from */
+#define SCTP_CWND_LOG_FROM_FR	1
+#define SCTP_CWND_LOG_FROM_RTX	2
+#define SCTP_CWND_LOG_FROM_BRST	3
+#define SCTP_CWND_LOG_FROM_SS	4
+#define SCTP_CWND_LOG_FROM_CA	5
+#define SCTP_CWND_LOG_FROM_SAT	6
+#define SCTP_BLOCK_LOG_INTO_BLK 7
+#define SCTP_BLOCK_LOG_OUTOF_BLK 8
+#define SCTP_BLOCK_LOG_CHECK     9
+#define SCTP_STR_LOG_FROM_INTO_STRD 10
+#define SCTP_STR_LOG_FROM_IMMED_DEL 11
+#define SCTP_STR_LOG_FROM_INSERT_HD 12
+#define SCTP_STR_LOG_FROM_INSERT_MD 13
+#define SCTP_STR_LOG_FROM_INSERT_TL 14
+#define SCTP_STR_LOG_FROM_MARK_TSN  15
+#define SCTP_STR_LOG_FROM_EXPRS_DEL 16
+#define SCTP_FR_LOG_BIGGEST_TSNS    17
+#define SCTP_FR_LOG_STRIKE_TEST     18
+#define SCTP_FR_LOG_STRIKE_CHUNK    19
+#define SCTP_FR_T3_TIMEOUT          20
+#define SCTP_MAP_PREPARE_SLIDE      21
+#define SCTP_MAP_SLIDE_FROM         22
+#define SCTP_MAP_SLIDE_RESULT       23
+#define SCTP_MAP_SLIDE_CLEARED	    24
+#define SCTP_MAP_SLIDE_NONE         25
+#define SCTP_FR_T3_MARK_TIME        26
+#define SCTP_FR_T3_MARKED           27
+#define SCTP_FR_T3_STOPPED          28
+#define SCTP_FR_MARKED              30
+#define SCTP_CWND_LOG_NOADV_SS      31
+#define SCTP_CWND_LOG_NOADV_CA      32
+#define SCTP_MAX_BURST_APPLIED      33
+#define SCTP_MAX_IFP_APPLIED        34
+#define SCTP_MAX_BURST_ERROR_STOP   35
+#define SCTP_INCREASE_PEER_RWND     36
+#define SCTP_DECREASE_PEER_RWND     37
+#define SCTP_SET_PEER_RWND_VIA_SACK 38
+#define SCTP_LOG_MBCNT_INCREASE     39
+#define SCTP_LOG_MBCNT_DECREASE     40
+#define SCTP_LOG_MBCNT_CHKSET       41
+/*
+ * To turn on various logging, you must first define SCTP_STAT_LOGGING.
+ * Then to get something to log you define one of the logging defines i.e.
+ *
+ * SCTP_CWND_LOGGING
+ * SCTP_BLK_LOGGING
+ * SCTP_STR_LOGGING
+ * SCTP_FR_LOGGING
+ *
+ * Any one or a combination of the logging can be turned on.
+ */
+#define SCTP_LOG_EVENT_CWND  1
+#define SCTP_LOG_EVENT_BLOCK 2
+#define SCTP_LOG_EVENT_STRM  3
+#define SCTP_LOG_EVENT_FR    4
+#define SCTP_LOG_EVENT_MAP   5
+#define SCTP_LOG_EVENT_MAXBURST 6
+#define SCTP_LOG_EVENT_RWND  7
+#define SCTP_LOG_EVENT_MBCNT 8
+
+
+/* number of associations by default for zone allocation */
+#define SCTP_MAX_NUM_OF_ASOC	40000
+/* how many addresses per assoc remote and local */
+#define SCTP_SCALE_FOR_ADDR	2
+
+/* default AUTO_ASCONF mode enable(1)/disable(0) value (sysctl) */
+#define SCTP_DEFAULT_AUTO_ASCONF	0
+
+/*
+ * If you wish to use MD5 instead of SLA uncomment the line below.
+ * Why you would like to do this:
+ * a) There may be IPR on SHA-1, or so the FIP-180-1 page says,
+ * b) MD5 is 3 times faster (has coded here).
+ *
+ * The disadvantage is it is thought that MD5 has been cracked... see RFC2104.
+ */
+/*#define USE_MD5 1 */
+/*
+ * Note: I can't seem to get this to compile now for some reason- the
+ * kernel can't link in the md5 crypto
+ */
+
+/* DEFINE HERE WHAT CRC YOU WANT TO USE */
+#define SCTP_USECRC_RFC2960  1
+/*#define SCTP_USECRC_FLETCHER 1*/
+/*#define SCTP_USECRC_SSHCRC32 1*/
+/*#define SCTP_USECRC_FASTCRC32 1*/
+/*#define SCTP_USECRC_CRC32 1*/
+/*#define SCTP_USECRC_TCP32 1*/
+/*#define SCTP_USECRC_CRC16SMAL 1*/
+/*#define SCTP_USECRC_CRC16 1 */
+/*#define SCTP_USECRC_MODADLER 1*/
+
+#ifndef SCTP_ADLER32_BASE
+#define SCTP_ADLER32_BASE 65521
+#endif
+
+#define SCTP_CWND_POSTS_LIST 256
+/*
+ * the SCTP protocol signature
+ * this includes the version number encoded in the last 4 bits
+ * of the signature.
+ */
+#define PROTO_SIGNATURE_A	0x30000000
+#define SCTP_VERSION_NUMBER	0x3
+
+#define MAX_TSN	0xffffffff
+#define MAX_SEQ	0xffff
+
+/* how many executions every N tick's */
+#define SCTP_MAX_ITERATOR_AT_ONCE 20
+
+/* number of clock ticks between iterator executions */
+#define SCTP_ITERATOR_TICKS 1
+
+/* option:
+ * If you comment out the following you will receive the old
+ * behavior of obeying cwnd for the fast retransmit algorithm.
+ * With this defined a FR happens right away with-out waiting
+ * for the flightsize to drop below the cwnd value (which is
+ * reduced by the FR to 1/2 the inflight packets).
+ */
+#define SCTP_IGNORE_CWND_ON_FR 1
+
+/*
+ * Adds implementors guide behavior to only use newest highest
+ * update in SACK gap ack's to figure out if you need to stroke
+ * a chunk for FR.
+ */
+#define SCTP_NO_FR_UNLESS_SEGMENT_SMALLER 1
+
+/* default max I can burst out after a fast retransmit */
+#define SCTP_DEF_MAX_BURST 8
+
+/* Packet transmit states in the sent field */
+#define SCTP_DATAGRAM_UNSENT		0
+#define SCTP_DATAGRAM_SENT		1
+#define SCTP_DATAGRAM_RESEND1		2 /* not used (in code, but may hit this value) */
+#define SCTP_DATAGRAM_RESEND2		3 /* not used (in code, but may hit this value) */
+#define SCTP_DATAGRAM_RESEND3		4 /* not used (in code, but may hit this value) */
+#define SCTP_DATAGRAM_RESEND		5
+#define SCTP_DATAGRAM_ACKED		10010
+#define SCTP_DATAGRAM_INBOUND		10011
+#define SCTP_READY_TO_TRANSMIT		10012
+#define SCTP_DATAGRAM_MARKED		20010
+#define SCTP_FORWARD_TSN_SKIP		30010
+
+/* SCTP chunk types */
+/* Moved to sctp.h so f/w and natd
+ * boxes can find the chunk types.
+ */
+
+/* align to 32-bit sizes */
+#define SCTP_SIZE32(x)	((((x)+3) >> 2) << 2)
+
+#define IS_SCTP_CONTROL(a) ((a)->chunk_type != SCTP_DATA)
+#define IS_SCTP_DATA(a) ((a)->chunk_type == SCTP_DATA)
+
+/* SCTP parameter types */
+#define SCTP_HEARTBEAT_INFO	    0x0001
+#define SCTP_IPV4_ADDRESS	    0x0005
+#define SCTP_IPV6_ADDRESS	    0x0006
+#define SCTP_STATE_COOKIE	    0x0007
+#define SCTP_UNRECOG_PARAM	    0x0008
+#define SCTP_COOKIE_PRESERVE	    0x0009
+#define SCTP_HOSTNAME_ADDRESS	    0x000b
+#define SCTP_SUPPORTED_ADDRTYPE	    0x000c
+#define SCTP_ECN_CAPABLE	    0x8000
+/* draft-ietf-stewart-strreset-xxx */
+#define SCTP_STR_RESET_REQUEST      0x000d
+#define SCTP_STR_RESET_RESPONSE     0x000e
+
+/* ECN Nonce: draft-ladha-sctp-ecn-nonce */
+#define SCTP_ECN_NONCE_SUPPORTED    0x8001
+/*
+ * draft-ietf-stewart-strreset-xxx
+ *   param=0x8001  len=0xNNNN
+ *   Byte | Byte | Byte | Byte
+ *   Byte | Byte ...
+ *
+ *  Where each Byte is a chunk type
+ *  extension supported so for example
+ *  to support all chunks one would have (in hex):
+ *
+ *  80 01 00 09
+ *  C0 C1 80 81
+ *  82 00 00 00
+ *
+ *  Has the parameter.
+ *   C0 = PR-SCTP    (RFC3758)
+ *   C1, 80 = ASCONF (addip draft)
+ *   81 = Packet Drop
+ *   82 = Stream Reset
+ */
+
+/* draft-ietf-tsvwg-prsctp */
+#define SCTP_SUPPORTED_CHUNK_EXT    0x8008
+
+/* number of extensions we support */
+#define SCTP_EXT_COUNT 5     	/* num of extensions we support chunk wise */
+#define SCTP_PAD_EXT_COUNT 3    /* num of pad bytes needed to get to 32 bit boundary */
+
+
+#define SCTP_PRSCTP_SUPPORTED	    0xc000
+/* draft-ietf-tsvwg-addip-sctp */
+#define SCTP_ADD_IP_ADDRESS	    0xc001
+#define SCTP_DEL_IP_ADDRESS	    0xc002
+#define SCTP_ERROR_CAUSE_IND	    0xc003
+#define SCTP_SET_PRIM_ADDR	    0xc004
+#define SCTP_SUCCESS_REPORT	    0xc005
+#define SCTP_ULP_ADAPTION	    0xc006
+
+/* Notification error codes */
+#define SCTP_NOTIFY_DATAGRAM_UNSENT	0x0001
+#define SCTP_NOTIFY_DATAGRAM_SENT	0x0002
+#define SCTP_FAILED_THRESHOLD		0x0004
+#define SCTP_HEARTBEAT_SUCCESS		0x0008
+#define SCTP_RESPONSE_TO_USER_REQ	0x000f
+#define SCTP_INTERNAL_ERROR		0x0010
+#define SCTP_SHUTDOWN_GUARD_EXPIRES	0x0020
+#define SCTP_RECEIVED_SACK		0x0040
+#define SCTP_PEER_FAULTY		0x0080
+
+/* Error causes used in SCTP op-err's and aborts */
+#define SCTP_CAUSE_INV_STRM		0x001
+#define SCTP_CAUSE_MISS_PARAM		0x002
+#define SCTP_CAUSE_STALE_COOKIE		0x003
+#define SCTP_CAUSE_OUT_OF_RESC		0x004
+#define SCTP_CAUSE_UNRESOLV_ADDR	0x005
+#define SCTP_CAUSE_UNRECOG_CHUNK	0x006
+#define SCTP_CAUSE_INVALID_PARAM	0x007
+/* This one is also the same as SCTP_UNRECOG_PARAM above */
+#define SCTP_CAUSE_UNRECOG_PARAM	0x008
+#define SCTP_CAUSE_NOUSER_DATA		0x009
+#define SCTP_CAUSE_COOKIE_IN_SHUTDOWN	0x00a
+#define SCTP_CAUSE_RESTART_W_NEWADDR	0x00b
+#define SCTP_CAUSE_USER_INITIATED_ABT	0x00c
+#define SCTP_CAUSE_PROTOCOL_VIOLATION	0x00d
+
+/* Error's from add ip */
+#define SCTP_CAUSE_DELETEING_LAST_ADDR	0x100
+#define SCTP_CAUSE_OPERATION_REFUSED	0x101
+#define SCTP_CAUSE_DELETING_SRC_ADDR	0x102
+#define SCTP_CAUSE_ILLEGAL_ASCONF	0x103
+
+/* bits for TOS field */
+#define SCTP_ECT0_BIT		0x02
+#define SCTP_ECT1_BIT		0x01
+#define SCTP_CE_BITS		0x03
+
+/* below turns off above */
+#define SCTP_FLEXIBLE_ADDRESS	0x20
+#define SCTP_NO_HEARTBEAT	0x40
+
+/* mask to get sticky */
+#define SCTP_STICKY_OPTIONS_MASK	0x0c
+
+/* MTU discovery flags */
+#define SCTP_DONT_FRAGMENT	0x0100
+#define SCTP_FRAGMENT_OK	0x0200
+#define SCTP_PR_SCTP_ENABLED	0x0400
+#define SCTP_PR_SCTP_BUFFER	0x0800
+
+/* Chunk flags */
+#define SCTP_WINDOW_PROBE	0x01
+
+/*
+ * SCTP states for internal state machine
+ * XXX (should match "user" values)
+ */
+#define SCTP_STATE_EMPTY		0x0000
+#define SCTP_STATE_INUSE		0x0001
+#define SCTP_STATE_COOKIE_WAIT		0x0002
+#define SCTP_STATE_COOKIE_ECHOED	0x0004
+#define SCTP_STATE_OPEN			0x0008
+#define SCTP_STATE_SHUTDOWN_SENT	0x0010
+#define SCTP_STATE_SHUTDOWN_RECEIVED	0x0020
+#define SCTP_STATE_SHUTDOWN_ACK_SENT	0x0040
+#define SCTP_STATE_SHUTDOWN_PENDING	0x0080
+#define SCTP_STATE_CLOSED_SOCKET	0x0100
+#define SCTP_STATE_MASK			0x007f
+
+#define SCTP_GET_STATE(asoc)	((asoc)->state & SCTP_STATE_MASK)
+
+/* SCTP reachability state for each address */
+#define SCTP_ADDR_REACHABLE		0x001
+#define SCTP_ADDR_NOT_REACHABLE		0x002
+#define SCTP_ADDR_NOHB			0x004
+#define SCTP_ADDR_BEING_DELETED		0x008
+#define SCTP_ADDR_NOT_IN_ASSOC		0x010
+#define SCTP_ADDR_WAS_PRIMARY		0x020
+#define SCTP_ADDR_SWITCH_PRIMARY	0x040
+#define SCTP_ADDR_OUT_OF_SCOPE		0x080
+#define SCTP_ADDR_DOUBLE_SWITCH		0x100
+#define SCTP_ADDR_UNCONFIRMED		0x200
+
+#define SCTP_REACHABLE_MASK		0x203
+
+/* bound address types (e.g. valid address types to allow) */
+#define SCTP_BOUND_V6		0x01
+#define SCTP_BOUND_V4		0x02
+
+/* How long a cookie lives in seconds */
+#define SCTP_DEFAULT_COOKIE_LIFE	60
+
+/* resource limit of streams */
+#define MAX_SCTP_STREAMS	2048
+
+/* Maximum the mapping array will  grow to (TSN mapping array) */
+#define SCTP_MAPPING_ARRAY	512
+
+/* size of the inital malloc on the mapping array */
+#define SCTP_INITIAL_MAPPING_ARRAY  16
+/* how much we grow the mapping array each call */
+#define SCTP_MAPPING_ARRAY_INCR     32
+
+/*
+ * Here we define the timer types used by the implementation
+ * as arguments in the set/get timer type calls.
+ */
+#define SCTP_TIMER_INIT 	0
+#define SCTP_TIMER_RECV 	1
+#define SCTP_TIMER_SEND 	2
+#define SCTP_TIMER_HEARTBEAT	3
+#define SCTP_TIMER_PMTU		4
+#define SCTP_TIMER_MAXSHUTDOWN	5
+#define SCTP_TIMER_SIGNATURE	6
+/*
+ * number of timer types in the base SCTP structure used in
+ * the set/get and has the base default.
+ */
+#define SCTP_NUM_TMRS	7
+
+/* timer types */
+#define SCTP_TIMER_TYPE_NONE		0
+#define SCTP_TIMER_TYPE_SEND		1
+#define SCTP_TIMER_TYPE_INIT		2
+#define SCTP_TIMER_TYPE_RECV		3
+#define SCTP_TIMER_TYPE_SHUTDOWN	4
+#define SCTP_TIMER_TYPE_HEARTBEAT	5
+#define SCTP_TIMER_TYPE_COOKIE		6
+#define SCTP_TIMER_TYPE_NEWCOOKIE	7
+#define SCTP_TIMER_TYPE_PATHMTURAISE	8
+#define SCTP_TIMER_TYPE_SHUTDOWNACK	9
+#define SCTP_TIMER_TYPE_ASCONF		10
+#define SCTP_TIMER_TYPE_SHUTDOWNGUARD	11
+#define SCTP_TIMER_TYPE_AUTOCLOSE	12
+#define SCTP_TIMER_TYPE_EVENTWAKE	13
+#define SCTP_TIMER_TYPE_STRRESET        14
+#define SCTP_TIMER_TYPE_INPKILL         15
+
+/*
+ * Number of ticks before the soxwakeup() event that
+ * is delayed is sent AFTER the accept() call
+ */
+#define SCTP_EVENTWAKEUP_WAIT_TICKS	3000
+
+/*
+ * Of course we really don't collect stale cookies, being folks
+ * of decerning taste. However we do count them, if we get too
+ * many before the association comes up.. we give up. Below is
+ * the constant that dictates when we give it up...this is a
+ * implemenation dependent treatment. In ours we do not ask for
+ * a extension of time, but just retry this many times...
+ */
+#define SCTP_MAX_STALE_COOKIES_I_COLLECT 10
+
+/* max number of TSN's dup'd that I will hold */
+#define SCTP_MAX_DUP_TSNS	20
+
+#define SCTP_TIMER_TYPE_ITERATOR        16
+/*
+ * Here we define the types used when setting the retry amounts.
+ */
+/* constants for type of set */
+#define SCTP_MAXATTEMPT_INIT	2
+#define SCTP_MAXATTEMPT_SEND	3
+
+/* Maximum TSN's we will summarize in a drop report */
+
+#define SCTP_MAX_DROP_REPORT 16
+
+/* How many drop re-attempts we make on  INIT/COOKIE-ECHO */
+#define SCTP_RETRY_DROPPED_THRESH 4
+
+/* And the max we will keep a history of in the tcb
+ * which MUST be lower than 256.
+ */
+
+#define SCTP_MAX_DROP_SAVE_REPORT 16
+
+/*
+ * Here we define the default timers and the default number
+ * of attemts we make for each respective side (send/init).
+ */
+
+/* Maxmium number of chunks a single association can have
+ * on it. Note that this is a squishy number since
+ * the count can run over this if the user sends a large
+ * message down .. the fragmented chunks don't count until
+ * AFTER the message is on queue.. it would be the next
+ * send that blocks things. This number will get tuned
+ * up at boot in the sctp_init and use the number
+ * of clusters as a base. This way high bandwidth
+ * environments will not get impacted by the lower
+ * bandwidth sending a bunch of 1 byte chunks
+ */
+#define SCTP_ASOC_MAX_CHUNKS_ON_QUEUE 512
+
+#define MSEC_TO_TICKS(x) (((x) * hz) / 1000)
+#define TICKS_TO_MSEC(x) (((x) * 1000) / hz)
+#define SEC_TO_TICKS(x) ((x) * hz)
+
+/* init timer def = 1 sec */
+#define SCTP_INIT_SEC	1
+
+/* send timer def = 1 seconds */
+#define SCTP_SEND_SEC	1
+
+/* recv timer def = 200ms  */
+#define SCTP_RECV_MSEC	200
+
+/* 30 seconds + RTO (in ms) */
+#define SCTP_HB_DEFAULT_MSEC	30000
+
+/* Max time I will wait for Shutdown to complete */
+#define SCTP_DEF_MAX_SHUTDOWN_SEC 180
+
+
+/* This is how long a secret lives, NOT how long a cookie lives
+ * how many ticks the current secret will live.
+ */
+#define SCTP_DEFAULT_SECRET_LIFE_SEC 3600
+
+#define SCTP_RTO_UPPER_BOUND	(60000)	/* 60 sec in ms */
+#define SCTP_RTO_UPPER_BOUND_SEC 60	/* for the init timer */
+#define SCTP_RTO_LOWER_BOUND	(1000)	/* 1 sec in ms */
+#define SCTP_RTO_INITIAL	(3000)	/* 3 sec in ms */
+
+
+#define SCTP_INP_KILL_TIMEOUT 1000 /* number of ms to retry kill of inpcb*/
+
+#define SCTP_DEF_MAX_INIT	8
+#define SCTP_DEF_MAX_SEND	10
+
+#define SCTP_DEF_PMTU_RAISE_SEC	600  /* 10 min between raise attempts */
+#define SCTP_DEF_PMTU_MIN	600
+
+#define SCTP_MSEC_IN_A_SEC	1000
+#define SCTP_USEC_IN_A_SEC	1000000
+#define SCTP_NSEC_IN_A_SEC	1000000000
+
+#define SCTP_MAX_OUTSTANDING_DG	10000
+
+/* How many streams I request initally by default */
+#define SCTP_OSTREAM_INITIAL 10
+
+#define SCTP_SEG_TO_RWND_UPD 32 /* How many smallest_mtu's need to increase before
+                                 * a window update sack is sent (should be a
+                                 * power of 2).
+                                 */
+#define SCTP_SCALE_OF_RWND_TO_UPD       4       /* Incr * this > hiwat, send
+                                                 * window update. Should be a
+                                                 * power of 2.
+                                                 */
+#define SCTP_MINIMAL_RWND		(4096) /* minimal rwnd */
+
+#define SCTP_ADDRMAX		20
+
+
+/* SCTP DEBUG Switch parameters */
+#define SCTP_DEBUG_TIMER1	0x00000001
+#define SCTP_DEBUG_TIMER2	0x00000002
+#define SCTP_DEBUG_TIMER3	0x00000004
+#define SCTP_DEBUG_TIMER4	0x00000008
+#define SCTP_DEBUG_OUTPUT1	0x00000010
+#define SCTP_DEBUG_OUTPUT2	0x00000020
+#define SCTP_DEBUG_OUTPUT3	0x00000040
+#define SCTP_DEBUG_OUTPUT4	0x00000080
+#define SCTP_DEBUG_UTIL1	0x00000100
+#define SCTP_DEBUG_UTIL2	0x00000200
+#define SCTP_DEBUG_INPUT1	0x00001000
+#define SCTP_DEBUG_INPUT2	0x00002000
+#define SCTP_DEBUG_INPUT3	0x00004000
+#define SCTP_DEBUG_INPUT4	0x00008000
+#define SCTP_DEBUG_ASCONF1	0x00010000
+#define SCTP_DEBUG_ASCONF2	0x00020000
+#define SCTP_DEBUG_OUTPUT5	0x00040000
+#define SCTP_DEBUG_PCB1		0x00100000
+#define SCTP_DEBUG_PCB2		0x00200000
+#define SCTP_DEBUG_PCB3		0x00400000
+#define SCTP_DEBUG_PCB4		0x00800000
+#define SCTP_DEBUG_INDATA1	0x01000000
+#define SCTP_DEBUG_INDATA2	0x02000000
+#define SCTP_DEBUG_INDATA3	0x04000000
+#define SCTP_DEBUG_INDATA4	0x08000000
+#define SCTP_DEBUG_USRREQ1	0x10000000
+#define SCTP_DEBUG_USRREQ2	0x20000000
+#define SCTP_DEBUG_PEEL1	0x40000000
+#define SCTP_DEBUG_ALL		0x7ff3f3ff
+#define SCTP_DEBUG_NOISY	0x00040000
+
+/* What sender needs to see to avoid SWS or we consider peers rwnd 0 */
+#define SCTP_SWS_SENDER_DEF	1420
+
+/*
+ * SWS is scaled to the sb_hiwat of the socket.
+ * A value of 2 is hiwat/4, 1 would be hiwat/2 etc.
+ */
+/* What receiver needs to see in sockbuf or we tell peer its 1 */
+#define SCTP_SWS_RECEIVER_DEF	3000
+
+
+#define SCTP_INITIAL_CWND 4380
+
+/* amount peer is obligated to have in rwnd or I will abort */
+#define SCTP_MIN_RWND	1500
+
+#define SCTP_WINDOW_MIN	1500	/* smallest rwnd can be */
+#define SCTP_WINDOW_MAX 1048576	/* biggest I can grow rwnd to
+				 * My playing around suggests a
+				 * value greater than 64k does not
+				 * do much, I guess via the kernel
+				 * limitations on the stream/socket.
+				 */
+
+#define SCTP_MAX_BUNDLE_UP	256	/* max number of chunks to bundle */
+
+/*  I can handle a 1meg re-assembly */
+#define SCTP_DEFAULT_MAXMSGREASM 1048576
+
+#define SCTP_DEFAULT_MAXSEGMENT 65535
+
+#define DEFAULT_CHUNK_BUFFER	2048
+#define DEFAULT_PARAM_BUFFER	512
+
+#define SCTP_DEFAULT_MINSEGMENT 512	/* MTU size ... if no mtu disc */
+#define SCTP_HOW_MANY_SECRETS	2	/* how many secrets I keep */
+
+#define SCTP_NUMBER_OF_SECRETS	8	/* or 8 * 4 = 32 octets */
+#define SCTP_SECRET_SIZE	32	/* number of octets in a 256 bits */
+
+#ifdef USE_MD5
+#define SCTP_SIGNATURE_SIZE	16	/* size of a MD5 signature */
+#else
+#define SCTP_SIGNATURE_SIZE	20	/* size of a SLA-1 signature */
+#endif /* USE_MD5 */
+
+#define SCTP_SIGNATURE_ALOC_SIZE 20
+
+/*
+ * SCTP upper layer notifications
+ */
+#define SCTP_NOTIFY_ASSOC_UP		1
+#define SCTP_NOTIFY_ASSOC_DOWN		2
+#define SCTP_NOTIFY_INTERFACE_DOWN	3
+#define SCTP_NOTIFY_INTERFACE_UP	4
+#define SCTP_NOTIFY_DG_FAIL		5
+#define SCTP_NOTIFY_STRDATA_ERR 	6
+#define SCTP_NOTIFY_ASSOC_ABORTED	7
+#define SCTP_NOTIFY_PEER_OPENED_STREAM	8
+#define SCTP_NOTIFY_STREAM_OPENED_OK	9
+#define SCTP_NOTIFY_ASSOC_RESTART	10
+#define SCTP_NOTIFY_HB_RESP             11
+#define SCTP_NOTIFY_ASCONF_SUCCESS	12
+#define SCTP_NOTIFY_ASCONF_FAILED	13
+#define SCTP_NOTIFY_PEER_SHUTDOWN	14
+#define SCTP_NOTIFY_ASCONF_ADD_IP	15
+#define SCTP_NOTIFY_ASCONF_DELETE_IP	16
+#define SCTP_NOTIFY_ASCONF_SET_PRIMARY	17
+#define SCTP_NOTIFY_PARTIAL_DELVIERY_INDICATION 18
+#define SCTP_NOTIFY_ADAPTION_INDICATION         19
+#define SCTP_NOTIFY_INTERFACE_CONFIRMED 20
+#define SCTP_NOTIFY_STR_RESET_RECV      21
+#define SCTP_NOTIFY_STR_RESET_SEND      22
+#define SCTP_NOTIFY_MAX			22
+
+
+
+
+
+/* clock variance is 10ms */
+#define SCTP_CLOCK_GRANULARITY	10
+
+#define IP_HDR_SIZE 40		/* we use the size of a IP6 header here
+				 * this detracts a small amount for ipv4
+				 * but it simplifies the ipv6 addition
+				 */
+
+#ifndef IPPROTO_SCTP
+#define IPPROTO_SCTP 132	/* the Official IANA number :-) */
+#endif /* !IPPROTO_SCTP */
+
+#define SCTP_MAX_DATA_BUNDLING		256
+#define SCTP_MAX_CONTROL_BUNDLING	20
+
+/* modular comparison */
+/* True if a > b (mod = M) */
+#define compare_with_wrap(a, b, M) (((a > b) && ((a - b) < ((M >> 1) + 1))) || \
+              ((b > a) && ((b - a) > ((M >> 1) + 1))))
+
+
+/* Mapping array manipulation routines */
+#define SCTP_IS_TSN_PRESENT(arry, gap) ((arry[(gap >> 3)] >> (gap & 0x07)) & 0x01)
+#define SCTP_SET_TSN_PRESENT(arry, gap) (arry[(gap >> 3)] |= (0x01 << ((gap & 0x07))))
+#define SCTP_UNSET_TSN_PRESENT(arry, gap) (arry[(gap >> 3)] &= ((~(0x01 << ((gap & 0x07)))) & 0xff))
+
+/* pegs */
+#define SCTP_NUMBER_OF_PEGS	96
+/* peg index's */
+#define SCTP_PEG_SACKS_SEEN	0
+#define SCTP_PEG_SACKS_SENT	1
+#define SCTP_PEG_TSNS_SENT	2
+#define SCTP_PEG_TSNS_RCVD	3
+#define SCTP_DATAGRAMS_SENT	4
+#define SCTP_DATAGRAMS_RCVD	5
+#define SCTP_RETRANTSN_SENT	6
+#define SCTP_DUPTSN_RECVD	7
+#define SCTP_HB_RECV		8
+#define SCTP_HB_ACK_RECV	9
+#define SCTP_HB_SENT		10
+#define SCTP_WINDOW_PROBES	11
+#define SCTP_DATA_DG_RECV	12
+#define SCTP_TMIT_TIMER		13
+#define SCTP_RECV_TIMER		14
+#define SCTP_HB_TIMER		15
+#define SCTP_FAST_RETRAN	16
+#define SCTP_TIMERS_EXP		17
+#define SCTP_FR_INAWINDOW	18
+#define SCTP_RWND_BLOCKED	19
+#define SCTP_CWND_BLOCKED	20
+#define SCTP_RWND_DROPS		21
+#define SCTP_BAD_STRMNO		22
+#define SCTP_BAD_SSN_WRAP	23
+#define SCTP_DROP_NOMEMORY	24
+#define SCTP_DROP_FRAG		25
+#define SCTP_BAD_VTAGS		26
+#define SCTP_BAD_CSUM		27
+#define SCTP_INPKTS		28
+#define SCTP_IN_MCAST		29
+#define SCTP_HDR_DROPS		30
+#define SCTP_NOPORTS		31
+#define SCTP_CWND_NOFILL	32
+#define SCTP_CALLS_TO_CO	33
+#define SCTP_CO_NODATASNT	34
+#define SCTP_CWND_NOUSE_SS	35
+#define SCTP_MAX_BURST_APL	36
+#define SCTP_EXPRESS_ROUTE	37
+#define SCTP_NO_COPY_IN		38
+#define SCTP_CACHED_SRC		39
+#define SCTP_CWND_NOCUM		40
+#define SCTP_CWND_SS		41
+#define SCTP_CWND_CA		42
+#define SCTP_CWND_SKIP		43
+#define SCTP_CWND_NOUSE_CA	44
+#define SCTP_MAX_CWND		45
+#define SCTP_CWND_DIFF_CA	46
+#define SCTP_CWND_DIFF_SA	47
+#define SCTP_OQS_AT_SS		48
+#define SCTP_SQQ_AT_SS		49
+#define SCTP_OQS_AT_CA		50
+#define SCTP_SQQ_AT_CA		51
+#define SCTP_MOVED_MTU		52
+#define SCTP_MOVED_QMAX		53
+#define SCTP_SQC_AT_SS		54
+#define SCTP_SQC_AT_CA		55
+#define SCTP_MOVED_MAX		56
+#define SCTP_MOVED_NLEF		57
+#define SCTP_NAGLE_NOQ		58
+#define SCTP_NAGLE_OFF		59
+#define SCTP_OUTPUT_FRM_SND	60
+#define SCTP_SOS_NOSNT		61
+#define SCTP_NOS_NOSNT		62
+#define SCTP_SOSE_NOSNT		63
+#define SCTP_NOSE_NOSNT		64
+#define SCTP_DATA_OUT_ERR	65
+#define SCTP_DUP_SSN_RCVD	66
+#define SCTP_DUP_FR		67
+#define SCTP_VTAG_EXPR		68
+#define SCTP_VTAG_BOGUS		69
+#define SCTP_T3_SAFEGRD		70
+#define SCTP_PDRP_FMBOX		71
+#define SCTP_PDRP_FEHOS		72
+#define SCTP_PDRP_MB_DA		73
+#define SCTP_PDRP_MB_CT		74
+#define SCTP_PDRP_BWRPT		75
+#define SCTP_PDRP_CRUPT		76
+#define SCTP_PDRP_NEDAT		77
+#define SCTP_PDRP_PDBRK		78
+#define SCTP_PDRP_TSNNF		79
+#define SCTP_PDRP_DNFND		80
+#define SCTP_PDRP_DIWNP		81
+#define SCTP_PDRP_DIZRW		82
+#define SCTP_PDRP_BADD		83
+#define SCTP_PDRP_MARK		84
+#define SCTP_ECNE_RCVD		85
+#define SCTP_CWR_PERFO		86
+#define SCTP_ECNE_SENT		87
+#define SCTP_MSGC_DROP		88
+#define SCTP_SEND_QUEUE_POP	89
+#define SCTP_ERROUT_FRM_USR	90
+#define SCTP_SENDTO_FULL_CWND	91
+#define SCTP_QUEONLY_BURSTLMT   92
+#define SCTP_IFP_QUEUE_FULL     93
+#define SCTP_RESV2              94
+#define SCTP_RESV3              95
+
+/*
+ * This value defines the number of vtag block time wait entry's
+ * per list element.  Each entry will take 2 4 byte ints (and of
+ * course the overhead of the next pointer as well). Using 15 as
+ * an example will yield * ((8 * 15) + 8) or 128 bytes of overhead
+ * for each timewait block that gets initialized. Increasing it to
+ * 31 would yeild 256 bytes per block.
+ */
+/* Undef the following turns on per EP behavior */
+#define SCTP_VTAG_TIMEWAIT_PER_STACK 1
+#ifdef SCTP_VTAG_TIMEWAIT_PER_STACK
+#define SCTP_NUMBER_IN_VTAG_BLOCK 15
+#else
+/* The hash list is smaller if we are on a ep basis */
+#define SCTP_NUMBER_IN_VTAG_BLOCK 3
+#endif
+/*
+ * If we use the STACK option, we have an array of this size head
+ * pointers. This array is mod'd the with the size to find which
+ * bucket and then all entries must be searched to see if the tag
+ * is in timed wait. If so we reject it.
+ */
+#define SCTP_STACK_VTAG_HASH_SIZE 31
+
+/*
+ * If we use the per-endpoint model than we do not have a hash
+ * table of entries but instead have a single head pointer and
+ * we must crawl through the entire list.
+ */
+
+/*
+ * Number of seconds of time wait, tied to MSL value (2 minutes),
+ * so 2 * MSL = 4 minutes or 480 seconds.
+ */
+#define SCTP_TIME_WAIT 480
+
+#define IN4_ISPRIVATE_ADDRESS(a) \
+   ((((u_char *)&(a)->s_addr)[0] == 10) || \
+    ((((u_char *)&(a)->s_addr)[0] == 172) && \
+     (((u_char *)&(a)->s_addr)[1] >= 16) && \
+     (((u_char *)&(a)->s_addr)[1] <= 32)) || \
+    ((((u_char *)&(a)->s_addr)[0] == 192) && \
+     (((u_char *)&(a)->s_addr)[1] == 168)))
+
+#define IN4_ISLOOPBACK_ADDRESS(a) \
+    ((((u_char *)&(a)->s_addr)[0] == 127) && \
+     (((u_char *)&(a)->s_addr)[1] == 0) && \
+     (((u_char *)&(a)->s_addr)[2] == 0) && \
+     (((u_char *)&(a)->s_addr)[3] == 1))
+
+
+#if defined(_KERNEL) || (defined(__APPLE__) && defined(KERNEL))
+
+#if defined(__FreeBSD__) || defined(__APPLE__) || defined(__DragonFly__)
+#define SCTP_GETTIME_TIMEVAL(x)	(microuptime(x))
+#define SCTP_GETTIME_TIMESPEC(x) (nanouptime(x))
+#else
+#define SCTP_GETTIME_TIMEVAL(x)	(microtime(x))
+#define SCTP_GETTIME_TIMESPEC(x) (nanotime(x))
+#endif /* __FreeBSD__ */
+
+#define sctp_sowwakeup(inp, so) \
+do { \
+	if (inp->sctp_flags & SCTP_PCB_FLAGS_DONT_WAKE) { \
+		inp->sctp_flags |= SCTP_PCB_FLAGS_WAKEOUTPUT; \
+	} else { \
+		sowwakeup(so); \
+	} \
+} while (0)
+
+#define sctp_sorwakeup(inp, so) \
+do { \
+	if (inp->sctp_flags & SCTP_PCB_FLAGS_DONT_WAKE) { \
+		inp->sctp_flags |= SCTP_PCB_FLAGS_WAKEINPUT; \
+	} else { \
+		sorwakeup(so); \
+	} \
+} while (0)
+
+#endif /* _KERNEL */
+#endif
Index: sys/netinet/sctp_crc32.c
===================================================================
RCS file: sys/netinet/sctp_crc32.c
diff -N sys/netinet/sctp_crc32.c
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ sys/netinet/sctp_crc32.c	18 Jun 2005 16:05:47 -0000
@@ -0,0 +1,180 @@
+/*	$KAME: sctp_crc32.c,v 1.11 2004/08/17 04:06:16 itojun Exp $	*/
+
+/*
+ * Copyright (c) 2001, 2002, 2003, 2004 Cisco Systems, Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *      This product includes software developed by Cisco Systems, Inc.
+ * 4. Neither the name of the project nor the names of its contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY CISCO SYSTEMS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL CISCO SYSTEMS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+#ifdef __APPLE__
+#include <sctp.h>
+#elif !defined(__OpenBSD__)
+#include "opt_sctp.h"
+#endif
+#include <sys/param.h>
+#include <netinet/sctp_crc32.h>
+
+#ifndef SCTP_USE_ADLER32
+
+#define SCTP_CRC32C_POLY 0x1EDC6F41
+#define SCTP_CRC32C(c, d) (c = ((c) >> 8) ^ sctp_crc_c[((c) ^ (d)) & 0xFF])
+
+/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
+/* Copyright 2001, D. Otis.  Use this program, code or tables    */
+/* extracted from it, as desired without restriction.            */
+/*                                                               */
+/* 32 Bit Reflected CRC table generation for SCTP.               */
+/* To accommodate serial byte data being shifted out least       */
+/* significant bit first, the table's 32 bit words are reflected */
+/* which flips both byte and bit MS and LS positions.  The CRC   */
+/* is calculated MS bits first from the perspective of the serial*/
+/* stream.  The x^32 term is implied and the x^0 term may also   */
+/* be shown as +1.  The polynomial code used is 0x1EDC6F41.      */
+/* Castagnoli93                                                  */
+/* x^32+x^28+x^27+x^26+x^25+x^23+x^22+x^20+x^19+x^18+x^14+x^13+  */
+/* x^11+x^10+x^9+x^8+x^6+x^0                                     */
+/* Guy Castagnoli Stefan Braeuer and Martin Herrman              */
+/* "Optimization of Cyclic Redundancy-Check Codes                */
+/* with 24 and 32 Parity Bits",                                  */
+/* IEEE Transactions on Communications, Vol.41, No.6, June 1993  */
+/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
+
+unsigned long  sctp_crc_c[256] = {
+	0x00000000L, 0xF26B8303L, 0xE13B70F7L, 0x1350F3F4L,
+	0xC79A971FL, 0x35F1141CL, 0x26A1E7E8L, 0xD4CA64EBL,
+	0x8AD958CFL, 0x78B2DBCCL, 0x6BE22838L, 0x9989AB3BL,
+	0x4D43CFD0L, 0xBF284CD3L, 0xAC78BF27L, 0x5E133C24L,
+	0x105EC76FL, 0xE235446CL, 0xF165B798L, 0x030E349BL,
+	0xD7C45070L, 0x25AFD373L, 0x36FF2087L, 0xC494A384L,
+	0x9A879FA0L, 0x68EC1CA3L, 0x7BBCEF57L, 0x89D76C54L,
+	0x5D1D08BFL, 0xAF768BBCL, 0xBC267848L, 0x4E4DFB4BL,
+	0x20BD8EDEL, 0xD2D60DDDL, 0xC186FE29L, 0x33ED7D2AL,
+	0xE72719C1L, 0x154C9AC2L, 0x061C6936L, 0xF477EA35L,
+	0xAA64D611L, 0x580F5512L, 0x4B5FA6E6L, 0xB93425E5L,
+	0x6DFE410EL, 0x9F95C20DL, 0x8CC531F9L, 0x7EAEB2FAL,
+	0x30E349B1L, 0xC288CAB2L, 0xD1D83946L, 0x23B3BA45L,
+	0xF779DEAEL, 0x05125DADL, 0x1642AE59L, 0xE4292D5AL,
+	0xBA3A117EL, 0x4851927DL, 0x5B016189L, 0xA96AE28AL,
+	0x7DA08661L, 0x8FCB0562L, 0x9C9BF696L, 0x6EF07595L,
+	0x417B1DBCL, 0xB3109EBFL, 0xA0406D4BL, 0x522BEE48L,
+	0x86E18AA3L, 0x748A09A0L, 0x67DAFA54L, 0x95B17957L,
+	0xCBA24573L, 0x39C9C670L, 0x2A993584L, 0xD8F2B687L,
+	0x0C38D26CL, 0xFE53516FL, 0xED03A29BL, 0x1F682198L,
+	0x5125DAD3L, 0xA34E59D0L, 0xB01EAA24L, 0x42752927L,
+	0x96BF4DCCL, 0x64D4CECFL, 0x77843D3BL, 0x85EFBE38L,
+	0xDBFC821CL, 0x2997011FL, 0x3AC7F2EBL, 0xC8AC71E8L,
+	0x1C661503L, 0xEE0D9600L, 0xFD5D65F4L, 0x0F36E6F7L,
+	0x61C69362L, 0x93AD1061L, 0x80FDE395L, 0x72966096L,
+	0xA65C047DL, 0x5437877EL, 0x4767748AL, 0xB50CF789L,
+	0xEB1FCBADL, 0x197448AEL, 0x0A24BB5AL, 0xF84F3859L,
+	0x2C855CB2L, 0xDEEEDFB1L, 0xCDBE2C45L, 0x3FD5AF46L,
+	0x7198540DL, 0x83F3D70EL, 0x90A324FAL, 0x62C8A7F9L,
+	0xB602C312L, 0x44694011L, 0x5739B3E5L, 0xA55230E6L,
+	0xFB410CC2L, 0x092A8FC1L, 0x1A7A7C35L, 0xE811FF36L,
+	0x3CDB9BDDL, 0xCEB018DEL, 0xDDE0EB2AL, 0x2F8B6829L,
+	0x82F63B78L, 0x709DB87BL, 0x63CD4B8FL, 0x91A6C88CL,
+	0x456CAC67L, 0xB7072F64L, 0xA457DC90L, 0x563C5F93L,
+	0x082F63B7L, 0xFA44E0B4L, 0xE9141340L, 0x1B7F9043L,
+	0xCFB5F4A8L, 0x3DDE77ABL, 0x2E8E845FL, 0xDCE5075CL,
+	0x92A8FC17L, 0x60C37F14L, 0x73938CE0L, 0x81F80FE3L,
+	0x55326B08L, 0xA759E80BL, 0xB4091BFFL, 0x466298FCL,
+	0x1871A4D8L, 0xEA1A27DBL, 0xF94AD42FL, 0x0B21572CL,
+	0xDFEB33C7L, 0x2D80B0C4L, 0x3ED04330L, 0xCCBBC033L,
+	0xA24BB5A6L, 0x502036A5L, 0x4370C551L, 0xB11B4652L,
+	0x65D122B9L, 0x97BAA1BAL, 0x84EA524EL, 0x7681D14DL,
+	0x2892ED69L, 0xDAF96E6AL, 0xC9A99D9EL, 0x3BC21E9DL,
+	0xEF087A76L, 0x1D63F975L, 0x0E330A81L, 0xFC588982L,
+	0xB21572C9L, 0x407EF1CAL, 0x532E023EL, 0xA145813DL,
+	0x758FE5D6L, 0x87E466D5L, 0x94B49521L, 0x66DF1622L,
+	0x38CC2A06L, 0xCAA7A905L, 0xD9F75AF1L, 0x2B9CD9F2L,
+	0xFF56BD19L, 0x0D3D3E1AL, 0x1E6DCDEEL, 0xEC064EEDL,
+	0xC38D26C4L, 0x31E6A5C7L, 0x22B65633L, 0xD0DDD530L,
+	0x0417B1DBL, 0xF67C32D8L, 0xE52CC12CL, 0x1747422FL,
+	0x49547E0BL, 0xBB3FFD08L, 0xA86F0EFCL, 0x5A048DFFL,
+	0x8ECEE914L, 0x7CA56A17L, 0x6FF599E3L, 0x9D9E1AE0L,
+	0xD3D3E1ABL, 0x21B862A8L, 0x32E8915CL, 0xC083125FL,
+	0x144976B4L, 0xE622F5B7L, 0xF5720643L, 0x07198540L,
+	0x590AB964L, 0xAB613A67L, 0xB831C993L, 0x4A5A4A90L,
+	0x9E902E7BL, 0x6CFBAD78L, 0x7FAB5E8CL, 0x8DC0DD8FL,
+	0xE330A81AL, 0x115B2B19L, 0x020BD8EDL, 0xF0605BEEL,
+	0x24AA3F05L, 0xD6C1BC06L, 0xC5914FF2L, 0x37FACCF1L,
+	0x69E9F0D5L, 0x9B8273D6L, 0x88D28022L, 0x7AB90321L,
+	0xAE7367CAL, 0x5C18E4C9L, 0x4F48173DL, 0xBD23943EL,
+	0xF36E6F75L, 0x0105EC76L, 0x12551F82L, 0xE03E9C81L,
+	0x34F4F86AL, 0xC69F7B69L, 0xD5CF889DL, 0x27A40B9EL,
+	0x79B737BAL, 0x8BDCB4B9L, 0x988C474DL, 0x6AE7C44EL,
+	0xBE2DA0A5L, 0x4C4623A6L, 0x5F16D052L, 0xAD7D5351L,
+};
+
+u_int32_t
+update_crc32(u_int32_t crc32,
+	     unsigned char *buffer,
+	     unsigned int length)
+{
+	unsigned int i;
+
+	for (i = 0; i < length; i++) {
+		SCTP_CRC32C(crc32, buffer[i]);
+	}
+	return (crc32);
+}
+
+
+u_int32_t
+sctp_csum_finalize(u_int32_t crc32)
+{
+	u_int32_t result;
+#if BYTE_ORDER == BIG_ENDIAN
+	u_int8_t byte0, byte1, byte2, byte3;
+#endif
+	/* Complement the result */
+	result = ~crc32;
+#if BYTE_ORDER == BIG_ENDIAN
+	/*
+	 * For BIG-ENDIAN.. aka Motorola byte order the result is in
+	 * little-endian form. So we must manually swap the bytes. Then
+	 * we can call htonl() which does nothing...
+	 */
+	byte0 = result & 0x000000ff;
+	byte1 = (result >> 8) & 0x000000ff;
+	byte2 = (result >> 16) & 0x000000ff;
+	byte3 = (result >> 24) & 0x000000ff;
+	result = ((byte0 << 24) | (byte1 << 16) | (byte2 << 8) | byte3);
+	crc32 = htonl(result);
+#else
+	/*
+	 * For INTEL platforms the result comes out in network order.
+	 * No htonl is required or the swap above. So we optimize out
+	 * both the htonl and the manual swap above.
+	 */
+	crc32 = result;
+#endif
+	return (crc32);
+}
+
+#endif
Index: sys/netinet/sctp_crc32.h
===================================================================
RCS file: sys/netinet/sctp_crc32.h
diff -N sys/netinet/sctp_crc32.h
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ sys/netinet/sctp_crc32.h	18 Jun 2005 16:05:47 -0000
@@ -0,0 +1,48 @@
+/*	$KAME: sctp_crc32.h,v 1.4 2003/11/25 06:40:52 ono Exp $	*/
+
+#ifndef __crc32c_h__
+#define __crc32c_h__
+
+/*
+ * Copyright (c) 2001, 2002, 2004 Cisco Systems, Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *      This product includes software developed by Cisco Systems, Inc.
+ * 4. Neither the name of the project nor the names of its contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY CISCO SYSTEMS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL CISCO SYSTEMS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/types.h>
+
+#ifndef SCTP_USE_ADLER32
+#if defined(_KERNEL) || (defined(__APPLE__) && defined(KERNEL))
+u_int32_t update_crc32(u_int32_t, unsigned char *, unsigned int);
+
+u_int32_t sctp_csum_finalize(u_int32_t);
+
+#endif /* _KERNEL */
+#endif /* !SCTP_USE_ADLER32 */
+#endif /* __crc32c_h__ */
Index: sys/netinet/sctp_hashdriver.c
===================================================================
RCS file: sys/netinet/sctp_hashdriver.c
diff -N sys/netinet/sctp_hashdriver.c
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ sys/netinet/sctp_hashdriver.c	18 Jun 2005 16:05:47 -0000
@@ -0,0 +1,261 @@
+/*	$KAME: sctp_hashdriver.c,v 1.5 2004/01/19 09:48:26 itojun Exp $	*/
+
+/*
+ * Copyright (c) 2001, 2002, 2003, 2004 Cisco Systems, Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *      This product includes software developed by Cisco Systems, Inc.
+ * 4. Neither the name of the project nor the names of its contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY CISCO SYSTEMS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL CISCO SYSTEMS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/malloc.h>
+#include <sys/mbuf.h>
+#include <sys/domain.h>
+#include <sys/protosw.h>
+#include <sys/socket.h>
+#include <sys/socketvar.h>
+#include <sys/proc.h>
+#include <sys/kernel.h>
+#include <sys/sysctl.h>
+#include <sys/types.h>
+#include <netinet/sctp_constants.h>
+#ifdef USE_MD5
+#include <crypto/md5.h>
+#else
+#include <netinet/sctp_sha1.h>
+#endif /* USE_MD5 */
+#include <netinet/sctp_hashdriver.h>
+
+/*
+ * Main driver for SCTP's hashing.
+ * passing a two pointers and two lengths, returning a digest pointer
+ * filled. The md5 code was taken directly from the RFC (2104) so to
+ * understand it you may want to go look at the RFC referenced in the
+ * SCTP spec. We did modify this code to either user OURs implementation
+ * of SLA1 or the MD5 that comes from its RFC. SLA1 may have IPR issues
+ * so you need to check in to this if you wish to use it... Or at least
+ * that is what the FIP-180.1 web page says.
+ */
+
+void sctp_hash_digest(char *key, int key_len, char *text, int text_len,
+    unsigned char *digest)
+{
+#ifdef USE_MD5
+	md5_ctxt context;
+#else
+	struct sha1_context context;
+#endif /* USE_MD5 */
+	/* inner padding - key XORd with ipad */
+	unsigned char k_ipad[65];
+	/* outer padding - key XORd with opad */
+	unsigned char k_opad[65];
+	unsigned char tk[20];
+	int i;
+
+	if (key_len > 64) {
+#ifdef USE_MD5
+		md5_ctxt tctx;
+		MD5Init(&tctx);
+		MD5Update(&tctx, key, key_len);
+		MD5Final(tk, &tctx);
+		key = tk;
+		key_len = 16;
+#else
+		struct sha1_context tctx;
+		SHA1_Init(&tctx);
+		SHA1_Process(&tctx, key, key_len);
+		SHA1_Final(&tctx, tk);
+		key = tk;
+		key_len = 20;
+#endif /* USE_MD5 */
+	}
+
+	/*
+	 * the HMAC_MD5 transform looks like:
+	 *
+	 * MD5(K XOR opad, MD5(K XOR ipad, text))
+	 *
+	 * where K is an n byte key
+	 * ipad is the byte 0x36 repeated 64 times
+	 * opad is the byte 0x5c repeated 64 times
+	 * and text is the data being protected
+	 */
+
+	/* start out by storing key in pads */
+	bzero(k_ipad, sizeof k_ipad);
+	bzero(k_opad, sizeof k_opad);
+	bcopy(key, k_ipad, key_len);
+	bcopy(key, k_opad, key_len);
+
+	/* XOR key with ipad and opad values */
+	for (i = 0; i < 64; i++) {
+		k_ipad[i] ^= 0x36;
+		k_opad[i] ^= 0x5c;
+	}
+	/*
+	 * perform inner MD5
+	 */
+#ifdef USE_MD5
+	MD5Init(&context);			/* init context for 1st pass */
+	MD5Update(&context, k_ipad, 64);	/* start with inner pad */
+	MD5Update(&context, text, text_len);	/* then text of datagram */
+	MD5Final(digest, &context);		/* finish up 1st pass */
+#else
+	SHA1_Init(&context);			/* init context for 1st pass */
+	SHA1_Process(&context, k_ipad, 64);	/* start with inner pad */
+	SHA1_Process(&context, text, text_len);	/* then text of datagram */
+	SHA1_Final(&context, digest);		/* finish up 1st pass */
+#endif /* USE_MD5 */
+
+	/*
+	 * perform outer MD5
+	 */
+#ifdef USE_MD5
+	MD5Init(&context);			/* init context for 2nd pass */
+	MD5Update(&context, k_opad, 64);	/* start with outer pad */
+	MD5Update(&context, digest, 16);	/* then results of 1st hash */
+	MD5Final(digest, &context);		/* finish up 2nd pass */
+#else
+	SHA1_Init(&context);			/* init context for 2nd pass */
+	SHA1_Process(&context, k_opad, 64);	/* start with outer pad */
+	SHA1_Process(&context, digest, 20);	/* then results of 1st hash */
+	SHA1_Final(&context, digest);		/* finish up 2nd pass */
+#endif /* USE_MD5 */
+}
+
+void sctp_hash_digest_m(char *key, int key_len, struct mbuf *m, int offset,
+    unsigned char *digest)
+{
+	struct mbuf *m_at;
+#ifdef USE_MD5
+	md5_ctxt context;
+#else
+	struct sha1_context context;
+#endif /* USE_MD5 */
+	/* inner padding - key XORd with ipad */
+	unsigned char k_ipad[65];
+	/* outer padding - key XORd with opad */
+	unsigned char k_opad[65];
+	unsigned char tk[20];
+	int i;
+
+	if (key_len > 64) {
+#ifdef USE_MD5
+		md5_ctxt tctx;
+		MD5Init(&tctx);
+		MD5Update(&tctx, key, key_len);
+		MD5Final(tk, &tctx);
+		key = tk;
+		key_len = 16;
+#else
+		struct sha1_context tctx;
+		SHA1_Init(&tctx);
+		SHA1_Process(&tctx, key, key_len);
+		SHA1_Final(&tctx, tk);
+		key = tk;
+		key_len = 20;
+#endif /* USE_MD5 */
+	}
+
+	/*
+	 * the HMAC_MD5 transform looks like:
+	 *
+	 * MD5(K XOR opad, MD5(K XOR ipad, text))
+	 *
+	 * where K is an n byte key
+	 * ipad is the byte 0x36 repeated 64 times
+	 * opad is the byte 0x5c repeated 64 times
+	 * and text is the data being protected
+	 */
+
+	/* start out by storing key in pads */
+	bzero(k_ipad, sizeof k_ipad);
+	bzero(k_opad, sizeof k_opad);
+	bcopy(key, k_ipad, key_len);
+	bcopy(key, k_opad, key_len);
+
+	/* XOR key with ipad and opad values */
+	for (i = 0; i < 64; i++) {
+		k_ipad[i] ^= 0x36;
+		k_opad[i] ^= 0x5c;
+	}
+
+	/* find the correct mbuf and offset into mbuf */
+	m_at = m;
+	while ((m_at != NULL) && (offset > m_at->m_len)) {
+		offset -= m_at->m_len;	/* update remaining offset left */
+		m_at = m_at->m_next;
+	}
+	/*
+	 * perform inner MD5
+	 */
+#ifdef USE_MD5
+	MD5Init(&context);			/* init context for 1st pass */
+	MD5Update(&context, k_ipad, 64);	/* start with inner pad */
+	/******/
+	while (m_at != NULL) {
+		/* then text of datagram... */
+		MD5Update(&context, mtod(m_at, char *)+offset,
+			  m_at->m_len-offset);
+		/* only offset on the first mbuf */
+		offset = 0;
+		m_at = m_at->m_next;
+	}
+	/******/
+	MD5Final(digest, &context);		/* finish up 1st pass */
+#else
+	SHA1_Init(&context);			/* init context for 1st pass */
+	SHA1_Process(&context, k_ipad, 64);	/* start with inner pad */
+	/******/
+	while (m_at != NULL) {
+		/* then text of datagram */
+		SHA1_Process(&context, mtod(m_at, char *)+offset,
+			     m_at->m_len-offset);
+		/* only offset on the first mbuf */
+		offset = 0;
+		m_at = m_at->m_next;
+	}
+	/******/
+	SHA1_Final(&context, digest);             /* finish up 1st pass */
+#endif /* USE_MD5 */
+
+	/*
+	 * perform outer MD5
+	 */
+#ifdef USE_MD5
+	MD5Init(&context);			/* init context for 2nd pass */
+	MD5Update(&context, k_opad, 64);	/* start with outer pad */
+	MD5Update(&context, digest, 16);	/* then results of 1st hash */
+	MD5Final(digest, &context);		/* finish up 2nd pass */
+#else
+	SHA1_Init(&context);			/* init context for 2nd pass */
+	SHA1_Process(&context, k_opad, 64);	/* start with outer pad */
+	SHA1_Process(&context, digest, 20);	/* then results of 1st hash */
+	SHA1_Final(&context, digest);		/* finish up 2nd pass */
+#endif /* USE_MD5 */
+}
Index: sys/netinet/sctp_hashdriver.h
===================================================================
RCS file: sys/netinet/sctp_hashdriver.h
diff -N sys/netinet/sctp_hashdriver.h
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ sys/netinet/sctp_hashdriver.h	18 Jun 2005 16:05:47 -0000
@@ -0,0 +1,42 @@
+/*	$KAME: sctp_hashdriver.h,v 1.3 2002/10/09 18:01:21 itojun Exp $	*/
+
+#ifndef __sctp_hashdriver_h__
+#define __sctp_hashdriver_h__
+
+/*
+ * Copyright (c) 2001, 2002 Cisco Systems, Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *      This product includes software developed by Cisco Systems, Inc.
+ * 4. Neither the name of the project nor the names of its contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY CISCO SYSTEMS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL CISCO SYSTEMS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+void sctp_hash_digest(char *, int, char *, int, unsigned char *);
+
+void sctp_hash_digest_m(char *, int, struct mbuf *, int, unsigned char *);
+
+#endif
Index: sys/netinet/sctp_header.h
===================================================================
RCS file: sys/netinet/sctp_header.h
diff -N sys/netinet/sctp_header.h
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ sys/netinet/sctp_header.h	18 Jun 2005 16:05:47 -0000
@@ -0,0 +1,488 @@
+/*	$KAME: sctp_header.h,v 1.13 2004/08/17 04:06:16 itojun Exp $	*/
+
+#ifndef __sctp_header_h__
+#define __sctp_header_h__
+
+/*
+ * Copyright (c) 2001, 2002, 2003, 2004 Cisco Systems, Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *      This product includes software developed by Cisco Systems, Inc.
+ * 4. Neither the name of the project nor the names of its contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY CISCO SYSTEMS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL CISCO SYSTEMS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/time.h>
+#include <netinet/sctp.h>
+#include <netinet/sctp_constants.h>
+
+/*
+ * Parameter structures
+ */
+struct sctp_ipv4addr_param {
+	struct sctp_paramhdr ph;	/* type=SCTP_IPV4_PARAM_TYPE, len=8 */
+	u_int32_t addr;			/* IPV4 address */
+};
+
+struct sctp_ipv6addr_param {
+	struct sctp_paramhdr ph;	/* type=SCTP_IPV6_PARAM_TYPE, len=20 */
+	u_int8_t  addr[16];		/* IPV6 address */
+};
+
+/* Cookie Preservative */
+struct sctp_cookie_perserve_param {
+	struct sctp_paramhdr ph;	/* type=SCTP_COOKIE_PRESERVE, len=8 */
+	u_int32_t time;			/* time in ms to extend cookie */
+};
+
+/* Host Name Address */
+struct sctp_host_name_param {
+	struct sctp_paramhdr ph;	/* type=SCTP_HOSTNAME_ADDRESS */
+	char name[1];			/* host name */
+};
+
+/* supported address type */
+struct sctp_supported_addr_param {
+	struct sctp_paramhdr ph;	/* type=SCTP_SUPPORTED_ADDRTYPE */
+	u_int16_t addr_type[1];		/* array of supported address types */
+};
+
+/* ECN parameter */
+struct sctp_ecn_supported_param {
+	struct sctp_paramhdr ph;	/* type=SCTP_ECN_CAPABLE */
+};
+
+
+/* heartbeat info parameter */
+struct sctp_heartbeat_info_param {
+	struct sctp_paramhdr ph;
+	u_int32_t time_value_1;
+	u_int32_t time_value_2;
+	u_int32_t random_value1;
+	u_int32_t random_value2;
+	u_int16_t user_req;
+	u_int8_t addr_family;
+	u_int8_t addr_len;
+	char address[SCTP_ADDRMAX];
+};
+
+
+/* draft-ietf-tsvwg-prsctp */
+/* PR-SCTP supported parameter */
+struct sctp_prsctp_supported_param {
+	struct sctp_paramhdr ph;
+};
+
+
+/* draft-ietf-tsvwg-addip-sctp */
+struct sctp_asconf_paramhdr {		/* an ASCONF "parameter" */
+	struct sctp_paramhdr ph;	/* a SCTP parameter header */
+	u_int32_t correlation_id;	/* correlation id for this param */
+};
+
+struct sctp_asconf_addr_param {		/* an ASCONF address parameter */
+	struct sctp_asconf_paramhdr aph;	/* asconf "parameter" */
+	struct sctp_ipv6addr_param  addrp;	/* max storage size */
+};
+
+struct sctp_asconf_addrv4_param {		/* an ASCONF address (v4) parameter */
+	struct sctp_asconf_paramhdr aph;	/* asconf "parameter" */
+	struct sctp_ipv4addr_param  addrp;	/* max storage size */
+};
+
+
+/* ECN Nonce: draft-ladha-sctp-ecn-nonce */
+struct sctp_ecn_nonce_supported_param {
+	struct sctp_paramhdr ph;	/* type = 0x8001  len = 4 */
+};
+
+struct sctp_supported_chunk_types_param {
+	struct sctp_paramhdr ph;	/* type = 0x8002  len = x */
+	u_int8_t chunk_types[0];
+};
+
+/*
+ * Structures for DATA chunks
+ */
+struct sctp_data {
+	u_int32_t tsn;
+	u_int16_t stream_id;
+	u_int16_t stream_sequence;
+	u_int32_t protocol_id;
+	/* user data follows */
+};
+
+struct sctp_data_chunk {
+	struct sctp_chunkhdr ch;
+	struct sctp_data dp;
+};
+
+/*
+ * Structures for the control chunks
+ */
+
+/* Initiate (INIT)/Initiate Ack (INIT ACK) */
+struct sctp_init {
+	u_int32_t initiate_tag;		/* initiate tag */
+	u_int32_t a_rwnd;		/* a_rwnd */
+	u_int16_t num_outbound_streams;	/* OS */
+	u_int16_t num_inbound_streams;	/* MIS */
+	u_int32_t initial_tsn;		/* I-TSN */
+	/* optional param's follow */
+};
+
+/* state cookie header */
+struct sctp_state_cookie {		/* this is our definition... */
+	u_int8_t  identification[16];	/* id of who we are */
+	u_int32_t cookie_life;		/* life I will award this cookie */
+	u_int32_t tie_tag_my_vtag;	/* my tag in old association */
+	u_int32_t tie_tag_peer_vtag;	/* peers tag in old association */
+	u_int32_t peers_vtag;		/* peers tag in INIT (for quick ref) */
+	u_int32_t my_vtag;		/* my tag in INIT-ACK (for quick ref) */
+	struct timeval time_entered;	/* the time I built cookie */
+	u_int32_t address[4];		/* 4 ints/128 bits */
+	u_int32_t addr_type;		/* address type */
+	u_int32_t laddress[4];		/* my local from address */
+	u_int32_t laddr_type;		/* my local from address type */
+	u_int32_t scope_id;		/* v6 scope id for link-locals */
+	u_int16_t peerport;		/* port address of the peer in the INIT */
+	u_int16_t myport;		/* my port address used in the INIT */
+	u_int8_t ipv4_addr_legal;	/* Are V4 addr legal? */
+	u_int8_t ipv6_addr_legal;	/* Are V6 addr legal? */
+	u_int8_t local_scope;		/* IPv6 local scope flag */
+	u_int8_t site_scope;		/* IPv6 site scope flag */
+	u_int8_t ipv4_scope;		/* IPv4 private addr scope */
+	u_int8_t loopback_scope;	/* loopback scope information */
+	u_int16_t reserved;
+	/*
+	 * at the end is tacked on the INIT chunk and the
+	 * INIT-ACK chunk (minus the cookie).
+	 */
+};
+
+struct sctp_inv_mandatory_param {
+	u_int16_t cause;
+	u_int16_t length;
+	u_int32_t num_param;
+	u_int16_t param;
+	/*
+	 * We include this to 0 it since only a missing cookie
+	 * will cause this error.
+	 */
+	u_int16_t resv;
+};
+
+struct sctp_unresolv_addr {
+	u_int16_t cause;
+	u_int16_t length;
+	u_int16_t addr_type;
+	u_int16_t reserved;	/* Only one invalid addr type */
+};
+
+/* state cookie parameter */
+struct sctp_state_cookie_param {
+	struct sctp_paramhdr ph;
+	struct sctp_state_cookie cookie;
+};
+
+struct sctp_init_chunk {
+	struct sctp_chunkhdr ch;
+	struct sctp_init init;
+};
+
+struct sctp_init_msg {
+	struct sctphdr sh;
+	struct sctp_init_chunk msg;
+};
+/* ... used for both INIT and INIT ACK */
+#define sctp_init_ack		sctp_init
+#define sctp_init_ack_chunk	sctp_init_chunk
+#define sctp_init_ack_msg	sctp_init_msg
+
+
+/* Selective Ack (SACK) */
+struct sctp_gap_ack_block {
+	u_int16_t start;		/* Gap Ack block start */
+	u_int16_t end;			/* Gap Ack block end */
+};
+
+struct sctp_sack {
+	u_int32_t cum_tsn_ack;		/* cumulative TSN Ack */
+	u_int32_t a_rwnd;		/* updated a_rwnd of sender */
+	u_int16_t num_gap_ack_blks;	/* number of Gap Ack blocks */
+	u_int16_t num_dup_tsns;		/* number of duplicate TSNs */
+	/* struct sctp_gap_ack_block's follow */
+	/* u_int32_t duplicate_tsn's follow */
+};
+
+struct sctp_sack_chunk {
+	struct sctp_chunkhdr ch;
+	struct sctp_sack sack;
+};
+
+
+/* Heartbeat Request (HEARTBEAT) */
+struct sctp_heartbeat {
+	struct sctp_heartbeat_info_param hb_info;
+};
+
+struct sctp_heartbeat_chunk {
+	struct sctp_chunkhdr ch;
+	struct sctp_heartbeat heartbeat;
+};
+/* ... used for Heartbeat Ack (HEARTBEAT ACK) */
+#define sctp_heartbeat_ack		sctp_heartbeat
+#define sctp_heartbeat_ack_chunk	sctp_heartbeat_chunk
+
+
+/* Abort Asssociation (ABORT) */
+struct sctp_abort_chunk {
+	struct sctp_chunkhdr ch;
+	/* optional error cause may follow */
+};
+
+struct sctp_abort_msg {
+	struct sctphdr sh;
+	struct sctp_abort_chunk msg;
+};
+
+
+/* Shutdown Association (SHUTDOWN) */
+struct sctp_shutdown_chunk {
+	struct sctp_chunkhdr ch;
+	u_int32_t cumulative_tsn_ack;
+};
+
+
+/* Shutdown Acknowledgment (SHUTDOWN ACK) */
+struct sctp_shutdown_ack_chunk {
+	struct sctp_chunkhdr ch;
+};
+
+
+/* Operation Error (ERROR) */
+struct sctp_error_chunk {
+	struct sctp_chunkhdr ch;
+	/* optional error causes follow */
+};
+
+
+/* Cookie Echo (COOKIE ECHO) */
+struct sctp_cookie_echo_chunk {
+	struct sctp_chunkhdr ch;
+	struct sctp_state_cookie cookie;
+};
+
+/* Cookie Acknowledgment (COOKIE ACK) */
+struct sctp_cookie_ack_chunk {
+	struct sctp_chunkhdr ch;
+};
+
+/* Explicit Congestion Notification Echo (ECNE) */
+struct sctp_ecne_chunk {
+	struct sctp_chunkhdr ch;
+	u_int32_t tsn;
+};
+
+/* Congestion Window Reduced (CWR) */
+struct sctp_cwr_chunk {
+	struct sctp_chunkhdr ch;
+	u_int32_t tsn;
+};
+
+/* Shutdown Complete (SHUTDOWN COMPLETE) */
+struct sctp_shutdown_complete_chunk {
+	struct sctp_chunkhdr ch;
+};
+
+/* Oper error holding a stale cookie */
+struct sctp_stale_cookie_msg {
+	struct sctp_paramhdr ph;	/* really an error cause */
+	u_int32_t time_usec;
+};
+
+struct sctp_adaption_layer_indication {
+	struct sctp_paramhdr ph;
+	u_int32_t indication;
+};
+
+struct sctp_cookie_while_shutting_down {
+	struct sctphdr sh;
+	struct sctp_chunkhdr ch;
+	struct sctp_paramhdr ph;	/* really an error cause */
+};
+
+struct sctp_shutdown_complete_msg {
+	struct sctphdr sh;
+	struct sctp_shutdown_complete_chunk shut_cmp;
+};
+
+/* draft-ietf-tsvwg-addip-sctp */
+/* Address/Stream Configuration Change (ASCONF) */
+struct sctp_asconf_chunk {
+	struct sctp_chunkhdr ch;
+	u_int32_t serial_number;
+	/* lookup address parameter (mandatory) */
+	/* asconf parameters follow */
+};
+
+/* Address/Stream Configuration Acknowledge (ASCONF ACK) */
+struct sctp_asconf_ack_chunk {
+	struct sctp_chunkhdr ch;
+	u_int32_t serial_number;
+	/* asconf parameters follow */
+};
+
+/* draft-ietf-tsvwg-prsctp */
+/* Forward Cumulative TSN (FORWARD TSN) */
+struct sctp_forward_tsn_chunk {
+	struct sctp_chunkhdr ch;
+	u_int32_t new_cumulative_tsn;
+	/* stream/sequence pairs (sctp_strseq) follow */
+};
+
+struct sctp_strseq {
+	u_int16_t stream;
+	u_int16_t sequence;
+};
+
+struct sctp_forward_tsn_msg {
+	struct sctphdr sh;
+	struct sctp_forward_tsn_chunk msg;
+};
+
+/* should be a multiple of 4 - 1 aka 3/7/11 etc. */
+
+#define SCTP_NUM_DB_TO_VERIFY 3
+
+struct sctp_chunk_desc {
+	u_int8_t chunk_type;
+	u_int8_t data_bytes[SCTP_NUM_DB_TO_VERIFY];
+	u_int32_t tsn_ifany;
+};
+
+
+struct sctp_pktdrop_chunk {
+	struct sctp_chunkhdr ch;
+	u_int32_t bottle_bw;
+	u_int32_t current_onq;
+	u_int16_t trunc_len;
+	u_int16_t reserved;
+	u_int8_t data[0];
+};
+
+#define SCTP_RESET_YOUR  0x01   /* reset your streams and send response */
+#define SCTP_RESET_ALL   0x02   /* reset all of your streams */
+#define SCTP_RECIPRICAL  0x04   /* reset my streams too */
+
+struct sctp_stream_reset_request {
+	struct sctp_paramhdr ph;
+	u_int8_t reset_flags;		   /* actual request */
+	u_int8_t reset_pad[3];
+	u_int32_t reset_req_seq;           /* monotonically increasing seq no */
+	u_int16_t list_of_streams[0];      /* if not all list of streams */
+};
+
+#define SCTP_RESET_PERFORMED        0x01   /* Peers sending str was reset */
+#define SCTP_RESET_DENIED           0x02   /* Asked for but refused       */
+
+struct sctp_stream_reset_response {
+	struct sctp_paramhdr ph;
+	u_int8_t reset_flags;		   /* actual request */
+	u_int8_t reset_pad[3];
+	u_int32_t reset_req_seq_resp;   /* copied from reset_req reset_req_seq */
+	u_int32_t reset_at_tsn;		/* resetters next TSN to be assigned send wise */
+	u_int32_t cumulative_tsn;	/* resetters cum-ack point receive wise */
+	u_int16_t list_of_streams[0];   /* if not all list of streams */
+};
+
+/* convience structures, note that if you
+ * are making a request for specific streams
+ * then the request will need to be an overlay
+ * structure.
+ */
+
+struct sctp_stream_reset_req {
+	struct sctp_chunkhdr ch;
+	struct sctp_stream_reset_request sr_req;
+};
+
+struct sctp_stream_reset_resp {
+	struct sctp_chunkhdr ch;
+	struct sctp_stream_reset_response sr_resp;
+};
+
+
+/*
+ * we pre-reserve enough room for a ECNE or CWR AND a SACK with no
+ * missing pieces. If ENCE is missing we could have a couple of blocks.
+ * This way we optimize so we MOST likely can bundle a SACK/ECN with
+ * the smallest size data chunk I will split into. We could increase
+ * throughput slightly by taking out these two but the  24-sack/8-CWR
+ * i.e. 32 bytes I pre-reserve I feel is worth it for now.
+ */
+#ifndef SCTP_MAX_OVERHEAD
+#ifdef AF_INET6
+#define SCTP_MAX_OVERHEAD (sizeof(struct sctp_data_chunk) + \
+			   sizeof(struct sctphdr) + \
+			   sizeof(struct sctp_ecne_chunk) + \
+			   sizeof(struct sctp_sack_chunk) + \
+			   sizeof(struct ip6_hdr))
+
+#define SCTP_MED_OVERHEAD (sizeof(struct sctp_data_chunk) + \
+			   sizeof(struct sctphdr) + \
+			   sizeof(struct ip6_hdr))
+
+
+#define SCTP_MIN_OVERHEAD (sizeof(struct ip6_hdr) + \
+			   sizeof(struct sctphdr))
+
+#else
+#define SCTP_MAX_OVERHEAD (sizeof(struct sctp_data_chunk) + \
+			   sizeof(struct sctphdr) + \
+			   sizeof(struct sctp_ecne_chunk) + \
+			   sizeof(struct sctp_sack_chunk) + \
+			   sizeof(struct ip))
+
+#define SCTP_MED_OVERHEAD (sizeof(struct sctp_data_chunk) + \
+			   sizeof(struct sctphdr) + \
+			   sizeof(struct ip))
+
+
+#define SCTP_MIN_OVERHEAD (sizeof(struct ip) + \
+			   sizeof(struct sctphdr))
+
+#endif /* AF_INET6 */
+#endif /* !SCTP_MAX_OVERHEAD */
+
+#define SCTP_MED_V4_OVERHEAD (sizeof(struct sctp_data_chunk) + \
+			      sizeof(struct sctphdr) + \
+			      sizeof(struct ip))
+
+#define SCTP_MIN_V4_OVERHEAD (sizeof(struct ip) + \
+			      sizeof(struct sctphdr))
+
+#endif /* !__sctp_header_h__ */
Index: sys/netinet/sctp_indata.c
===================================================================
RCS file: sys/netinet/sctp_indata.c
diff -N sys/netinet/sctp_indata.c
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ sys/netinet/sctp_indata.c	18 Jun 2005 16:05:47 -0000
@@ -0,0 +1,4756 @@
+/*	$KAME: sctp_indata.c,v 1.35 2004/08/17 04:06:17 itojun Exp $	*/
+
+/*
+ * Copyright (C) 2002, 2003, 2004 Cisco Systems Inc,
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the project nor the names of its contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#if !(defined(__OpenBSD__) || defined(__APPLE__))
+#include "opt_ipsec.h"
+#endif
+#if defined(__FreeBSD__) || defined(__DragonFly__)
+#include "opt_inet6.h"
+#include "opt_inet.h"
+#endif
+#if defined(__NetBSD__)
+#include "opt_inet.h"
+#endif
+
+#ifdef __APPLE__
+#include <sctp.h>
+#elif !defined(__OpenBSD__)
+#include "opt_sctp.h"
+#endif
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/mbuf.h>
+#include <sys/malloc.h>
+#include <sys/socket.h>
+#include <sys/socketvar.h>
+#include <sys/sysctl.h>
+
+#include <net/if.h>
+#include <net/route.h>
+
+
+#if defined(__FreeBSD__) && __FreeBSD_version >= 500000
+#include <sys/limits.h>
+#else
+#include <machine/limits.h>
+#endif
+#include <machine/cpu.h>
+
+#include <netinet/in.h>
+#include <netinet/in_systm.h>
+#include <netinet/ip.h>
+#ifdef INET6
+#include <netinet/ip6.h>
+#endif /* INET6 */
+#include <netinet/in_pcb.h>
+#include <netinet/in_var.h>
+#include <netinet/ip_var.h>
+#ifdef INET6
+#include <netinet6/ip6_var.h>
+#endif /* INET6 */
+#include <netinet/ip_icmp.h>
+#include <netinet/icmp_var.h>
+#include <netinet/sctp_var.h>
+#include <netinet/sctp_pcb.h>
+#include <netinet/sctp_header.h>
+#include <netinet/sctputil.h>
+#include <netinet/sctp_output.h>
+#include <netinet/sctp_input.h>
+#include <netinet/sctp_hashdriver.h>
+#include <netinet/sctp_indata.h>
+#include <netinet/sctp_uio.h>
+#include <netinet/sctp_timer.h>
+#ifdef IPSEC
+#ifndef __OpenBSD__
+#include <netinet6/ipsec.h>
+#include <netkey/key.h>
+#else
+#undef IPSEC
+#endif
+#endif /*IPSEC*/
+
+#include <net/net_osdep.h>
+
+#ifdef SCTP_DEBUG
+extern u_int32_t sctp_debug_on;
+#endif
+
+/*
+ * NOTES: On the outbound side of things I need to check the sack timer to
+ * see if I should generate a sack into the chunk queue (if I have data to
+ * send that is and will be sending it .. for bundling.
+ *
+ * The callback in sctp_usrreq.c will get called when the socket is read
+ * from. This will cause sctp_service_queues() to get called on the top
+ * entry in the list.
+ */
+
+extern int sctp_strict_sacks;
+
+void
+sctp_set_rwnd(struct sctp_tcb *stcb, struct sctp_association *asoc)
+{
+	u_int32_t calc, calc_w_oh;
+
+#ifdef SCTP_DEBUG
+	if (sctp_debug_on & SCTP_DEBUG_INDATA4) {
+		printf("cc:%lu hiwat:%lu lowat:%lu mbcnt:%lu mbmax:%lu\n",
+		       (u_long)stcb->sctp_socket->so_rcv.sb_cc,
+		       (u_long)stcb->sctp_socket->so_rcv.sb_hiwat,
+		       (u_long)stcb->sctp_socket->so_rcv.sb_lowat,
+		       (u_long)stcb->sctp_socket->so_rcv.sb_mbcnt,
+		       (u_long)stcb->sctp_socket->so_rcv.sb_mbmax);
+		printf("Setting rwnd to: sb:%ld - (del:%d + reasm:%d str:%d)\n",
+		       sctp_sbspace(&stcb->sctp_socket->so_rcv),
+		       asoc->size_on_delivery_queue,
+		       asoc->size_on_reasm_queue,
+		       asoc->size_on_all_streams);
+	}
+#endif
+	if (stcb->sctp_socket->so_rcv.sb_cc == 0 &&
+	    asoc->size_on_delivery_queue == 0 &&
+	    asoc->size_on_reasm_queue == 0 &&
+	    asoc->size_on_all_streams == 0) {
+		/* Full rwnd granted */
+		asoc->my_rwnd = max(stcb->sctp_socket->so_rcv.sb_hiwat,
+				    SCTP_MINIMAL_RWND);
+		return;
+	}
+	/* get actual space */
+	calc = (u_int32_t)sctp_sbspace(&stcb->sctp_socket->so_rcv);
+
+	/* take out what has NOT been put on socket queue and
+	 * we yet hold for putting up.
+	 */
+	calc = sctp_sbspace_sub(calc, (u_int32_t)asoc->size_on_delivery_queue);
+	calc = sctp_sbspace_sub(calc, (u_int32_t)asoc->size_on_reasm_queue);
+	calc = sctp_sbspace_sub(calc, (u_int32_t)asoc->size_on_all_streams);
+
+	/* what is the overhead of all these rwnd's */
+	calc_w_oh = sctp_sbspace_sub(calc, stcb->asoc.my_rwnd_control_len);
+
+	asoc->my_rwnd = calc;
+	if (calc_w_oh == 0) {
+		/* If our overhead is greater than the advertised
+		 * rwnd, we clamp the rwnd to 1. This lets us
+		 * still accept inbound segments, but hopefully will
+		 * shut the sender down when he finally gets the message.
+		 */
+ 		asoc->my_rwnd = 1;
+	} else {
+		/* SWS threshold */
+		if (asoc->my_rwnd &&
+		    (asoc->my_rwnd < stcb->sctp_ep->sctp_ep.sctp_sws_receiver)) {
+			/* SWS engaged, tell peer none left */
+			asoc->my_rwnd = 1;
+#ifdef SCTP_DEBUG
+			if (sctp_debug_on & SCTP_DEBUG_INDATA4) {
+				printf(" - SWS zeros\n");
+			}
+		} else {
+			if (sctp_debug_on & SCTP_DEBUG_INDATA4) {
+				printf("\n");
+			}
+#endif
+		}
+	}
+}
+
+/*
+ * Take a chk structure and build it into an mbuf. Hmm should we change things
+ * so that instead we store the data side in a chunk?
+ */
+static struct mbuf *
+sctp_build_ctl_nchunk(struct sctp_tcb *stcb, uint32_t tsn, uint32_t ppid,
+    uint32_t context, uint16_t stream_no, uint16_t stream_seq, uint8_t flags)
+{
+	struct sctp_sndrcvinfo *outinfo;
+	struct cmsghdr *cmh;
+	struct mbuf *ret;
+
+	if ((stcb->sctp_ep->sctp_flags & SCTP_PCB_FLAGS_RECVDATAIOEVNT) == 0) {
+		/* user does not want the sndrcv ctl */
+		return (NULL);
+	}
+
+	MGETHDR(ret, MB_DONTWAIT, MT_CONTROL);
+	if (ret == NULL) {
+		/* No space */
+		return (ret);
+	}
+	/* We need a CMSG header followed by the struct  */
+	cmh = mtod(ret, struct cmsghdr *);
+	outinfo = (struct sctp_sndrcvinfo *)CMSG_DATA(cmh);
+	cmh->cmsg_level = IPPROTO_SCTP;
+	cmh->cmsg_type = SCTP_SNDRCV;
+	cmh->cmsg_len = CMSG_LEN(sizeof(struct sctp_sndrcvinfo));
+	outinfo->sinfo_stream = stream_no;
+	outinfo->sinfo_ssn = stream_seq;
+	if (flags & SCTP_DATA_UNORDERED) {
+		outinfo->sinfo_flags = MSG_UNORDERED;
+	} else {
+		outinfo->sinfo_flags = 0;
+	}
+	outinfo->sinfo_ppid = ppid;
+	outinfo->sinfo_context = context;
+	outinfo->sinfo_assoc_id = sctp_get_associd(stcb);
+	outinfo->sinfo_tsn = tsn;
+	outinfo->sinfo_cumtsn = stcb->asoc.cumulative_tsn;
+	ret->m_len = cmh->cmsg_len;
+	ret->m_pkthdr.len = ret->m_len;
+	/*
+	 * We track how many control len's have gone upon the sb
+	 * and do not count these in the rwnd calculation.
+	 */
+	stcb->asoc.my_rwnd_control_len +=
+	    CMSG_LEN(sizeof(struct sctp_sndrcvinfo));
+
+	return (ret);
+}
+
+/*
+ * Take a chk structure and build it into an mbuf.  Should we change things
+ * so that instead we store the data side in a chunk?
+ */
+static
+struct mbuf *
+sctp_build_ctl(struct sctp_tcb *stcb, struct sctp_tmit_chunk *chk)
+{
+	struct sctp_sndrcvinfo *outinfo;
+	struct cmsghdr *cmh;
+	struct mbuf *ret;
+	if ((stcb->sctp_ep->sctp_flags & SCTP_PCB_FLAGS_RECVDATAIOEVNT) == 0) {
+		/* user does not want the sndrcv ctl */
+		return (NULL);
+	}
+	MGET(ret, MB_DONTWAIT, MT_CONTROL);
+	if (ret == NULL) {
+		/* No space */
+		return (ret);
+	}
+
+	/* We need a CMSG header followed by the struct  */
+	cmh = mtod(ret, struct cmsghdr *);
+	outinfo = (struct sctp_sndrcvinfo *)CMSG_DATA(cmh);
+	cmh->cmsg_level = IPPROTO_SCTP;
+	cmh->cmsg_type = SCTP_SNDRCV;
+	cmh->cmsg_len = CMSG_LEN(sizeof(struct sctp_sndrcvinfo));
+	outinfo->sinfo_stream = chk->rec.data.stream_number;
+	outinfo->sinfo_ssn = chk->rec.data.stream_seq;
+	if (chk->rec.data.rcv_flags & SCTP_DATA_UNORDERED) {
+		outinfo->sinfo_flags = MSG_UNORDERED;
+	} else {
+		outinfo->sinfo_flags = 0;
+	}
+	outinfo->sinfo_ppid = chk->rec.data.payloadtype;
+	outinfo->sinfo_context = chk->rec.data.context;
+	outinfo->sinfo_assoc_id = sctp_get_associd(stcb);
+	outinfo->sinfo_tsn = chk->rec.data.TSN_seq;
+	outinfo->sinfo_cumtsn = stcb->asoc.cumulative_tsn;
+	ret->m_len = cmh->cmsg_len;
+	stcb->asoc.my_rwnd_control_len +=
+	    CMSG_LEN(sizeof(struct sctp_sndrcvinfo));
+
+	return (ret);
+}
+
+int
+sctp_deliver_data(struct sctp_tcb *stcb, struct sctp_association *asoc,
+    struct sctp_tmit_chunk *chk, int hold_locks)
+{
+	struct mbuf *control, *m;
+	int free_it;
+	struct sockaddr_in6 sin6;
+	struct sockaddr *to;
+
+#ifdef SCTP_DEBUG
+	if (sctp_debug_on & SCTP_DEBUG_INDATA1) {
+		printf("I am now in Deliver data! (%p)\n", chk);
+	}
+#endif
+	/* get a write lock on the inp if not already */
+	if (hold_locks == 0) {
+		SCTP_TCB_UNLOCK(stcb);
+		SCTP_INP_WLOCK(stcb->sctp_ep);
+		SCTP_TCB_LOCK(stcb);
+	}
+	free_it = 0;
+	/* We always add it to the queue */
+	if (stcb && (stcb->sctp_ep->sctp_flags & SCTP_PCB_FLAGS_SOCKET_GONE)) {
+		/* socket above is long gone */
+#ifdef SCTP_DEBUG
+		if (sctp_debug_on & SCTP_DEBUG_INDATA1) {
+			printf("gone is gone!\n");
+		}
+#endif
+		if (chk != NULL) {
+			if (chk->data)
+				sctp_m_freem(chk->data);
+			chk->data = NULL;
+			sctp_free_remote_addr(chk->whoTo);
+			SCTP_ZONE_FREE(sctppcbinfo.ipi_zone_chunk, chk);
+			sctppcbinfo.ipi_count_chunk--;
+			if ((int)sctppcbinfo.ipi_count_chunk < 0) {
+				panic("Chunk count is negative");
+			}
+			sctppcbinfo.ipi_gencnt_chunk++;
+		}
+		TAILQ_FOREACH(chk, &asoc->delivery_queue, sctp_next) {
+			asoc->size_on_delivery_queue -= chk->send_size;
+			asoc->cnt_on_delivery_queue--;
+			/*
+			 * Lose the data pointer, since its in the socket buffer
+			 */
+			if (chk->data)
+				sctp_m_freem(chk->data);
+			chk->data = NULL;
+			/* Now free the address and data */
+			sctp_free_remote_addr(chk->whoTo);
+			SCTP_ZONE_FREE(sctppcbinfo.ipi_zone_chunk, chk);
+			sctppcbinfo.ipi_count_chunk--;
+			if ((int)sctppcbinfo.ipi_count_chunk < 0) {
+				panic("Chunk count is negative");
+			}
+			sctppcbinfo.ipi_gencnt_chunk++;
+		}
+		if (hold_locks == 0)
+			SCTP_INP_WUNLOCK(stcb->sctp_ep);
+		return (0);
+	}
+	if (chk != NULL) {
+		TAILQ_INSERT_TAIL(&asoc->delivery_queue, chk, sctp_next);
+		asoc->size_on_delivery_queue += chk->send_size;
+		asoc->cnt_on_delivery_queue++;
+	}
+	if (asoc->fragmented_delivery_inprogress) {
+		/*
+		 * oh oh, fragmented delivery in progress
+		 * return out of here.
+		 */
+#ifdef SCTP_DEBUG
+		if (sctp_debug_on & SCTP_DEBUG_INDATA1) {
+			printf("Fragmented delivery in progress?\n");
+		}
+#endif
+		if (hold_locks == 0)
+			SCTP_INP_WUNLOCK(stcb->sctp_ep);
+		return (0);
+	}
+	/* Now grab the first one  */
+	chk = TAILQ_FIRST(&asoc->delivery_queue);
+	if (chk == NULL) {
+		/* Nothing in queue */
+#ifdef SCTP_DEBUG
+		if (sctp_debug_on & SCTP_DEBUG_INDATA1) {
+			printf("Nothing in queue?\n");
+		}
+#endif
+		asoc->size_on_delivery_queue = 0;
+		asoc->cnt_on_delivery_queue = 0;
+		if (hold_locks == 0)
+			SCTP_INP_WUNLOCK(stcb->sctp_ep);
+		return (0);
+	}
+
+	if (stcb->sctp_socket->so_rcv.sb_cc >= stcb->sctp_socket->so_rcv.sb_hiwat) {
+		/* Boy, there really is NO room */
+		if (hold_locks == 0)
+			SCTP_INP_WUNLOCK(stcb->sctp_ep);
+		return (0);
+	}
+#ifdef SCTP_DEBUG
+	if (sctp_debug_on & SCTP_DEBUG_INDATA1) {
+		printf("Now to the delivery with chk(%p)!\n", chk);
+	}
+#endif
+	/* XXX need to append PKTHDR to the socket buffer first */
+	if ((chk->data->m_flags & M_PKTHDR) == 0) {
+		MGETHDR(m, MB_DONTWAIT, MT_DATA);
+		if (m == NULL) {
+			/* no room! */
+			if (hold_locks == 0)
+				SCTP_INP_WUNLOCK(stcb->sctp_ep);
+			return (0);
+		}
+		m->m_pkthdr.len = chk->send_size;
+		m->m_len = 0;
+		m->m_next = chk->data;
+		chk->data = m;
+	}
+	if (chk->rec.data.rcv_flags & SCTP_DATA_LAST_FRAG) {
+		if (chk->data->m_next == NULL) {
+			/* hopefully we hit here most of the time */
+			chk->data->m_flags |= M_EOR;
+		} else {
+			/* Add the flag to the LAST mbuf in the chain */
+			m = chk->data;
+			while (m->m_next != NULL) {
+				m = m->m_next;
+			}
+			m->m_flags |= M_EOR;
+		}
+	}
+
+	if (chk->rec.data.rcv_flags & SCTP_DATA_FIRST_FRAG) {
+		struct sockaddr_in6 lsa6;
+
+		control = sctp_build_ctl(stcb, chk);
+		to = (struct sockaddr *)&chk->whoTo->ro._l_addr;
+		if ((stcb->sctp_ep->sctp_flags & SCTP_PCB_FLAGS_NEEDS_MAPPED_V4) &&
+		    to->sa_family == AF_INET) {
+			struct sockaddr_in *sin;
+
+			sin = (struct sockaddr_in *)to;
+			bzero(&sin6, sizeof(sin6));
+			sin6.sin6_family = AF_INET6;
+			sin6.sin6_len = sizeof(struct sockaddr_in6);
+			sin6.sin6_addr.s6_addr16[2] = 0xffff;
+			bcopy(&sin->sin_addr, &sin6.sin6_addr.s6_addr16[3],
+			    sizeof(sin6.sin6_addr.s6_addr16[3]));
+			sin6.sin6_port = sin->sin_port;
+			to = (struct sockaddr *)&sin6;
+		}
+		/* check and strip embedded scope junk */
+		to = (struct sockaddr *)sctp_recover_scope((struct sockaddr_in6 *)to,
+		    &lsa6);
+		if (((struct sockaddr_in *)to)->sin_port == 0) {
+			printf("Huh a, port is %d not net:%x %d?\n",
+			       ((struct sockaddr_in *)to)->sin_port,
+			       (u_int)chk->whoTo,
+			       (int)(ntohs(stcb->rport)));
+			((struct sockaddr_in *)to)->sin_port = stcb->rport;
+		}
+		if (sctp_sbspace(&stcb->sctp_socket->so_rcv) < (long)chk->send_size) {
+			/* Gak not enough room */
+			if (control) {
+				sctp_m_freem(control);
+				stcb->asoc.my_rwnd_control_len -=
+				    CMSG_LEN(sizeof(struct sctp_sndrcvinfo));
+			}
+			goto skip;
+		}
+		if (!sbappendaddr_nocheck(&stcb->sctp_socket->so_rcv,
+		    to, chk->data, control, stcb->asoc.my_vtag,
+		    stcb->sctp_ep)) {
+			/* Gak not enough room */
+			if (control) {
+				sctp_m_freem(control);
+				stcb->asoc.my_rwnd_control_len -=
+				    CMSG_LEN(sizeof(struct sctp_sndrcvinfo));
+			}
+		} else {
+			if ((stcb->sctp_ep->sctp_flags & SCTP_PCB_FLAGS_IN_TCPPOOL) == 0) {
+				if (sctp_add_to_socket_q(stcb->sctp_ep, stcb)) {
+					stcb->asoc.my_rwnd_control_len +=
+						sizeof(struct mbuf);
+				}
+			} else {
+				stcb->asoc.my_rwnd_control_len += sizeof(struct mbuf);
+			}
+			free_it = 1;
+		}
+	} else {
+		/* append to a already started message. */
+		if (sctp_sbspace(&stcb->sctp_socket->so_rcv) >=
+		    (long)chk->send_size) {
+			sbappend(&stcb->sctp_socket->so_rcv, chk->data);
+			free_it = 1;
+		}
+	}
+ skip:
+	if (hold_locks == 0)
+		SCTP_INP_WUNLOCK(stcb->sctp_ep);
+	/* free up the one we inserted */
+	if (free_it) {
+		/* Pull it off the queue */
+#ifdef SCTP_DEBUG
+		if (sctp_debug_on & SCTP_DEBUG_INDATA1) {
+			printf("Free_it true, doing tickle wakeup\n");
+		}
+#endif
+		sctp_sorwakeup(stcb->sctp_ep, stcb->sctp_socket);
+		TAILQ_REMOVE(&asoc->delivery_queue, chk, sctp_next);
+		asoc->size_on_delivery_queue -= chk->send_size;
+		asoc->cnt_on_delivery_queue--;
+		/* Lose the data pointer, since its in the socket buffer */
+		chk->data = NULL;
+		/* Now free the address and data */
+		sctp_free_remote_addr(chk->whoTo);
+		SCTP_ZONE_FREE(sctppcbinfo.ipi_zone_chunk, chk);
+		sctppcbinfo.ipi_count_chunk--;
+		if ((int)sctppcbinfo.ipi_count_chunk < 0) {
+			panic("Chunk count is negative");
+		}
+		sctppcbinfo.ipi_gencnt_chunk++;
+	}
+	return (free_it);
+}
+
+/*
+ * We are delivering currently from the reassembly queue. We must continue to
+ * deliver until we either:
+ * 1) run out of space.
+ * 2) run out of sequential TSN's
+ * 3) hit the SCTP_DATA_LAST_FRAG flag.
+ */
+static void
+sctp_service_reassembly(struct sctp_tcb *stcb, struct sctp_association *asoc, int hold_locks)
+{
+	struct sockaddr *to;
+	struct sockaddr_in6 sin6;
+	struct sctp_tmit_chunk *chk, *at;
+	struct mbuf *control, *m;
+	u_int16_t nxt_todel;
+	u_int16_t stream_no;
+	int cntDel;
+	cntDel = stream_no = 0;
+	if (hold_locks == 0) {
+		/*
+		 * you always have the TCB lock, we need
+		 * to have the inp write lock as well.
+		 */
+		SCTP_TCB_UNLOCK(stcb);
+		SCTP_INP_WLOCK(stcb->sctp_ep);
+		SCTP_TCB_LOCK(stcb);
+	}
+	if (stcb && (stcb->sctp_ep->sctp_flags & SCTP_PCB_FLAGS_SOCKET_GONE)) {
+		/* socket above is long gone */
+		asoc->fragmented_delivery_inprogress = 0;
+		TAILQ_FOREACH(chk, &asoc->reasmqueue, sctp_next) {
+			asoc->size_on_delivery_queue -= chk->send_size;
+			asoc->cnt_on_delivery_queue--;
+			/*
+			 * Lose the data pointer, since its in the socket buffer
+			 */
+			if (chk->data)
+				sctp_m_freem(chk->data);
+			chk->data = NULL;
+			/* Now free the address and data */
+			sctp_free_remote_addr(chk->whoTo);
+			SCTP_ZONE_FREE(sctppcbinfo.ipi_zone_chunk, chk);
+			sctppcbinfo.ipi_count_chunk--;
+			if ((int)sctppcbinfo.ipi_count_chunk < 0) {
+				panic("Chunk count is negative");
+			}
+			sctppcbinfo.ipi_gencnt_chunk++;
+		}
+		if (hold_locks == 0)
+			SCTP_INP_WUNLOCK(stcb->sctp_ep);
+		return;
+	}
+	do {
+		if (stcb->sctp_socket->so_rcv.sb_cc >=
+		    stcb->sctp_socket->so_rcv.sb_hiwat) {
+			if (cntDel) {
+				sctp_sorwakeup(stcb->sctp_ep,
+					       stcb->sctp_socket);
+			}
+			if (hold_locks == 0)
+				SCTP_INP_WUNLOCK(stcb->sctp_ep);
+			return;
+		}
+		chk = TAILQ_FIRST(&asoc->reasmqueue);
+		if (chk == NULL) {
+			if (cntDel) {
+				sctp_sorwakeup(stcb->sctp_ep,
+					       stcb->sctp_socket);
+			}
+			if (hold_locks == 0)
+				SCTP_INP_WUNLOCK(stcb->sctp_ep);
+			return;
+		}
+		if (chk->rec.data.TSN_seq != (asoc->tsn_last_delivered + 1)) {
+			/* Can't deliver more :< */
+			if (cntDel) {
+				sctp_sorwakeup(stcb->sctp_ep,
+					       stcb->sctp_socket);
+			}
+			if (hold_locks == 0)
+				SCTP_INP_WUNLOCK(stcb->sctp_ep);
+			return;
+		}
+		stream_no = chk->rec.data.stream_number;
+		nxt_todel = asoc->strmin[stream_no].last_sequence_delivered + 1;
+		if (nxt_todel != chk->rec.data.stream_seq &&
+		    (chk->rec.data.rcv_flags & SCTP_DATA_UNORDERED) == 0) {
+			/*
+			 * Not the next sequence to deliver in its stream OR
+			 * unordered
+			 */
+			if (cntDel) {
+				sctp_sorwakeup(stcb->sctp_ep,
+					       stcb->sctp_socket);
+			}
+			if (hold_locks == 0)
+				SCTP_INP_WUNLOCK(stcb->sctp_ep);
+			return;
+		}
+
+		if ((chk->data->m_flags & M_PKTHDR) == 0) {
+			MGETHDR(m, MB_DONTWAIT, MT_DATA);
+			if (m == NULL) {
+				/* no room! */
+				if (hold_locks == 0)
+					SCTP_INP_WUNLOCK(stcb->sctp_ep);
+				return;
+			}
+			m->m_pkthdr.len = chk->send_size;
+			m->m_len = 0;
+			m->m_next = chk->data;
+			chk->data = m;
+		}
+		if (chk->rec.data.rcv_flags & SCTP_DATA_LAST_FRAG) {
+			if (chk->data->m_next == NULL) {
+				/* hopefully we hit here most of the time */
+				chk->data->m_flags |= M_EOR;
+			} else {
+				/* Add the flag to the LAST mbuf in the chain */
+				m = chk->data;
+				while (m->m_next != NULL) {
+					m = m->m_next;
+				}
+				m->m_flags |= M_EOR;
+			}
+		}
+		if (chk->rec.data.rcv_flags & SCTP_DATA_FIRST_FRAG) {
+			struct sockaddr_in6 lsa6;
+
+			control = sctp_build_ctl(stcb, chk);
+			to = (struct sockaddr *)&chk->whoTo->ro._l_addr;
+			if ((stcb->sctp_ep->sctp_flags & SCTP_PCB_FLAGS_NEEDS_MAPPED_V4) &&
+			    to->sa_family == AF_INET) {
+				struct sockaddr_in *sin;
+				sin = (struct sockaddr_in *)to;
+				bzero(&sin6, sizeof(sin6));
+				sin6.sin6_family = AF_INET6;
+				sin6.sin6_len = sizeof(struct sockaddr_in6);
+				sin6.sin6_addr.s6_addr16[2] = 0xffff;
+				bcopy(&sin->sin_addr,
+				      &sin6.sin6_addr.s6_addr16[3],
+				      sizeof(sin6.sin6_addr.s6_addr16[3]));
+				sin6.sin6_port = sin->sin_port;
+				to = (struct sockaddr *)&sin6;
+			}
+			/* check and strip embedded scope junk */
+			to = (struct sockaddr *)sctp_recover_scope((struct sockaddr_in6 *)to,
+								   &lsa6);
+			if (((struct sockaddr_in *)to)->sin_port == 0) {
+				printf("Huh b, port is %d not net:%x %d?\n",
+				       ((struct sockaddr_in *)to)->sin_port,
+				       (u_int)chk->whoTo,
+				       (int)(ntohs(stcb->rport)));
+				((struct sockaddr_in *)to)->sin_port = stcb->rport;
+			}
+			if (sctp_sbspace(&stcb->sctp_socket->so_rcv) <
+			    (long)chk->send_size) {
+				if (control) {
+					sctp_m_freem(control);
+					stcb->asoc.my_rwnd_control_len -=
+						CMSG_LEN(sizeof(struct sctp_sndrcvinfo));
+				}
+				sctp_sorwakeup(stcb->sctp_ep,
+					       stcb->sctp_socket);
+				if (hold_locks == 0)
+					SCTP_INP_WUNLOCK(stcb->sctp_ep);
+				return;
+			}
+			if (!sbappendaddr_nocheck(&stcb->sctp_socket->so_rcv,
+						  to, chk->data, control, stcb->asoc.my_vtag,
+						  stcb->sctp_ep)) {
+				/* Gak not enough room */
+				if (control) {
+					sctp_m_freem(control);
+					stcb->asoc.my_rwnd_control_len -=
+						CMSG_LEN(sizeof(struct sctp_sndrcvinfo));
+				}
+				sctp_sorwakeup(stcb->sctp_ep,
+					       stcb->sctp_socket);
+				if (hold_locks == 0)
+					SCTP_INP_WUNLOCK(stcb->sctp_ep);
+				return;
+			}
+			if ((stcb->sctp_ep->sctp_flags & SCTP_PCB_FLAGS_IN_TCPPOOL) == 0) {
+				if (sctp_add_to_socket_q(stcb->sctp_ep, stcb)) {
+					stcb->asoc.my_rwnd_control_len +=
+						sizeof(struct mbuf);
+				}
+			} else {
+				stcb->asoc.my_rwnd_control_len += sizeof(struct mbuf);
+			}
+			cntDel++;
+		} else {
+			if (sctp_sbspace(&stcb->sctp_socket->so_rcv) >=
+			    (long)chk->send_size) {
+				sbappend(&stcb->sctp_socket->so_rcv, chk->data);
+				cntDel++;
+			} else {
+				/* out of space in the sb */
+				sctp_sorwakeup(stcb->sctp_ep,
+					       stcb->sctp_socket);
+				if (hold_locks == 0)
+					SCTP_INP_WUNLOCK(stcb->sctp_ep);
+				return;
+			}
+		}
+		/* pull it we did it */
+		TAILQ_REMOVE(&asoc->reasmqueue, chk, sctp_next);
+		if (chk->rec.data.rcv_flags & SCTP_DATA_LAST_FRAG) {
+			asoc->fragmented_delivery_inprogress = 0;
+			if ((chk->rec.data.rcv_flags & SCTP_DATA_UNORDERED) == 0) {
+				asoc->strmin[stream_no].last_sequence_delivered++;
+			}
+		}
+		asoc->tsn_last_delivered = chk->rec.data.TSN_seq;
+		asoc->size_on_reasm_queue -= chk->send_size;
+		asoc->cnt_on_reasm_queue--;
+		/* free up the chk */
+		sctp_free_remote_addr(chk->whoTo);
+		chk->data = NULL;
+		SCTP_ZONE_FREE(sctppcbinfo.ipi_zone_chunk, chk);
+		sctppcbinfo.ipi_count_chunk--;
+		if ((int)sctppcbinfo.ipi_count_chunk < 0) {
+			panic("Chunk count is negative");
+		}
+		sctppcbinfo.ipi_gencnt_chunk++;
+		if (asoc->fragmented_delivery_inprogress == 0) {
+			/*
+			 * Now lets see if we can deliver the next one on the
+			 * stream
+			 */
+			u_int16_t nxt_todel;
+			struct sctp_stream_in *strm;
+
+			strm = &asoc->strmin[stream_no];
+			nxt_todel = strm->last_sequence_delivered + 1;
+			chk = TAILQ_FIRST(&strm->inqueue);
+			if (chk && (nxt_todel == chk->rec.data.stream_seq)) {
+				while (chk != NULL) {
+					/* all delivered */
+					if (nxt_todel ==
+					    chk->rec.data.stream_seq) {
+						at = TAILQ_NEXT(chk, sctp_next);
+						TAILQ_REMOVE(&strm->inqueue,
+							     chk, sctp_next);
+						asoc->size_on_all_streams -=
+							chk->send_size;
+						asoc->cnt_on_all_streams--;
+						strm->last_sequence_delivered++;
+						/*
+						 * We ignore the return of
+						 * deliver_data here since we
+						 * always can hold the chunk on
+						 * the d-queue. And we have a
+						 * finite number that can be
+						 * delivered from the strq.
+						 */
+						sctp_deliver_data(stcb, asoc, chk, 1);
+						chk = at;
+					} else {
+						break;
+					}
+					nxt_todel =
+						strm->last_sequence_delivered + 1;
+				}
+			}
+			if (!TAILQ_EMPTY(&asoc->delivery_queue)) {
+				/* Here if deliver_data fails, we must break */
+				if (sctp_deliver_data(stcb, asoc, NULL, 1) == 0)
+					break;
+			}
+			sctp_sorwakeup(stcb->sctp_ep, stcb->sctp_socket);
+			if (hold_locks == 0)
+				SCTP_INP_WUNLOCK(stcb->sctp_ep);
+			return;
+		}
+		chk = TAILQ_FIRST(&asoc->reasmqueue);
+	} while (chk);
+	if (cntDel) {
+		sctp_sorwakeup(stcb->sctp_ep, stcb->sctp_socket);
+	}
+	if (hold_locks == 0)
+		SCTP_INP_WUNLOCK(stcb->sctp_ep);
+}
+
+/*
+ * Queue the chunk either right into the socket buffer if it is the next one
+ * to go OR put it in the correct place in the delivery queue.  If we do
+ * append to the so_buf, keep doing so until we are out of order.
+ * One big question still remains, what to do when the socket buffer is FULL??
+ */
+static void
+sctp_queue_data_to_stream(struct sctp_tcb *stcb, struct sctp_association *asoc,
+    struct sctp_tmit_chunk *chk, int *abort_flag)
+{
+	struct sctp_stream_in *strm;
+	struct sctp_tmit_chunk *at;
+	int queue_needed;
+	u_int16_t nxt_todel;
+	struct mbuf *oper;
+
+/*** FIX FIX FIX ???
+ * Need to add code to deal with 16 bit seq wrap
+ * without a TSN wrap for ordered delivery (maybe).
+ * FIX FIX FIX ???
+ */
+	queue_needed = 1;
+	asoc->size_on_all_streams += chk->send_size;
+	asoc->cnt_on_all_streams++;
+	strm = &asoc->strmin[chk->rec.data.stream_number];
+	nxt_todel = strm->last_sequence_delivered + 1;
+#ifdef SCTP_STR_LOGGING
+	sctp_log_strm_del(chk, NULL, SCTP_STR_LOG_FROM_INTO_STRD);
+#endif
+#ifdef SCTP_DEBUG
+	if (sctp_debug_on & SCTP_DEBUG_INDATA1) {
+		printf("queue to stream called for ssn:%u lastdel:%u nxt:%u\n",
+		    (u_int)chk->rec.data.stream_seq,
+		    (u_int)strm->last_sequence_delivered, (u_int)nxt_todel);
+	}
+#endif
+	if (compare_with_wrap(strm->last_sequence_delivered,
+	    chk->rec.data.stream_seq, MAX_SEQ) ||
+	    (strm->last_sequence_delivered == chk->rec.data.stream_seq)) {
+		/* The incoming sseq is behind where we last delivered? */
+#ifdef SCTP_DEBUG
+		if (sctp_debug_on & SCTP_DEBUG_INDATA1) {
+			printf("Duplicate S-SEQ:%d delivered:%d from peer, Abort  association\n",
+			    chk->rec.data.stream_seq,
+			    strm->last_sequence_delivered);
+		}
+#endif
+		/*
+		 * throw it in the stream so it gets cleaned up in
+		 * association destruction
+		 */
+		TAILQ_INSERT_HEAD(&strm->inqueue, chk, sctp_next);
+		MGET(oper, MB_DONTWAIT, MT_DATA);
+		if (oper) {
+			struct sctp_paramhdr *ph;
+			u_int32_t *ippp;
+
+			oper->m_len = sizeof(struct sctp_paramhdr) +
+			    sizeof(*ippp);
+			ph = mtod(oper, struct sctp_paramhdr *);
+			ph->param_type = htons(SCTP_CAUSE_PROTOCOL_VIOLATION);
+			ph->param_length = htons(oper->m_len);
+			ippp = (u_int32_t *)(ph + 1);
+			*ippp = htonl(0x00000001);
+		}
+		sctp_abort_an_association(stcb->sctp_ep, stcb,
+		    SCTP_PEER_FAULTY, oper);
+
+		*abort_flag = 1;
+		return;
+
+	}
+	if (nxt_todel == chk->rec.data.stream_seq) {
+		/* can be delivered right away */
+#ifdef SCTP_DEBUG
+		if (sctp_debug_on & SCTP_DEBUG_INDATA1) {
+			printf("It's NEXT!\n");
+		}
+#endif
+#ifdef SCTP_STR_LOGGING
+		sctp_log_strm_del(chk, NULL, SCTP_STR_LOG_FROM_IMMED_DEL);
+#endif
+		queue_needed = 0;
+		asoc->size_on_all_streams -= chk->send_size;
+		asoc->cnt_on_all_streams--;
+		strm->last_sequence_delivered++;
+		sctp_deliver_data(stcb, asoc, chk, 0);
+		chk = TAILQ_FIRST(&strm->inqueue);
+		while (chk != NULL) {
+			/* all delivered */
+			nxt_todel = strm->last_sequence_delivered + 1;
+			if (nxt_todel == chk->rec.data.stream_seq) {
+				at = TAILQ_NEXT(chk, sctp_next);
+				TAILQ_REMOVE(&strm->inqueue, chk, sctp_next);
+				asoc->size_on_all_streams -= chk->send_size;
+				asoc->cnt_on_all_streams--;
+				strm->last_sequence_delivered++;
+				/*
+				 * We ignore the return of deliver_data here
+				 * since we always can hold the chunk on the
+				 * d-queue. And we have a finite number that
+				 * can be delivered from the strq.
+				 */
+#ifdef SCTP_STR_LOGGING
+				sctp_log_strm_del(chk, NULL,
+				    SCTP_STR_LOG_FROM_IMMED_DEL);
+#endif
+				sctp_deliver_data(stcb, asoc, chk, 0);
+				chk = at;
+				continue;
+			}
+			break;
+		}
+	}
+	if (queue_needed) {
+		/*
+		 * Ok, we did not deliver this guy, find
+		 * the correct place to put it on the queue.
+		 */
+#ifdef SCTP_DEBUG
+		if (sctp_debug_on & SCTP_DEBUG_INDATA1) {
+			printf("Queue Needed!\n");
+		}
+#endif
+		if (TAILQ_EMPTY(&strm->inqueue)) {
+			/* Empty queue */
+#ifdef SCTP_STR_LOGGING
+			sctp_log_strm_del(chk, NULL, SCTP_STR_LOG_FROM_INSERT_HD);
+#endif
+			TAILQ_INSERT_HEAD(&strm->inqueue, chk, sctp_next);
+		} else {
+			TAILQ_FOREACH(at, &strm->inqueue, sctp_next) {
+				if (compare_with_wrap(at->rec.data.stream_seq,
+				    chk->rec.data.stream_seq, MAX_SEQ)) {
+					/*
+					 * one in queue is bigger than the new
+					 * one, insert before this one
+					 */
+#ifdef SCTP_STR_LOGGING
+					sctp_log_strm_del(chk, at,
+					    SCTP_STR_LOG_FROM_INSERT_MD);
+#endif
+					TAILQ_INSERT_BEFORE(at, chk, sctp_next);
+					break;
+				} else if (at->rec.data.stream_seq ==
+				    chk->rec.data.stream_seq) {
+					/*
+					 * Gak, He sent me a duplicate str seq
+					 * number
+					 */
+					/*
+					 * foo bar, I guess I will just free
+					 * this new guy, should we abort too?
+					 * FIX ME MAYBE? Or it COULD be that
+					 * the SSN's have wrapped. Maybe I
+					 * should compare to TSN somehow...
+					 * sigh for now just blow away the
+					 * chunk!
+					 */
+
+					if (chk->data)
+						sctp_m_freem(chk->data);
+					chk->data = NULL;
+					asoc->size_on_all_streams -= chk->send_size;
+					asoc->cnt_on_all_streams--;
+					sctp_pegs[SCTP_DUP_SSN_RCVD]++;
+					sctp_free_remote_addr(chk->whoTo);
+					SCTP_ZONE_FREE(sctppcbinfo.ipi_zone_chunk, chk);
+					sctppcbinfo.ipi_count_chunk--;
+					if ((int)sctppcbinfo.ipi_count_chunk <
+					    0) {
+						panic("Chunk count is negative");
+					}
+					sctppcbinfo.ipi_gencnt_chunk++;
+					return;
+				} else {
+					if (TAILQ_NEXT(at, sctp_next) == NULL) {
+						/*
+						 * We are at the end, insert it
+						 * after this one
+						 */
+#ifdef SCTP_STR_LOGGING
+						sctp_log_strm_del(chk, at,
+						    SCTP_STR_LOG_FROM_INSERT_TL);
+#endif
+						TAILQ_INSERT_AFTER(&strm->inqueue,
+						    at, chk, sctp_next);
+						break;
+					}
+				}
+			}
+		}
+	} else {
+		/* We delivered some chunks, wake them up */
+
+#ifdef SCTP_DEBUG
+		if (sctp_debug_on & SCTP_DEBUG_INDATA1) {
+			printf("Doing WAKEUP!\n");
+		}
+#endif
+		sctp_sorwakeup(stcb->sctp_ep, stcb->sctp_socket);
+	}
+}
+
+/*
+ * Returns two things: You get the total size of the deliverable parts of the
+ * first fragmented message on the reassembly queue. And you get a 1 back if
+ * all of the message is ready or a 0 back if the message is still incomplete
+ */
+static int
+sctp_is_all_msg_on_reasm(struct sctp_association *asoc, int *t_size)
+{
+	struct sctp_tmit_chunk *chk;
+	u_int32_t tsn;
+
+	*t_size = 0;
+	chk = TAILQ_FIRST(&asoc->reasmqueue);
+	if (chk == NULL) {
+		/* nothing on the queue */
+		return (0);
+	}
+	if ((chk->rec.data.rcv_flags & SCTP_DATA_FIRST_FRAG) == 0) {
+		/* Not a first on the queue */
+		return (0);
+	}
+	tsn = chk->rec.data.TSN_seq;
+	while (chk) {
+		if (tsn != chk->rec.data.TSN_seq) {
+			return (0);
+		}
+		*t_size += chk->send_size;
+		if (chk->rec.data.rcv_flags & SCTP_DATA_LAST_FRAG) {
+			return (1);
+		}
+		tsn++;
+		chk = TAILQ_NEXT(chk, sctp_next);
+	}
+	return (0);
+}
+
+/*
+ * Dump onto the re-assembly queue, in its proper place. After dumping on
+ * the queue, see if anthing can be delivered. If so pull it off (or as much
+ * as we can. If we run out of space then we must dump what we can and set
+ * the appropriate flag to say we queued what we could.
+ */
+static void
+sctp_queue_data_for_reasm(struct sctp_tcb *stcb, struct sctp_association *asoc,
+    struct sctp_tmit_chunk *chk, int *abort_flag)
+{
+	struct mbuf *oper;
+	u_int16_t nxt_todel;
+	u_int32_t cum_ackp1, last_tsn, prev_tsn, post_tsn;
+	int tsize;
+	u_char last_flags;
+	struct sctp_tmit_chunk *at, *prev, *next;
+
+	prev = next = NULL;
+	cum_ackp1 = asoc->tsn_last_delivered + 1;
+
+	if (TAILQ_EMPTY(&asoc->reasmqueue)) {
+		/* This is the first one on the queue */
+		TAILQ_INSERT_HEAD(&asoc->reasmqueue, chk, sctp_next);
+		/*
+		 * we do not check for delivery of anything when
+		 * only one fragment is here
+		 */
+		asoc->size_on_reasm_queue = chk->send_size;
+		asoc->cnt_on_reasm_queue++;
+		if (chk->rec.data.TSN_seq == cum_ackp1) {
+			if (asoc->fragmented_delivery_inprogress == 0  &&
+			    (chk->rec.data.rcv_flags & SCTP_DATA_FIRST_FRAG) !=
+			    SCTP_DATA_FIRST_FRAG) {
+				/*
+				 * An empty queue, no delivery inprogress, we
+				 * hit the next one and it does NOT have a
+				 * FIRST fragment mark.
+				 */
+#ifdef SCTP_DEBUG
+				if (sctp_debug_on & SCTP_DEBUG_INDATA1) {
+					printf("Gak, Evil plot, its not first, no fragmented delivery in progress\n");
+				}
+#endif
+				MGET(oper, MB_DONTWAIT, MT_DATA);
+				if (oper) {
+					struct sctp_paramhdr *ph;
+					u_int32_t *ippp;
+
+					oper->m_len =
+					    sizeof(struct sctp_paramhdr) +
+					    sizeof(*ippp);
+					ph = mtod(oper, struct sctp_paramhdr *);
+					ph->param_type =
+					    htons(SCTP_CAUSE_PROTOCOL_VIOLATION);
+					ph->param_length = htons(oper->m_len);
+					ippp = (u_int32_t *)(ph + 1);
+					*ippp = htonl(0x10000001);
+				}
+				sctp_abort_an_association(stcb->sctp_ep, stcb,
+				    SCTP_PEER_FAULTY, oper);
+				*abort_flag = 1;
+			} else if (asoc->fragmented_delivery_inprogress &&
+			    (chk->rec.data.rcv_flags & SCTP_DATA_FIRST_FRAG) == SCTP_DATA_FIRST_FRAG) {
+				/*
+				 * We are doing a partial delivery and the NEXT
+				 * chunk MUST be either the LAST or MIDDLE
+				 * fragment NOT a FIRST
+				 */
+#ifdef SCTP_DEBUG
+				if (sctp_debug_on & SCTP_DEBUG_INDATA1) {
+					printf("Gak, Evil plot, it IS a first and fragmented delivery in progress\n");
+				}
+#endif
+				MGET(oper, MB_DONTWAIT, MT_DATA);
+				if (oper) {
+					struct sctp_paramhdr *ph;
+					u_int32_t *ippp;
+
+					oper->m_len =
+					    sizeof(struct sctp_paramhdr) +
+					    sizeof(*ippp);
+					ph = mtod(oper, struct sctp_paramhdr *);
+					ph->param_type =
+					    htons(SCTP_CAUSE_PROTOCOL_VIOLATION);
+					ph->param_length = htons(oper->m_len);
+					ippp = (u_int32_t *)(ph + 1);
+					*ippp = htonl(0x10000002);
+				}
+				sctp_abort_an_association(stcb->sctp_ep, stcb,
+				    SCTP_PEER_FAULTY, oper);
+				*abort_flag = 1;
+			} else if (asoc->fragmented_delivery_inprogress) {
+				/* Here we are ok with a MIDDLE or LAST piece */
+				if (chk->rec.data.stream_number !=
+				    asoc->str_of_pdapi) {
+					/* Got to be the right STR No */
+#ifdef SCTP_DEBUG
+					if (sctp_debug_on & SCTP_DEBUG_INDATA1) {
+						printf("Gak, Evil plot, it IS not same stream number %d vs %d\n",
+						    chk->rec.data.stream_number,
+						    asoc->str_of_pdapi);
+					}
+#endif
+					MGET(oper, MB_DONTWAIT, MT_DATA);
+					if (oper) {
+						struct sctp_paramhdr *ph;
+						u_int32_t *ippp;
+						oper->m_len =
+						    sizeof(struct sctp_paramhdr) +
+						    sizeof(*ippp);
+						ph = mtod(oper,
+						    struct sctp_paramhdr *);
+						ph->param_type =
+						    htons(SCTP_CAUSE_PROTOCOL_VIOLATION);
+						ph->param_length =
+						    htons(oper->m_len);
+						ippp = (u_int32_t *)(ph + 1);
+						*ippp = htonl(0x10000003);
+					}
+					sctp_abort_an_association(stcb->sctp_ep,
+					    stcb, SCTP_PEER_FAULTY, oper);
+					*abort_flag = 1;
+				} else if ((asoc->fragment_flags & SCTP_DATA_UNORDERED) !=
+				    SCTP_DATA_UNORDERED &&
+				    chk->rec.data.stream_seq !=
+				    asoc->ssn_of_pdapi) {
+					/* Got to be the right STR Seq */
+#ifdef SCTP_DEBUG
+					if (sctp_debug_on & SCTP_DEBUG_INDATA1) {
+						printf("Gak, Evil plot, it IS not same stream seq %d vs %d\n",
+						    chk->rec.data.stream_seq,
+						    asoc->ssn_of_pdapi);
+					}
+#endif
+					MGET(oper, MB_DONTWAIT, MT_DATA);
+					if (oper) {
+						struct sctp_paramhdr *ph;
+						u_int32_t *ippp;
+						oper->m_len =
+						    sizeof(struct sctp_paramhdr) +
+						    sizeof(*ippp);
+						ph = mtod(oper,
+						    struct sctp_paramhdr *);
+						ph->param_type =
+						    htons(SCTP_CAUSE_PROTOCOL_VIOLATION);
+						ph->param_length =
+						    htons(oper->m_len);
+						ippp = (u_int32_t *)(ph + 1);
+						*ippp = htonl(0x10000004);
+					}
+					sctp_abort_an_association(stcb->sctp_ep,
+					    stcb, SCTP_PEER_FAULTY, oper);
+					*abort_flag = 1;
+				}
+			}
+		}
+		return;
+	}
+	/* Find its place */
+	at = TAILQ_FIRST(&asoc->reasmqueue);
+
+	/* Grab the top flags */
+	TAILQ_FOREACH(at, &asoc->reasmqueue, sctp_next) {
+		if (compare_with_wrap(at->rec.data.TSN_seq,
+		    chk->rec.data.TSN_seq, MAX_TSN)) {
+			/*
+			 * one in queue is bigger than the new one, insert
+			 * before this one
+			 */
+			/* A check */
+			asoc->size_on_reasm_queue += chk->send_size;
+			asoc->cnt_on_reasm_queue++;
+			next = at;
+			TAILQ_INSERT_BEFORE(at, chk, sctp_next);
+			break;
+		} else if (at->rec.data.TSN_seq == chk->rec.data.TSN_seq) {
+			/* Gak, He sent me a duplicate str seq number */
+			/*
+			 * foo bar, I guess I will just free this new guy,
+			 * should we abort too? FIX ME MAYBE? Or it COULD be
+			 * that the SSN's have wrapped. Maybe I should compare
+			 * to TSN somehow... sigh for now just blow away the
+			 * chunk!
+			 */
+			if (chk->data)
+				sctp_m_freem(chk->data);
+			chk->data = NULL;
+			sctp_free_remote_addr(chk->whoTo);
+			SCTP_ZONE_FREE(sctppcbinfo.ipi_zone_chunk, chk);
+			sctppcbinfo.ipi_count_chunk--;
+			if ((int)sctppcbinfo.ipi_count_chunk < 0) {
+				panic("Chunk count is negative");
+			}
+			sctppcbinfo.ipi_gencnt_chunk++;
+			return;
+		} else {
+			last_flags = at->rec.data.rcv_flags;
+			last_tsn = at->rec.data.TSN_seq;
+			prev = at;
+			if (TAILQ_NEXT(at, sctp_next) == NULL) {
+				/*
+				 * We are at the end, insert it after this one
+				 */
+				/* check it first */
+				asoc->size_on_reasm_queue += chk->send_size;
+				asoc->cnt_on_reasm_queue++;
+				TAILQ_INSERT_AFTER(&asoc->reasmqueue, at, chk, sctp_next);
+				break;
+			}
+		}
+	}
+	/* Now the audits */
+	if (prev) {
+		prev_tsn = chk->rec.data.TSN_seq - 1;
+		if (prev_tsn == prev->rec.data.TSN_seq) {
+			/*
+			 * Ok the one I am dropping onto the end
+			 * is the NEXT. A bit of valdiation here.
+			 */
+			if ((prev->rec.data.rcv_flags & SCTP_DATA_FRAG_MASK) ==
+			    SCTP_DATA_FIRST_FRAG ||
+			    (prev->rec.data.rcv_flags & SCTP_DATA_FRAG_MASK) ==
+			    SCTP_DATA_MIDDLE_FRAG) {
+				/*
+				 * Insert chk MUST be a MIDDLE or LAST fragment
+				 */
+				if ((chk->rec.data.rcv_flags & SCTP_DATA_FRAG_MASK) ==
+				    SCTP_DATA_FIRST_FRAG) {
+#ifdef SCTP_DEBUG
+					if (sctp_debug_on & SCTP_DEBUG_INDATA1) {
+						printf("Prev check - It can be a midlle or last but not a first\n");
+						printf("Gak, Evil plot, it's a FIRST!\n");
+					}
+#endif
+					MGET(oper, MB_DONTWAIT, MT_DATA);
+					if (oper) {
+						struct sctp_paramhdr *ph;
+						u_int32_t *ippp;
+
+						oper->m_len =
+						    sizeof(struct sctp_paramhdr) +
+						    sizeof(*ippp);
+						ph = mtod(oper,
+						    struct sctp_paramhdr *);
+						ph->param_type =
+						    htons(SCTP_CAUSE_PROTOCOL_VIOLATION);
+						ph->param_length =
+						    htons(oper->m_len);
+
+						ippp = (u_int32_t *)(ph + 1);
+						*ippp = htonl(0x10000005);
+					}
+					sctp_abort_an_association(stcb->sctp_ep,
+					    stcb, SCTP_PEER_FAULTY, oper);
+					*abort_flag = 1;
+					return;
+				}
+				if (chk->rec.data.stream_number !=
+				    prev->rec.data.stream_number) {
+					/*
+					 * Huh, need the correct STR here, they
+					 * must be the same.
+					 */
+#ifdef SCTP_DEBUG
+					if (sctp_debug_on & SCTP_DEBUG_INDATA1) {
+						printf("Prev check - Gak, Evil plot, ssn:%d not the same as at:%d\n",
+						    chk->rec.data.stream_number,
+						    prev->rec.data.stream_number);
+					}
+#endif
+					MGET(oper, MB_DONTWAIT, MT_DATA);
+					if (oper) {
+						struct sctp_paramhdr *ph;
+						u_int32_t *ippp;
+
+						oper->m_len =
+						    sizeof(struct sctp_paramhdr) +
+						    sizeof(*ippp);
+						ph = mtod(oper,
+						    struct sctp_paramhdr *);
+						ph->param_type =
+						    htons(SCTP_CAUSE_PROTOCOL_VIOLATION);
+						ph->param_length =
+						    htons(oper->m_len);
+						ippp = (u_int32_t *)(ph + 1);
+						*ippp = htonl(0x10000006);
+					}
+
+					sctp_abort_an_association(stcb->sctp_ep,
+					    stcb, SCTP_PEER_FAULTY, oper);
+
+					*abort_flag = 1;
+					return;
+				}
+				if ((prev->rec.data.rcv_flags & SCTP_DATA_UNORDERED) == 0 &&
+				    chk->rec.data.stream_seq !=
+				    prev->rec.data.stream_seq) {
+					/*
+					 * Huh, need the correct STR here, they
+					 * must be the same.
+					 */
+#ifdef SCTP_DEBUG
+					if (sctp_debug_on & SCTP_DEBUG_INDATA1) {
+						printf("Prev check - Gak, Evil plot, sseq:%d not the same as at:%d\n",
+						    chk->rec.data.stream_seq,
+						    prev->rec.data.stream_seq);
+					}
+#endif
+					MGET(oper, MB_DONTWAIT, MT_DATA);
+					if (oper) {
+						struct sctp_paramhdr *ph;
+						u_int32_t *ippp;
+
+						oper->m_len =
+						    sizeof(struct sctp_paramhdr) +
+						    sizeof(*ippp);
+						ph = mtod(oper,
+						    struct sctp_paramhdr *);
+						ph->param_type =
+						    htons(SCTP_CAUSE_PROTOCOL_VIOLATION);
+						ph->param_length =
+						    htons(oper->m_len);
+						ippp = (u_int32_t *)(ph + 1);
+						*ippp = htonl(0x10000007);
+					}
+
+					sctp_abort_an_association(stcb->sctp_ep,
+					    stcb, SCTP_PEER_FAULTY, oper);
+
+					*abort_flag = 1;
+					return;
+				}
+			} else if ((prev->rec.data.rcv_flags & SCTP_DATA_FRAG_MASK) ==
+			    SCTP_DATA_LAST_FRAG) {
+				/* Insert chk MUST be a FIRST */
+				if ((chk->rec.data.rcv_flags & SCTP_DATA_FRAG_MASK) !=
+				    SCTP_DATA_FIRST_FRAG) {
+#ifdef SCTP_DEBUG
+					if (sctp_debug_on & SCTP_DEBUG_INDATA1) {
+						printf("Prev check - Gak, evil plot, its not FIRST and it must be!\n");
+					}
+#endif
+					MGET(oper, MB_DONTWAIT, MT_DATA);
+					if (oper) {
+						struct sctp_paramhdr *ph;
+						u_int32_t *ippp;
+
+						oper->m_len =
+						    sizeof(struct sctp_paramhdr) +
+						    sizeof(*ippp);
+						ph = mtod(oper,
+						    struct sctp_paramhdr *);
+						ph->param_type =
+						    htons(SCTP_CAUSE_PROTOCOL_VIOLATION);
+						ph->param_length =
+						    htons(oper->m_len);
+						ippp = (u_int32_t *)(ph + 1);
+						*ippp = htonl(0x10000008);
+					}
+
+					sctp_abort_an_association(stcb->sctp_ep,
+					    stcb, SCTP_PEER_FAULTY, oper);
+
+					*abort_flag = 1;
+					return;
+				}
+			}
+		}
+	}
+
+	if (next) {
+		post_tsn = chk->rec.data.TSN_seq + 1;
+		if (post_tsn == next->rec.data.TSN_seq) {
+			/*
+			 * Ok the one I am inserting ahead of
+			 * is my NEXT one. A bit of valdiation here.
+			 */
+			if (next->rec.data.rcv_flags & SCTP_DATA_FIRST_FRAG) {
+				/* Insert chk MUST be a last fragment */
+				if ((chk->rec.data.rcv_flags&SCTP_DATA_FRAG_MASK)
+				   != SCTP_DATA_LAST_FRAG) {
+#ifdef SCTP_DEBUG
+					if (sctp_debug_on & SCTP_DEBUG_INDATA1) {
+						printf("Next chk - Next is FIRST, we must be LAST\n");
+						printf("Gak, Evil plot, its not a last!\n");
+					}
+#endif
+					MGET(oper, MB_DONTWAIT, MT_DATA);
+					if (oper) {
+						struct sctp_paramhdr *ph;
+						u_int32_t *ippp;
+
+						oper->m_len =
+						    sizeof(struct sctp_paramhdr) +
+						    sizeof(*ippp);
+						ph = mtod(oper,
+						    struct sctp_paramhdr *);
+						ph->param_type =
+						    htons(SCTP_CAUSE_PROTOCOL_VIOLATION);
+						ph->param_length =
+						    htons(oper->m_len);
+						ippp = (u_int32_t *)(ph + 1);
+						*ippp = htonl(0x10000009);
+					}
+
+					sctp_abort_an_association(stcb->sctp_ep,
+					    stcb, SCTP_PEER_FAULTY, oper);
+
+					*abort_flag = 1;
+					return;
+				}
+			} else if ((next->rec.data.rcv_flags & SCTP_DATA_FRAG_MASK) ==
+			    SCTP_DATA_MIDDLE_FRAG ||
+			    (next->rec.data.rcv_flags&SCTP_DATA_FRAG_MASK) ==
+			    SCTP_DATA_LAST_FRAG) {
+				/* Insert chk CAN be MIDDLE or FIRST NOT LAST */
+				if ((chk->rec.data.rcv_flags & SCTP_DATA_FRAG_MASK) ==
+				    SCTP_DATA_LAST_FRAG) {
+#ifdef SCTP_DEBUG
+					if (sctp_debug_on & SCTP_DEBUG_INDATA1) {
+						printf("Next chk - Next is a MIDDLE/LAST\n");
+						printf("Gak, Evil plot, new prev chunk is a LAST\n");
+					}
+#endif
+					MGET(oper, MB_DONTWAIT, MT_DATA);
+					if (oper) {
+						struct sctp_paramhdr *ph;
+						u_int32_t *ippp;
+
+						oper->m_len =
+						    sizeof(struct sctp_paramhdr) +
+						    sizeof(*ippp);
+						ph = mtod(oper,
+						    struct sctp_paramhdr *);
+						ph->param_type =
+						    htons(SCTP_CAUSE_PROTOCOL_VIOLATION);
+						ph->param_length =
+						    htons(oper->m_len);
+						ippp = (u_int32_t *)(ph + 1);
+						*ippp = htonl(0x1000000a);
+					}
+					sctp_abort_an_association(stcb->sctp_ep,
+					    stcb, SCTP_PEER_FAULTY, oper);
+
+					*abort_flag = 1;
+					return;
+				}
+				if (chk->rec.data.stream_number !=
+				    next->rec.data.stream_number) {
+					/*
+					 * Huh, need the correct STR here, they
+					 * must be the same.
+					 */
+#ifdef SCTP_DEBUG
+					if (sctp_debug_on & SCTP_DEBUG_INDATA1) {
+						printf("Next chk - Gak, Evil plot, ssn:%d not the same as at:%d\n",
+						    chk->rec.data.stream_number,
+						    next->rec.data.stream_number);
+					}
+#endif
+					MGET(oper, MB_DONTWAIT, MT_DATA);
+					if (oper) {
+						struct sctp_paramhdr *ph;
+						u_int32_t *ippp;
+
+						oper->m_len =
+						    sizeof(struct sctp_paramhdr) +
+						    sizeof(*ippp);
+						ph = mtod(oper,
+						    struct sctp_paramhdr *);
+						ph->param_type =
+						    htons(SCTP_CAUSE_PROTOCOL_VIOLATION);
+						ph->param_length =
+						    htons(oper->m_len);
+						ippp = (u_int32_t *)(ph + 1);
+						*ippp = htonl(0x1000000b);
+					}
+
+					sctp_abort_an_association(stcb->sctp_ep,
+					    stcb, SCTP_PEER_FAULTY, oper);
+
+					*abort_flag = 1;
+					return;
+				}
+				if ((next->rec.data.rcv_flags & SCTP_DATA_UNORDERED) == 0 &&
+				    chk->rec.data.stream_seq !=
+				    next->rec.data.stream_seq) {
+					/*
+					 * Huh, need the correct STR here, they
+					 * must be the same.
+					 */
+#ifdef SCTP_DEBUG
+					if (sctp_debug_on & SCTP_DEBUG_INDATA1) {
+						printf("Next chk - Gak, Evil plot, sseq:%d not the same as at:%d\n",
+						    chk->rec.data.stream_seq,
+						    next->rec.data.stream_seq);
+					}
+#endif
+					MGET(oper, MB_DONTWAIT, MT_DATA);
+					if (oper) {
+						struct sctp_paramhdr *ph;
+						u_int32_t *ippp;
+
+						oper->m_len =
+						    sizeof(struct sctp_paramhdr) +
+						    sizeof(*ippp);
+						ph = mtod(oper,
+						    struct sctp_paramhdr *);
+						ph->param_type =
+						    htons(SCTP_CAUSE_PROTOCOL_VIOLATION);
+						ph->param_length =
+						    htons(oper->m_len);
+						ippp = (u_int32_t *)(ph + 1);
+						*ippp = htonl(0x1000000c);
+					}
+
+					sctp_abort_an_association(stcb->sctp_ep,
+					    stcb, SCTP_PEER_FAULTY, oper);
+
+					*abort_flag = 1;
+					return;
+
+				}
+			}
+		}
+	}
+	/*
+	 * now that we have all in there place we must check a number of
+	 * things to see if we can send data to the ULP.
+	 */
+	/* we need to do some delivery, if we can */
+	chk = TAILQ_FIRST(&asoc->reasmqueue);
+	if (chk == NULL) {
+		/* Huh? */
+		asoc->size_on_reasm_queue = 0;
+		asoc->cnt_on_reasm_queue = 0;
+		return;
+	}
+	if (asoc->fragmented_delivery_inprogress == 0) {
+		nxt_todel =
+		    asoc->strmin[chk->rec.data.stream_number].last_sequence_delivered + 1;
+		if ((chk->rec.data.rcv_flags & SCTP_DATA_FIRST_FRAG) &&
+		    (nxt_todel == chk->rec.data.stream_seq ||
+		     (chk->rec.data.rcv_flags & SCTP_DATA_UNORDERED))) {
+			/*
+			 * Yep the first one is here and its
+			 * ok to deliver but should we?
+			 */
+			if (TAILQ_EMPTY(&asoc->delivery_queue) &&
+			    (sctp_is_all_msg_on_reasm(asoc, &tsize) ||
+			     (asoc->size_on_reasm_queue >=
+			      (stcb->sctp_socket->so_rcv.sb_hiwat >> 2) &&
+			      tsize))) {
+				/*
+				 * Yes, we setup to
+				 * start reception, by backing down the TSN
+				 * just in case we can't deliver. If we
+				 */
+				asoc->fragmented_delivery_inprogress = 1;
+				asoc->tsn_last_delivered =
+				    chk->rec.data.TSN_seq - 1;
+				asoc->str_of_pdapi =
+				    chk->rec.data.stream_number;
+				asoc->ssn_of_pdapi = chk->rec.data.stream_seq;
+				asoc->fragment_flags = chk->rec.data.rcv_flags;
+				sctp_service_reassembly(stcb, asoc, 0);
+			}
+		}
+	} else {
+		sctp_service_reassembly(stcb, asoc, 0);
+	}
+}
+
+/*
+ * This is an unfortunate routine. It checks to make sure a evil guy is not
+ * stuffing us full of bad packet fragments. A broken peer could also do this
+ * but this is doubtful. It is to bad I must worry about evil crackers sigh
+ * :< more cycles.
+ */
+static int
+sctp_does_chk_belong_to_reasm(struct sctp_association *asoc,
+    struct sctp_tmit_chunk *chk)
+{
+	struct sctp_tmit_chunk *at;
+	u_int32_t tsn_est;
+
+	TAILQ_FOREACH(at, &asoc->reasmqueue, sctp_next) {
+		if (compare_with_wrap(chk->rec.data.TSN_seq,
+		    at->rec.data.TSN_seq, MAX_TSN)) {
+			/* is it one bigger? */
+			tsn_est = at->rec.data.TSN_seq + 1;
+			if (tsn_est == chk->rec.data.TSN_seq) {
+				/* yep. It better be a last then*/
+				if ((at->rec.data.rcv_flags & SCTP_DATA_FRAG_MASK) !=
+				    SCTP_DATA_LAST_FRAG) {
+					/*
+					 * Ok this guy belongs next to a guy
+					 * that is NOT last, it should be a
+					 * middle/last, not a complete chunk.
+					 */
+					return (1);
+				} else {
+					/*
+					 * This guy is ok since its a LAST and
+					 * the new chunk is a fully self-
+					 * contained one.
+					 */
+					return (0);
+				}
+			}
+		} else if (chk->rec.data.TSN_seq == at->rec.data.TSN_seq) {
+			/* Software error since I have a dup? */
+			return (1);
+		} else {
+			/*
+			 * Ok, 'at' is larger than new chunk but does it
+			 * need to be right before it.
+			 */
+			tsn_est = chk->rec.data.TSN_seq + 1;
+			if (tsn_est == at->rec.data.TSN_seq) {
+				/* Yep, It better be a first */
+				if ((at->rec.data.rcv_flags & SCTP_DATA_FRAG_MASK) !=
+				    SCTP_DATA_FIRST_FRAG) {
+					return (1);
+				} else {
+					return (0);
+				}
+			}
+		}
+	}
+	return (0);
+}
+
+extern unsigned int sctp_max_chunks_on_queue;
+static int
+sctp_process_a_data_chunk(struct sctp_tcb *stcb, struct sctp_association *asoc,
+    struct mbuf **m, int offset, struct sctp_data_chunk *ch, int chk_length,
+    struct sctp_nets *net, u_int32_t *high_tsn, int *abort_flag,
+    int *break_flag, int last_chunk)
+{
+	/* Process a data chunk */
+	/*  struct sctp_tmit_chunk *chk;*/
+	struct sctp_tmit_chunk *chk;
+	u_int32_t tsn, gap;
+	struct mbuf *dmbuf;
+	int indx, the_len;
+	u_int16_t strmno, strmseq;
+	struct mbuf *oper;
+
+	chk = NULL;
+	tsn = ntohl(ch->dp.tsn);
+#ifdef SCTP_MAP_LOGGING
+	sctp_log_map(0, tsn, asoc->cumulative_tsn, SCTP_MAP_PREPARE_SLIDE);
+#endif
+	if (compare_with_wrap(asoc->cumulative_tsn, tsn, MAX_TSN) ||
+	    asoc->cumulative_tsn == tsn) {
+		/* It is a duplicate */
+		sctp_pegs[SCTP_DUPTSN_RECVD]++;
+		if (asoc->numduptsns < SCTP_MAX_DUP_TSNS) {
+			/* Record a dup for the next outbound sack */
+			asoc->dup_tsns[asoc->numduptsns] = tsn;
+			asoc->numduptsns++;
+		}
+		return (0);
+	}
+	/* Calculate the number of TSN's between the base and this TSN */
+	if (tsn >= asoc->mapping_array_base_tsn) {
+		gap  = tsn - asoc->mapping_array_base_tsn;
+	} else {
+		gap = (MAX_TSN - asoc->mapping_array_base_tsn) + tsn + 1;
+	}
+	if (gap >= (SCTP_MAPPING_ARRAY << 3)) {
+		/* Can't hold the bit in the mapping at max array, toss it */
+		return (0);
+	}
+	if (gap >= (uint32_t)(asoc->mapping_array_size << 3)) {
+		if (sctp_expand_mapping_array(asoc)) {
+			/* Can't expand, drop it */
+			return (0);
+		}
+	}
+	if (compare_with_wrap(tsn, *high_tsn, MAX_TSN)) {
+		*high_tsn = tsn;
+	}
+	/* See if we have received this one already */
+	if (SCTP_IS_TSN_PRESENT(asoc->mapping_array, gap)) {
+		sctp_pegs[SCTP_DUPTSN_RECVD]++;
+		if (asoc->numduptsns < SCTP_MAX_DUP_TSNS) {
+			/* Record a dup for the next outbound sack */
+			asoc->dup_tsns[asoc->numduptsns] = tsn;
+			asoc->numduptsns++;
+		}
+		if (!callout_pending(&asoc->dack_timer.timer)) {
+			/*
+			 * By starting the timer we assure that we
+			 * WILL sack at the end of the packet
+			 * when sctp_sack_check gets called.
+			 */
+			sctp_timer_start(SCTP_TIMER_TYPE_RECV, stcb->sctp_ep,
+			    stcb, NULL);
+		}
+		return (0);
+	}
+	/*
+	 * Check to see about the GONE flag, duplicates would cause
+	 * a sack to be sent up above
+	 */
+	if (stcb && (stcb->sctp_ep->sctp_flags & SCTP_PCB_FLAGS_SOCKET_GONE)) {
+		/*
+		 * wait a minute, this guy is gone, there is no
+		 * longer a receiver. Send peer an ABORT!
+		 */
+		struct mbuf *op_err;
+		op_err = sctp_generate_invmanparam(SCTP_CAUSE_OUT_OF_RESC);
+		sctp_abort_an_association(stcb->sctp_ep, stcb, 0, op_err);
+		*abort_flag = 1;
+		return (0);
+	}
+	/*
+	 * Now before going further we see if there is room. If NOT then
+	 * we MAY let one through only IF this TSN is the one we are
+	 * waiting for on a partial delivery API.
+	 */
+
+	/* now do the tests */
+	if (((asoc->cnt_on_all_streams +
+	 asoc->cnt_on_delivery_queue +
+	 asoc->cnt_on_reasm_queue +
+	  asoc->cnt_msg_on_sb) > sctp_max_chunks_on_queue) ||
+	   (((int)asoc->my_rwnd) <= 0)) {
+		/*
+		 * When we have NO room in the rwnd we check
+		 * to make sure the reader is doing its job...
+		 */
+		if (stcb->sctp_socket->so_rcv.sb_cc) {
+			/* some to read, wake-up */
+			sctp_sorwakeup(stcb->sctp_ep, stcb->sctp_socket);
+		}
+		/* now is it in the mapping array of what we have accepted? */
+		if (compare_with_wrap(tsn,
+		    asoc->highest_tsn_inside_map, MAX_TSN)) {
+
+			/* Nope not in the valid range dump it */
+#ifdef SCTP_DEBUG
+			if (sctp_debug_on & SCTP_DEBUG_INDATA1) {
+				printf("My rwnd overrun1:tsn:%lx rwnd %lu sbspace:%ld delq:%d!\n",
+				    (u_long)tsn, (u_long)asoc->my_rwnd,
+				    sctp_sbspace(&stcb->sctp_socket->so_rcv),
+				    stcb->asoc.cnt_on_delivery_queue);
+			}
+#endif
+			sctp_set_rwnd(stcb, asoc);
+			if ((asoc->cnt_on_all_streams +
+			    asoc->cnt_on_delivery_queue +
+			    asoc->cnt_on_reasm_queue +
+			    asoc->cnt_msg_on_sb) > sctp_max_chunks_on_queue) {
+				sctp_pegs[SCTP_MSGC_DROP]++;
+			} else {
+				sctp_pegs[SCTP_RWND_DROPS]++;
+			}
+			indx = *break_flag;
+			*break_flag = 1;
+			return (0);
+		}
+	}
+	strmno = ntohs(ch->dp.stream_id);
+	if (strmno >= asoc->streamincnt) {
+		struct sctp_paramhdr *phdr;
+		struct mbuf *mb;
+
+		MGETHDR(mb, MB_DONTWAIT, MT_DATA);
+		if (mb != NULL) {
+			/* add some space up front so prepend will work well */
+			mb->m_data += sizeof(struct sctp_chunkhdr);
+			phdr = mtod(mb, struct sctp_paramhdr *);
+			/*
+			 * Error causes are just param's and this one has
+			 * two back to back phdr, one with the error type
+			 * and size, the other with the streamid and a rsvd
+		 	 */
+			mb->m_pkthdr.len = mb->m_len =
+			    (sizeof(struct sctp_paramhdr) * 2);
+			phdr->param_type = htons(SCTP_CAUSE_INV_STRM);
+			phdr->param_length =
+			    htons(sizeof(struct sctp_paramhdr) * 2);
+			phdr++;
+			/* We insert the stream in the type field */
+			phdr->param_type = ch->dp.stream_id;
+			/* And set the length to 0 for the rsvd field */
+			phdr->param_length = 0;
+			sctp_queue_op_err(stcb, mb);
+		}
+		sctp_pegs[SCTP_BAD_STRMNO]++;
+		return (0);
+	}
+	/*
+	 * Before we continue lets validate that we are not
+	 * being fooled by an evil attacker. We can only
+	 * have 4k chunks based on our TSN spread allowed
+	 * by the mapping array 512 * 8 bits, so there is
+	 * no way our stream sequence numbers could have wrapped.
+	 * We of course only validate the FIRST fragment so the
+	 * bit must be set.
+	 */
+	strmseq = ntohs(ch->dp.stream_sequence);
+	if ((ch->ch.chunk_flags & SCTP_DATA_FIRST_FRAG) &&
+	    (ch->ch.chunk_flags & SCTP_DATA_UNORDERED) == 0 &&
+	    (compare_with_wrap(asoc->strmin[strmno].last_sequence_delivered,
+	     strmseq, MAX_SEQ) ||
+	     asoc->strmin[strmno].last_sequence_delivered == strmseq)) {
+		/* The incoming sseq is behind where we last delivered? */
+#ifdef SCTP_DEBUG
+		if (sctp_debug_on & SCTP_DEBUG_INDATA1) {
+			printf("EVIL/Broken-Dup S-SEQ:%d delivered:%d from peer, Abort!\n",
+			    strmseq,
+			    asoc->strmin[strmno].last_sequence_delivered);
+		}
+#endif
+		/*
+		 * throw it in the stream so it gets cleaned up in
+		 * association destruction
+		 */
+		MGET(oper, MB_DONTWAIT, MT_DATA);
+		if (oper) {
+			struct sctp_paramhdr *ph;
+			u_int32_t *ippp;
+
+			oper->m_len = sizeof(struct sctp_paramhdr) +
+			    sizeof(*ippp);
+			ph = mtod(oper, struct sctp_paramhdr *);
+			ph->param_type = htons(SCTP_CAUSE_PROTOCOL_VIOLATION);
+			ph->param_length = htons(oper->m_len);
+			ippp = (u_int32_t *)(ph + 1);
+			*ippp = htonl(0x20000001);
+		}
+		sctp_abort_an_association(stcb->sctp_ep, stcb, SCTP_PEER_FAULTY,
+		    oper);
+		sctp_pegs[SCTP_BAD_SSN_WRAP]++;
+		*abort_flag = 1;
+		return (0);
+	}
+
+	the_len = (chk_length-sizeof(struct sctp_data_chunk));
+	if (last_chunk == 0) {
+		dmbuf = sctp_m_copym(*m,
+		    (offset + sizeof(struct sctp_data_chunk)),
+		    the_len, MB_DONTWAIT);
+	} else {
+		/* We can steal the last chunk */
+		dmbuf = *m;
+		/* lop off the top part */
+		m_adj(dmbuf, (offset + sizeof(struct sctp_data_chunk)));
+		if (dmbuf->m_pkthdr.len > the_len) {
+			/* Trim the end round bytes off  too */
+			m_adj(dmbuf, -(dmbuf->m_pkthdr.len-the_len));
+		}
+		sctp_pegs[SCTP_NO_COPY_IN]++;
+	}
+	if (dmbuf == NULL) {
+		sctp_pegs[SCTP_DROP_NOMEMORY]++;
+		return (0);
+	}
+	if ((ch->ch.chunk_flags & SCTP_DATA_NOT_FRAG) == SCTP_DATA_NOT_FRAG &&
+	    asoc->fragmented_delivery_inprogress == 0 &&
+	    TAILQ_EMPTY(&asoc->delivery_queue) &&
+	    ((ch->ch.chunk_flags & SCTP_DATA_UNORDERED) ||
+	     ((asoc->strmin[strmno].last_sequence_delivered + 1) == strmseq &&
+	      TAILQ_EMPTY(&asoc->strmin[strmno].inqueue))) &&
+	    ((long)(stcb->sctp_socket->so_rcv.sb_hiwat -
+	            stcb->sctp_socket->so_rcv.sb_cc) >= (long)the_len)) {
+		/* Candidate for express delivery */
+		/*
+		 * Its not fragmented,
+		 * No PD-API is up,
+		 * Nothing in the delivery queue,
+		 * Its un-ordered OR ordered and the next to deliver AND
+		 * nothing else is stuck on the stream queue,
+		 * And there is room for it in the socket buffer.
+		 * Lets just stuff it up the buffer....
+		 */
+
+		struct mbuf *control, *mmm;
+		struct sockaddr_in6 sin6;
+		struct sockaddr_in6 lsa6;
+		struct sockaddr *to;
+
+		/* It would be nice to avoid this copy if we could :< */
+		control = sctp_build_ctl_nchunk(stcb, tsn,
+		    ch->dp.protocol_id, 0, strmno, strmseq,
+		    ch->ch.chunk_flags);
+		/* XXX need to append PKTHDR to the socket buffer first */
+
+		if ((dmbuf->m_flags & M_PKTHDR) == 0) {
+			struct mbuf *tmp;
+			MGETHDR(tmp, MB_DONTWAIT, MT_DATA);
+			if (tmp == NULL) {
+
+				/* no room! */
+				if (control) {
+					sctp_m_freem(control);
+					stcb->asoc.my_rwnd_control_len -=
+					    CMSG_LEN(sizeof(struct sctp_sndrcvinfo));
+				}
+
+				goto failed_express_del;
+			}
+			tmp->m_pkthdr.len = the_len;
+			tmp->m_len = 0;
+			tmp->m_next = dmbuf;
+			dmbuf = tmp;
+		}
+		to = (struct sockaddr *)&net->ro._l_addr;
+		if ((stcb->sctp_ep->sctp_flags & SCTP_PCB_FLAGS_NEEDS_MAPPED_V4) &&
+		    to->sa_family == AF_INET) {
+			struct sockaddr_in *sin;
+
+			sin = (struct sockaddr_in *)to;
+			bzero(&sin6, sizeof(sin6));
+			sin6.sin6_family = AF_INET6;
+			sin6.sin6_len = sizeof(struct sockaddr_in6);
+			sin6.sin6_addr.s6_addr16[2] = 0xffff;
+			bcopy(&sin->sin_addr,
+			    &sin6.sin6_addr.s6_addr16[3],
+			    sizeof(sin6.sin6_addr.s6_addr16[3]));
+			sin6.sin6_port = sin->sin_port;
+			to = (struct sockaddr *)&sin6;
+		}
+
+		/* check and strip embedded scope junk */
+		to = (struct sockaddr *)sctp_recover_scope((struct sockaddr_in6 *)to,
+		    &lsa6);
+		if (((struct sockaddr_in *)to)->sin_port == 0) {
+			printf("Huh c, port is %d not net:%x %d?\n",
+			       ((struct sockaddr_in *)to)->sin_port,
+			       (u_int)net,
+			       (int)(ntohs(stcb->rport)));
+			((struct sockaddr_in *)to)->sin_port = stcb->rport;
+		}
+
+		mmm = dmbuf;
+		/* Mark the EOR */
+		while (mmm->m_next != NULL) {
+			mmm = mmm->m_next;
+		}
+		mmm->m_flags |= M_EOR;
+		if (compare_with_wrap(tsn, asoc->highest_tsn_inside_map, MAX_TSN)) {
+			/* we have a new high score */
+			asoc->highest_tsn_inside_map = tsn;
+#ifdef SCTP_MAP_LOGGING
+			sctp_log_map(0, 1, asoc->highest_tsn_inside_map, SCTP_MAP_SLIDE_RESULT);
+#endif
+		}
+		SCTP_TCB_UNLOCK(stcb);
+		SCTP_INP_WLOCK(stcb->sctp_ep);
+		SCTP_TCB_LOCK(stcb);
+		if (!sbappendaddr_nocheck(&stcb->sctp_socket->so_rcv, to, dmbuf,
+		    control, stcb->asoc.my_vtag, stcb->sctp_ep)) {
+			if (control) {
+				sctp_m_freem(control);
+				stcb->asoc.my_rwnd_control_len -=
+				    CMSG_LEN(sizeof(struct sctp_sndrcvinfo));
+			}
+			sctp_m_freem(dmbuf);
+			goto failed_express_del;
+		}
+		if ((stcb->sctp_ep->sctp_flags & SCTP_PCB_FLAGS_IN_TCPPOOL) == 0) {
+			if (sctp_add_to_socket_q(stcb->sctp_ep, stcb)) {
+				stcb->asoc.my_rwnd_control_len += sizeof(struct mbuf);
+			}
+		} else {
+			stcb->asoc.my_rwnd_control_len += sizeof(struct mbuf);
+		}
+		SCTP_INP_WUNLOCK(stcb->sctp_ep);
+		sctp_sorwakeup(stcb->sctp_ep, stcb->sctp_socket);
+		if ((ch->ch.chunk_flags & SCTP_DATA_UNORDERED) == 0) {
+
+			/* for ordered, bump what we delivered */
+			asoc->strmin[strmno].last_sequence_delivered++;
+		}
+		sctp_pegs[SCTP_EXPRESS_ROUTE]++;
+#ifdef SCTP_STR_LOGGING
+		sctp_log_strm_del_alt(tsn, strmseq,
+		    SCTP_STR_LOG_FROM_EXPRS_DEL);
+#endif
+#ifdef SCTP_DEBUG
+		if (sctp_debug_on & SCTP_DEBUG_INDATA1) {
+			printf("Express Delivery succeeds\n");
+		}
+#endif
+		goto finish_express_del;
+	}
+
+ failed_express_del:
+	/* If we reach here this is a new chunk */
+	chk = (struct sctp_tmit_chunk *)SCTP_ZONE_GET(sctppcbinfo.ipi_zone_chunk);
+	if (chk == NULL) {
+		/* No memory so we drop the chunk */
+		sctp_pegs[SCTP_DROP_NOMEMORY]++;
+		if (last_chunk == 0) {
+			/* we copied it, free the copy */
+			sctp_m_freem(dmbuf);
+		}
+		return (0);
+	}
+	sctppcbinfo.ipi_count_chunk++;
+	sctppcbinfo.ipi_gencnt_chunk++;
+	chk->rec.data.TSN_seq = tsn;
+	chk->rec.data.stream_seq = strmseq;
+	chk->rec.data.stream_number = strmno;
+	chk->rec.data.payloadtype = ch->dp.protocol_id;
+	chk->rec.data.context = 0;
+	chk->rec.data.doing_fast_retransmit = 0;
+	chk->rec.data.rcv_flags = ch->ch.chunk_flags;
+	chk->asoc = asoc;
+	chk->send_size = the_len;
+	chk->whoTo = net;
+	net->ref_count++;
+	chk->data = dmbuf;
+
+
+	/* Mark it as received */
+	/* Now queue it where it belongs */
+	if ((chk->rec.data.rcv_flags & SCTP_DATA_NOT_FRAG) ==
+	    SCTP_DATA_NOT_FRAG) {
+		/* First a sanity check */
+		if (asoc->fragmented_delivery_inprogress) {
+			/*
+			 * Ok, we have a fragmented delivery in progress
+			 * if this chunk is next to deliver OR belongs in
+			 * our view to the reassembly, the peer is evil
+			 * or broken.
+			 */
+			u_int32_t estimate_tsn;
+			estimate_tsn = asoc->tsn_last_delivered + 1;
+			if (TAILQ_EMPTY(&asoc->reasmqueue) &&
+			    (estimate_tsn == chk->rec.data.TSN_seq)) {
+				/* Evil/Broke peer */
+				MGET(oper, MB_DONTWAIT, MT_DATA);
+				if (oper) {
+					struct sctp_paramhdr *ph;
+					u_int32_t *ippp;
+
+					oper->m_len =
+					    sizeof(struct sctp_paramhdr) +
+					    sizeof(*ippp);
+					ph = mtod(oper, struct sctp_paramhdr *);
+					ph->param_type =
+					    htons(SCTP_CAUSE_PROTOCOL_VIOLATION);
+					ph->param_length = htons(oper->m_len);
+					ippp = (u_int32_t *)(ph + 1);
+					*ippp = htonl(0x20000002);
+				}
+				sctp_abort_an_association(stcb->sctp_ep, stcb,
+				    SCTP_PEER_FAULTY, oper);
+
+				*abort_flag = 1;
+				sctp_pegs[SCTP_DROP_FRAG]++;
+				return (0);
+			} else {
+				if (sctp_does_chk_belong_to_reasm(asoc, chk)) {
+					MGET(oper, MB_DONTWAIT, MT_DATA);
+					if (oper) {
+						struct sctp_paramhdr *ph;
+						u_int32_t *ippp;
+
+						oper->m_len =
+						    sizeof(struct sctp_paramhdr) +
+						    sizeof(*ippp);
+						ph = mtod(oper,
+						    struct sctp_paramhdr *);
+						ph->param_type =
+						    htons(SCTP_CAUSE_PROTOCOL_VIOLATION);
+						ph->param_length =
+						    htons(oper->m_len);
+						ippp = (u_int32_t *)(ph + 1);
+						*ippp = htonl(0x20000003);
+					}
+					sctp_abort_an_association(stcb->sctp_ep,
+					    stcb, SCTP_PEER_FAULTY, oper);
+
+					*abort_flag = 1;
+					sctp_pegs[SCTP_DROP_FRAG]++;
+					return (0);
+				}
+			}
+		} else {
+			if (!TAILQ_EMPTY(&asoc->reasmqueue)) {
+				/*
+				 * Reassembly queue is NOT empty
+				 * validate that this chk does not need to
+				 * be in reasembly queue. If it does then
+				 * our peer is broken or evil.
+				 */
+				if (sctp_does_chk_belong_to_reasm(asoc, chk)) {
+					MGET(oper, MB_DONTWAIT, MT_DATA);
+					if (oper) {
+						struct sctp_paramhdr *ph;
+						u_int32_t *ippp;
+
+						oper->m_len =
+						    sizeof(struct sctp_paramhdr) +
+						    sizeof(*ippp);
+						ph = mtod(oper,
+						    struct sctp_paramhdr *);
+						ph->param_type =
+						    htons(SCTP_CAUSE_PROTOCOL_VIOLATION);
+						ph->param_length =
+						    htons(oper->m_len);
+						ippp = (u_int32_t *)(ph + 1);
+						*ippp = htonl(0x20000004);
+					}
+					sctp_abort_an_association(stcb->sctp_ep,
+					    stcb, SCTP_PEER_FAULTY, oper);
+
+					*abort_flag = 1;
+					sctp_pegs[SCTP_DROP_FRAG]++;
+					return (0);
+				}
+			}
+		}
+		if (chk->rec.data.rcv_flags & SCTP_DATA_UNORDERED) {
+			/* queue directly into socket buffer */
+			sctp_deliver_data(stcb, asoc, chk, 0);
+			sctp_sorwakeup(stcb->sctp_ep, stcb->sctp_socket);
+		} else {
+			/* Special check for when streams are resetting.
+			 * We could be more smart about this and check the
+			 * actual stream to see if it is not being reset.. that
+			 * way we would not create a HOLB when amongst streams
+			 * being reset and those not being reset.
+			 *
+			 * We take complete messages that have a stream reset
+			 * intervening (aka the TSN is after where our cum-ack needs
+			 * to be) off and put them on a pending_reply_queue. The
+			 * reassembly ones we do not have to worry about since
+			 * they are all sorted and proceessed by TSN order. It
+			 * is only the singletons I must worry about.
+			 */
+			if ((asoc->pending_reply) &&
+			   ((compare_with_wrap(tsn, ntohl(asoc->pending_reply->reset_at_tsn), MAX_TSN)) ||
+			    (tsn == ntohl(asoc->pending_reply->reset_at_tsn)))
+				) {
+				/* yep its past where we need to reset... go ahead and
+				 * queue it.
+				 */
+				TAILQ_INSERT_TAIL(&asoc->pending_reply_queue , chk, sctp_next);
+			}  else {
+				sctp_queue_data_to_stream(stcb, asoc, chk, abort_flag);
+			}
+		}
+	} else {
+		/* Into the re-assembly queue */
+		sctp_queue_data_for_reasm(stcb, asoc, chk, abort_flag);
+		if (*abort_flag) {
+			sctp_pegs[SCTP_DROP_FRAG]++;
+			return (0);
+		}
+	}
+	if (compare_with_wrap(tsn, asoc->highest_tsn_inside_map, MAX_TSN)) {
+		/* we have a new high score */
+		asoc->highest_tsn_inside_map = tsn;
+#ifdef SCTP_MAP_LOGGING
+		sctp_log_map(0, 2, asoc->highest_tsn_inside_map, SCTP_MAP_SLIDE_RESULT);
+#endif
+	}
+ finish_express_del:
+	if (last_chunk) {
+		*m = NULL;
+	}
+	sctp_pegs[SCTP_PEG_TSNS_RCVD]++;
+	/* Set it present please */
+#ifdef SCTP_STR_LOGGING
+	sctp_log_strm_del_alt(tsn, strmseq, SCTP_STR_LOG_FROM_MARK_TSN);
+#endif
+#ifdef SCTP_MAP_LOGGING
+	sctp_log_map(asoc->mapping_array_base_tsn, asoc->cumulative_tsn,
+		     asoc->highest_tsn_inside_map, SCTP_MAP_PREPARE_SLIDE);
+#endif
+	SCTP_SET_TSN_PRESENT(asoc->mapping_array, gap);
+	return (1);
+}
+
+void
+sctp_sack_check(struct sctp_tcb *stcb, int ok_to_sack, int was_a_gap, int *abort_flag)
+{
+	/*
+	 * Now we also need to check the mapping array in a couple of ways.
+	 * 1) Did we move the cum-ack point?
+	 */
+	struct sctp_association *asoc;
+	int i, at;
+	int m_size, all_ones;
+	int slide_from, slide_end, lgap, distance;
+#ifdef SCTP_MAP_LOGGING
+	uint32_t old_cumack, old_base, old_highest;
+	unsigned char aux_array[64];
+#endif
+
+	asoc = &stcb->asoc;
+	at = 0;
+
+#ifdef SCTP_MAP_LOGGING
+	old_cumack = asoc->cumulative_tsn;
+	old_base = asoc->mapping_array_base_tsn;
+	old_highest = asoc->highest_tsn_inside_map;
+	if (asoc->mapping_array_size < 64)
+		memcpy(aux_array, asoc->mapping_array,
+		    asoc->mapping_array_size);
+	else
+		memcpy(aux_array, asoc->mapping_array, 64);
+#endif
+
+	/*
+	 * We could probably improve this a small bit by calculating the
+	 * offset of the current cum-ack as the starting point.
+	 */
+	all_ones = 1;
+	m_size = stcb->asoc.mapping_array_size << 3;
+	for (i = 0; i < m_size; i++) {
+		if (!SCTP_IS_TSN_PRESENT(asoc->mapping_array, i)) {
+			/*
+			 * Ok we found the first place that we are
+			 * missing a TSN.
+			 */
+			at = i;
+			all_ones = 0;
+			asoc->cumulative_tsn = asoc->mapping_array_base_tsn +
+			    (i - 1);
+			break;
+		}
+	}
+	if (compare_with_wrap(asoc->cumulative_tsn,
+			      asoc->highest_tsn_inside_map,
+			      MAX_TSN)) {
+		panic("huh, cumack greater than high-tsn in map");
+	}
+	if (all_ones ||
+	    (asoc->cumulative_tsn == asoc->highest_tsn_inside_map && at >= 8)) {
+		/* The complete array was completed by a single FR */
+		/* higest becomes the cum-ack */
+		int clr;
+		asoc->cumulative_tsn = asoc->highest_tsn_inside_map;
+		/* clear the array */
+		if (all_ones)
+			clr = asoc->mapping_array_size;
+		else {
+			clr = (at >> 3) + 1;
+			/*
+			 * this should be the allones case
+			 * but just in case :>
+			 */
+			if (clr > asoc->mapping_array_size)
+				clr = asoc->mapping_array_size;
+		}
+		memset(asoc->mapping_array, 0, clr);
+		/* base becomes one ahead of the cum-ack */
+		asoc->mapping_array_base_tsn = asoc->cumulative_tsn + 1;
+#ifdef SCTP_MAP_LOGGING
+		sctp_log_map(old_base, old_cumack, old_highest,
+		    SCTP_MAP_PREPARE_SLIDE);
+		sctp_log_map(asoc->mapping_array_base_tsn, asoc->cumulative_tsn,
+		    asoc->highest_tsn_inside_map, SCTP_MAP_SLIDE_CLEARED);
+#endif
+	} else if (at >= 8) {
+		/* we can slide the mapping array down */
+		/* Calculate the new byte postion we can move down */
+		slide_from = at >> 3;
+		/* now calculate the ceiling of the move using our highest TSN value */
+		if (asoc->highest_tsn_inside_map >= asoc->mapping_array_base_tsn) {
+			lgap = asoc->highest_tsn_inside_map -
+			    asoc->mapping_array_base_tsn;
+		} else {
+			lgap = (MAX_TSN - asoc->mapping_array_base_tsn) +
+			    asoc->highest_tsn_inside_map + 1;
+		}
+		slide_end = lgap >> 3;
+		if (slide_end < slide_from) {
+			panic("impossible slide");
+		}
+		distance = (slide_end-slide_from) + 1;
+#ifdef SCTP_MAP_LOGGING
+		sctp_log_map(old_base, old_cumack, old_highest,
+		    SCTP_MAP_PREPARE_SLIDE);
+		sctp_log_map((uint32_t)slide_from, (uint32_t)slide_end,
+		    (uint32_t)lgap, SCTP_MAP_SLIDE_FROM);
+#endif
+		if (distance + slide_from > asoc->mapping_array_size ||
+		    distance < 0) {
+#ifdef SCTP_DEBUG
+			if (sctp_debug_on & SCTP_DEBUG_INDATA1) {
+				printf("Ugh bad addition.. you can't hrumpp!\n");
+			}
+#endif
+			/*
+			 * Here we do NOT slide forward the array so that
+			 * hopefully when more data comes in to fill it up
+			 * we will be able to slide it forward. Really
+			 * I don't think this should happen :-0
+			 */
+
+#ifdef SCTP_MAP_LOGGING
+			sctp_log_map((uint32_t)distance, (uint32_t)slide_from,
+			    (uint32_t)asoc->mapping_array_size,
+			    SCTP_MAP_SLIDE_NONE);
+#endif
+		} else {
+			int ii;
+			for (ii = 0; ii < distance; ii++) {
+				asoc->mapping_array[ii] =
+				    asoc->mapping_array[slide_from + ii];
+			}
+			for (ii = distance;ii <= slide_end; ii++) {
+				asoc->mapping_array[ii] = 0;
+			}
+			asoc->mapping_array_base_tsn += (slide_from << 3);
+#ifdef SCTP_MAP_LOGGING
+			sctp_log_map(asoc->mapping_array_base_tsn,
+			    asoc->cumulative_tsn, asoc->highest_tsn_inside_map,
+			    SCTP_MAP_SLIDE_RESULT);
+#endif
+		}
+	}
+
+        /* check the special flag for stream resets */
+	if ((asoc->pending_reply) &&
+	   ((compare_with_wrap((asoc->cumulative_tsn+1), ntohl(asoc->pending_reply->reset_at_tsn), MAX_TSN)) ||
+	    ((asoc->cumulative_tsn+1) ==  ntohl(asoc->pending_reply->reset_at_tsn)))
+		) {
+		/* we have finished working through the backlogged TSN's now
+		 * time to reset streams.
+		 * 1: call reset function.
+		 * 2: free pending_reply space
+		 * 3: distribute any chunks in pending_reply_queue.
+		 */
+		struct sctp_tmit_chunk *chk;
+		sctp_handle_stream_reset_response(stcb, asoc->pending_reply);
+		FREE(asoc->pending_reply, M_PCB);
+		asoc->pending_reply = NULL;
+		chk = TAILQ_FIRST(&asoc->pending_reply_queue);
+		while (chk) {
+			TAILQ_REMOVE(&asoc->pending_reply_queue, chk, sctp_next);
+			sctp_queue_data_to_stream(stcb, asoc, chk, abort_flag);
+			if (*abort_flag) {
+				return;
+			}
+			chk = TAILQ_FIRST(&asoc->pending_reply_queue);
+		}
+	}
+	/*
+	 * Now we need to see if we need to queue a sack or just start
+	 * the timer (if allowed).
+	 */
+	if (ok_to_sack) {
+		if (SCTP_GET_STATE(asoc) == SCTP_STATE_SHUTDOWN_SENT) {
+			/*
+			 * Ok special case, in SHUTDOWN-SENT case.
+			 * here we maker sure SACK timer is off and
+			 * instead send a SHUTDOWN and a SACK
+			 */
+			if (callout_pending(&stcb->asoc.dack_timer.timer)) {
+				sctp_timer_stop(SCTP_TIMER_TYPE_RECV,
+				    stcb->sctp_ep, stcb, NULL);
+			}
+#ifdef SCTP_DEBUG
+			if (sctp_debug_on & SCTP_DEBUG_OUTPUT4) {
+				printf("%s:%d sends a shutdown\n",
+				       __FILE__,
+				       __LINE__
+				       );
+			}
+#endif
+			sctp_send_shutdown(stcb, stcb->asoc.primary_destination);
+			sctp_send_sack(stcb);
+		} else {
+			int is_a_gap;
+			/* is there a gap now ? */
+			is_a_gap = compare_with_wrap(stcb->asoc.highest_tsn_inside_map,
+			    stcb->asoc.cumulative_tsn, MAX_TSN);
+			if ((stcb->asoc.first_ack_sent == 0) ||	/* First time we send a sack */
+			    ((was_a_gap) && (is_a_gap == 0)) || /* was a gap, but no longer is one */
+			    (stcb->asoc.numduptsns) ||		/* we have dup's */
+			    (is_a_gap) ||			/* is still a gap */
+			    (callout_pending(&stcb->asoc.dack_timer.timer)) /* timer was up . second packet */
+				) {
+				/*
+			 	 * Ok we must build a SACK since the timer
+				 * is pending, we got our first packet OR
+				 * there are gaps or duplicates.
+				 */
+				stcb->asoc.first_ack_sent = 1;
+				sctp_send_sack(stcb);
+				/* The sending will stop the timer */
+			} else {
+				sctp_timer_start(SCTP_TIMER_TYPE_RECV,
+				    stcb->sctp_ep, stcb, NULL);
+			}
+		}
+	}
+}
+
+void
+sctp_service_queues(struct sctp_tcb *stcb, struct sctp_association *asoc, int hold_locks)
+{
+	struct sctp_tmit_chunk *chk;
+	int tsize, cntDel;
+	u_int16_t nxt_todel;
+
+	cntDel = 0;
+	if (asoc->fragmented_delivery_inprogress) {
+		sctp_service_reassembly(stcb, asoc, hold_locks);
+	}
+	/* Can we proceed further, i.e. the PD-API is complete */
+	if (asoc->fragmented_delivery_inprogress) {
+		/* no */
+		return;
+	}
+
+	/*
+	 * Yes, reassembly delivery no longer in progress see if we
+	 * have some on the sb hold queue.
+	 */
+	do {
+		if (stcb->sctp_socket->so_rcv.sb_cc >= stcb->sctp_socket->so_rcv.sb_hiwat) {
+			if (cntDel == 0)
+				sctp_sorwakeup(stcb->sctp_ep, stcb->sctp_socket);
+			break;
+		}
+		/* If deliver_data says no we must stop */
+		if (sctp_deliver_data(stcb, asoc, (struct sctp_tmit_chunk *)NULL, hold_locks) == 0)
+			break;
+		cntDel++;
+		chk = TAILQ_FIRST(&asoc->delivery_queue);
+	} while (chk);
+	if (cntDel) {
+		sctp_sorwakeup(stcb->sctp_ep, stcb->sctp_socket);
+	}
+	/*
+	 * Now is there some other chunk I can deliver
+	 * from the reassembly queue.
+	 */
+	chk = TAILQ_FIRST(&asoc->reasmqueue);
+	if (chk == NULL) {
+		asoc->size_on_reasm_queue = 0;
+		asoc->cnt_on_reasm_queue = 0;
+		return;
+	}
+	nxt_todel = asoc->strmin[chk->rec.data.stream_number].last_sequence_delivered + 1;
+	if ((chk->rec.data.rcv_flags & SCTP_DATA_FIRST_FRAG) &&
+	    ((nxt_todel == chk->rec.data.stream_seq) ||
+	     (chk->rec.data.rcv_flags & SCTP_DATA_UNORDERED))) {
+		/*
+		 * Yep the first one is here. We setup to
+		 * start reception, by backing down the TSN
+		 * just in case we can't deliver.
+		 */
+
+		/*
+		 * Before we start though either all of the
+		 * message should be here or 1/4 the socket buffer
+		 * max or nothing on the delivery queue and something
+		 * can be delivered.
+		 */
+		if (TAILQ_EMPTY(&asoc->delivery_queue) &&
+		    (sctp_is_all_msg_on_reasm(asoc, &tsize) ||
+		     (asoc->size_on_reasm_queue >=
+		      (stcb->sctp_socket->so_rcv.sb_hiwat >> 2) && tsize))) {
+			asoc->fragmented_delivery_inprogress = 1;
+			asoc->tsn_last_delivered = chk->rec.data.TSN_seq-1;
+			asoc->str_of_pdapi = chk->rec.data.stream_number;
+			asoc->ssn_of_pdapi = chk->rec.data.stream_seq;
+			asoc->fragment_flags = chk->rec.data.rcv_flags;
+			sctp_service_reassembly(stcb, asoc, hold_locks);
+		}
+	}
+}
+
+int
+sctp_process_data(struct mbuf **mm, int iphlen, int *offset, int length,
+    struct sctphdr *sh, struct sctp_inpcb *inp, struct sctp_tcb *stcb,
+    struct sctp_nets *net, u_int32_t *high_tsn)
+{
+	struct sctp_data_chunk *ch, chunk_buf;
+	struct sctp_association *asoc;
+	int num_chunks = 0;	/* number of control chunks processed */
+	int chk_length, break_flag, last_chunk;
+	int abort_flag = 0, was_a_gap = 0;
+	struct mbuf *m;
+
+	/* set the rwnd */
+	sctp_set_rwnd(stcb, &stcb->asoc);
+
+	m = *mm;
+	asoc = &stcb->asoc;
+	if (compare_with_wrap(stcb->asoc.highest_tsn_inside_map,
+	    stcb->asoc.cumulative_tsn, MAX_TSN)) {
+		/* there was a gap before this data was processed */
+		was_a_gap = 1;
+	}
+	/*
+	 * setup where we got the last DATA packet from for
+	 * any SACK that may need to go out. Don't bump
+	 * the net. This is done ONLY when a chunk
+	 * is assigned.
+	 */
+	asoc->last_data_chunk_from = net;
+
+	/*
+	 * Now before we proceed we must figure out if this
+	 * is a wasted cluster... i.e. it is a small packet
+	 * sent in and yet the driver underneath allocated a
+	 * full cluster for it. If so we must copy it to a
+	 * smaller mbuf and free up the cluster mbuf. This
+	 * will help with cluster starvation.
+	 */
+	if (m->m_len < (long)MHLEN && m->m_next == NULL) {
+		/* we only handle mbufs that are singletons.. not chains */
+#ifdef __DragonFly__
+		if ((*mm)->m_flags & M_PKTHDR)
+			MGETHDR(m, MB_DONTWAIT, MT_HEADER);
+		else
+#endif
+			MGET(m, MB_DONTWAIT, MT_DATA);
+		if (m) {
+			/* ok lets see if we can copy the data up */
+			caddr_t *from, *to;
+
+			if ((*mm)->m_flags & M_PKTHDR) {
+				/* got to copy the header first */
+#ifdef __APPLE__
+				M_COPY_PKTHDR(m, (*mm));
+#else
+				M_MOVE_PKTHDR(m, (*mm));
+#endif
+			}
+			/* get the pointers and copy */
+			to = mtod(m, caddr_t *);
+			from = mtod((*mm), caddr_t *);
+			memcpy(to, from, (*mm)->m_len);
+			/* copy the length and free up the old */
+			m->m_len = (*mm)->m_len;
+			sctp_m_freem(*mm);
+			/* sucess, back copy */
+			*mm = m;
+		} else {
+			/* We are in trouble in the mbuf world .. yikes */
+			m = *mm;
+		}
+	}
+
+	/* get pointer to the first chunk header */
+	ch = (struct sctp_data_chunk *)sctp_m_getptr(m, *offset,
+	    sizeof(chunk_buf), (u_int8_t *)&chunk_buf);
+	if (ch == NULL) {
+		printf(" ... its short\n");
+		return (1);
+	}
+	/*
+	 * process all DATA chunks...
+	 */
+
+#ifdef SCTP_DEBUG
+	if (sctp_debug_on & SCTP_DEBUG_INPUT1) {
+		printf("In process data off:%d length:%d iphlen:%d ch->type:%d\n",
+		    *offset, length, iphlen, (int)ch->ch.chunk_type);
+	}
+#endif
+
+	*high_tsn = asoc->cumulative_tsn;
+	break_flag = 0;
+	while (ch->ch.chunk_type == SCTP_DATA) {
+		/* validate chunk length */
+		chk_length = ntohs(ch->ch.chunk_length);
+		if ((size_t)chk_length < sizeof(struct sctp_data_chunk) + 1 ||
+		    length - *offset < chk_length) {
+			/*
+			 * Need to send an abort since we had a invalid
+			 * data chunk.
+			 */
+			struct mbuf *op_err;
+			MGET(op_err, MB_DONTWAIT, MT_DATA);
+			if (op_err) {
+				struct sctp_paramhdr *ph;
+				u_int32_t *ippp;
+
+				op_err->m_len = sizeof(struct sctp_paramhdr) +
+				    sizeof(*ippp);
+				ph = mtod(op_err, struct sctp_paramhdr *);
+				ph->param_type =
+				    htons(SCTP_CAUSE_PROTOCOL_VIOLATION);
+				ph->param_length = htons(op_err->m_len);
+				ippp = (u_int32_t *)(ph + 1);
+				*ippp = htonl(0x30000001);
+			}
+			sctp_abort_association(inp, stcb, m, iphlen, sh,
+			    op_err);
+			return (2);
+		}
+#ifdef SCTP_DEBUG
+		if (sctp_debug_on & SCTP_DEBUG_INPUT1) {
+			printf("A chunk of len:%d to process (tot:%d)\n",
+			    chk_length, length - *offset);
+		}
+#endif
+
+#ifdef SCTP_AUDITING_ENABLED
+		sctp_audit_log(0xB1, 0);
+#endif
+		if (SCTP_SIZE32(chk_length) == *offset - length) {
+			last_chunk = 1;
+		} else {
+			last_chunk = 0;
+		}
+		if (sctp_process_a_data_chunk(stcb, asoc, mm, *offset, ch,
+		    chk_length, net, high_tsn, &abort_flag, &break_flag,
+		    last_chunk)) {
+			num_chunks++;
+#ifdef SCTP_DEBUG
+			if (sctp_debug_on & SCTP_DEBUG_INPUT1) {
+				printf("Now incr num_chunks to %d\n",
+				    num_chunks);
+			}
+#endif
+		}
+		if (abort_flag)
+			return (2);
+
+		if (break_flag) {
+			/*
+			 * Set because of out of rwnd space and no drop rep
+			 * space left.
+			 */
+			break;
+		}
+
+		*offset += SCTP_SIZE32(chk_length);
+		if (*offset >= length) {
+			/* no more data left in the mbuf chain */
+			break;
+		}
+		ch = (struct sctp_data_chunk *)sctp_m_getptr(m, *offset,
+		    sizeof(chunk_buf), (u_int8_t *)&chunk_buf);
+		if (ch == NULL) {
+			*offset = length;
+			break;
+		}
+	} /* while */
+	if (break_flag) {
+		/*
+		 * we need to report rwnd overrun drops.
+		 */
+		sctp_send_packet_dropped(stcb, net, *mm, iphlen, 0);
+	}
+	if (num_chunks) {
+		/*
+		 * Did we get data, if so update the time for
+		 * auto-close and give peer credit for being
+		 * alive.
+		 */
+		sctp_pegs[SCTP_DATA_DG_RECV]++;
+		stcb->asoc.overall_error_count = 0;
+		SCTP_GETTIME_TIMEVAL(&stcb->asoc.time_last_rcvd);
+	}
+	/* now service all of the reassm queue and delivery queue */
+	sctp_service_queues(stcb, asoc, 0);
+	if (SCTP_GET_STATE(asoc) == SCTP_STATE_SHUTDOWN_SENT) {
+		/*
+		 * Assure that we ack right away by making
+		 * sure that a d-ack timer is running. So the
+		 * sack_check will send a sack.
+		 */
+		sctp_timer_start(SCTP_TIMER_TYPE_RECV, stcb->sctp_ep, stcb,
+		    net);
+	}
+	/* Start a sack timer or QUEUE a SACK for sending */
+	sctp_sack_check(stcb, 1, was_a_gap, &abort_flag);
+	if (abort_flag)
+		return (2);
+
+	return (0);
+}
+
+static void
+sctp_handle_segments(struct sctp_tcb *stcb, struct sctp_association *asoc,
+    struct sctp_sack_chunk *ch, u_long last_tsn, u_long *biggest_tsn_acked,
+    u_long *biggest_newly_acked_tsn, int num_seg, int *ecn_seg_sums)
+{
+	/************************************************/
+	/* process fragments and update sendqueue        */
+	/************************************************/
+	struct sctp_sack *sack;
+	struct sctp_gap_ack_block *frag;
+	struct sctp_tmit_chunk *tp1;
+	int i;
+	unsigned int j;
+#ifdef SCTP_FR_LOGGING
+	int num_frs=0;
+#endif
+	uint16_t frag_strt, frag_end, primary_flag_set;
+	u_long last_frag_high;
+
+	if (asoc->primary_destination->dest_state & SCTP_ADDR_SWITCH_PRIMARY) {
+		primary_flag_set = 1;
+	} else {
+		primary_flag_set = 0;
+	}
+
+	sack = &ch->sack;
+	frag = (struct sctp_gap_ack_block *)((caddr_t)sack +
+	    sizeof(struct sctp_sack));
+	tp1 = NULL;
+	last_frag_high = 0;
+	for (i = 0; i < num_seg; i++) {
+		frag_strt = ntohs(frag->start);
+		frag_end = ntohs(frag->end);
+		/* some sanity checks on the fargment offsets */
+		if (frag_strt > frag_end) {
+			/* this one is malformed, skip */
+			frag++;
+			continue;
+		}
+		if (compare_with_wrap((frag_end+last_tsn), *biggest_tsn_acked,
+		    MAX_TSN))
+			*biggest_tsn_acked = frag_end+last_tsn;
+
+		/* mark acked dgs and find out the highestTSN being acked */
+		if (tp1 == NULL) {
+			tp1 = TAILQ_FIRST(&asoc->sent_queue);
+
+			/* save the locations of the last frags */
+			last_frag_high = frag_end + last_tsn;
+		} else {
+			/*
+			 * now lets see if we need to reset the queue
+			 * due to a out-of-order SACK fragment
+			 */
+			if (compare_with_wrap(frag_strt+last_tsn,
+			    last_frag_high, MAX_TSN)) {
+				/*
+				 * if the new frag starts after the last TSN
+				 * frag covered, we are ok
+				 * and this one is beyond the last one
+				 */
+				;
+			} else {
+				/*
+				 * ok, they have reset us, so we need to reset
+				 * the queue this will cause extra hunting but
+				 * hey, they chose the performance
+				 * hit when they failed to order there gaps..
+				 */
+				tp1 = TAILQ_FIRST(&asoc->sent_queue);
+			}
+			last_frag_high = frag_end + last_tsn;
+		}
+		for (j = frag_strt + last_tsn; j <= frag_end + last_tsn; j++) {
+			while (tp1) {
+#ifdef SCTP_FR_LOGGING
+				if (tp1->rec.data.doing_fast_retransmit)
+					num_frs++;
+#endif
+
+				if (tp1->rec.data.TSN_seq == j) {
+					if (tp1->sent != SCTP_DATAGRAM_UNSENT) {
+						/* must be held until cum-ack passes */
+						/* ECN Nonce: Add the nonce value to the sender's nonce sum */
+						if (tp1->sent < SCTP_DATAGRAM_ACKED) {
+							/*
+							 * If it is less than
+							 * ACKED, it is now
+							 * no-longer in flight.
+							 * Higher values may
+							 * already be set via
+							 * previous Gap Ack
+							 * Blocks...
+							 * i.e. ACKED or MARKED.
+							 */
+							if (compare_with_wrap(tp1->rec.data.TSN_seq,
+							    *biggest_newly_acked_tsn,
+							    MAX_TSN)) {
+								*biggest_newly_acked_tsn =
+								    tp1->rec.data.TSN_seq;
+							}
+							tp1->whoTo->flight_size -= tp1->book_size;
+							if (tp1->whoTo->flight_size < 0) {
+								tp1->whoTo->flight_size = 0;
+							}
+							asoc->total_flight -=
+							    tp1->book_size;
+
+							if (asoc->total_flight < 0) {
+								asoc->total_flight = 0;
+							}
+
+							asoc->total_flight_count--;
+							if (asoc->total_flight_count < 0) {
+								asoc->total_flight_count = 0;
+							}
+
+							if (tp1->snd_count < 2) {
+								/* True non-retransmited chunk */
+								tp1->whoTo->net_ack2 +=
+								    tp1->send_size;
+
+								/* update RTO too? */
+								if (tp1->do_rtt) {
+									tp1->whoTo->RTO =
+									    sctp_calculate_rto(stcb,
+									    asoc,
+									    tp1->whoTo,
+									    &tp1->sent_rcv_time);
+									tp1->whoTo->rto_pending = 0;
+									tp1->do_rtt = 0;
+								}
+							}
+						}
+						if (tp1->sent <= SCTP_DATAGRAM_RESEND &&
+						    tp1->sent != SCTP_DATAGRAM_UNSENT &&
+						    compare_with_wrap(tp1->rec.data.TSN_seq,
+						    asoc->this_sack_highest_gap,
+						    MAX_TSN)) {
+							asoc->this_sack_highest_gap =
+							    tp1->rec.data.TSN_seq;
+							if (primary_flag_set) {
+								tp1->whoTo->cacc_saw_newack = 1;
+							}
+						}
+						if (tp1->sent == SCTP_DATAGRAM_RESEND) {
+#ifdef SCTP_DEBUG
+							if (sctp_debug_on &
+							    SCTP_DEBUG_INDATA3) {
+								printf("Hmm. one that is in RESEND that is now ACKED\n");
+							}
+#endif
+							asoc->sent_queue_retran_cnt--;
+#ifdef SCTP_AUDITING_ENABLED
+							sctp_audit_log(0xB2,
+							    (asoc->sent_queue_retran_cnt & 0x000000ff));
+#endif
+
+							if (asoc->sent_queue_retran_cnt < 0) {
+								printf("huh3 retran went negative?\n");
+#ifdef SCTP_AUDITING_ENABLED
+								sctp_auditing(30,
+								    inp, tcb,
+								    NULL);
+#else
+								asoc->sent_queue_retran_cnt = 0;
+#endif
+							}
+						}
+						(*ecn_seg_sums) += tp1->rec.data.ect_nonce;
+						(*ecn_seg_sums) &= SCTP_SACK_NONCE_SUM;
+						tp1->sent = SCTP_DATAGRAM_MARKED;
+					}
+					break;
+				} /* if (tp1->TSN_seq == j) */
+				if (compare_with_wrap(tp1->rec.data.TSN_seq, j,
+				    MAX_TSN))
+					break;
+				tp1 = TAILQ_NEXT(tp1, sctp_next);
+			}/* end while (tp1) */
+		}  /* end for (j = fragStart */
+		frag++; /* next one */
+	}
+#ifdef SCTP_FR_LOGGING
+	if (num_frs)
+		sctp_log_fr(*biggest_tsn_acked, *biggest_newly_acked_tsn,
+		    last_tsn, SCTP_FR_LOG_BIGGEST_TSNS);
+#endif
+}
+
+static void
+sctp_check_for_revoked(struct sctp_association *asoc, u_long cum_ack,
+    u_long biggest_tsn_acked)
+{
+	struct sctp_tmit_chunk *tp1;
+	int tot_revoked=0;
+
+	tp1 = TAILQ_FIRST(&asoc->sent_queue);
+	while (tp1) {
+		if (compare_with_wrap(tp1->rec.data.TSN_seq, cum_ack,
+		    MAX_TSN)) {
+			/*
+			 * ok this guy is either ACK or MARKED. If it is ACKED
+			 * it has been previously acked but not this time i.e.
+			 * revoked.  If it is MARKED it was ACK'ed again.
+			 */
+			if (tp1->sent == SCTP_DATAGRAM_ACKED) {
+				/* it has been revoked */
+				/*
+				 * We do NOT add back to flight size here since
+				 * it is really NOT in flight. Resend (when/if
+				 * it occurs will add to flight size
+				 */
+				tp1->sent = SCTP_DATAGRAM_SENT;
+				tot_revoked++;
+			} else if (tp1->sent == SCTP_DATAGRAM_MARKED) {
+				/* it has been re-acked in this SACK */
+				tp1->sent = SCTP_DATAGRAM_ACKED;
+			}
+		}
+		if (compare_with_wrap(tp1->rec.data.TSN_seq, biggest_tsn_acked,
+		    MAX_TSN)) {
+			/* above the sack */
+			break;
+		}
+		if (tp1->sent == SCTP_DATAGRAM_UNSENT)
+			break;
+		tp1 = TAILQ_NEXT(tp1, sctp_next);
+	}
+	if (tot_revoked > 0) {
+		/* Setup the ecn nonce re-sync point. We
+		 * do this since once data is revoked
+		 * we begin to retransmit things, which
+		 * do NOT have the ECN bits set. This means
+		 * we are now out of sync and must wait until
+		 * we get back in sync with the peer to
+		 * check ECN bits.
+		 */
+		tp1 = TAILQ_FIRST(&asoc->send_queue);
+		if (tp1 == NULL) {
+			asoc->nonce_resync_tsn = asoc->sending_seq;
+		} else {
+			asoc->nonce_resync_tsn = tp1->rec.data.TSN_seq;
+		}
+		asoc->nonce_wait_for_ecne = 0;
+		asoc->nonce_sum_check = 0;
+	}
+
+}
+
+extern int sctp_peer_chunk_oh;
+
+static void
+sctp_strike_gap_ack_chunks(struct sctp_tcb *stcb, struct sctp_association *asoc,
+    u_long biggest_tsn_acked, int strike_enabled,
+    u_long biggest_tsn_newly_acked, int accum_moved)
+{
+	struct sctp_tmit_chunk *tp1;
+	int strike_flag=0;
+	struct timeval now;
+	int tot_retrans=0;
+	u_int32_t sending_seq;
+	int primary_switch_active = 0;
+	int double_switch_active = 0;
+
+	/* select the sending_seq, this is
+	 * either the next thing ready to
+	 * be sent but not transmitted, OR,
+	 * the next seq we assign.
+	 */
+	tp1 = TAILQ_FIRST(&stcb->asoc.send_queue);
+	if (tp1 == NULL) {
+		sending_seq = asoc->sending_seq;
+	} else {
+		sending_seq = tp1->rec.data.TSN_seq;
+	}
+
+	if (asoc->primary_destination->dest_state & SCTP_ADDR_SWITCH_PRIMARY) {
+		primary_switch_active = 1;
+	}
+	if (asoc->primary_destination->dest_state & SCTP_ADDR_DOUBLE_SWITCH) {
+		double_switch_active = 1;
+	}
+	if (stcb->asoc.peer_supports_prsctp ) {
+		SCTP_GETTIME_TIMEVAL(&now);
+	}
+	tp1 = TAILQ_FIRST(&asoc->sent_queue);
+	while (tp1) {
+		strike_flag=0;
+		if (compare_with_wrap(tp1->rec.data.TSN_seq, biggest_tsn_acked,
+		    MAX_TSN) ||
+		    tp1->sent == SCTP_DATAGRAM_UNSENT) {
+			/* done */
+			break;
+		}
+		if ((tp1->flags & (SCTP_PR_SCTP_ENABLED|SCTP_PR_SCTP_BUFFER)) ==
+		    SCTP_PR_SCTP_ENABLED &&
+		    tp1->sent < SCTP_DATAGRAM_ACKED) {
+			/* Is it expired? */
+#ifndef __FreeBSD__
+			if (timercmp(&now, &tp1->rec.data.timetodrop, >))
+#else
+			if (timevalcmp(&now, &tp1->rec.data.timetodrop, >))
+#endif
+			{
+				/* Yes so drop it */
+				if (tp1->data != NULL) {
+					sctp_release_pr_sctp_chunk(stcb, tp1,
+					    (SCTP_RESPONSE_TO_USER_REQ|SCTP_NOTIFY_DATAGRAM_SENT),
+					    &asoc->sent_queue);
+				}
+				tp1 = TAILQ_NEXT(tp1, sctp_next);
+				continue;
+			}
+		}
+
+		if (compare_with_wrap(tp1->rec.data.TSN_seq,
+		    asoc->this_sack_highest_gap, MAX_TSN)) {
+			/* we are beyond the tsn in the sack  */
+ 			break;
+		}
+		if (tp1->sent >= SCTP_DATAGRAM_RESEND) {
+			/* either a RESEND, ACKED, or MARKED */
+			/* skip */
+			tp1 = TAILQ_NEXT(tp1, sctp_next);
+			continue;
+		}
+		if (primary_switch_active && (strike_enabled == 0)) {
+			if (tp1->whoTo != asoc->primary_destination) {
+				/*
+				 * We can only strike things on the primary if
+				 * the strike_enabled flag is clear
+				 */
+				tp1 = TAILQ_NEXT(tp1, sctp_next);
+				continue;
+			}
+		} else if (primary_switch_active) {
+			if (tp1->whoTo->cacc_saw_newack == 0) {
+				/*
+				 * Only one was received but it was NOT
+				 * this one.
+				 */
+				tp1 = TAILQ_NEXT(tp1, sctp_next);
+				continue;
+			}
+		}
+		if (double_switch_active &&
+		    (compare_with_wrap(asoc->primary_destination->next_tsn_at_change,
+		    tp1->rec.data.TSN_seq, MAX_TSN))) {
+			/*
+			 * With a double switch we do NOT mark unless we
+			 * are beyond the switch point.
+			 */
+			tp1 = TAILQ_NEXT(tp1, sctp_next);
+			continue;
+		}
+		/*
+		 * Here we check to see if we were have already done a FR
+		 * and if so we see if the biggest TSN we saw in the sack is
+		 * smaller than the recovery point. If so we don't strike the
+		 * tsn... otherwise we CAN strike the TSN.
+		 */
+		if (accum_moved && asoc->fast_retran_loss_recovery) {
+		 	/*
+		 	 * Strike the TSN if in fast-recovery and
+			 * cum-ack moved.
+			 */
+			tp1->sent++;
+		} else if (tp1->rec.data.doing_fast_retransmit) {
+			/*
+			 * For those that have done a FR we must
+			 * take special consideration if we strike. I.e
+			 * the biggest_newly_acked must be higher
+			 * than the sending_seq at the time we did
+			 * the FR.
+			 */
+#ifdef SCTP_FR_TO_ALTERNATE
+			/*
+			 * If FR's go to new networks, then we
+			 * must only do this for singly homed asoc's. However
+			 * if the FR's go to the same network (Armando's work)
+			 * then its ok to FR multiple times.
+			 */
+			if (asoc->numnets < 2)
+#else
+			if (1)
+#endif
+			{
+				if ((compare_with_wrap(biggest_tsn_newly_acked,
+				    tp1->rec.data.fast_retran_tsn, MAX_TSN)) ||
+				    (biggest_tsn_newly_acked ==
+				     tp1->rec.data.fast_retran_tsn)) {
+					/*
+					 * Strike the TSN, since this ack is
+					 * beyond where things were when we did
+					 * a FR.
+					 */
+#ifdef SCTP_FR_LOGGING
+					sctp_log_fr(biggest_tsn_newly_acked,
+					    tp1->rec.data.TSN_seq,
+					    tp1->rec.data.fast_retran_tsn,
+					    SCTP_FR_LOG_STRIKE_CHUNK);
+#endif
+					tp1->sent++;
+					strike_flag=1;
+				}
+			}
+ 		} else if (compare_with_wrap(tp1->rec.data.TSN_seq,
+ 		    biggest_tsn_newly_acked, MAX_TSN)) {
+			/*
+			 * We don't strike these:
+			 * This is the  HTNA algorithm i.e. we don't strike
+			 * If our TSN is larger than the Highest TSN Newly
+			 * Acked.
+			 */
+			;
+	 	} else {
+		 	/* Strike the TSN */
+			tp1->sent++;
+		}
+		if (tp1->sent == SCTP_DATAGRAM_RESEND) {
+			/* Increment the count to resend */
+			struct sctp_nets *alt;
+
+#ifdef SCTP_FR_LOGGING
+			sctp_log_fr(tp1->rec.data.TSN_seq, tp1->snd_count,
+			    0, SCTP_FR_MARKED);
+#endif
+			if (strike_flag) {
+				/* This is a subsequent FR */
+				sctp_pegs[SCTP_DUP_FR]++;
+			}
+			asoc->sent_queue_retran_cnt++;
+#ifdef SCTP_FR_TO_ALTERNATE
+			/* Can we find an alternate? */
+			alt = sctp_find_alternate_net(stcb, tp1->whoTo);
+#else
+			/*
+			 * default behavior is to NOT retransmit FR's
+			 * to an alternate. Armando Caro's paper details
+			 * why.
+			 */
+			alt = tp1->whoTo;
+#endif
+			tp1->rec.data.doing_fast_retransmit = 1;
+			tot_retrans++;
+			/* mark the sending seq for possible subsequent FR's */
+			if (TAILQ_EMPTY(&asoc->send_queue)) {
+				/*
+				 * If the queue of send is empty then its the
+				 * next sequence number that will be assigned so
+				 * we subtract one from this to get the one we
+				 * last sent.
+				 */
+ 				tp1->rec.data.fast_retran_tsn = sending_seq - 1;
+			} else {
+				/*
+			 	 * If there are chunks on the send queue
+				 * (unsent data that has made it from the
+				 * stream queues but not out the door, we take
+				 * the first one (which will have the lowest
+				 * TSN) and subtract one to get the one we last
+				 * sent.
+				 */
+				struct sctp_tmit_chunk *ttt;
+				ttt = TAILQ_FIRST(&asoc->send_queue);
+				tp1->rec.data.fast_retran_tsn =
+				    ttt->rec.data.TSN_seq - 1;
+			}
+			if (tp1->do_rtt) {
+				/*
+				 * this guy had a RTO calculation pending on it,
+				 * cancel it
+				 */
+				tp1->whoTo->rto_pending = 0;
+				tp1->do_rtt = 0;
+			}
+			/* fix counts and things */
+
+			tp1->whoTo->net_ack++;
+			tp1->whoTo->flight_size -= tp1->book_size;
+			if (tp1->whoTo->flight_size < 0) {
+				tp1->whoTo->flight_size = 0;
+			}
+#ifdef SCTP_LOG_RWND
+			sctp_log_rwnd(SCTP_INCREASE_PEER_RWND,
+				      asoc->peers_rwnd , tp1->send_size, sctp_peer_chunk_oh);
+#endif
+			/* add back to the rwnd */
+			asoc->peers_rwnd += (tp1->send_size + sctp_peer_chunk_oh);
+
+			/* remove from the total flight */
+			asoc->total_flight -= tp1->book_size;
+			if (asoc->total_flight < 0) {
+				asoc->total_flight = 0;
+			}
+			asoc->total_flight_count--;
+			if (asoc->total_flight_count < 0) {
+				asoc->total_flight_count = 0;
+			}
+			if (alt != tp1->whoTo) {
+				/* yes, there is an alternate. */
+				sctp_free_remote_addr(tp1->whoTo);
+				tp1->whoTo = alt;
+				alt->ref_count++;
+			}
+		}
+		tp1 = TAILQ_NEXT(tp1, sctp_next);
+	} /* while (tp1) */
+
+	if (tot_retrans > 0) {
+		/* Setup the ecn nonce re-sync point. We
+		 * do this since once we go to FR something
+		 * we introduce a Karn's rule scenario and
+		 * won't know the totals for the ECN bits.
+		 */
+		asoc->nonce_resync_tsn = sending_seq;
+		asoc->nonce_wait_for_ecne = 0;
+		asoc->nonce_sum_check = 0;
+	}
+
+}
+
+struct sctp_tmit_chunk *
+sctp_try_advance_peer_ack_point(struct sctp_tcb *stcb,
+    struct sctp_association *asoc)
+{
+	struct sctp_tmit_chunk *tp1, *tp2, *a_adv=NULL;
+	struct timeval now;
+	int now_filled=0;
+
+	if (asoc->peer_supports_prsctp == 0) {
+		return (NULL);
+	}
+	tp1 = TAILQ_FIRST(&asoc->sent_queue);
+	while (tp1) {
+		if (tp1->sent != SCTP_FORWARD_TSN_SKIP &&
+		    tp1->sent != SCTP_DATAGRAM_RESEND) {
+			/* no chance to advance, out of here */
+			break;
+		}
+		if ((tp1->flags & SCTP_PR_SCTP_ENABLED) == 0) {
+			/*
+			 * We can't fwd-tsn past any that are reliable
+			 * aka retransmitted until the asoc fails.
+			 */
+			break;
+		}
+		if (!now_filled) {
+			SCTP_GETTIME_TIMEVAL(&now);
+			now_filled = 1;
+		}
+		tp2 = TAILQ_NEXT(tp1, sctp_next);
+		/*
+		 * now we got a chunk which is marked for another
+		 * retransmission to a PR-stream but has run
+		 * out its chances already maybe OR has been
+		 * marked to skip now. Can we skip it if its a
+		 * resend?
+		 */
+		if (tp1->sent == SCTP_DATAGRAM_RESEND &&
+		    (tp1->flags & SCTP_PR_SCTP_BUFFER) == 0) {
+			/*
+			 * Now is this one marked for resend and its time
+			 * is now up?
+			 */
+#ifndef __FreeBSD__
+			if (timercmp(&now, &tp1->rec.data.timetodrop, >))
+#else
+			if (timevalcmp(&now, &tp1->rec.data.timetodrop, >))
+#endif
+			{
+				/* Yes so drop it */
+				if (tp1->data) {
+					sctp_release_pr_sctp_chunk(stcb, tp1,
+					    (SCTP_RESPONSE_TO_USER_REQ|SCTP_NOTIFY_DATAGRAM_SENT),
+					    &asoc->sent_queue);
+				}
+			} else {
+				/*
+				 * No, we are done when hit one for resend whos
+				 * time as not expired.
+				 */
+				break;
+			}
+		}
+		/*
+		 * Ok now if this chunk is marked to drop it
+		 * we can clean up the chunk, advance our peer ack point
+		 * and we can check the next chunk.
+		 */
+		if (tp1->sent == SCTP_FORWARD_TSN_SKIP) {
+			/* advance PeerAckPoint goes forward */
+			asoc->advanced_peer_ack_point = tp1->rec.data.TSN_seq;
+			a_adv = tp1;
+			/*
+			 * we don't want to de-queue it here. Just wait for the
+			 * next peer SACK to come with a new cumTSN and then
+			 * the chunk will be droped in the normal fashion.
+			 */
+			if (tp1->data) {
+				sctp_free_bufspace(stcb, asoc, tp1);
+#ifdef SCTP_DEBUG
+				if (sctp_debug_on & SCTP_DEBUG_OUTPUT2) {
+					printf("--total out:%lu total_mbuf_out:%lu\n",
+					    (u_long)asoc->total_output_queue_size,
+					    (u_long)asoc->total_output_mbuf_queue_size);
+				}
+#endif
+				/*
+				 * Maybe there should be another notification
+				 * type
+				 */
+				sctp_ulp_notify(SCTP_NOTIFY_DG_FAIL, stcb,
+				    (SCTP_RESPONSE_TO_USER_REQ|SCTP_NOTIFY_DATAGRAM_SENT),
+				    tp1);
+				sctp_m_freem(tp1->data);
+				tp1->data = NULL;
+				sctp_sowwakeup(stcb->sctp_ep,
+				    stcb->sctp_socket);
+			}
+		} else {
+			/* If it is still in RESEND we can advance no further */
+			break;
+		}
+		/*
+		 * If we hit here we just dumped tp1, move to next
+		 * tsn on sent queue.
+		 */
+		tp1 = tp2;
+	}
+	return (a_adv);
+}
+
+#ifdef SCTP_HIGH_SPEED
+struct sctp_hs_raise_drop {
+	int32_t cwnd;
+	int32_t increase;
+	int32_t drop_percent;
+};
+
+#define SCTP_HS_TABLE_SIZE 73
+
+struct sctp_hs_raise_drop sctp_cwnd_adjust[SCTP_HS_TABLE_SIZE] = {
+	{38,1,50},	/* 0   */
+	{118,2,44},	/* 1   */
+	{221,3,41},	/* 2   */
+	{347,4,38},	/* 3   */
+	{495,5,37},	/* 4   */
+	{663,6,35},	/* 5   */
+	{851,7,34},	/* 6   */
+	{1058,8,33},	/* 7   */
+	{1284,9,32},	/* 8   */
+	{1529,10,31},	/* 9   */
+	{1793,11,30},	/* 10  */
+	{2076,12,29},	/* 11  */
+	{2378,13,28},	/* 12  */
+	{2699,14,28},	/* 13  */
+	{3039,15,27},	/* 14  */
+	{3399,16,27},	/* 15  */
+	{3778,17,26},	/* 16  */
+	{4177,18,26},	/* 17  */
+	{4596,19,25},	/* 18  */
+	{5036,20,25},	/* 19  */
+	{5497,21,24},	/* 20  */
+	{5979,22,24},	/* 21  */
+	{6483,23,23},	/* 22  */
+	{7009,24,23},	/* 23  */
+	{7558,25,22},	/* 24  */
+	{8130,26,22},	/* 25  */
+	{8726,27,22},	/* 26  */
+	{9346,28,21},	/* 27  */
+	{9991,29,21},	/* 28  */
+	{10661,30,21},  /* 29  */
+	{11358,31,20},  /* 30  */
+	{12082,32,20},  /* 31  */
+	{12834,33,20},  /* 32  */
+	{13614,34,19},  /* 33  */
+	{14424,35,19},  /* 34  */
+	{15265,36,19},  /* 35  */
+	{16137,37,19},  /* 36  */
+	{17042,38,18},  /* 37  */
+	{17981,39,18},  /* 38  */
+	{18955,40,18},  /* 39  */
+	{19965,41,17},  /* 40  */
+	{21013,42,17},  /* 41  */
+	{22101,43,17},  /* 42  */
+	{23230,44,17},  /* 43  */
+	{24402,45,16},  /* 44  */
+	{25618,46,16},  /* 45  */
+	{26881,47,16},  /* 46  */
+	{28193,48,16},  /* 47  */
+	{29557,49,15},  /* 48  */
+	{30975,50,15},  /* 49  */
+	{32450,51,15},  /* 50  */
+	{33986,52,15},  /* 51  */
+	{35586,53,14},  /* 52  */
+	{37253,54,14},  /* 53  */
+	{38992,55,14},  /* 54  */
+	{40808,56,14},  /* 55  */
+	{42707,57,13},  /* 56  */
+	{44694,58,13},  /* 57  */
+	{46776,59,13},  /* 58  */
+	{48961,60,13},  /* 59  */
+	{51258,61,13},  /* 60  */
+	{53677,62,12},  /* 61  */
+	{56230,63,12},  /* 62  */
+	{58932,64,12},  /* 63  */
+	{61799,65,12},  /* 64  */
+	{64851,66,11},  /* 65  */
+	{68113,67,11},  /* 66  */
+	{71617,68,11},  /* 67  */
+	{75401,69,10},  /* 68  */
+	{79517,70,10},  /* 69  */
+	{84035,71,10},  /* 70  */
+	{89053,72,10},  /* 71  */
+	{94717,73,9}    /* 72  */
+};
+
+static void
+sctp_hs_cwnd_increase(struct sctp_nets *net)
+{
+ 	int cur_val, i, indx, incr;
+
+	cur_val = net->cwnd >> 10;
+	indx = SCTP_HS_TABLE_SIZE - 1;
+
+	if (cur_val < sctp_cwnd_adjust[0].cwnd) {
+		/* normal mode */
+		if (net->net_ack > net->mtu) {
+			net->cwnd += net->mtu;
+#ifdef SCTP_CWND_LOGGING
+			sctp_log_cwnd(net, net->mtu, SCTP_CWND_LOG_FROM_SS);
+#endif
+		} else {
+			net->cwnd += net->net_ack;
+#ifdef SCTP_CWND_LOGGING
+			sctp_log_cwnd(net, net->net_ack, SCTP_CWND_LOG_FROM_SS);
+#endif
+		}
+	} else {
+		for (i=net->last_hs_used; i<SCTP_HS_TABLE_SIZE; i++) {
+			if (cur_val < sctp_cwnd_adjust[i].cwnd) {
+				indx = i;
+				break;
+			}
+		}
+		net->last_hs_used = indx;
+		incr = ((sctp_cwnd_adjust[indx].increase) << 10);
+		net->cwnd += incr;
+#ifdef SCTP_CWND_LOGGING
+		sctp_log_cwnd(net, incr, SCTP_CWND_LOG_FROM_SS);
+#endif
+	}
+}
+
+static void
+sctp_hs_cwnd_decrease(struct sctp_nets *net)
+{
+ 	int cur_val, i, indx;
+#ifdef SCTP_CWND_LOGGING
+	int old_cwnd = net->cwnd;
+#endif
+
+	cur_val = net->cwnd >> 10;
+	indx = net->last_hs_used;
+	if (cur_val < sctp_cwnd_adjust[0].cwnd) {
+		/* normal mode */
+		net->ssthresh = net->cwnd / 2;
+		if (net->ssthresh < (net->mtu*2)) {
+			net->ssthresh = 2 * net->mtu;
+		}
+		net->cwnd = net->ssthresh;
+#ifdef SCTP_CWND_LOGGING
+		sctp_log_cwnd(net, (net->cwnd-old_cwnd), SCTP_CWND_LOG_FROM_FR);
+#endif
+	} else {
+		/* drop by the proper amount */
+		net->ssthresh = net->cwnd - (int)((net->cwnd / 100) *
+		    sctp_cwnd_adjust[net->last_hs_used].drop_percent);
+		net->cwnd = net->ssthresh;
+		/* now where are we */
+		indx = net->last_hs_used;
+		cur_val = net->cwnd >> 10;
+		/* reset where we are in the table */
+		if (cur_val < sctp_cwnd_adjust[0].cwnd) {
+			/* feel out of hs */
+			net->last_hs_used = 0;
+		} else {
+			for (i = indx; i >= 1; i--) {
+				if (cur_val > sctp_cwnd_adjust[i - 1].cwnd) {
+					break;
+				}
+			}
+			net->last_hs_used = indx;
+		}
+	}
+}
+#endif
+
+void
+sctp_handle_sack(struct sctp_sack_chunk *ch, struct sctp_tcb *stcb,
+    struct sctp_nets *net_from, int *abort_now)
+{
+	struct sctp_association *asoc;
+	struct sctp_sack *sack;
+	struct sctp_tmit_chunk *tp1, *tp2;
+	u_long cum_ack, last_tsn, biggest_tsn_acked, biggest_tsn_newly_acked;
+	uint16_t num_seg;
+	unsigned int sack_length;
+	uint32_t send_s;
+	int some_on_streamwheel;
+	long j;
+	int strike_enabled = 0, cnt_of_cacc = 0;
+	int accum_moved = 0;
+	int marking_allowed = 1;
+	int will_exit_fast_recovery=0;
+	u_int32_t a_rwnd;
+	struct sctp_nets *net = NULL;
+	int nonce_sum_flag, ecn_seg_sums=0;
+	asoc = &stcb->asoc;
+
+	/*
+	 * Handle the incoming sack on data I have been sending.
+	 */
+
+	/*
+	 * we take any chance we can to service our queues since we
+	 * cannot get awoken when the socket is read from :<
+	 */
+	asoc->overall_error_count = 0;
+
+	if (asoc->sent_queue_retran_cnt) {
+#ifdef SCTP_DEBUG
+		if (sctp_debug_on & SCTP_DEBUG_INDATA1) {
+			printf("Handling SACK for asoc:%p retran:%d\n",
+			       asoc, asoc->sent_queue_retran_cnt);
+		}
+#endif
+	}
+
+	sctp_service_queues(stcb, asoc, 0);
+
+	/*
+	 * Now perform the actual SACK handling:
+	 * 1) Verify that it is not an old sack, if so discard.
+	 * 2) If there is nothing left in the send queue (cum-ack is equal
+	 *    to last acked) then you have a duplicate too, update any rwnd
+	 *    change and verify no timers are running. then return.
+	 * 3) Process any new consequtive data i.e. cum-ack moved
+	 *    process these first and note that it moved.
+	 * 4) Process any sack blocks.
+	 * 5) Drop any acked from the queue.
+	 * 6) Check for any revoked blocks and mark.
+	 * 7) Update the cwnd.
+	 * 8) Nothing left, sync up flightsizes and things, stop all timers
+	 *    and also check for shutdown_pending state. If so then go ahead
+	 *    and send off the shutdown. If in shutdown recv, send off the
+	 *    shutdown-ack and start that timer, Ret.
+	 * 9) Strike any non-acked things and do FR procedure if needed being
+	 *    sure to set the FR flag.
+	 * 10) Do pr-sctp procedures.
+	 * 11) Apply any FR penalties.
+	 * 12) Assure we will SACK if in shutdown_recv state.
+	 */
+
+	j = 0;
+	sack_length = ntohs(ch->ch.chunk_length);
+	if (sack_length < sizeof(struct sctp_sack_chunk)) {
+#ifdef SCTP_DEBUG
+		if (sctp_debug_on & SCTP_DEBUG_INDATA1) {
+			printf("Bad size on sack chunk .. to small\n");
+		}
+#endif
+		return;
+	}
+	/* ECN Nonce */
+	nonce_sum_flag = ch->ch.chunk_flags & SCTP_SACK_NONCE_SUM;
+	sack = &ch->sack;
+	cum_ack = last_tsn = ntohl(sack->cum_tsn_ack);
+	num_seg = ntohs(sack->num_gap_ack_blks);
+
+	/* reality check */
+	if (TAILQ_EMPTY(&asoc->send_queue)) {
+		send_s = asoc->sending_seq;
+	} else {
+		tp1 = TAILQ_FIRST(&asoc->send_queue);
+		send_s = tp1->rec.data.TSN_seq;
+	}
+
+	if (sctp_strict_sacks) {
+		if (cum_ack == send_s ||
+		    compare_with_wrap(cum_ack, send_s, MAX_TSN)) {
+			struct mbuf *oper;
+			/*
+			 * no way, we have not even sent this TSN out yet.
+			 * Peer is hopelessly messed up with us.
+			 */
+		hopeless_peer:
+			*abort_now = 1;
+			/* XXX */
+			MGET(oper, MB_DONTWAIT, MT_DATA);
+			if (oper) {
+				struct sctp_paramhdr *ph;
+				u_int32_t *ippp;
+
+				oper->m_len = sizeof(struct sctp_paramhdr) +
+					sizeof(*ippp);
+				ph = mtod(oper, struct sctp_paramhdr *);
+				ph->param_type = htons(SCTP_CAUSE_PROTOCOL_VIOLATION);
+				ph->param_length = htons(oper->m_len);
+				ippp = (u_int32_t *)(ph + 1);
+				*ippp = htonl(0x30000002);
+			}
+			sctp_abort_an_association(stcb->sctp_ep, stcb, SCTP_PEER_FAULTY, oper);
+			return;
+		}
+	}
+	/* update the Rwnd of the peer */
+	a_rwnd = (u_int32_t)ntohl(sack->a_rwnd);
+	if (asoc->sent_queue_retran_cnt) {
+#ifdef SCTP_DEBUG
+		if (sctp_debug_on & SCTP_DEBUG_INDATA1) {
+			printf("cum_ack:%lx num_seg:%u last_acked_seq:%x\n",
+			       cum_ack, (u_int)num_seg, asoc->last_acked_seq);
+		}
+#endif
+	}
+	if (compare_with_wrap(asoc->t3timeout_highest_marked, cum_ack, MAX_TSN)) {
+		/* we are not allowed to mark for FR */
+		marking_allowed = 0;
+	}
+	/**********************/
+	/* 1) check the range */
+	/**********************/
+	if (compare_with_wrap(asoc->last_acked_seq, last_tsn, MAX_TSN)) {
+		/* acking something behind */
+		if (asoc->sent_queue_retran_cnt) {
+#ifdef SCTP_DEBUG
+			if (sctp_debug_on & SCTP_DEBUG_INDATA1) {
+				printf("The cum-ack is behind us\n");
+			}
+#endif
+		}
+		return;
+	}
+
+	if (TAILQ_EMPTY(&asoc->sent_queue)) {
+		/* nothing left on sendqueue.. consider done */
+#ifdef SCTP_LOG_RWND
+		sctp_log_rwnd_set(SCTP_SET_PEER_RWND_VIA_SACK,
+				  asoc->peers_rwnd, 0, 0, a_rwnd);
+#endif
+		asoc->peers_rwnd = a_rwnd;
+		if (asoc->sent_queue_retran_cnt) {
+#ifdef SCTP_DEBUG
+			if (sctp_debug_on & SCTP_DEBUG_INDATA1) {
+				printf("Huh? retran set but none on queue\n");
+			}
+#endif
+			asoc->sent_queue_retran_cnt = 0;
+		}
+		if (asoc->peers_rwnd < stcb->sctp_ep->sctp_ep.sctp_sws_sender) {
+			/* SWS sender side engages */
+			asoc->peers_rwnd = 0;
+		}
+		/* stop any timers */
+		TAILQ_FOREACH(net, &asoc->nets, sctp_next) {
+			sctp_timer_stop(SCTP_TIMER_TYPE_SEND, stcb->sctp_ep,
+					stcb, net);
+			net->partial_bytes_acked = 0;
+			net->flight_size = 0;
+		}
+		asoc->total_flight = 0;
+		asoc->total_flight_count = 0;
+		return;
+	}
+	/*
+	 * We init netAckSz and netAckSz2 to 0. These are used to track 2
+	 * things. The total byte count acked is tracked in netAckSz AND
+	 * netAck2 is used to track the total bytes acked that are un-
+	 * amibguious and were never retransmitted. We track these on a
+	 * per destination address basis.
+	 */
+	TAILQ_FOREACH(net, &asoc->nets, sctp_next) {
+		net->prev_cwnd = net->cwnd;
+		net->net_ack = 0;
+		net->net_ack2 = 0;
+	}
+	/* process the new consecutive TSN first */
+	tp1 = TAILQ_FIRST(&asoc->sent_queue);
+	while (tp1) {
+		if (compare_with_wrap(last_tsn, tp1->rec.data.TSN_seq,
+				      MAX_TSN) ||
+		    last_tsn == tp1->rec.data.TSN_seq) {
+			if (tp1->sent != SCTP_DATAGRAM_UNSENT) {
+				/* ECN Nonce: Add the nonce to the sender's nonce sum */
+				asoc->nonce_sum_expect_base += tp1->rec.data.ect_nonce;
+				accum_moved = 1;
+				if (tp1->sent < SCTP_DATAGRAM_ACKED) {
+					/*
+					 * If it is less than ACKED, it is now
+					 * no-longer in flight. Higher values
+					 * may occur during marking
+					 */
+					if ((tp1->whoTo->dest_state &
+					     SCTP_ADDR_UNCONFIRMED) &&
+					    (tp1->snd_count < 2) ) {
+						/*
+						 * If there was no retran and
+						 * the address is un-confirmed
+						 * and we sent there and are
+						 * now sacked.. its confirmed,
+						 * mark it so.
+						 */
+						tp1->whoTo->dest_state &=
+							~SCTP_ADDR_UNCONFIRMED;
+					}
+					tp1->whoTo->flight_size -=
+						tp1->book_size;
+					if (tp1->whoTo->flight_size < 0) {
+						tp1->whoTo->flight_size = 0;
+					}
+					asoc->total_flight -= tp1->book_size;
+					if (asoc->total_flight < 0) {
+						asoc->total_flight = 0;
+					}
+					asoc->total_flight_count--;
+					if (asoc->total_flight_count < 0) {
+						asoc->total_flight_count = 0;
+					}
+					tp1->whoTo->net_ack += tp1->send_size;
+					if (tp1->snd_count < 2) {
+						/* True non-retransmited chunk */
+						tp1->whoTo->net_ack2 +=
+							tp1->send_size;
+						/* update RTO too? */
+						if (tp1->do_rtt) {
+							tp1->whoTo->RTO =
+								sctp_calculate_rto(stcb,
+										   asoc, tp1->whoTo,
+										   &tp1->sent_rcv_time);
+							tp1->whoTo->rto_pending = 0;
+							tp1->do_rtt = 0;
+						}
+					}
+				}
+				if (tp1->sent == SCTP_DATAGRAM_RESEND) {
+#ifdef SCTP_DEBUG
+					if (sctp_debug_on & SCTP_DEBUG_INDATA3) {
+						printf("Hmm. one that is in RESEND that is now ACKED\n");
+					}
+#endif
+					asoc->sent_queue_retran_cnt--;
+#ifdef SCTP_AUDITING_ENABLED
+					sctp_audit_log(0xB3,
+						       (asoc->sent_queue_retran_cnt & 0x000000ff));
+#endif
+					if (asoc->sent_queue_retran_cnt < 0) {
+						printf("huh4 retran went negative?\n");
+#ifdef SCTP_AUDITING_ENABLED
+						sctp_auditing(31, inp, tcb,
+							      NULL);
+#else
+						asoc->sent_queue_retran_cnt = 0;
+#endif
+					}
+
+				}
+				tp1->sent = SCTP_DATAGRAM_ACKED;
+			}
+		} else {
+			break;
+		}
+		tp1 = TAILQ_NEXT(tp1, sctp_next);
+	}
+	/*******************************************/
+	/* cancel ALL T3-send timer if accum moved */
+	/*******************************************/
+	if (accum_moved) {
+		TAILQ_FOREACH(net, &asoc->nets, sctp_next) {
+			sctp_timer_stop(SCTP_TIMER_TYPE_SEND, stcb->sctp_ep,
+					stcb, net);
+		}
+	}
+	biggest_tsn_newly_acked = biggest_tsn_acked = last_tsn;
+	/* always set this up to cum-ack */
+	asoc->this_sack_highest_gap = last_tsn;
+
+	if (((num_seg * sizeof (sizeof(struct sctp_gap_ack_block))) + sizeof(struct sctp_sack_chunk)) > sack_length) {
+		/* skip corrupt segments */
+		strike_enabled = 0;
+		goto skip_segments;
+	}
+
+	if (num_seg > 0) {
+		if (asoc->primary_destination->dest_state &
+		    SCTP_ADDR_SWITCH_PRIMARY) {
+			/* clear the nets CACC flags */
+			TAILQ_FOREACH(net, &asoc->nets, sctp_next) {
+				net->cacc_saw_newack = 0;
+			}
+		}
+		/*
+		 * thisSackHighestGap will increase while handling NEW segments
+		 */
+
+		sctp_handle_segments(stcb, asoc, ch, last_tsn,
+		    &biggest_tsn_acked, &biggest_tsn_newly_acked,
+		    num_seg, &ecn_seg_sums);
+
+		if (sctp_strict_sacks) {
+			/* validate the biggest_tsn_acked in the gap acks
+			 * if strict adherence is wanted.
+			 */
+			if ((biggest_tsn_acked == send_s) ||
+			    (compare_with_wrap(biggest_tsn_acked, send_s, MAX_TSN))) {
+				/*
+				 * peer is either confused or we are under
+				 * attack. We must abort.
+				 */
+				goto hopeless_peer;
+			}
+		}
+
+		if (asoc->primary_destination->dest_state &
+		    SCTP_ADDR_SWITCH_PRIMARY) {
+			/* clear the nets CACC flags */
+			TAILQ_FOREACH(net, &asoc->nets, sctp_next) {
+				if (net->cacc_saw_newack) {
+					cnt_of_cacc++;
+				}
+			}
+		}
+
+	}
+
+	if (cnt_of_cacc < 2) {
+		strike_enabled = 1;
+	} else {
+		strike_enabled = 0;
+	}
+ skip_segments:
+	/********************************************/
+	/* drop the acked chunks from the sendqueue */
+	/********************************************/
+	asoc->last_acked_seq = cum_ack;
+	if (asoc->primary_destination->dest_state & SCTP_ADDR_SWITCH_PRIMARY) {
+		if ((cum_ack == asoc->primary_destination->next_tsn_at_change) ||
+		    (compare_with_wrap(cum_ack,
+				       asoc->primary_destination->next_tsn_at_change, MAX_TSN))) {
+			struct sctp_nets *lnet;
+			/* Turn off the switch flag for ALL addresses */
+			TAILQ_FOREACH(lnet, &asoc->nets, sctp_next) {
+				asoc->primary_destination->dest_state &=
+					~(SCTP_ADDR_SWITCH_PRIMARY|SCTP_ADDR_DOUBLE_SWITCH);
+			}
+		}
+	}
+	/* Drag along the t3 timeout point so we don't have a problem at wrap */
+	if (marking_allowed) {
+		asoc->t3timeout_highest_marked = cum_ack;
+	}
+	tp1 = TAILQ_FIRST(&asoc->sent_queue);
+	do {
+		if (compare_with_wrap(tp1->rec.data.TSN_seq, cum_ack,
+				      MAX_TSN)) {
+			break;
+		}
+		if (tp1->sent == SCTP_DATAGRAM_UNSENT) {
+			/* no more sent on list */
+			break;
+		}
+		tp2 = TAILQ_NEXT(tp1, sctp_next);
+		TAILQ_REMOVE(&asoc->sent_queue, tp1, sctp_next);
+		if (tp1->data) {
+			sctp_free_bufspace(stcb, asoc, tp1);
+#ifdef SCTP_DEBUG
+			if (sctp_debug_on & SCTP_DEBUG_OUTPUT2) {
+				printf("--total out:%lu total_mbuf_out:%lu\n",
+				       (u_long)asoc->total_output_queue_size,
+				       (u_long)asoc->total_output_mbuf_queue_size);
+			}
+#endif
+
+			sctp_m_freem(tp1->data);
+			if (tp1->flags & SCTP_PR_SCTP_BUFFER) {
+				asoc->sent_queue_cnt_removeable--;
+			}
+
+		}
+		tp1->data = NULL;
+		asoc->sent_queue_cnt--;
+		sctp_free_remote_addr(tp1->whoTo);
+		sctppcbinfo.ipi_count_chunk--;
+		asoc->chunks_on_out_queue--;
+
+		if ((int)sctppcbinfo.ipi_count_chunk < 0) {
+			panic("Chunk count is going negative");
+		}
+		SCTP_ZONE_FREE(sctppcbinfo.ipi_zone_chunk, tp1);
+		sctppcbinfo.ipi_gencnt_chunk++;
+		sctp_sowwakeup(stcb->sctp_ep, stcb->sctp_socket);
+		tp1 = tp2;
+	} while (tp1 != NULL);
+
+
+	if (asoc->fast_retran_loss_recovery && accum_moved) {
+		if (compare_with_wrap(asoc->last_acked_seq,
+				      asoc->fast_recovery_tsn, MAX_TSN) ||
+		    asoc->last_acked_seq == asoc->fast_recovery_tsn) {
+			/* Setup so we will exit RFC2582 fast recovery */
+			will_exit_fast_recovery = 1;
+		}
+	}
+
+	/* Check for revoked fragments if we hand
+	 * fragments in a previous segment. If we
+	 * had no previous fragments we cannot have
+	 * a revoke issue.
+	 */
+	if (asoc->saw_sack_with_frags)
+		sctp_check_for_revoked(asoc, cum_ack, biggest_tsn_acked);
+
+	if (num_seg)
+		asoc->saw_sack_with_frags = 1;
+	else
+		asoc->saw_sack_with_frags = 0;
+
+	/******************************/
+	/* update cwnd                */
+	/******************************/
+	TAILQ_FOREACH(net, &asoc->nets, sctp_next) {
+		/* if nothing was acked on this destination skip it */
+		if (net->net_ack == 0)
+			continue;
+
+		if (net->net_ack2 > 0) {
+			/*
+			 * Karn's rule applies to clearing error count,
+			 * this is optional.
+			 */
+			net->error_count = 0;
+			if ((net->dest_state&SCTP_ADDR_NOT_REACHABLE) ==
+			    SCTP_ADDR_NOT_REACHABLE) {
+				/* addr came good */
+				net->dest_state &= ~SCTP_ADDR_NOT_REACHABLE;
+				net->dest_state |= SCTP_ADDR_REACHABLE;
+				sctp_ulp_notify(SCTP_NOTIFY_INTERFACE_UP, stcb,
+						SCTP_RECEIVED_SACK, (void *)net);
+				/* now was it the primary? if so restore */
+				if (net->dest_state & SCTP_ADDR_WAS_PRIMARY) {
+					sctp_set_primary_addr(stcb, (struct sockaddr *)NULL, net);
+				}
+			}
+		}
+
+		if (asoc->fast_retran_loss_recovery &&
+		    will_exit_fast_recovery == 0) {
+			/* If we are in loss recovery we skip any cwnd update */
+			sctp_pegs[SCTP_CWND_SKIP]++;
+			goto skip_cwnd_update;
+		}
+		if (accum_moved) {
+			/* If the cumulative ack moved we can proceed */
+			if (net->cwnd <= net->ssthresh) {
+				/* We are in slow start */
+				if (net->flight_size + net->net_ack >=
+				    net->cwnd ) {
+#ifdef SCTP_HIGH_SPEED
+					sctp_hs_cwnd_increase(net);
+#else
+					if (net->net_ack > net->mtu) {
+						net->cwnd += net->mtu;
+#ifdef SCTP_CWND_LOGGING
+						sctp_log_cwnd(net, net->mtu,
+							      SCTP_CWND_LOG_FROM_SS);
+#endif
+
+					} else {
+						net->cwnd += net->net_ack;
+#ifdef SCTP_CWND_LOGGING
+						sctp_log_cwnd(net, net->net_ack,
+							      SCTP_CWND_LOG_FROM_SS);
+#endif
+
+					}
+#endif
+					sctp_pegs[SCTP_CWND_SS]++;
+				} else {
+					unsigned int dif;
+					sctp_pegs[SCTP_CWND_NOUSE_SS]++;
+					dif = net->cwnd - (net->flight_size +
+							   net->net_ack);
+#ifdef SCTP_CWND_LOGGING
+/*					sctp_log_cwnd(net, net->net_ack,
+					SCTP_CWND_LOG_NOADV_SS);*/
+#endif
+					if (dif > sctp_pegs[SCTP_CWND_DIFF_SA]) {
+						sctp_pegs[SCTP_CWND_DIFF_SA] =
+							dif;
+						sctp_pegs[SCTP_OQS_AT_SS] =
+							asoc->total_output_queue_size;
+						sctp_pegs[SCTP_SQQ_AT_SS] =
+							asoc->sent_queue_cnt;
+						sctp_pegs[SCTP_SQC_AT_SS] =
+							asoc->send_queue_cnt;
+					}
+				}
+			} else {
+				/* We are in congestion avoidance */
+				if (net->flight_size + net->net_ack >=
+				    net->cwnd) {
+					/*
+					 * add to pba only if we had a cwnd's
+					 * worth (or so) in flight OR the
+					 * burst limit was applied.
+					 */
+					net->partial_bytes_acked +=
+						net->net_ack;
+
+					/*
+					 * Do we need to increase
+					 * (if pba is > cwnd)?
+					 */
+					if (net->partial_bytes_acked >=
+					    net->cwnd) {
+						if (net->cwnd <
+						    net->partial_bytes_acked) {
+							net->partial_bytes_acked -=
+								net->cwnd;
+						} else {
+							net->partial_bytes_acked =
+								0;
+						}
+						net->cwnd += net->mtu;
+#ifdef SCTP_CWND_LOGGING
+						sctp_log_cwnd(net, net->mtu,
+							      SCTP_CWND_LOG_FROM_CA);
+#endif
+						sctp_pegs[SCTP_CWND_CA]++;
+					}
+				} else {
+					unsigned int dif;
+					sctp_pegs[SCTP_CWND_NOUSE_CA]++;
+#ifdef SCTP_CWND_LOGGING
+/*					sctp_log_cwnd(net, net->net_ack,
+					SCTP_CWND_LOG_NOADV_CA);
+*/
+#endif
+					dif = net->cwnd - (net->flight_size +
+							   net->net_ack);
+					if (dif > sctp_pegs[SCTP_CWND_DIFF_CA]) {
+						sctp_pegs[SCTP_CWND_DIFF_CA] =
+							dif;
+						sctp_pegs[SCTP_OQS_AT_CA] =
+							asoc->total_output_queue_size;
+						sctp_pegs[SCTP_SQQ_AT_CA] =
+							asoc->sent_queue_cnt;
+						sctp_pegs[SCTP_SQC_AT_CA] =
+							asoc->send_queue_cnt;
+
+					}
+
+				}
+			}
+		} else {
+			sctp_pegs[SCTP_CWND_NOCUM]++;
+		}
+	skip_cwnd_update:
+		/*
+		 * NOW, according to Karn's rule do we need to restore the
+		 * RTO timer back? Check our net_ack2. If not set then we
+		 * have a ambiguity.. i.e. all data ack'd was sent to more
+		 * than one place.
+		 */
+
+		if (net->net_ack2) {
+			/* restore any doubled timers */
+			net->RTO = ((net->lastsa >> 2) + net->lastsv) >> 1;
+			if (net->RTO < stcb->asoc.minrto) {
+				net->RTO = stcb->asoc.minrto;
+			}
+			if (net->RTO > stcb->asoc.maxrto) {
+				net->RTO = stcb->asoc.maxrto;
+			}
+		}
+		if (net->cwnd > sctp_pegs[SCTP_MAX_CWND]) {
+			sctp_pegs[SCTP_MAX_CWND] = net->cwnd;
+		}
+	}
+	/**********************************/
+	/* Now what about shutdown issues */
+	/**********************************/
+	some_on_streamwheel = 0;
+	if (!TAILQ_EMPTY(&asoc->out_wheel)) {
+		/* Check to see if some data queued */
+		struct sctp_stream_out *outs;
+		TAILQ_FOREACH(outs, &asoc->out_wheel, next_spoke) {
+			if (!TAILQ_EMPTY(&outs->outqueue)) {
+				some_on_streamwheel = 1;
+				break;
+			}
+		}
+	}
+	if (TAILQ_EMPTY(&asoc->send_queue) && TAILQ_EMPTY(&asoc->sent_queue) &&
+	    some_on_streamwheel == 0) {
+		/* nothing left on sendqueue.. consider done */
+		/* stop all timers */
+#ifdef SCTP_LOG_RWND
+		sctp_log_rwnd_set(SCTP_SET_PEER_RWND_VIA_SACK,
+				  asoc->peers_rwnd, 0, 0,  a_rwnd);
+#endif
+		asoc->peers_rwnd = a_rwnd;
+		if (asoc->peers_rwnd < stcb->sctp_ep->sctp_ep.sctp_sws_sender) {
+			/* SWS sender side engages */
+			asoc->peers_rwnd = 0;
+		}
+		/* stop any timers */
+		TAILQ_FOREACH(net, &asoc->nets, sctp_next) {
+			sctp_timer_stop(SCTP_TIMER_TYPE_SEND, stcb->sctp_ep,
+					stcb, net);
+			net->flight_size = 0;
+			net->partial_bytes_acked = 0;
+		}
+		asoc->total_flight = 0;
+		asoc->total_flight_count = 0;
+		/* clean up */
+		if (asoc->state & SCTP_STATE_SHUTDOWN_PENDING) {
+			asoc->state = SCTP_STATE_SHUTDOWN_SENT;
+#ifdef SCTP_DEBUG
+			if (sctp_debug_on & SCTP_DEBUG_OUTPUT4) {
+				printf("%s:%d sends a shutdown\n",
+				       __FILE__,
+				       __LINE__
+				       );
+			}
+#endif
+			sctp_send_shutdown(stcb,
+					   stcb->asoc.primary_destination);
+			sctp_timer_start(SCTP_TIMER_TYPE_SHUTDOWN,
+					 stcb->sctp_ep, stcb, asoc->primary_destination);
+			sctp_timer_start(SCTP_TIMER_TYPE_SHUTDOWNGUARD,
+					 stcb->sctp_ep, stcb, asoc->primary_destination);
+		} else if (SCTP_GET_STATE(asoc) == SCTP_STATE_SHUTDOWN_RECEIVED) {
+			asoc->state = SCTP_STATE_SHUTDOWN_ACK_SENT;
+
+			sctp_send_shutdown_ack(stcb,
+					       stcb->asoc.primary_destination);
+
+			sctp_timer_start(SCTP_TIMER_TYPE_SHUTDOWNACK,
+					 stcb->sctp_ep, stcb, asoc->primary_destination);
+		}
+		return;
+	}
+	/*
+	 * Now here we are going to recycle net_ack for a different
+	 * use... HEADS UP.
+	 */
+	TAILQ_FOREACH(net, &asoc->nets, sctp_next) {
+		net->net_ack = 0;
+	}
+	if ((num_seg > 0) && marking_allowed) {
+		sctp_strike_gap_ack_chunks(stcb, asoc, biggest_tsn_acked,
+					   strike_enabled, biggest_tsn_newly_acked, accum_moved);
+	}
+
+	/*********************************************/
+	/* Here we perform PR-SCTP procedures        */
+	/* (section 4.2)                             */
+	/*********************************************/
+	/* C1. update advancedPeerAckPoint */
+	if (compare_with_wrap(cum_ack, asoc->advanced_peer_ack_point, MAX_TSN)) {
+		asoc->advanced_peer_ack_point = cum_ack;
+	}
+	/* C2. try to further move advancedPeerAckPoint ahead */
+	if (asoc->peer_supports_prsctp) {
+		struct sctp_tmit_chunk *lchk;
+		lchk = sctp_try_advance_peer_ack_point(stcb, asoc);
+		/* C3. See if we need to send a Fwd-TSN */
+		if (compare_with_wrap(asoc->advanced_peer_ack_point, cum_ack,
+				      MAX_TSN)) {
+			/*
+			 * ISSUE with ECN, see FWD-TSN processing for notes
+			 * on issues that will occur when the ECN NONCE stuff
+			 * is put into SCTP for cross checking.
+			 */
+			send_forward_tsn(stcb, asoc);
+
+			/* ECN Nonce: Disable Nonce Sum check when FWD TSN is sent and store resync tsn*/
+			asoc->nonce_sum_check = 0;
+			asoc->nonce_resync_tsn = asoc->advanced_peer_ack_point;
+			if (lchk) {
+				/* Assure a timer is up */
+				sctp_timer_start(SCTP_TIMER_TYPE_SEND,
+						 stcb->sctp_ep, stcb, lchk->whoTo);
+			}
+		}
+	}
+	TAILQ_FOREACH(net, &asoc->nets, sctp_next) {
+		if (asoc->fast_retran_loss_recovery == 0) {
+			/* out of a RFC2582 Fast recovery window? */
+			if (net->net_ack > 0) {
+				/*
+				 * per section 7.2.3, are there
+				 * any destinations that had a fast
+				 * retransmit to them. If so what we
+				 * need to do is adjust ssthresh and
+				 * cwnd.
+				 */
+				struct sctp_tmit_chunk *lchk;
+#ifdef  SCTP_HIGH_SPEED
+				sctp_hs_cwnd_decrease(net);
+#else
+#ifdef SCTP_CWND_LOGGING
+				int old_cwnd = net->cwnd;
+#endif
+				net->ssthresh = net->cwnd / 2;
+				if (net->ssthresh < (net->mtu*2)) {
+					net->ssthresh = 2 * net->mtu;
+				}
+				net->cwnd = net->ssthresh;
+#ifdef SCTP_CWND_LOGGING
+				sctp_log_cwnd(net, (net->cwnd-old_cwnd),
+					      SCTP_CWND_LOG_FROM_FR);
+#endif
+#endif
+
+				lchk = TAILQ_FIRST(&asoc->send_queue);
+
+				net->partial_bytes_acked = 0;
+				/* Turn on fast recovery window */
+				asoc->fast_retran_loss_recovery = 1;
+				if (lchk == NULL) {
+					/* Mark end of the window */
+					asoc->fast_recovery_tsn = asoc->sending_seq - 1;
+				} else {
+					asoc->fast_recovery_tsn = lchk->rec.data.TSN_seq - 1;
+				}
+
+
+				/* Disable Nonce Sum Checking and store the resync tsn*/
+				asoc->nonce_sum_check = 0;
+				asoc->nonce_resync_tsn = asoc->fast_recovery_tsn + 1;
+
+				sctp_timer_stop(SCTP_TIMER_TYPE_SEND,
+						stcb->sctp_ep, stcb, net);
+				sctp_timer_start(SCTP_TIMER_TYPE_SEND,
+						 stcb->sctp_ep, stcb, net);
+			}
+		} else if (net->net_ack > 0) {
+			/*
+			 * Mark a peg that we WOULD have done a cwnd reduction
+			 * but RFC2582 prevented this action.
+			 */
+			sctp_pegs[SCTP_FR_INAWINDOW]++;
+		}
+	}
+
+
+	/******************************************************************
+	 *  Here we do the stuff with ECN Nonce checking.
+	 *  We basically check to see if the nonce sum flag was incorrect
+	 *  or if resynchronization needs to be done. Also if we catch a
+	 *  misbehaving receiver we give him the kick.
+	 ******************************************************************/
+
+	if (asoc->ecn_nonce_allowed) {
+		if (asoc->nonce_sum_check) {
+			if (nonce_sum_flag != ((asoc->nonce_sum_expect_base + ecn_seg_sums) & SCTP_SACK_NONCE_SUM)) {
+				if (asoc->nonce_wait_for_ecne == 0) {
+					struct sctp_tmit_chunk *lchk;
+					lchk = TAILQ_FIRST(&asoc->send_queue);
+					asoc->nonce_wait_for_ecne = 1;
+					if (lchk) {
+						asoc->nonce_wait_tsn = lchk->rec.data.TSN_seq;
+					} else {
+						asoc->nonce_wait_tsn = asoc->sending_seq;
+					}
+				} else {
+					if (compare_with_wrap(asoc->last_acked_seq, asoc->nonce_wait_tsn, MAX_TSN) ||
+					   (asoc->last_acked_seq == asoc->nonce_wait_tsn)) {
+						/* Misbehaving peer. We need to react to this guy */
+						printf("Mis-behaving peer detected\n");
+						asoc->ecn_allowed = 0;
+						asoc->ecn_nonce_allowed = 0;
+					}
+				}
+			}
+		} else {
+			/* See if Resynchronization Possible */
+			if (compare_with_wrap(asoc->last_acked_seq, asoc->nonce_resync_tsn, MAX_TSN)) {
+				asoc->nonce_sum_check = 1;
+				/* now we must calculate what the base
+				 * is. We do this based on two things, we know
+				 * the total's for all the segments gap-acked
+				 * in the SACK, its stored in ecn_seg_sums.
+				 * We also know the SACK's nonce sum, its
+				 * in nonce_sum_flag. So we can build a truth
+				 * table to back-calculate the new value of asoc->nonce_sum_expect_base:
+                                 *
+                                 *   SACK-flag-Value         Seg-Sums              Base
+				 *         0                    0                   0
+				 *         1                    0                   1
+				 *         0                    1                   1
+				 *         1                    1                   0
+				 */
+				asoc->nonce_sum_expect_base = (ecn_seg_sums ^ nonce_sum_flag) & SCTP_SACK_NONCE_SUM;
+			}
+		}
+	}
+	/* Now are we exiting loss recovery ? */
+	if (will_exit_fast_recovery) {
+		/* Ok, we must exit fast recovery */
+		asoc->fast_retran_loss_recovery = 0;
+	}
+	if ((asoc->sat_t3_loss_recovery) &&
+	    ((compare_with_wrap(asoc->last_acked_seq, asoc->sat_t3_recovery_tsn,
+				MAX_TSN) ||
+	      (asoc->last_acked_seq == asoc->sat_t3_recovery_tsn)))) {
+		/* end satellite t3 loss recovery */
+		asoc->sat_t3_loss_recovery = 0;
+	}
+	/* Adjust and set the new rwnd value */
+#ifdef SCTP_LOG_RWND
+	sctp_log_rwnd_set(SCTP_SET_PEER_RWND_VIA_SACK,
+			  asoc->peers_rwnd,  asoc->total_flight, (asoc->sent_queue_cnt * sctp_peer_chunk_oh), a_rwnd);
+#endif
+
+	asoc->peers_rwnd = sctp_sbspace_sub(a_rwnd,
+					    (u_int32_t)(asoc->total_flight + (asoc->sent_queue_cnt * sctp_peer_chunk_oh)));
+	if (asoc->peers_rwnd < stcb->sctp_ep->sctp_ep.sctp_sws_sender) {
+		/* SWS sender side engages */
+		asoc->peers_rwnd = 0;
+	}
+	/*
+	 * Now we must setup so we have a timer up for anyone with
+	 * outstanding data.
+	 */
+	TAILQ_FOREACH(net, &asoc->nets, sctp_next) {
+		struct sctp_tmit_chunk *chk;
+		TAILQ_FOREACH(chk, &asoc->sent_queue, sctp_next) {
+			if (chk->whoTo == net &&
+			    (chk->sent < SCTP_DATAGRAM_ACKED ||
+			     chk->sent == SCTP_FORWARD_TSN_SKIP)) {
+				/*
+				 * Not ack'ed and still outstanding to this
+				 * destination or marked and must be
+				 * sacked after fwd-tsn sent.
+				 */
+				sctp_timer_start(SCTP_TIMER_TYPE_SEND,
+						 stcb->sctp_ep, stcb, net);
+				break;
+			}
+		}
+	}
+}
+
+void
+sctp_update_acked(struct sctp_tcb *stcb, struct sctp_shutdown_chunk *cp,
+    struct sctp_nets *netp, int *abort_flag)
+{
+	/* Mutate a shutdown into a SACK */
+	struct sctp_sack_chunk sack;
+
+	/* Copy cum-ack */
+	sack.sack.cum_tsn_ack = cp->cumulative_tsn_ack;
+	/* Arrange so a_rwnd does NOT change */
+	sack.ch.chunk_type = SCTP_SELECTIVE_ACK;
+	sack.ch.chunk_flags = 0;
+	sack.ch.chunk_length = ntohs(sizeof(struct sctp_sack_chunk));
+	sack.sack.a_rwnd =
+	    htonl(stcb->asoc.peers_rwnd + stcb->asoc.total_flight);
+	/*
+	 * no gaps in this one. This may cause a temporal view to reneging,
+	 * but hopefully the second chunk is a true SACK in the packet and
+	 * will correct this view. One will come soon after no matter what
+	 * to fix this.
+	 */
+	sack.sack.num_gap_ack_blks = 0;
+	sack.sack.num_dup_tsns = 0;
+	/* Now call the SACK processor */
+	sctp_handle_sack(&sack, stcb, netp, abort_flag);
+}
+
+static void
+sctp_kick_prsctp_reorder_queue(struct sctp_tcb *stcb,
+    struct sctp_stream_in *strmin)
+{
+	struct sctp_tmit_chunk *chk, *nchk;
+	struct sctp_association *asoc;
+	int tt;
+
+	asoc = &stcb->asoc;
+	tt = strmin->last_sequence_delivered;
+	/*
+	 * First deliver anything prior to and including the stream no that
+	 * came in
+	 */
+	chk = TAILQ_FIRST(&strmin->inqueue);
+	while (chk) {
+		nchk = TAILQ_NEXT(chk, sctp_next);
+		if (compare_with_wrap(tt, chk->rec.data.stream_seq, MAX_SEQ) ||
+		    (tt == chk->rec.data.stream_seq)) {
+			/* this is deliverable now */
+			TAILQ_REMOVE(&strmin->inqueue, chk, sctp_next);
+			/* subtract pending on streams */
+			asoc->size_on_all_streams -= chk->send_size;
+			asoc->cnt_on_all_streams--;
+			/* deliver it to at least the delivery-q */
+			sctp_deliver_data(stcb, &stcb->asoc, chk, 0);
+		} else {
+			/* no more delivery now. */
+			break;
+		}
+		chk = nchk;
+	}
+	/*
+	 * now we must deliver things in queue the normal way  if any
+	 * are now ready.
+	 */
+	tt = strmin->last_sequence_delivered + 1;
+	chk = TAILQ_FIRST(&strmin->inqueue);
+	while (chk) {
+		nchk = TAILQ_NEXT(chk, sctp_next);
+		if (tt == chk->rec.data.stream_seq) {
+			/* this is deliverable now */
+			TAILQ_REMOVE(&strmin->inqueue, chk, sctp_next);
+			/* subtract pending on streams */
+			asoc->size_on_all_streams -= chk->send_size;
+			asoc->cnt_on_all_streams--;
+			/* deliver it to at least the delivery-q */
+			strmin->last_sequence_delivered =
+			    chk->rec.data.stream_seq;
+			sctp_deliver_data(stcb, &stcb->asoc, chk, 0);
+			tt = strmin->last_sequence_delivered + 1;
+		} else {
+			break;
+		}
+		chk = nchk;
+	}
+
+}
+
+void
+sctp_handle_forward_tsn(struct sctp_tcb *stcb,
+    struct sctp_forward_tsn_chunk *fwd, int *abort_flag)
+{
+	/*
+	 * ISSUES that MUST be fixed for ECN! When we are the
+	 * sender of the forward TSN, when the SACK comes back
+	 * that acknowledges the FWD-TSN we must reset the
+	 * NONCE sum to match correctly. This will get quite
+	 * tricky since we may have sent more data interveneing and
+	 * must carefully account for what the SACK says on the
+	 * nonce and any gaps that are reported. This work
+	 * will NOT be done here, but I note it here since
+	 * it is really related to PR-SCTP and FWD-TSN's
+	 */
+
+	/* The pr-sctp fwd tsn */
+	/*
+	 * here we will perform all the data receiver side steps for
+	 * processing FwdTSN, as required in by pr-sctp draft:
+	 *
+	 * Assume we get FwdTSN(x):
+	 *
+	 * 1) update local cumTSN to x
+	 * 2) try to further advance cumTSN to x + others we have
+	 * 3) examine and update re-ordering queue on pr-in-streams
+	 * 4) clean up re-assembly queue
+	 * 5) Send a sack to report where we are.
+	 */
+	struct sctp_strseq *stseq;
+	struct sctp_association *asoc;
+	u_int32_t new_cum_tsn, gap, back_out_htsn;
+	unsigned int i, cnt_gone, fwd_sz, cumack_set_flag, m_size;
+	struct sctp_stream_in *strm;
+	struct sctp_tmit_chunk *chk, *at;
+
+	cumack_set_flag = 0;
+	asoc = &stcb->asoc;
+	cnt_gone = 0;
+	if ((fwd_sz = ntohs(fwd->ch.chunk_length)) < sizeof(struct sctp_forward_tsn_chunk)) {
+#ifdef SCTP_DEBUG
+		if (sctp_debug_on & SCTP_DEBUG_INDATA1) {
+			printf("Bad size too small/big fwd-tsn\n");
+		}
+#endif
+		return;
+	}
+	m_size = (stcb->asoc.mapping_array_size << 3);
+	/*************************************************************/
+	/* 1. Here we update local cumTSN and shift the bitmap array */
+	/*************************************************************/
+	new_cum_tsn = ntohl(fwd->new_cumulative_tsn);
+
+	if (compare_with_wrap(asoc->cumulative_tsn, new_cum_tsn, MAX_TSN) ||
+	    asoc->cumulative_tsn == new_cum_tsn) {
+		/* Already got there ... */
+		return;
+	}
+
+	back_out_htsn = asoc->highest_tsn_inside_map;
+	if (compare_with_wrap(new_cum_tsn, asoc->highest_tsn_inside_map,
+	    MAX_TSN)) {
+		asoc->highest_tsn_inside_map = new_cum_tsn;
+#ifdef SCTP_MAP_LOGGING
+			sctp_log_map(0, 0, asoc->highest_tsn_inside_map, SCTP_MAP_SLIDE_RESULT);
+#endif
+	}
+	/*
+	 * now we know the new TSN is more advanced, let's find the
+	 * actual gap
+	 */
+	if ((compare_with_wrap(new_cum_tsn, asoc->mapping_array_base_tsn,
+			       MAX_TSN)) ||
+	     (new_cum_tsn == asoc->mapping_array_base_tsn)) {
+		gap = new_cum_tsn - asoc->mapping_array_base_tsn;
+	} else {
+		/* try to prevent underflow here */
+		gap = new_cum_tsn + (MAX_TSN - asoc->mapping_array_base_tsn) + 1;
+	}
+
+	if (gap > m_size  || gap < 0) {
+		asoc->highest_tsn_inside_map = back_out_htsn;
+		if ((long)gap > sctp_sbspace(&stcb->sctp_socket->so_rcv)) {
+			/*
+			 * out of range (of single byte chunks in the rwnd I
+			 * give out)
+			 * too questionable. better to drop it silently
+			 */
+			return;
+		}
+		if (asoc->highest_tsn_inside_map >
+		    asoc->mapping_array_base_tsn) {
+			gap = asoc->highest_tsn_inside_map -
+			    asoc->mapping_array_base_tsn;
+		} else {
+			gap = asoc->highest_tsn_inside_map +
+			    (MAX_TSN - asoc->mapping_array_base_tsn) + 1;
+		}
+		cumack_set_flag = 1;
+	}
+	for (i = 0; i <= gap; i++) {
+		SCTP_SET_TSN_PRESENT(asoc->mapping_array, i);
+	}
+	/*
+	 * Now after marking all, slide thing forward but no
+	 * sack please.
+	 */
+	sctp_sack_check(stcb, 0, 0, abort_flag);
+	if (*abort_flag)
+		return;
+
+	if (cumack_set_flag) {
+		/*
+		 * fwd-tsn went outside my gap array - not a
+		 * common occurance. Do the same thing we
+		 * do when a cookie-echo arrives.
+		 */
+		asoc->highest_tsn_inside_map =  new_cum_tsn - 1;
+		asoc->mapping_array_base_tsn = new_cum_tsn;
+		asoc->cumulative_tsn = asoc->highest_tsn_inside_map;
+#ifdef SCTP_MAP_LOGGING
+		sctp_log_map(0, 3, asoc->highest_tsn_inside_map, SCTP_MAP_SLIDE_RESULT);
+#endif
+		asoc->last_echo_tsn = asoc->highest_tsn_inside_map;
+	}
+	/*************************************************************/
+	/* 2. Clear up re-assembly queue                             */
+	/*************************************************************/
+
+	/*
+	 * First service it if pd-api is up, just in case we can
+	 * progress it forward
+	 */
+	if (asoc->fragmented_delivery_inprogress) {
+		sctp_service_reassembly(stcb, asoc, 0);
+	}
+	if (!TAILQ_EMPTY(&asoc->reasmqueue)) {
+		/* For each one on here see if we need to toss it */
+		/*
+		 * For now large messages held on the reasmqueue that are
+		 * complete will be tossed too. We could in theory do more
+		 * work to spin through and stop after dumping one msg
+		 * aka seeing the start of a new msg at the head, and call
+		 * the delivery function... to see if it can be delivered...
+		 * But for now we just dump everything on the queue.
+		 */
+		chk = TAILQ_FIRST(&asoc->reasmqueue);
+		while (chk) {
+			at = TAILQ_NEXT(chk, sctp_next);
+			if (compare_with_wrap(asoc->cumulative_tsn,
+			    chk->rec.data.TSN_seq, MAX_TSN) ||
+			    asoc->cumulative_tsn == chk->rec.data.TSN_seq) {
+				/* It needs to be tossed */
+				TAILQ_REMOVE(&asoc->reasmqueue, chk, sctp_next);
+				if (compare_with_wrap(chk->rec.data.TSN_seq,
+				    asoc->tsn_last_delivered, MAX_TSN)) {
+					asoc->tsn_last_delivered =
+					    chk->rec.data.TSN_seq;
+					asoc->str_of_pdapi =
+					    chk->rec.data.stream_number;
+					asoc->ssn_of_pdapi =
+					    chk->rec.data.stream_seq;
+					asoc->fragment_flags =
+					    chk->rec.data.rcv_flags;
+				}
+				asoc->size_on_reasm_queue -= chk->send_size;
+				asoc->cnt_on_reasm_queue--;
+				cnt_gone++;
+
+				/* Clear up any stream problem */
+				if ((chk->rec.data.rcv_flags & SCTP_DATA_UNORDERED) !=
+				    SCTP_DATA_UNORDERED &&
+				    (compare_with_wrap(chk->rec.data.stream_seq,
+				    asoc->strmin[chk->rec.data.stream_number].last_sequence_delivered,
+				    MAX_SEQ))) {
+					/*
+					 * We must dump forward this streams
+					 * sequence number if the chunk is not
+					 * unordered that is being skipped.
+					 * There is a chance that if the peer
+					 * does not include the last fragment
+					 * in its FWD-TSN we WILL have a problem
+					 * here since you would have a partial
+					 * chunk in queue that may not be
+					 * deliverable.
+					 * Also if a Partial delivery API as
+					 * started the user may get a partial
+					 * chunk. The next read returning a new
+					 * chunk... really ugly but I see no way
+					 * around it! Maybe a notify??
+					 */
+					asoc->strmin[chk->rec.data.stream_number].last_sequence_delivered =
+					    chk->rec.data.stream_seq;
+				}
+				if (chk->data) {
+					sctp_m_freem(chk->data);
+					chk->data = NULL;
+				}
+				sctp_free_remote_addr(chk->whoTo);
+				SCTP_ZONE_FREE(sctppcbinfo.ipi_zone_chunk, chk);
+				sctppcbinfo.ipi_count_chunk--;
+				if ((int)sctppcbinfo.ipi_count_chunk < 0) {
+					panic("Chunk count is negative");
+				}
+				sctppcbinfo.ipi_gencnt_chunk++;
+			} else {
+				/*
+				 * Ok we have gone beyond the end of the
+				 * fwd-tsn's mark. Some checks...
+				 */
+				if ((asoc->fragmented_delivery_inprogress) &&
+				    (chk->rec.data.rcv_flags & SCTP_DATA_FIRST_FRAG)) {
+					/* Special case PD-API is up and what we fwd-tsn'
+					 * over includes one that had the LAST_FRAG. We
+					 * no longer need to do the PD-API.
+					 */
+					asoc->fragmented_delivery_inprogress = 0;
+					sctp_ulp_notify(SCTP_NOTIFY_PARTIAL_DELVIERY_INDICATION,
+					    stcb, SCTP_PARTIAL_DELIVERY_ABORTED, (void *)NULL);
+
+				}
+				break;
+			}
+			chk = at;
+		}
+	}
+	if (asoc->fragmented_delivery_inprogress) {
+		/*
+		 * Ok we removed cnt_gone chunks in the PD-API queue that
+		 * were being delivered. So now we must turn off the
+		 * flag.
+		 */
+		sctp_ulp_notify(SCTP_NOTIFY_PARTIAL_DELVIERY_INDICATION,
+		    stcb, SCTP_PARTIAL_DELIVERY_ABORTED, (void *)NULL);
+		asoc->fragmented_delivery_inprogress = 0;
+	}
+	/*************************************************************/
+	/* 3. Update the PR-stream re-ordering queues                */
+	/*************************************************************/
+	stseq = (struct sctp_strseq *)((caddr_t)fwd + sizeof(*fwd));
+	fwd_sz -= sizeof(*fwd);
+	{
+		/* New method. */
+		int num_str, i;
+		num_str = fwd_sz/sizeof(struct sctp_strseq);
+#ifdef SCTP_DEBUG
+		if (sctp_debug_on & SCTP_DEBUG_INDATA1) {
+			printf("Using NEW method, %d strseq's reported in FWD-TSN\n",
+			    num_str);
+		}
+#endif
+		for (i = 0; i < num_str; i++) {
+			u_int16_t st;
+			unsigned char *xx;
+			/* Convert */
+			xx = (unsigned char *)&stseq[i];
+			st = ntohs(stseq[i].stream);
+			stseq[i].stream = st;
+			st = ntohs(stseq[i].sequence);
+			stseq[i].sequence = st;
+			/* now process */
+			if (stseq[i].stream > asoc->streamincnt) {
+#ifdef SCTP_DEBUG
+				if (sctp_debug_on & SCTP_DEBUG_INDATA1) {
+					printf("Bogus stream number %d "
+					    "streamincnt is %d\n",
+					    stseq[i].stream, asoc->streamincnt);
+				}
+#endif
+				/*
+				 * It is arguable if we should continue. Since
+				 * the peer sent bogus stream info we may be in
+				 * deep trouble..
+ 				 * a return may be a better choice?
+ 				 */
+ 				continue;
+ 			}
+			strm = &asoc->strmin[stseq[i].stream];
+			if (compare_with_wrap(stseq[i].sequence,
+			    strm->last_sequence_delivered, MAX_SEQ)) {
+				/* Update the sequence number */
+				strm->last_sequence_delivered =
+				    stseq[i].sequence;
+			}
+			/* now kick the stream the new way */
+			sctp_kick_prsctp_reorder_queue(stcb, strm);
+		}
+	}
+}
Index: sys/netinet/sctp_indata.h
===================================================================
RCS file: sys/netinet/sctp_indata.h
diff -N sys/netinet/sctp_indata.h
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ sys/netinet/sctp_indata.h	18 Jun 2005 16:05:47 -0000
@@ -0,0 +1,62 @@
+/*	$KAME: sctp_indata.h,v 1.8 2004/08/17 04:06:17 itojun Exp $	*/
+
+#ifndef __sctp_indata_h__
+#define __sctp_indata_h__
+
+/*
+ * Copyright (C) 2002, 2003, 2004 Cisco Systems Inc,
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the project nor the names of its contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+
+#if defined(_KERNEL) || (defined(__APPLE__) && defined(KERNEL))
+int sctp_deliver_data(struct sctp_tcb *, struct sctp_association *,
+    struct sctp_tmit_chunk *, int);
+
+void sctp_set_rwnd(struct sctp_tcb *, struct sctp_association *);
+
+void sctp_handle_sack(struct sctp_sack_chunk *, struct sctp_tcb *,
+	struct sctp_nets *, int *);
+
+/* draft-ietf-tsvwg-usctp */
+void sctp_handle_forward_tsn(struct sctp_tcb *,
+	struct sctp_forward_tsn_chunk *, int *);
+
+struct sctp_tmit_chunk *
+sctp_try_advance_peer_ack_point(struct sctp_tcb *, struct sctp_association *);
+
+void sctp_service_queues(struct sctp_tcb *, struct sctp_association *, int have_lock);
+
+void sctp_update_acked(struct sctp_tcb *, struct sctp_shutdown_chunk *,
+	struct sctp_nets *, int *);
+
+int sctp_process_data(struct mbuf **, int, int *, int, struct sctphdr *,
+    struct sctp_inpcb *, struct sctp_tcb *, struct sctp_nets *, u_int32_t *);
+
+void sctp_sack_check(struct sctp_tcb *, int, int, int *);
+#endif
+#endif
Index: sys/netinet/sctp_input.c
===================================================================
RCS file: sys/netinet/sctp_input.c
diff -N sys/netinet/sctp_input.c
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ sys/netinet/sctp_input.c	18 Jun 2005 16:05:48 -0000
@@ -0,0 +1,4424 @@
+/*	$KAME: sctp_input.c,v 1.27 2005/03/06 16:04:17 itojun Exp $	*/
+
+/*
+ * Copyright (C) 2002, 2003, 2004 Cisco Systems Inc,
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the project nor the names of its contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#if !(defined(__OpenBSD__) || defined(__APPLE__))
+#include "opt_ipsec.h"
+#endif
+#if defined(__FreeBSD__) || defined(__DragonFly__)
+#include "opt_compat.h"
+#include "opt_inet6.h"
+#include "opt_inet.h"
+#endif
+#if defined(__NetBSD__)
+#include "opt_inet.h"
+#endif
+#ifdef __APPLE__
+#include <sctp.h>
+#elif !defined(__OpenBSD__)
+#include "opt_sctp.h"
+#endif
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/malloc.h>
+#include <sys/mbuf.h>
+#include <sys/socket.h>
+#include <sys/socketvar.h>
+#include <sys/sysctl.h>
+#include <sys/domain.h>
+#include <sys/protosw.h>
+#include <sys/kernel.h>
+#include <sys/errno.h>
+#include <sys/syslog.h>
+#include <sys/thread2.h>
+
+#if (defined(__FreeBSD__) && __FreeBSD_version >= 500000)
+#include <sys/limits.h>
+#else
+#include <machine/limits.h>
+#endif
+#include <machine/cpu.h>
+
+#include <net/if.h>
+#include <net/route.h>
+#include <net/if_types.h>
+
+#include <netinet/in.h>
+#include <netinet/in_systm.h>
+#include <netinet/ip.h>
+#include <netinet/in_pcb.h>
+#include <netinet/in_var.h>
+#include <netinet/ip_var.h>
+
+#ifdef INET6
+#include <netinet/ip6.h>
+#include <netinet6/ip6_var.h>
+#endif /* INET6 */
+
+#include <netinet/ip_icmp.h>
+#include <netinet/icmp_var.h>
+#include <netinet/sctp_var.h>
+#include <netinet/sctp_pcb.h>
+#include <netinet/sctp_header.h>
+#include <netinet/sctputil.h>
+#include <netinet/sctp_output.h>
+#include <netinet/sctp_input.h>
+#include <netinet/sctp_hashdriver.h>
+#include <netinet/sctp_indata.h>
+#include <netinet/sctp_asconf.h>
+
+#ifdef __APPLE__
+#include <stdarg.h>
+#elif !defined(__FreeBSD__)
+#include <machine/stdarg.h>
+#endif
+
+#ifdef IPSEC
+#ifndef __OpenBSD__
+#include <netinet6/ipsec.h>
+#include <netkey/key.h>
+#else
+#undef IPSEC
+#endif
+#endif /*IPSEC*/
+
+#include <net/net_osdep.h>
+
+#ifdef SCTP_DEBUG
+extern u_int32_t sctp_debug_on;
+#endif
+
+/* INIT handler */
+static void
+sctp_handle_init(struct mbuf *m, int iphlen, int offset,
+    struct sctphdr *sh, struct sctp_init_chunk *cp, struct sctp_inpcb *inp,
+    struct sctp_tcb *stcb, struct sctp_nets *net)
+{
+	struct sctp_init *init;
+	struct mbuf *op_err;
+#ifdef SCTP_DEBUG
+	if (sctp_debug_on & SCTP_DEBUG_INPUT2) {
+		printf("sctp_handle_init: handling INIT tcb:%p\n", stcb);
+	}
+#endif
+	op_err = NULL;
+	init = &cp->init;
+	/* First are we accepting? */
+	if ((inp->sctp_flags & SCTP_PCB_FLAGS_ACCEPTING) == 0 ||
+	    (inp->sctp_socket->so_qlimit == 0)) {
+		sctp_abort_association(inp, stcb, m, iphlen, sh, op_err);
+		return;
+	}
+	if (ntohs(cp->ch.chunk_length) < sizeof(struct sctp_init_chunk)) {
+		/* Invalid length */
+		op_err = sctp_generate_invmanparam(SCTP_CAUSE_INVALID_PARAM);
+		sctp_abort_association(inp, stcb, m, iphlen, sh, op_err);
+		return;
+	}
+	/* validate parameters */
+	if (init->initiate_tag == 0) {
+		/* protocol error... send abort */
+		op_err = sctp_generate_invmanparam(SCTP_CAUSE_INVALID_PARAM);
+		sctp_abort_association(inp, stcb, m, iphlen, sh, op_err);
+		return;
+	}
+	if (ntohl(init->a_rwnd) < SCTP_MIN_RWND) {
+		/* invalid parameter... send abort */
+		op_err = sctp_generate_invmanparam(SCTP_CAUSE_INVALID_PARAM);
+		sctp_abort_association(inp, stcb, m, iphlen, sh, op_err);
+		return;
+	}
+	if (init->num_inbound_streams == 0) {
+		/* protocol error... send abort */
+		op_err = sctp_generate_invmanparam(SCTP_CAUSE_INVALID_PARAM);
+		sctp_abort_association(inp, stcb, m, iphlen, sh, op_err);
+		return;
+	}
+	if (init->num_outbound_streams == 0) {
+		/* protocol error... send abort */
+		op_err = sctp_generate_invmanparam(SCTP_CAUSE_INVALID_PARAM);
+		sctp_abort_association(inp, stcb, m, iphlen, sh, op_err);
+		return;
+	}
+
+	/* send an INIT-ACK w/cookie */
+#ifdef SCTP_DEBUG
+	if (sctp_debug_on & SCTP_DEBUG_INPUT3) {
+		printf("sctp_handle_init: sending INIT-ACK\n");
+	}
+#endif
+
+	sctp_send_initiate_ack(inp, stcb, m, iphlen, offset, sh, cp);
+}
+
+/*
+ * process peer "INIT/INIT-ACK" chunk
+ * returns value < 0 on error
+ */
+
+static int
+sctp_process_init(struct sctp_init_chunk *cp, struct sctp_tcb *stcb,
+    struct sctp_nets *net)
+{
+	struct sctp_init *init;
+	struct sctp_association *asoc;
+	struct sctp_nets *lnet;
+	unsigned int i;
+
+	init = &cp->init;
+	asoc = &stcb->asoc;
+	/* save off parameters */
+	asoc->peer_vtag = ntohl(init->initiate_tag);
+	asoc->peers_rwnd = ntohl(init->a_rwnd);
+
+	if (TAILQ_FIRST(&asoc->nets)) {
+		/* update any ssthresh's that may have a default */
+		TAILQ_FOREACH(lnet, &asoc->nets, sctp_next) {
+			lnet->ssthresh = asoc->peers_rwnd;
+		}
+	}
+	if (asoc->pre_open_streams > ntohs(init->num_inbound_streams)) {
+		unsigned int newcnt;
+		struct sctp_stream_out *outs;
+		struct sctp_tmit_chunk *chk;
+
+		/* cut back on number of streams */
+		newcnt = ntohs(init->num_inbound_streams);
+		/* This if is probably not needed but I am cautious */
+		if (asoc->strmout) {
+			/* First make sure no data chunks are trapped */
+			for (i=newcnt; i < asoc->pre_open_streams; i++) {
+				outs = &asoc->strmout[i];
+				chk = TAILQ_FIRST(&outs->outqueue);
+				while (chk) {
+					TAILQ_REMOVE(&outs->outqueue, chk,
+						     sctp_next);
+					asoc->stream_queue_cnt--;
+					sctp_ulp_notify(SCTP_NOTIFY_DG_FAIL,
+					    stcb, SCTP_NOTIFY_DATAGRAM_UNSENT,
+					    chk);
+					if (chk->data) {
+						sctp_m_freem(chk->data);
+						chk->data = NULL;
+					}
+					sctp_free_remote_addr(chk->whoTo);
+					chk->whoTo = NULL;
+					chk->asoc = NULL;
+					/* Free the chunk */
+					SCTP_ZONE_FREE(sctppcbinfo.ipi_zone_chunk, chk);
+					sctppcbinfo.ipi_count_chunk--;
+					if ((int)sctppcbinfo.ipi_count_chunk < 0) {
+						panic("Chunk count is negative");
+					}
+					sctppcbinfo.ipi_gencnt_chunk++;
+					chk = TAILQ_FIRST(&outs->outqueue);
+				}
+			}
+		}
+		/* cut back the count and abandon the upper streams */
+		asoc->pre_open_streams = newcnt;
+	}
+	asoc->streamincnt = ntohs(init->num_outbound_streams);
+	if (asoc->streamincnt > MAX_SCTP_STREAMS) {
+		asoc->streamincnt = MAX_SCTP_STREAMS;
+	}
+
+	asoc->streamoutcnt = asoc->pre_open_streams;
+	/* init tsn's */
+	asoc->highest_tsn_inside_map = asoc->asconf_seq_in = ntohl(init->initial_tsn) - 1;
+#ifdef SCTP_MAP_LOGGING
+	sctp_log_map(0, 5, asoc->highest_tsn_inside_map, SCTP_MAP_SLIDE_RESULT);
+#endif
+	/* This is the next one we expect */
+	asoc->str_reset_seq_in = asoc->asconf_seq_in + 1;
+
+	asoc->mapping_array_base_tsn = ntohl(init->initial_tsn);
+	asoc->cumulative_tsn = asoc->asconf_seq_in;
+	asoc->last_echo_tsn = asoc->asconf_seq_in;
+	asoc->advanced_peer_ack_point = asoc->last_acked_seq;
+	/* open the requested streams */
+	if (asoc->strmin != NULL) {
+		/* Free the old ones */
+		FREE(asoc->strmin, M_PCB);
+	}
+	MALLOC(asoc->strmin, struct sctp_stream_in *, asoc->streamincnt *
+	       sizeof(struct sctp_stream_in), M_PCB, M_NOWAIT);
+	if (asoc->strmin == NULL) {
+		/* we didn't get memory for the streams! */
+#ifdef SCTP_DEBUG
+		if (sctp_debug_on & SCTP_DEBUG_INPUT2) {
+			printf("process_init: couldn't get memory for the streams!\n");
+		}
+#endif
+		return (-1);
+	}
+	for (i = 0; i < asoc->streamincnt; i++) {
+		asoc->strmin[i].stream_no = i;
+		asoc->strmin[i].last_sequence_delivered = 0xffff;
+		/*
+		 * U-stream ranges will be set when the cookie
+		 * is unpacked. Or for the INIT sender they
+		 * are un set (if pr-sctp not supported) when the
+		 * INIT-ACK arrives.
+		 */
+		TAILQ_INIT(&asoc->strmin[i].inqueue);
+		/*
+		 * we are not on any wheel, pr-sctp streams
+		 * will go on the wheel when they have data waiting
+		 * for reorder.
+		 */
+		asoc->strmin[i].next_spoke.tqe_next = 0;
+		asoc->strmin[i].next_spoke.tqe_prev = 0;
+	}
+
+	/*
+	 * load_address_from_init will put the addresses into the
+	 * association when the COOKIE is processed or the INIT-ACK
+	 * is processed. Both types of COOKIE's existing and new
+	 * call this routine. It will remove addresses that
+	 * are no longer in the association (for the restarting
+	 * case where addresses are removed). Up front when the
+	 * INIT arrives we will discard it if it is a restart
+	 * and new addresses have been added.
+	 */
+	return (0);
+}
+
+/*
+ * INIT-ACK message processing/consumption
+ * returns value < 0 on error
+ */
+static int
+sctp_process_init_ack(struct mbuf *m, int iphlen, int offset,
+    struct sctphdr *sh, struct sctp_init_ack_chunk *cp, struct sctp_tcb *stcb,
+    struct sctp_nets *net)
+{
+	struct sctp_association *asoc;
+	struct mbuf *op_err;
+	int retval, abort_flag;
+	uint32_t initack_limit;
+	/* First verify that we have no illegal param's */
+	abort_flag = 0;
+	op_err = NULL;
+
+	op_err = sctp_arethere_unrecognized_parameters(m,
+	    (offset+sizeof(struct sctp_init_chunk)) ,
+	    &abort_flag, (struct sctp_chunkhdr *)cp);
+	if (abort_flag) {
+		/* Send an abort and notify peer */
+		if (op_err != NULL) {
+			sctp_send_operr_to(m, iphlen, op_err, cp->init.initiate_tag);
+		} else {
+			/*
+			 * Just notify (abort_assoc does this if
+			 * we send an abort).
+			 */
+			sctp_abort_notification(stcb, 0);
+			/*
+			 * No sense in further INIT's since
+			 * we will get the same param back
+			 */
+			sctp_free_assoc(stcb->sctp_ep, stcb);
+		}
+		return (-1);
+	}
+	asoc = &stcb->asoc;
+	/* process the peer's parameters in the INIT-ACK */
+	retval = sctp_process_init((struct sctp_init_chunk *)cp, stcb, net);
+	if (retval < 0) {
+		return (retval);
+	}
+
+	initack_limit = offset + ntohs(cp->ch.chunk_length);
+	/* load all addresses */
+	if (sctp_load_addresses_from_init(stcb, m, iphlen,
+	    (offset + sizeof(struct sctp_init_chunk)), initack_limit, sh,
+	    NULL)) {
+		/* Huh, we should abort */
+		sctp_abort_notification(stcb, 0);
+		sctp_free_assoc(stcb->sctp_ep, stcb);
+		return (-1);
+	}
+	if (op_err) {
+		sctp_queue_op_err(stcb, op_err);
+		/* queuing will steal away the mbuf chain to the out queue */
+		op_err = NULL;
+	}
+	/* extract the cookie and queue it to "echo" it back... */
+	stcb->asoc.overall_error_count = 0;
+	net->error_count = 0;
+	retval = sctp_send_cookie_echo(m, offset, stcb, net);
+	if (retval < 0) {
+		/*
+		 * No cookie, we probably should send a op error.
+		 * But in any case if there is no cookie in the INIT-ACK,
+		 * we can abandon the peer, its broke.
+		 */
+		if (retval == -3) {
+			/* We abort with an error of missing mandatory param */
+			struct mbuf *op_err;
+			op_err =
+			    sctp_generate_invmanparam(SCTP_CAUSE_MISS_PARAM);
+			if (op_err) {
+				/*
+				 * Expand beyond to include the mandatory
+				 * param cookie
+				 */
+				struct sctp_inv_mandatory_param *mp;
+				op_err->m_len =
+				    sizeof(struct sctp_inv_mandatory_param);
+				mp = mtod(op_err,
+				    struct sctp_inv_mandatory_param *);
+				/* Subtract the reserved param */
+				mp->length =
+				    htons(sizeof(struct sctp_inv_mandatory_param) - 2);
+				mp->num_param = htonl(1);
+				mp->param = htons(SCTP_STATE_COOKIE);
+				mp->resv = 0;
+			}
+			sctp_abort_association(stcb->sctp_ep, stcb, m, iphlen,
+			    sh, op_err);
+		}
+		return (retval);
+	}
+
+	/*
+	 * Cancel the INIT timer, We do this first before queueing
+	 * the cookie. We always cancel at the primary to assue that
+	 * we are canceling the timer started by the INIT which always
+	 * goes to the primary.
+	 */
+	sctp_timer_stop(SCTP_TIMER_TYPE_INIT, stcb->sctp_ep, stcb,
+	    asoc->primary_destination);
+
+	/* calculate the RTO */
+	net->RTO = sctp_calculate_rto(stcb, asoc, net, &asoc->time_entered);
+
+	return (0);
+}
+
+static void
+sctp_handle_heartbeat_ack(struct sctp_heartbeat_chunk *cp,
+    struct sctp_tcb *stcb, struct sctp_nets *net)
+{
+	struct sockaddr_storage store;
+	struct sockaddr_in *sin;
+	struct sockaddr_in6 *sin6;
+	struct sctp_nets *r_net;
+	struct timeval tv;
+
+	if (ntohs(cp->ch.chunk_length) != sizeof(struct sctp_heartbeat_chunk)) {
+		/* Invalid length */
+		return;
+	}
+
+	sin = (struct sockaddr_in *)&store;
+	sin6 = (struct sockaddr_in6 *)&store;
+
+	memset(&store, 0, sizeof(store));
+	if (cp->heartbeat.hb_info.addr_family == AF_INET &&
+	    cp->heartbeat.hb_info.addr_len == sizeof(struct sockaddr_in)) {
+		sin->sin_family = cp->heartbeat.hb_info.addr_family;
+		sin->sin_len = cp->heartbeat.hb_info.addr_len;
+		sin->sin_port = stcb->rport;
+		memcpy(&sin->sin_addr, cp->heartbeat.hb_info.address,
+		    sizeof(sin->sin_addr));
+	} else if (cp->heartbeat.hb_info.addr_family == AF_INET6 &&
+	    cp->heartbeat.hb_info.addr_len == sizeof(struct sockaddr_in6)) {
+		sin6->sin6_family = cp->heartbeat.hb_info.addr_family;
+		sin6->sin6_len = cp->heartbeat.hb_info.addr_len;
+		sin6->sin6_port = stcb->rport;
+		memcpy(&sin6->sin6_addr, cp->heartbeat.hb_info.address,
+		    sizeof(sin6->sin6_addr));
+	} else {
+#ifdef SCTP_DEBUG
+		if (sctp_debug_on & SCTP_DEBUG_INPUT1) {
+			printf("unsupported address family");
+		}
+#endif
+		return;
+	}
+	r_net = sctp_findnet(stcb, (struct sockaddr *)sin);
+	if (r_net == NULL) {
+#ifdef SCTP_DEBUG
+		if (sctp_debug_on & SCTP_DEBUG_INPUT1) {
+			printf("Huh? I can't find the address I sent it to, discard\n");
+		}
+#endif
+		return;
+	}
+	if ((r_net && (r_net->dest_state & SCTP_ADDR_UNCONFIRMED)) &&
+	    (r_net->heartbeat_random1 == cp->heartbeat.hb_info.random_value1) &&
+	    (r_net->heartbeat_random2 == cp->heartbeat.hb_info.random_value2)) {
+		/*
+		 * If the its a HB and it's random value is correct when
+		 * can confirm the destination.
+		 */
+		r_net->dest_state &= ~SCTP_ADDR_UNCONFIRMED;
+		sctp_ulp_notify(SCTP_NOTIFY_INTERFACE_CONFIRMED,
+		    stcb, 0, (void *)r_net);
+	}
+	r_net->error_count = 0;
+	r_net->hb_responded = 1;
+	tv.tv_sec = cp->heartbeat.hb_info.time_value_1;
+	tv.tv_usec = cp->heartbeat.hb_info.time_value_2;
+	if (r_net->dest_state & SCTP_ADDR_NOT_REACHABLE) {
+		r_net->dest_state = SCTP_ADDR_REACHABLE;
+		sctp_ulp_notify(SCTP_NOTIFY_INTERFACE_UP, stcb,
+		    SCTP_HEARTBEAT_SUCCESS, (void *)r_net);
+
+		/* now was it the primary? if so restore */
+		if (r_net->dest_state & SCTP_ADDR_WAS_PRIMARY) {
+			sctp_set_primary_addr(stcb, (struct sockaddr *)NULL, r_net);
+		}
+	}
+	/* Now lets do a RTO with this */
+	r_net->RTO = sctp_calculate_rto(stcb, &stcb->asoc, r_net, &tv);
+}
+
+static void
+sctp_handle_abort(struct sctp_abort_chunk *cp,
+    struct sctp_tcb *stcb, struct sctp_nets *net)
+{
+	struct sctp_inpcb *inp;
+
+#ifdef SCTP_DEBUG
+	if (sctp_debug_on & SCTP_DEBUG_INPUT2) {
+		printf("sctp_handle_abort: handling ABORT\n");
+	}
+#endif
+	if (stcb == NULL)
+		return;
+	/* verify that the destination addr is in the association */
+	/* ignore abort for addresses being deleted */
+
+	/* stop any receive timers */
+	sctp_timer_stop(SCTP_TIMER_TYPE_RECV, stcb->sctp_ep, stcb, net);
+	/* notify user of the abort and clean up... */
+	sctp_abort_notification(stcb, 0);
+	/* free the tcb */
+	inp = stcb->sctp_ep;
+	sctp_free_assoc(stcb->sctp_ep, stcb);
+#ifdef SCTP_DEBUG
+	if (sctp_debug_on & SCTP_DEBUG_INPUT2) {
+		printf("sctp_handle_abort: finished\n");
+	}
+#endif
+}
+
+static void
+sctp_handle_shutdown(struct sctp_shutdown_chunk *cp,
+    struct sctp_tcb *stcb, struct sctp_nets *net, int *abort_flag)
+{
+	struct sctp_association *asoc;
+	int some_on_streamwheel;
+
+#ifdef SCTP_DEBUG
+	if (sctp_debug_on & SCTP_DEBUG_INPUT2) {
+		printf("sctp_handle_shutdown: handling SHUTDOWN\n");
+	}
+#endif
+	if (stcb == NULL)
+		return;
+
+	if ((SCTP_GET_STATE(&stcb->asoc) == SCTP_STATE_COOKIE_WAIT) ||
+	    (SCTP_GET_STATE(&stcb->asoc) == SCTP_STATE_COOKIE_ECHOED)) {
+	    return;
+	}
+
+	if (ntohs(cp->ch.chunk_length) != sizeof(struct sctp_shutdown_chunk)) {
+		/* update current data status */
+#ifdef SCTP_DEBUG
+		if (sctp_debug_on & SCTP_DEBUG_INPUT1) {
+			printf("Warning Shutdown NOT the expected size.. skipping (%d:%d)\n",
+			       ntohs(cp->ch.chunk_length),
+			       (int)sizeof(struct sctp_shutdown_chunk));
+		}
+#endif
+		return;
+	} else {
+		sctp_update_acked(stcb, cp, net, abort_flag);
+	}
+	asoc = &stcb->asoc;
+	/* goto SHUTDOWN_RECEIVED state to block new requests */
+	if ((SCTP_GET_STATE(asoc) != SCTP_STATE_SHUTDOWN_RECEIVED) &&
+	    (SCTP_GET_STATE(asoc) != SCTP_STATE_SHUTDOWN_SENT)) {
+		asoc->state = SCTP_STATE_SHUTDOWN_RECEIVED;
+#ifdef SCTP_DEBUG
+		if (sctp_debug_on & SCTP_DEBUG_INPUT1) {
+			printf("Moving to SHUTDOWN-RECEIVED state\n");
+		}
+#endif
+		/* notify upper layer that peer has initiated a shutdown */
+		sctp_ulp_notify(SCTP_NOTIFY_PEER_SHUTDOWN, stcb, 0, NULL);
+
+		if ((stcb->sctp_ep->sctp_flags & SCTP_PCB_FLAGS_TCPTYPE) ||
+ 		    (stcb->sctp_ep->sctp_flags & SCTP_PCB_FLAGS_IN_TCPPOOL)) {
+
+			/* Set the flag so we cannot send more, we
+			 * would call the function but we don't want to
+			 * wake up the ulp necessarily.
+			 */
+#if defined(__FreeBSD__) && __FreeBSD_version >= 502115
+			stcb->sctp_ep->sctp_socket->so_rcv.sb_state |= SBS_CANTSENDMORE;
+#else
+			stcb->sctp_ep->sctp_socket->so_state |= SS_CANTSENDMORE;
+#endif
+		}
+		/* reset time */
+		SCTP_GETTIME_TIMEVAL(&asoc->time_entered);
+	}
+	if (SCTP_GET_STATE(asoc) == SCTP_STATE_SHUTDOWN_SENT) {
+		/*
+		 * stop the shutdown timer, since we WILL move
+		 * to SHUTDOWN-ACK-SENT.
+		 */
+		sctp_timer_stop(SCTP_TIMER_TYPE_SHUTDOWN, stcb->sctp_ep, stcb, net);
+	}
+	/* Now are we there yet? */
+	some_on_streamwheel = 0;
+	if (!TAILQ_EMPTY(&asoc->out_wheel)) {
+		/* Check to see if some data queued */
+		struct sctp_stream_out *outs;
+		TAILQ_FOREACH(outs, &asoc->out_wheel, next_spoke) {
+			if (!TAILQ_EMPTY(&outs->outqueue)) {
+				some_on_streamwheel = 1;
+				break;
+			}
+		}
+	}
+#ifdef SCTP_DEBUG
+	if (sctp_debug_on & SCTP_DEBUG_INPUT1) {
+		printf("some_on_streamwheel:%d send_q_empty:%d sent_q_empty:%d\n",
+		       some_on_streamwheel,
+		       !TAILQ_EMPTY(&asoc->send_queue),
+		       !TAILQ_EMPTY(&asoc->sent_queue));
+	}
+#endif
+ 	if (!TAILQ_EMPTY(&asoc->send_queue) ||
+	    !TAILQ_EMPTY(&asoc->sent_queue) ||
+	    some_on_streamwheel) {
+		/* By returning we will push more data out */
+		return;
+	} else {
+		/* no outstanding data to send, so move on... */
+		/* send SHUTDOWN-ACK */
+		sctp_send_shutdown_ack(stcb, stcb->asoc.primary_destination);
+		/* move to SHUTDOWN-ACK-SENT state */
+		asoc->state = SCTP_STATE_SHUTDOWN_ACK_SENT;
+#ifdef SCTP_DEBUG
+		if (sctp_debug_on & SCTP_DEBUG_INPUT1) {
+			printf("moving to SHUTDOWN_ACK state\n");
+		}
+#endif
+		/* start SHUTDOWN timer */
+		sctp_timer_start(SCTP_TIMER_TYPE_SHUTDOWNACK, stcb->sctp_ep,
+		    stcb, net);
+	}
+}
+
+static void
+sctp_handle_shutdown_ack(struct sctp_shutdown_ack_chunk *cp,
+    struct sctp_tcb *stcb, struct sctp_nets *net)
+{
+	struct sctp_association *asoc;
+
+#ifdef SCTP_DEBUG
+	if (sctp_debug_on & SCTP_DEBUG_INPUT2) {
+		printf("sctp_handle_shutdown_ack: handling SHUTDOWN ACK\n");
+	}
+#endif
+	if (stcb == NULL)
+		return;
+
+	asoc = &stcb->asoc;
+	/* process according to association state */
+	if ((SCTP_GET_STATE(asoc) != SCTP_STATE_SHUTDOWN_SENT) &&
+	    (SCTP_GET_STATE(asoc) != SCTP_STATE_SHUTDOWN_ACK_SENT)) {
+		/* unexpected SHUTDOWN-ACK... so ignore... */
+		return;
+	}
+	/* are the queues empty? */
+	if (!TAILQ_EMPTY(&asoc->send_queue) ||
+	    !TAILQ_EMPTY(&asoc->sent_queue) ||
+	    !TAILQ_EMPTY(&asoc->out_wheel)) {
+		sctp_report_all_outbound(stcb);
+	}
+	/* stop the timer */
+	sctp_timer_stop(SCTP_TIMER_TYPE_SHUTDOWN, stcb->sctp_ep, stcb, net);
+	/* send SHUTDOWN-COMPLETE */
+	sctp_send_shutdown_complete(stcb, net);
+	/* notify upper layer protocol */
+	sctp_ulp_notify(SCTP_NOTIFY_ASSOC_DOWN, stcb, 0, NULL);
+	if ((stcb->sctp_ep->sctp_flags & SCTP_PCB_FLAGS_TCPTYPE) ||
+	    (stcb->sctp_ep->sctp_flags & SCTP_PCB_FLAGS_IN_TCPPOOL)) {
+		stcb->sctp_ep->sctp_flags &= ~SCTP_PCB_FLAGS_CONNECTED;
+		/* Set the connected flag to disconnected */
+		stcb->sctp_ep->sctp_socket->so_snd.sb_cc = 0;
+		stcb->sctp_ep->sctp_socket->so_snd.sb_mbcnt = 0;
+		soisdisconnected(stcb->sctp_ep->sctp_socket);
+	}
+	/* free the TCB but first save off the ep */
+	sctp_free_assoc(stcb->sctp_ep, stcb);
+}
+
+/*
+ * Skip past the param header and then we will find the chunk that
+ * caused the problem. There are two possiblities ASCONF or FWD-TSN
+ * other than that and our peer must be broken.
+ */
+static void
+sctp_process_unrecog_chunk(struct sctp_tcb *stcb, struct sctp_paramhdr *phdr,
+    struct sctp_nets *net)
+{
+	struct sctp_chunkhdr *chk;
+
+	chk = (struct sctp_chunkhdr *)((caddr_t)phdr + sizeof(*phdr));
+	switch (chk->chunk_type) {
+	case SCTP_ASCONF_ACK:
+#ifdef SCTP_DEBUG
+		if (sctp_debug_on & SCTP_DEBUG_INPUT2) {
+			printf("Strange peer, snds ASCONF but does not recongnize asconf-ack?\n");
+		}
+#endif
+	case SCTP_ASCONF:
+#ifdef SCTP_DEBUG
+		if (sctp_debug_on & SCTP_DEBUG_INPUT2) {
+			printf("Peer does not support ASCONF/ASCONF-ACK chunks\n");
+		}
+#endif /* SCTP_DEBUG */
+		sctp_asconf_cleanup(stcb, net);
+		break;
+	case SCTP_FORWARD_CUM_TSN:
+		stcb->asoc.peer_supports_prsctp = 0;
+		break;
+	default:
+#ifdef SCTP_DEBUG
+		if (sctp_debug_on & SCTP_DEBUG_INPUT2) {
+			printf("Peer does not support chunk type %d(%x)??\n",
+			       chk->chunk_type, (u_int)chk->chunk_type);
+		}
+#endif
+		break;
+	}
+}
+
+/*
+ * Skip past the param header and then we will find the param that
+ * caused the problem.  There are a number of param's in a ASCONF
+ * OR the prsctp param these will turn of specific features.
+ */
+static void
+sctp_process_unrecog_param(struct sctp_tcb *stcb, struct sctp_paramhdr *phdr)
+{
+	struct sctp_paramhdr *pbad;
+
+	pbad = phdr + 1;
+	switch (ntohs(pbad->param_type)) {
+		/* pr-sctp draft */
+	case SCTP_PRSCTP_SUPPORTED:
+		stcb->asoc.peer_supports_prsctp = 0;
+		break;
+	case SCTP_SUPPORTED_CHUNK_EXT:
+		break;
+		/* draft-ietf-tsvwg-addip-sctp */
+	case SCTP_ECN_NONCE_SUPPORTED:
+		stcb->asoc.peer_supports_ecn_nonce = 0;
+		stcb->asoc.ecn_nonce_allowed = 0;
+		stcb->asoc.ecn_allowed = 0;
+		break;
+	case SCTP_ADD_IP_ADDRESS:
+	case SCTP_DEL_IP_ADDRESS:
+		stcb->asoc.peer_supports_asconf = 0;
+		break;
+	case SCTP_SET_PRIM_ADDR:
+		stcb->asoc.peer_supports_asconf_setprim = 0;
+		break;
+	case SCTP_SUCCESS_REPORT:
+	case SCTP_ERROR_CAUSE_IND:
+#ifdef SCTP_DEBUG
+		if (sctp_debug_on & SCTP_DEBUG_INPUT2) {
+			printf("Huh, the peer does not support success? or error cause?\n");
+			printf("Turning off ASCONF to this strange peer\n");
+		}
+#endif
+		stcb->asoc.peer_supports_asconf = 0;
+		stcb->asoc.peer_supports_asconf_setprim = 0;
+		break;
+	default:
+#ifdef SCTP_DEBUG
+		if (sctp_debug_on & SCTP_DEBUG_INPUT2) {
+			printf("Peer does not support base param type %d(%x)??\n",
+			    pbad->param_type, (u_int)pbad->param_type);
+		}
+#endif
+		break;
+	}
+}
+
+static int
+sctp_handle_error(struct sctp_chunkhdr *ch,
+    struct sctp_tcb *stcb, struct sctp_nets *net)
+{
+	int chklen;
+	struct sctp_paramhdr *phdr;
+	uint16_t error_type;
+	uint16_t error_len;
+	struct sctp_association *asoc;
+
+	int adjust;
+	/* parse through all of the errors and process */
+	asoc = &stcb->asoc;
+	phdr = (struct sctp_paramhdr *)((caddr_t)ch +
+	    sizeof(struct sctp_chunkhdr));
+	chklen = ntohs(ch->chunk_length) - sizeof(struct sctp_chunkhdr);
+	while ((size_t)chklen >= sizeof(struct sctp_paramhdr)) {
+		/* Process an Error Cause */
+		error_type = ntohs(phdr->param_type);
+		error_len = ntohs(phdr->param_length);
+		if ((error_len > chklen) || (error_len == 0)) {
+			/* invalid param length for this param */
+#ifdef SCTP_DEBUG
+			if (sctp_debug_on & SCTP_DEBUG_INPUT1) {
+				printf("Bogus length in error param- chunk left:%d errorlen:%d\n",
+				       chklen, error_len);
+			}
+#endif /* SCTP_DEBUG */
+			return (0);
+		}
+		switch (error_type) {
+		case SCTP_CAUSE_INV_STRM:
+		case SCTP_CAUSE_MISS_PARAM:
+		case SCTP_CAUSE_INVALID_PARAM:
+		case SCTP_CAUSE_NOUSER_DATA:
+#ifdef SCTP_DEBUG
+			if (sctp_debug_on & SCTP_DEBUG_INPUT1) {
+				printf("Software error we got a %d back? We have a bug :/ (or do they?)\n",
+				       error_type);
+			}
+#endif
+			break;
+		case SCTP_CAUSE_STALE_COOKIE:
+			/* We only act if we have echoed a cookie and are waiting. */
+			if (SCTP_GET_STATE(asoc) == SCTP_STATE_COOKIE_ECHOED) {
+				int *p;
+				p = (int *)((caddr_t)phdr + sizeof(*phdr));
+				/* Save the time doubled */
+				asoc->cookie_preserve_req = ntohl(*p) << 1;
+				asoc->stale_cookie_count++;
+				if (asoc->stale_cookie_count >
+				    asoc->max_init_times) {
+					sctp_abort_notification(stcb, 0);
+					/* now free the asoc */
+					sctp_free_assoc(stcb->sctp_ep, stcb);
+					return (-1);
+				}
+				/* blast back to INIT state */
+				asoc->state &= ~SCTP_STATE_COOKIE_ECHOED;
+				asoc->state |= SCTP_STATE_COOKIE_WAIT;
+				sctp_timer_stop(SCTP_TIMER_TYPE_COOKIE,
+				    stcb->sctp_ep, stcb, net);
+				sctp_send_initiate(stcb->sctp_ep, stcb);
+			}
+			break;
+		case SCTP_CAUSE_UNRESOLV_ADDR:
+			/*
+			 * Nothing we can do here, we don't do hostname
+			 * addresses so if the peer does not like my IPv6 (or
+			 * IPv4 for that matter) it does not matter. If they
+			 * don't support that type of address, they can NOT
+			 * possibly get that packet type... i.e. with no IPv6
+			 * you can't recieve a IPv6 packet. so we can safely
+			 * ignore this one. If we ever added support for
+			 * HOSTNAME Addresses, then we would need to do
+			 * something here.
+			 */
+			break;
+		case SCTP_CAUSE_UNRECOG_CHUNK:
+			sctp_process_unrecog_chunk(stcb, phdr, net);
+			break;
+		case SCTP_CAUSE_UNRECOG_PARAM:
+			sctp_process_unrecog_param(stcb, phdr);
+			break;
+		case SCTP_CAUSE_COOKIE_IN_SHUTDOWN:
+			/*
+			 * We ignore this since the timer will drive out a new
+			 * cookie anyway and there timer will drive us to send
+			 * a SHUTDOWN_COMPLETE. We can't send one here since
+			 * we don't have their tag.
+			 */
+			break;
+		case SCTP_CAUSE_DELETEING_LAST_ADDR:
+		case SCTP_CAUSE_OPERATION_REFUSED:
+		case SCTP_CAUSE_DELETING_SRC_ADDR:
+			/* We should NOT get these here, but in a ASCONF-ACK. */
+#ifdef SCTP_DEBUG
+			if (sctp_debug_on & SCTP_DEBUG_INPUT2) {
+				printf("Peer sends ASCONF errors in a Operational Error?<%d>?\n",
+				       error_type);
+			}
+#endif
+			break;
+		case SCTP_CAUSE_OUT_OF_RESC:
+			/*
+			 * And what, pray tell do we do with the fact
+			 * that the peer is out of resources? Not
+			 * really sure we could do anything but abort.
+			 * I suspect this should have came WITH an
+			 * abort instead of in a OP-ERROR.
+			 */
+			break;
+	 	default:
+#ifdef SCTP_DEBUG
+			if (sctp_debug_on & SCTP_DEBUG_INPUT1) {
+				/* don't know what this error cause is... */
+				printf("sctp_handle_error: unknown error type = 0x%xh\n",
+				       error_type);
+			}
+#endif /* SCTP_DEBUG */
+			break;
+		}
+		adjust = SCTP_SIZE32(error_len);
+		chklen -= adjust;
+		phdr = (struct sctp_paramhdr *)((caddr_t)phdr + adjust);
+	}
+	return (0);
+}
+
+static int
+sctp_handle_init_ack(struct mbuf *m, int iphlen, int offset, struct sctphdr *sh,
+    struct sctp_init_ack_chunk *cp, struct sctp_tcb *stcb,
+    struct sctp_nets *net)
+{
+	struct sctp_init_ack *init_ack;
+	int *state;
+	struct mbuf *op_err;
+
+#ifdef SCTP_DEBUG
+	if (sctp_debug_on & SCTP_DEBUG_INPUT2) {
+		printf("sctp_handle_init_ack: handling INIT-ACK\n");
+	}
+#endif
+	if (stcb == NULL) {
+#ifdef SCTP_DEBUG
+		if (sctp_debug_on & SCTP_DEBUG_INPUT2) {
+			printf("sctp_handle_init_ack: TCB is null\n");
+		}
+#endif
+		return (-1);
+	}
+	if (ntohs(cp->ch.chunk_length) < sizeof(struct sctp_init_ack_chunk)) {
+		/* Invalid length */
+		op_err = sctp_generate_invmanparam(SCTP_CAUSE_INVALID_PARAM);
+		sctp_abort_association(stcb->sctp_ep, stcb, m, iphlen, sh,
+		    op_err);
+		return (-1);
+	}
+	init_ack = &cp->init;
+	/* validate parameters */
+	if (init_ack->initiate_tag == 0) {
+		/* protocol error... send an abort */
+		op_err = sctp_generate_invmanparam(SCTP_CAUSE_INVALID_PARAM);
+		sctp_abort_association(stcb->sctp_ep, stcb, m, iphlen, sh,
+		    op_err);
+		return (-1);
+	}
+	if (ntohl(init_ack->a_rwnd) < SCTP_MIN_RWND) {
+		/* protocol error... send an abort */
+		op_err = sctp_generate_invmanparam(SCTP_CAUSE_INVALID_PARAM);
+		sctp_abort_association(stcb->sctp_ep, stcb, m, iphlen, sh,
+		    op_err);
+		return (-1);
+	}
+	if (init_ack->num_inbound_streams == 0) {
+		/* protocol error... send an abort */
+		op_err = sctp_generate_invmanparam(SCTP_CAUSE_INVALID_PARAM);
+		sctp_abort_association(stcb->sctp_ep, stcb, m, iphlen, sh,
+		    op_err);
+		return (-1);
+	}
+	if (init_ack->num_outbound_streams == 0) {
+		/* protocol error... send an abort */
+		op_err = sctp_generate_invmanparam(SCTP_CAUSE_INVALID_PARAM);
+		sctp_abort_association(stcb->sctp_ep, stcb, m, iphlen, sh,
+		    op_err);
+		return (-1);
+	}
+
+	/* process according to association state... */
+	state = &stcb->asoc.state;
+	switch (*state & SCTP_STATE_MASK) {
+	case SCTP_STATE_COOKIE_WAIT:
+		/* this is the expected state for this chunk */
+		/* process the INIT-ACK parameters */
+		if (stcb->asoc.primary_destination->dest_state &
+		    SCTP_ADDR_UNCONFIRMED) {
+			/*
+			 * The primary is where we sent the INIT, we can
+			 * always consider it confirmed when the INIT-ACK
+			 * is returned. Do this before we load addresses
+			 * though.
+			 */
+			stcb->asoc.primary_destination->dest_state &=
+			    ~SCTP_ADDR_UNCONFIRMED;
+			sctp_ulp_notify(SCTP_NOTIFY_INTERFACE_CONFIRMED,
+			    stcb, 0, (void *)stcb->asoc.primary_destination);
+		}
+		if (sctp_process_init_ack(m, iphlen, offset, sh, cp, stcb, net
+		    ) < 0) {
+			/* error in parsing parameters */
+#ifdef SCTP_DEBUG
+			if (sctp_debug_on & SCTP_DEBUG_INPUT2) {
+				printf("sctp_process_init_ack: error in msg, discarding\n");
+			}
+#endif
+			return (-1);
+		}
+		/* update our state */
+#ifdef SCTP_DEBUG
+		if (sctp_debug_on & SCTP_DEBUG_INPUT2) {
+			printf("moving to COOKIE-ECHOED state\n");
+		}
+#endif
+		if (*state & SCTP_STATE_SHUTDOWN_PENDING) {
+			*state = SCTP_STATE_COOKIE_ECHOED |
+				SCTP_STATE_SHUTDOWN_PENDING;
+		} else {
+			*state = SCTP_STATE_COOKIE_ECHOED;
+		}
+
+		/* reset the RTO calc */
+		stcb->asoc.overall_error_count = 0;
+		SCTP_GETTIME_TIMEVAL(&stcb->asoc.time_entered);
+		/*
+		 * collapse the init timer back in case of a exponential backoff
+		 */
+		sctp_timer_start(SCTP_TIMER_TYPE_COOKIE, stcb->sctp_ep,
+		    stcb, net);
+		/*
+		 * the send at the end of the inbound data processing will
+		 * cause the cookie to be sent
+		 */
+		break;
+	case SCTP_STATE_SHUTDOWN_SENT:
+		/* incorrect state... discard */
+		break;
+	case SCTP_STATE_COOKIE_ECHOED:
+		/* incorrect state... discard */
+		break;
+	case SCTP_STATE_OPEN:
+		/* incorrect state... discard */
+		break;
+	case SCTP_STATE_EMPTY:
+	case SCTP_STATE_INUSE:
+	default:
+		/* incorrect state... discard */
+#ifdef SCTP_DEBUG
+		if (sctp_debug_on & SCTP_DEBUG_INPUT1) {
+			printf("Leaving handle-init-ack default\n");
+		}
+#endif
+		return (-1);
+		break;
+	} /* end switch asoc state */
+#ifdef SCTP_DEBUG
+	if (sctp_debug_on & SCTP_DEBUG_INPUT1) {
+		printf("Leaving handle-init-ack end\n");
+	}
+#endif
+	return (0);
+}
+
+
+/*
+ * handle a state cookie for an existing association
+ * m: input packet mbuf chain-- assumes a pullup on IP/SCTP/COOKIE-ECHO chunk
+ *    note: this is a "split" mbuf and the cookie signature does not exist
+ * offset: offset into mbuf to the cookie-echo chunk
+ */
+static struct sctp_tcb *
+sctp_process_cookie_existing(struct mbuf *m, int iphlen, int offset,
+    struct sctphdr *sh, struct sctp_state_cookie *cookie, int cookie_len,
+    struct sctp_inpcb *inp, struct sctp_tcb *stcb, struct sctp_nets *net,
+    struct sockaddr *init_src, int *notification)
+{
+	struct sctp_association *asoc;
+	struct sctp_init_chunk *init_cp, init_buf;
+	struct sctp_init_ack_chunk *initack_cp, initack_buf;
+	int chk_length;
+	int init_offset, initack_offset;
+	int retval;
+
+	/* I know that the TCB is non-NULL from the caller */
+	asoc = &stcb->asoc;
+
+	if (SCTP_GET_STATE(asoc) == SCTP_STATE_SHUTDOWN_ACK_SENT) {
+		/* SHUTDOWN came in after sending INIT-ACK */
+		struct mbuf *op_err;
+		struct sctp_paramhdr *ph;
+
+		sctp_send_shutdown_ack(stcb, stcb->asoc.primary_destination);
+#ifdef SCTP_DEBUG
+		if (sctp_debug_on & SCTP_DEBUG_INPUT2) {
+			printf("sctp_handle_cookie: got a cookie, while shutting down!\n");
+		}
+#endif
+		MGETHDR(op_err, MB_DONTWAIT, MT_HEADER);
+		if (op_err == NULL) {
+			/* FOOBAR */
+			return (NULL);
+		}
+		/* pre-reserve some space */
+		op_err->m_data += sizeof(struct ip6_hdr);
+		op_err->m_data += sizeof(struct sctphdr);
+		op_err->m_data += sizeof(struct sctp_chunkhdr);
+		/* Set the len */
+		op_err->m_len = op_err->m_pkthdr.len = sizeof(struct sctp_paramhdr);
+		ph = mtod(op_err, struct sctp_paramhdr *);
+		ph->param_type = htons(SCTP_CAUSE_COOKIE_IN_SHUTDOWN);
+		ph->param_length = htons(sizeof(struct sctp_paramhdr));
+		sctp_send_operr_to(m, iphlen, op_err, cookie->peers_vtag);
+		return (NULL);
+	}
+	/*
+	 * find and validate the INIT chunk in the cookie (peer's info)
+	 * the INIT should start after the cookie-echo header struct
+	 * (chunk header, state cookie header struct)
+	 */
+	init_offset = offset += sizeof(struct sctp_cookie_echo_chunk);
+
+	init_cp = (struct sctp_init_chunk *)
+	    sctp_m_getptr(m, init_offset, sizeof(struct sctp_init_chunk),
+	    (u_int8_t *)&init_buf);
+	if (init_cp == NULL) {
+		/* could not pull a INIT chunk in cookie */
+#ifdef SCTP_DEBUG
+		if (sctp_debug_on & SCTP_DEBUG_INPUT2) {
+			printf("process_cookie_existing: could not pull INIT chunk hdr\n");
+		}
+#endif /* SCTP_DEBUG */
+		return (NULL);
+	}
+	chk_length = ntohs(init_cp->ch.chunk_length);
+	if (init_cp->ch.chunk_type != SCTP_INITIATION) {
+#ifdef SCTP_DEBUG
+		if (sctp_debug_on & SCTP_DEBUG_INPUT2) {
+			printf("process_cookie_existing: could not find INIT chunk!\n");
+		}
+#endif /* SCTP_DEBUG */
+		return (NULL);
+	}
+
+	/*
+	 * find and validate the INIT-ACK chunk in the cookie (my info)
+	 * the INIT-ACK follows the INIT chunk
+	 */
+	initack_offset = init_offset + SCTP_SIZE32(chk_length);
+	initack_cp = (struct sctp_init_ack_chunk *)
+	    sctp_m_getptr(m, initack_offset, sizeof(struct sctp_init_ack_chunk),
+	    (u_int8_t *)&initack_buf);
+	if (initack_cp == NULL) {
+		/* could not pull INIT-ACK chunk in cookie */
+#ifdef SCTP_DEBUG
+		if (sctp_debug_on & SCTP_DEBUG_INPUT2) {
+			printf("process_cookie_existing: could not pull INIT-ACK chunk hdr\n");
+		}
+#endif /* SCTP_DEBUG */
+		return (NULL);
+	}
+	chk_length = ntohs(initack_cp->ch.chunk_length);
+	if (initack_cp->ch.chunk_type != SCTP_INITIATION_ACK) {
+#ifdef SCTP_DEBUG
+		if (sctp_debug_on & SCTP_DEBUG_INPUT2) {
+			printf("process_cookie_existing: could not find INIT-ACK chunk!\n");
+		}
+#endif /* SCTP_DEBUG */
+		return (NULL);
+	}
+	if ((ntohl(initack_cp->init.initiate_tag) == asoc->my_vtag) &&
+	    (ntohl(init_cp->init.initiate_tag) == asoc->peer_vtag)) {
+		/*
+		 * case D in Section 5.2.4 Table 2: MMAA
+		 * process accordingly to get into the OPEN state
+		 */
+		switch SCTP_GET_STATE(asoc) {
+		case SCTP_STATE_COOKIE_WAIT:
+			/*
+			 * INIT was sent, but got got a COOKIE_ECHO with
+			 * the correct tags... just accept it...
+			 */
+			/* First we must process the INIT !! */
+			retval = sctp_process_init(init_cp, stcb, net);
+			if (retval < 0) {
+#ifdef SCTP_DEBUG
+				printf("process_cookie_existing: INIT processing failed\n");
+#endif
+				return (NULL);
+			}
+			/* intentional fall through to below... */
+
+		case SCTP_STATE_COOKIE_ECHOED:
+			/* Duplicate INIT case */
+			/* we have already processed the INIT so no problem */
+			sctp_timer_stop(SCTP_TIMER_TYPE_HEARTBEAT, inp, stcb,
+			    net);
+			sctp_timer_stop(SCTP_TIMER_TYPE_INIT, inp, stcb, net);
+			sctp_timer_stop(SCTP_TIMER_TYPE_COOKIE, inp, stcb,
+			    net);
+			/* update current state */
+			if (asoc->state & SCTP_STATE_SHUTDOWN_PENDING) {
+				asoc->state = SCTP_STATE_OPEN |
+				    SCTP_STATE_SHUTDOWN_PENDING;
+			} else if ((asoc->state & SCTP_STATE_SHUTDOWN_SENT) == 0) {
+				/* if ok, move to OPEN state */
+				asoc->state = SCTP_STATE_OPEN;
+			}
+			if (((stcb->sctp_ep->sctp_flags & SCTP_PCB_FLAGS_TCPTYPE) ||
+			    (stcb->sctp_ep->sctp_flags & SCTP_PCB_FLAGS_IN_TCPPOOL)) &&
+			    (!(stcb->sctp_ep->sctp_flags & SCTP_PCB_FLAGS_ACCEPTING))) {
+				/*
+				 * Here is where collision would go if we did a
+				 * connect() and instead got a
+				 * init/init-ack/cookie done before the
+				 * init-ack came back..
+				 */
+				stcb->sctp_ep->sctp_flags |=
+				    SCTP_PCB_FLAGS_CONNECTED;
+				soisconnected(stcb->sctp_ep->sctp_socket);
+			}
+			/* notify upper layer */
+			*notification = SCTP_NOTIFY_ASSOC_UP;
+			sctp_timer_start(SCTP_TIMER_TYPE_HEARTBEAT, inp, stcb,
+			    net);
+			/*
+			 * since we did not send a HB make sure we don't double
+			 * things
+			 */
+			net->hb_responded = 1;
+
+			if (stcb->asoc.sctp_autoclose_ticks &&
+			    (inp->sctp_flags & SCTP_PCB_FLAGS_AUTOCLOSE)) {
+				sctp_timer_start(SCTP_TIMER_TYPE_AUTOCLOSE,
+				    inp, stcb, NULL);
+			}
+			break;
+		default:
+			/*
+			 * we're in the OPEN state (or beyond), so peer
+			 * must have simply lost the COOKIE-ACK
+			 */
+			break;
+		} /* end switch */
+
+		/*
+		 * We ignore the return code here.. not sure if we should
+		 * somehow abort.. but we do have an existing asoc. This
+		 * really should not fail.
+		 */
+		if (sctp_load_addresses_from_init(stcb, m, iphlen,
+		    init_offset + sizeof(struct sctp_init_chunk),
+		    initack_offset, sh, init_src)) {
+#ifdef SCTP_DEBUG
+			if (sctp_debug_on & SCTP_DEBUG_INPUT1) {
+				printf("Weird cookie load_address failure on cookie existing - 1\n");
+			}
+#endif
+			return (NULL);
+		}
+
+		/* respond with a COOKIE-ACK */
+		sctp_send_cookie_ack(stcb);
+		return (stcb);
+	} /* end if */
+	if (ntohl(initack_cp->init.initiate_tag) != asoc->my_vtag &&
+	    ntohl(init_cp->init.initiate_tag) == asoc->peer_vtag &&
+	    cookie->tie_tag_my_vtag == 0 &&
+	    cookie->tie_tag_peer_vtag == 0) {
+		/*
+		 * case C in Section 5.2.4 Table 2: XMOO
+		 * silently discard
+		 */
+		return (NULL);
+	}
+	if (ntohl(initack_cp->init.initiate_tag) == asoc->my_vtag &&
+	    (ntohl(init_cp->init.initiate_tag) != asoc->peer_vtag ||
+	     init_cp->init.initiate_tag == 0)) {
+		/*
+		 * case B in Section 5.2.4 Table 2: MXAA or MOAA
+		 * my info should be ok, re-accept peer info
+		 */
+		sctp_timer_stop(SCTP_TIMER_TYPE_HEARTBEAT, inp, stcb, net);
+		sctp_timer_stop(SCTP_TIMER_TYPE_INIT, inp, stcb, net);
+		sctp_timer_stop(SCTP_TIMER_TYPE_COOKIE, inp, stcb, net);
+		sctp_timer_start(SCTP_TIMER_TYPE_HEARTBEAT, inp, stcb, net);
+		/*
+		 * since we did not send a HB make sure we don't double things
+		 */
+		net->hb_responded = 1;
+		if (stcb->asoc.sctp_autoclose_ticks &&
+		    (inp->sctp_flags & SCTP_PCB_FLAGS_AUTOCLOSE)) {
+			sctp_timer_start(SCTP_TIMER_TYPE_AUTOCLOSE, inp, stcb,
+			    NULL);
+		}
+		asoc->my_rwnd = ntohl(initack_cp->init.a_rwnd);
+		asoc->pre_open_streams =
+		    ntohs(initack_cp->init.num_outbound_streams);
+		asoc->init_seq_number = ntohl(initack_cp->init.initial_tsn);
+		asoc->sending_seq = asoc->asconf_seq_out = asoc->str_reset_seq_out =
+		    asoc->init_seq_number;
+		asoc->t3timeout_highest_marked = asoc->asconf_seq_out;
+		asoc->last_cwr_tsn = asoc->init_seq_number - 1;
+		asoc->asconf_seq_in = asoc->last_acked_seq = asoc->init_seq_number - 1;
+		asoc->str_reset_seq_in = asoc->init_seq_number;
+		asoc->advanced_peer_ack_point = asoc->last_acked_seq;
+
+		/* process the INIT info (peer's info) */
+		retval = sctp_process_init(init_cp, stcb, net);
+		if (retval < 0) {
+#ifdef SCTP_DEBUG
+			if (sctp_debug_on & SCTP_DEBUG_INPUT1) {
+				printf("process_cookie_existing: INIT processing failed\n");
+			}
+#endif
+			return (NULL);
+		}
+		if (sctp_load_addresses_from_init(stcb, m, iphlen,
+		    init_offset + sizeof(struct sctp_init_chunk),
+		    initack_offset, sh, init_src)) {
+#ifdef SCTP_DEBUG
+			if (sctp_debug_on & SCTP_DEBUG_INPUT1) {
+				printf("Weird cookie load_address failure on cookie existing - 2\n");
+			}
+#endif
+			return (NULL);
+		}
+
+		if ((asoc->state & SCTP_STATE_COOKIE_WAIT) ||
+		    (asoc->state & SCTP_STATE_COOKIE_ECHOED)) {
+			*notification = SCTP_NOTIFY_ASSOC_UP;
+
+			if (((stcb->sctp_ep->sctp_flags & SCTP_PCB_FLAGS_TCPTYPE) ||
+			     (stcb->sctp_ep->sctp_flags & SCTP_PCB_FLAGS_IN_TCPPOOL)) &&
+			    !(stcb->sctp_ep->sctp_flags & SCTP_PCB_FLAGS_ACCEPTING)) {
+				stcb->sctp_ep->sctp_flags |=
+				    SCTP_PCB_FLAGS_CONNECTED;
+				soisconnected(stcb->sctp_ep->sctp_socket);
+			}
+		}
+		if (asoc->state & SCTP_STATE_SHUTDOWN_PENDING) {
+			asoc->state = SCTP_STATE_OPEN |
+			    SCTP_STATE_SHUTDOWN_PENDING;
+		} else {
+			asoc->state = SCTP_STATE_OPEN;
+		}
+		sctp_send_cookie_ack(stcb);
+		return (stcb);
+	}
+
+	if ((ntohl(initack_cp->init.initiate_tag) != asoc->my_vtag &&
+	     ntohl(init_cp->init.initiate_tag) != asoc->peer_vtag) &&
+	    cookie->tie_tag_my_vtag == asoc->my_vtag_nonce &&
+	    cookie->tie_tag_peer_vtag == asoc->peer_vtag_nonce &&
+	    cookie->tie_tag_peer_vtag != 0) {
+		/*
+		 * case A in Section 5.2.4 Table 2: XXMM (peer restarted)
+		 */
+		sctp_timer_stop(SCTP_TIMER_TYPE_INIT, inp, stcb, net);
+		sctp_timer_stop(SCTP_TIMER_TYPE_COOKIE, inp, stcb, net);
+		sctp_timer_stop(SCTP_TIMER_TYPE_HEARTBEAT, inp, stcb, net);
+
+		/* notify upper layer */
+		*notification = SCTP_NOTIFY_ASSOC_RESTART;
+
+		/* send up all the data */
+		sctp_report_all_outbound(stcb);
+
+		/* process the INIT-ACK info (my info) */
+		asoc->my_vtag = ntohl(initack_cp->init.initiate_tag);
+		asoc->my_rwnd = ntohl(initack_cp->init.a_rwnd);
+		asoc->pre_open_streams =
+		    ntohs(initack_cp->init.num_outbound_streams);
+		asoc->init_seq_number = ntohl(initack_cp->init.initial_tsn);
+		asoc->sending_seq = asoc->asconf_seq_out = asoc->str_reset_seq_out =
+		    asoc->init_seq_number;
+		asoc->t3timeout_highest_marked = asoc->asconf_seq_out;
+		asoc->last_cwr_tsn = asoc->init_seq_number - 1;
+		asoc->asconf_seq_in = asoc->last_acked_seq = asoc->init_seq_number - 1;
+		asoc->str_reset_seq_in = asoc->init_seq_number;
+
+		asoc->advanced_peer_ack_point = asoc->last_acked_seq;
+		if (asoc->mapping_array)
+			memset(asoc->mapping_array, 0,
+			    asoc->mapping_array_size);
+		/* process the INIT info (peer's info) */
+		retval = sctp_process_init(init_cp, stcb, net);
+		if (retval < 0) {
+#ifdef SCTP_DEBUG
+			if (sctp_debug_on & SCTP_DEBUG_INPUT1) {
+				printf("process_cookie_existing: INIT processing failed\n");
+			}
+#endif
+			return (NULL);
+		}
+
+		sctp_timer_start(SCTP_TIMER_TYPE_HEARTBEAT, inp, stcb, net);
+		/*
+		 * since we did not send a HB make sure we don't double things
+		 */
+		net->hb_responded = 1;
+
+		if (sctp_load_addresses_from_init(stcb, m, iphlen,
+		    init_offset + sizeof(struct sctp_init_chunk),
+		    initack_offset, sh, init_src)) {
+#ifdef SCTP_DEBUG
+			if (sctp_debug_on & SCTP_DEBUG_INPUT1) {
+				printf("Weird cookie load_address failure on cookie existing - 3\n");
+			}
+#endif
+			return (NULL);
+		}
+
+		if (asoc->state & SCTP_STATE_SHUTDOWN_PENDING) {
+			asoc->state = SCTP_STATE_OPEN |
+			    SCTP_STATE_SHUTDOWN_PENDING;
+		} else if (!(asoc->state & SCTP_STATE_SHUTDOWN_SENT)) {
+			/* move to OPEN state, if not in SHUTDOWN_SENT */
+			asoc->state = SCTP_STATE_OPEN;
+		}
+		/* respond with a COOKIE-ACK */
+		sctp_send_cookie_ack(stcb);
+
+		return (stcb);
+	}
+	/* all other cases... */
+	return (NULL);
+}
+
+/*
+ * handle a state cookie for a new association
+ * m: input packet mbuf chain-- assumes a pullup on IP/SCTP/COOKIE-ECHO chunk
+ *    note: this is a "split" mbuf and the cookie signature does not exist
+ * offset: offset into mbuf to the cookie-echo chunk
+ * length: length of the cookie chunk
+ * to: where the init was from
+ * returns a new TCB
+ */
+static struct sctp_tcb *
+sctp_process_cookie_new(struct mbuf *m, int iphlen, int offset,
+    struct sctphdr *sh, struct sctp_state_cookie *cookie, int cookie_len,
+    struct sctp_inpcb *inp, struct sctp_nets **netp,
+    struct sockaddr *init_src, int *notification)
+{
+	struct sctp_tcb *stcb;
+	struct sctp_init_chunk *init_cp, init_buf;
+	struct sctp_init_ack_chunk *initack_cp, initack_buf;
+	struct sockaddr_storage sa_store;
+	struct sockaddr *initack_src = (struct sockaddr *)&sa_store;
+	struct sockaddr_in *sin;
+	struct sockaddr_in6 *sin6;
+	struct sctp_association *asoc;
+	int chk_length;
+	int init_offset, initack_offset, initack_limit;
+	int retval;
+	int error = 0;
+	u_int32_t old_tag;
+	/*
+	 * find and validate the INIT chunk in the cookie (peer's info)
+	 * the INIT should start after the cookie-echo header struct
+	 * (chunk header, state cookie header struct)
+	 */
+	init_offset = offset + sizeof(struct sctp_cookie_echo_chunk);
+	init_cp = (struct sctp_init_chunk *)
+	    sctp_m_getptr(m, init_offset, sizeof(struct sctp_init_chunk),
+	    (u_int8_t *)&init_buf);
+	if (init_cp == NULL) {
+		/* could not pull a INIT chunk in cookie */
+#ifdef SCTP_DEBUG
+		if (sctp_debug_on & SCTP_DEBUG_INPUT1) {
+			printf("process_cookie_new: could not pull INIT chunk hdr\n");
+		}
+#endif /* SCTP_DEBUG */
+		return (NULL);
+	}
+	chk_length = ntohs(init_cp->ch.chunk_length);
+	if (init_cp->ch.chunk_type != SCTP_INITIATION) {
+#ifdef SCTP_DEBUG
+		if (sctp_debug_on & SCTP_DEBUG_INPUT1) {
+			printf("HUH? process_cookie_new: could not find INIT chunk!\n");
+		}
+#endif /* SCTP_DEBUG */
+		return (NULL);
+	}
+
+	initack_offset = init_offset + SCTP_SIZE32(chk_length);
+	/*
+	 * find and validate the INIT-ACK chunk in the cookie (my info)
+	 * the INIT-ACK follows the INIT chunk
+	 */
+	initack_cp = (struct sctp_init_ack_chunk *)
+	    sctp_m_getptr(m, initack_offset, sizeof(struct sctp_init_ack_chunk),
+	    (u_int8_t *)&initack_buf);
+	if (initack_cp == NULL) {
+		/* could not pull INIT-ACK chunk in cookie */
+#ifdef SCTP_DEBUG
+		if (sctp_debug_on & SCTP_DEBUG_INPUT1) {
+			printf("process_cookie_new: could not pull INIT-ACK chunk hdr\n");
+		}
+#endif /* SCTP_DEBUG */
+		return (NULL);
+	}
+	chk_length = ntohs(initack_cp->ch.chunk_length);
+	if (initack_cp->ch.chunk_type != SCTP_INITIATION_ACK) {
+#ifdef SCTP_DEBUG
+		if (sctp_debug_on & SCTP_DEBUG_INPUT2) {
+			u_int8_t *pp;
+			pp = (u_int8_t *)initack_cp;
+			printf("process_cookie_new: could not find INIT-ACK chunk!\n");
+			printf("Found bytes %x %x %x %x at postion %d\n",
+			    (u_int)pp[0], (u_int)pp[1], (u_int)pp[2],
+			    (u_int)pp[3], initack_offset);
+		}
+#endif /* SCTP_DEBUG */
+		return (NULL);
+	}
+	initack_limit = initack_offset + SCTP_SIZE32(chk_length);
+
+	/*
+	 * now that we know the INIT/INIT-ACK are in place,
+	 * create a new TCB and popluate
+	 */
+ 	stcb = sctp_aloc_assoc(inp, init_src, 0, &error, ntohl(initack_cp->init.initiate_tag));
+	if (stcb == NULL) {
+		struct mbuf *op_err;
+		/* memory problem? */
+#ifdef SCTP_DEBUG
+		if (sctp_debug_on & SCTP_DEBUG_INPUT1) {
+			printf("process_cookie_new: no room for another TCB!\n");
+		}
+#endif /* SCTP_DEBUG */
+		op_err = sctp_generate_invmanparam(SCTP_CAUSE_OUT_OF_RESC);
+		sctp_abort_association(inp, (struct sctp_tcb *)NULL, m, iphlen,
+		    sh, op_err);
+		return (NULL);
+	}
+
+	/* get the correct sctp_nets */
+	*netp = sctp_findnet(stcb, init_src);
+	asoc = &stcb->asoc;
+	/* get scope variables out of cookie */
+	asoc->ipv4_local_scope = cookie->ipv4_scope;
+	asoc->site_scope = cookie->site_scope;
+	asoc->local_scope = cookie->local_scope;
+	asoc->loopback_scope = cookie->loopback_scope;
+
+	if ((asoc->ipv4_addr_legal != cookie->ipv4_addr_legal) ||
+	    (asoc->ipv6_addr_legal != cookie->ipv6_addr_legal)) {
+		struct mbuf *op_err;
+		/*
+		 * Houston we have a problem. The EP changed while the cookie
+		 * was in flight. Only recourse is to abort the association.
+		 */
+		op_err = sctp_generate_invmanparam(SCTP_CAUSE_OUT_OF_RESC);
+		sctp_abort_association(inp, (struct sctp_tcb *)NULL, m, iphlen,
+		    sh, op_err);
+		return (NULL);
+	}
+
+	/* process the INIT-ACK info (my info) */
+	old_tag = asoc->my_vtag;
+	asoc->my_vtag = ntohl(initack_cp->init.initiate_tag);
+	asoc->my_rwnd = ntohl(initack_cp->init.a_rwnd);
+	asoc->pre_open_streams = ntohs(initack_cp->init.num_outbound_streams);
+	asoc->init_seq_number = ntohl(initack_cp->init.initial_tsn);
+	asoc->sending_seq = asoc->asconf_seq_out = asoc->str_reset_seq_out = asoc->init_seq_number;
+	asoc->t3timeout_highest_marked = asoc->asconf_seq_out;
+	asoc->last_cwr_tsn = asoc->init_seq_number - 1;
+	asoc->asconf_seq_in = asoc->last_acked_seq = asoc->init_seq_number - 1;
+	asoc->str_reset_seq_in = asoc->init_seq_number;
+
+	asoc->advanced_peer_ack_point = asoc->last_acked_seq;
+
+	/* process the INIT info (peer's info) */
+	retval = sctp_process_init(init_cp, stcb, *netp);
+	if (retval < 0) {
+#ifdef SCTP_DEBUG
+		if (sctp_debug_on & SCTP_DEBUG_INPUT1) {
+			printf("process_cookie_new: INIT processing failed\n");
+		}
+#endif
+		sctp_free_assoc(inp, stcb);
+		return (NULL);
+	}
+	/* load all addresses */
+	if (sctp_load_addresses_from_init(stcb, m, iphlen,
+	    init_offset + sizeof(struct sctp_init_chunk), initack_offset, sh,
+	    init_src)) {
+		sctp_free_assoc(inp, stcb);
+		return (NULL);
+	}
+
+	/* update current state */
+#ifdef SCTP_DEBUG
+	if (sctp_debug_on & SCTP_DEBUG_INPUT1) {
+		printf("moving to OPEN state\n");
+	}
+#endif
+	if (asoc->state & SCTP_STATE_SHUTDOWN_PENDING) {
+		asoc->state = SCTP_STATE_OPEN | SCTP_STATE_SHUTDOWN_PENDING;
+	} else {
+		asoc->state = SCTP_STATE_OPEN;
+	}
+	/* calculate the RTT */
+	(*netp)->RTO = sctp_calculate_rto(stcb, asoc, *netp,
+	    &cookie->time_entered);
+
+	/*
+	 * if we're doing ASCONFs, check to see if we have any new
+	 * local addresses that need to get added to the peer (eg.
+	 * addresses changed while cookie echo in flight).  This needs
+	 * to be done after we go to the OPEN state to do the correct
+	 * asconf processing.
+	 * else, make sure we have the correct addresses in our lists
+	 */
+
+	/* warning, we re-use sin, sin6, sa_store here! */
+	/* pull in local_address (our "from" address) */
+	if (cookie->laddr_type == SCTP_IPV4_ADDRESS) {
+		/* source addr is IPv4 */
+		sin = (struct sockaddr_in *)initack_src;
+		memset(sin, 0, sizeof(*sin));
+		sin->sin_family = AF_INET;
+		sin->sin_len = sizeof(struct sockaddr_in);
+		sin->sin_addr.s_addr = cookie->laddress[0];
+	} else if (cookie->laddr_type == SCTP_IPV6_ADDRESS) {
+		/* source addr is IPv6 */
+		sin6 = (struct sockaddr_in6 *)initack_src;
+		memset(sin6, 0, sizeof(*sin6));
+		sin6->sin6_family = AF_INET6;
+		sin6->sin6_len = sizeof(struct sockaddr_in6);
+		sin6->sin6_scope_id = cookie->scope_id;
+		memcpy(&sin6->sin6_addr, cookie->laddress,
+		    sizeof(sin6->sin6_addr));
+	} else {
+		sctp_free_assoc(inp, stcb);
+		return (NULL);
+	}
+
+	sctp_check_address_list(stcb, m, initack_offset +
+	    sizeof(struct sctp_init_ack_chunk), initack_limit,
+	    initack_src, cookie->local_scope, cookie->site_scope,
+	    cookie->ipv4_scope, cookie->loopback_scope);
+
+
+	/* set up to notify upper layer */
+	*notification = SCTP_NOTIFY_ASSOC_UP;
+	if (((stcb->sctp_ep->sctp_flags & SCTP_PCB_FLAGS_TCPTYPE) ||
+	     (stcb->sctp_ep->sctp_flags & SCTP_PCB_FLAGS_IN_TCPPOOL))  &&
+	    !(stcb->sctp_ep->sctp_flags & SCTP_PCB_FLAGS_ACCEPTING)) {
+		/*
+		 * This is an endpoint that called connect()
+		 * how it got a cookie that is NEW is a bit of
+		 * a mystery. It must be that the INIT was sent, but
+		 * before it got there.. a complete INIT/INIT-ACK/COOKIE
+		 * arrived. But of course then it should have went to
+		 * the other code.. not here.. oh well.. a bit of protection
+		 * is worth having..
+		 */
+		stcb->sctp_ep->sctp_flags |= SCTP_PCB_FLAGS_CONNECTED;
+		soisconnected(stcb->sctp_ep->sctp_socket);
+		sctp_timer_start(SCTP_TIMER_TYPE_HEARTBEAT, inp, stcb, *netp);
+	} else if ((stcb->sctp_ep->sctp_flags & SCTP_PCB_FLAGS_TCPTYPE) &&
+		   (stcb->sctp_ep->sctp_flags & SCTP_PCB_FLAGS_ACCEPTING)) {
+		/*
+		 * We don't want to do anything with this
+		 * one. Since it is the listening guy. The timer will
+		 * get started for accepted connections in the caller.
+		 */
+		;
+	} else {
+		sctp_timer_start(SCTP_TIMER_TYPE_HEARTBEAT, inp, stcb, *netp);
+	}
+	/* since we did not send a HB make sure we don't double things */
+	(*netp)->hb_responded = 1;
+
+	if (stcb->asoc.sctp_autoclose_ticks &&
+	    (inp->sctp_flags & SCTP_PCB_FLAGS_AUTOCLOSE)) {
+		sctp_timer_start(SCTP_TIMER_TYPE_AUTOCLOSE, inp, stcb, NULL);
+	}
+
+	/* respond with a COOKIE-ACK */
+	sctp_send_cookie_ack(stcb);
+
+	return (stcb);
+}
+
+
+/*
+ * handles a COOKIE-ECHO message
+ * stcb: modified to either a new or left as existing (non-NULL) TCB
+ */
+static struct mbuf *
+sctp_handle_cookie_echo(struct mbuf *m, int iphlen, int offset,
+    struct sctphdr *sh, struct sctp_cookie_echo_chunk *cp,
+    struct sctp_inpcb **inp_p, struct sctp_tcb **stcb, struct sctp_nets **netp)
+{
+	struct sctp_state_cookie *cookie;
+	struct sockaddr_in6 sin6;
+	struct sockaddr_in sin;
+	struct sctp_tcb *l_stcb=*stcb;
+	struct sctp_inpcb *l_inp;
+	struct sockaddr *to;
+	struct sctp_pcb *ep;
+	struct mbuf *m_sig;
+	uint8_t calc_sig[SCTP_SIGNATURE_SIZE], tmp_sig[SCTP_SIGNATURE_SIZE];
+	uint8_t *sig;
+	uint8_t cookie_ok = 0;
+	unsigned int size_of_pkt, sig_offset, cookie_offset;
+	unsigned int cookie_len;
+	struct timeval now;
+	struct timeval time_expires;
+	struct sockaddr_storage dest_store;
+	struct sockaddr *localep_sa = (struct sockaddr *)&dest_store;
+	struct ip *iph;
+	int notification = 0;
+	struct sctp_nets *netl;
+	int had_a_existing_tcb = 0;
+
+#ifdef SCTP_DEBUG
+	if (sctp_debug_on & SCTP_DEBUG_INPUT2) {
+		printf("sctp_handle_cookie: handling COOKIE-ECHO\n");
+	}
+#endif
+
+	if (inp_p == NULL) {
+#ifdef SCTP_DEBUG
+		if (sctp_debug_on & SCTP_DEBUG_INPUT1) {
+			printf("sctp_handle_cookie: null inp_p!\n");
+		}
+#endif
+		return (NULL);
+	}
+	/* First get the destination address setup too. */
+	iph = mtod(m, struct ip *);
+	if (iph->ip_v == IPVERSION) {
+		/* its IPv4 */
+		struct sockaddr_in *sin;
+		sin = (struct sockaddr_in *)(localep_sa);
+		memset(sin, 0, sizeof(*sin));
+		sin->sin_family = AF_INET;
+		sin->sin_len = sizeof(*sin);
+		sin->sin_port = sh->dest_port;
+		sin->sin_addr.s_addr = iph->ip_dst.s_addr ;
+	} else if (iph->ip_v == (IPV6_VERSION >> 4)) {
+		/* its IPv6 */
+		struct ip6_hdr *ip6;
+		struct sockaddr_in6 *sin6;
+		sin6 = (struct sockaddr_in6 *)(localep_sa);
+		memset(sin6, 0, sizeof(*sin6));
+		sin6->sin6_family = AF_INET6;
+		sin6->sin6_len = sizeof(struct sockaddr_in6);
+		ip6 = mtod(m, struct ip6_hdr *);
+		sin6->sin6_port = sh->dest_port;
+		sin6->sin6_addr = ip6->ip6_dst;
+	} else {
+		return (NULL);
+	}
+
+	cookie = &cp->cookie;
+	cookie_offset = offset + sizeof(struct sctp_chunkhdr);
+	cookie_len = ntohs(cp->ch.chunk_length);
+
+	if ((cookie->peerport != sh->src_port) &&
+	    (cookie->myport != sh->dest_port) &&
+	    (cookie->my_vtag != sh->v_tag)) {
+		/*
+		 * invalid ports or bad tag.  Note that we always leave
+		 * the v_tag in the header in network order and when we
+		 * stored it in the my_vtag slot we also left it in network
+		 * order. This maintians the match even though it may be in
+		 * the opposite byte order of the machine :->
+		 */
+		return (NULL);
+	}
+
+	/* compute size of packet */
+	if (m->m_flags & M_PKTHDR) {
+		size_of_pkt = m->m_pkthdr.len;
+	} else {
+		/* Should have a pkt hdr really */
+		struct mbuf *mat;
+		mat = m;
+		size_of_pkt = 0;
+		while (mat != NULL) {
+			size_of_pkt += mat->m_len;
+			mat = mat->m_next;
+		}
+	}
+	if (cookie_len > size_of_pkt ||
+	    cookie_len < sizeof(struct sctp_cookie_echo_chunk) +
+	    sizeof(struct sctp_init_chunk) +
+	    sizeof(struct sctp_init_ack_chunk) + SCTP_SIGNATURE_SIZE) {
+		/* cookie too long!  or too small */
+#ifdef SCTP_DEBUG
+		if (sctp_debug_on & SCTP_DEBUG_INPUT2) {
+			printf("sctp_handle_cookie: cookie_len=%u, pkt size=%u\n", cookie_len, size_of_pkt);
+		}
+#endif /* SCTP_DEBUG */
+		return (NULL);
+	}
+	/*
+	 * split off the signature into its own mbuf (since it
+	 * should not be calculated in the sctp_hash_digest_m() call).
+	 */
+	sig_offset = offset + cookie_len - SCTP_SIGNATURE_SIZE;
+	if (sig_offset > size_of_pkt) {
+		/* packet not correct size! */
+		/* XXX this may already be accounted for earlier... */
+#ifdef SCTP_DEBUG
+		if (sctp_debug_on & SCTP_DEBUG_INPUT2) {
+			printf("sctp_handle_cookie: sig offset=%u, pkt size=%u\n", sig_offset, size_of_pkt);
+		}
+#endif
+		return (NULL);
+	}
+
+	m_sig = m_split(m, sig_offset, MB_DONTWAIT);
+	if (m_sig == NULL) {
+		/* out of memory or ?? */
+#ifdef SCTP_DEBUG
+		if (sctp_debug_on & SCTP_DEBUG_INPUT1) {
+			printf("sctp_handle_cookie: couldn't m_split the signature\n");
+		}
+#endif
+		return (NULL);
+	}
+	/*
+	 * compute the signature/digest for the cookie
+	 */
+	ep = &(*inp_p)->sctp_ep;
+	l_inp = *inp_p;
+	if (l_stcb) {
+		SCTP_TCB_UNLOCK(l_stcb);
+	}
+	SCTP_INP_RLOCK(l_inp);
+	if (l_stcb) {
+		SCTP_TCB_LOCK(l_stcb);
+	}
+	/* which cookie is it? */
+	if ((cookie->time_entered.tv_sec < (long)ep->time_of_secret_change) &&
+	    (ep->current_secret_number != ep->last_secret_number)) {
+		/* it's the old cookie */
+#ifdef SCTP_DEBUG
+		if (sctp_debug_on & SCTP_DEBUG_INPUT2) {
+			printf("sctp_handle_cookie: old cookie sig\n");
+		}
+#endif
+		sctp_hash_digest_m((char *)ep->secret_key[(int)ep->last_secret_number],
+		    SCTP_SECRET_SIZE, m, cookie_offset, calc_sig);
+	} else {
+		/* it's the current cookie */
+#ifdef SCTP_DEBUG
+		if (sctp_debug_on & SCTP_DEBUG_INPUT2) {
+			printf("sctp_handle_cookie: current cookie sig\n");
+		}
+#endif
+		sctp_hash_digest_m((char *)ep->secret_key[(int)ep->current_secret_number],
+		    SCTP_SECRET_SIZE, m, cookie_offset, calc_sig);
+	}
+	/* get the signature */
+	SCTP_INP_RUNLOCK(l_inp);
+	sig = (u_int8_t *)sctp_m_getptr(m_sig, 0, SCTP_SIGNATURE_SIZE, (u_int8_t *)&tmp_sig);
+	if (sig == NULL) {
+		/* couldn't find signature */
+#ifdef SCTP_DEBUG
+		if (sctp_debug_on & SCTP_DEBUG_INPUT1) {
+			printf("sctp_handle_cookie: couldn't pull the signature\n");
+		}
+#endif
+		return (NULL);
+	}
+	/* compare the received digest with the computed digest */
+	if (memcmp(calc_sig, sig, SCTP_SIGNATURE_SIZE) != 0) {
+		/* try the old cookie? */
+		if ((cookie->time_entered.tv_sec == (long)ep->time_of_secret_change) &&
+		    (ep->current_secret_number != ep->last_secret_number)) {
+			/* compute digest with old */
+#ifdef SCTP_DEBUG
+			if (sctp_debug_on & SCTP_DEBUG_INPUT2) {
+				printf("sctp_handle_cookie: old cookie sig\n");
+			}
+#endif
+			sctp_hash_digest_m((char *)ep->secret_key[(int)ep->last_secret_number],
+			    SCTP_SECRET_SIZE, m, cookie_offset, calc_sig);
+			/* compare */
+			if (memcmp(calc_sig, sig, SCTP_SIGNATURE_SIZE) == 0)
+				cookie_ok = 1;
+		}
+	} else {
+		cookie_ok = 1;
+	}
+
+	/*
+	 * Now before we continue we must reconstruct our mbuf so
+	 * that normal processing of any other chunks will work.
+	 */
+	{
+		struct mbuf *m_at;
+		m_at = m;
+		while (m_at->m_next != NULL) {
+			m_at = m_at->m_next;
+		}
+		m_at->m_next = m_sig;
+		if (m->m_flags & M_PKTHDR) {
+			/*
+			 * We should only do this if and only if the front
+			 * mbuf has a m_pkthdr... it should in theory.
+			 */
+			if (m_sig->m_flags & M_PKTHDR) {
+				/* Add back to the pkt hdr of main m chain */
+				m->m_pkthdr.len += m_sig->m_len;
+			} else {
+				/*
+				 * Got a problem, no pkthdr in split chain.
+				 * TSNH but we will handle it just in case
+				 */
+				int mmlen = 0;
+				struct mbuf *lat;
+				printf("Warning: Hitting m_split join TSNH code - fixed\n");
+				lat = m_sig;
+				while (lat) {
+					mmlen += lat->m_len;
+					lat = lat->m_next;
+				}
+				m->m_pkthdr.len += mmlen;
+			}
+		}
+	}
+
+	if (cookie_ok == 0) {
+#ifdef SCTP_DEBUG
+		if (sctp_debug_on & SCTP_DEBUG_INPUT2) {
+			printf("handle_cookie_echo: cookie signature validation failed!\n");
+			printf("offset = %u, cookie_offset = %u, sig_offset = %u\n",
+			    (u_int32_t)offset, cookie_offset, sig_offset);
+		}
+#endif
+		return (NULL);
+	}
+#ifdef SCTP_DEBUG
+	if (sctp_debug_on & SCTP_DEBUG_INPUT2) {
+		printf("handle_cookie_echo: cookie signature validation passed\n");
+	}
+#endif
+
+	/*
+	 * check the cookie timestamps to be sure it's not stale
+	 */
+	SCTP_GETTIME_TIMEVAL(&now);
+	/* Expire time is in Ticks, so we convert to seconds */
+	time_expires.tv_sec = cookie->time_entered.tv_sec + cookie->cookie_life;
+	time_expires.tv_usec = cookie->time_entered.tv_usec;
+#ifndef __FreeBSD__
+	if (timercmp(&now, &time_expires, >))
+#else
+	if (timevalcmp(&now, &time_expires, >))
+#endif
+	{
+		/* cookie is stale! */
+		struct mbuf *op_err;
+		struct sctp_stale_cookie_msg *scm;
+		u_int32_t tim;
+#ifdef SCTP_DEBUG
+		if (sctp_debug_on & SCTP_DEBUG_INPUT3) {
+			printf("sctp_handle_cookie: got a STALE cookie!\n");
+		}
+#endif
+		MGETHDR(op_err, MB_DONTWAIT, MT_HEADER);
+		if (op_err == NULL) {
+			/* FOOBAR */
+			return (NULL);
+		}
+		/* pre-reserve some space */
+		op_err->m_data += sizeof(struct ip6_hdr);
+		op_err->m_data += sizeof(struct sctphdr);
+		op_err->m_data += sizeof(struct sctp_chunkhdr);
+
+		/* Set the len */
+		op_err->m_len = op_err->m_pkthdr.len = sizeof(struct sctp_stale_cookie_msg);
+		scm = mtod(op_err, struct sctp_stale_cookie_msg *);
+		scm->ph.param_type = htons(SCTP_CAUSE_STALE_COOKIE);
+		scm->ph.param_length = htons((sizeof(struct sctp_paramhdr) +
+		    (sizeof(u_int32_t))));
+		/* seconds to usec */
+		tim = (now.tv_sec - time_expires.tv_sec) * 1000000;
+		/* add in usec */
+		if (tim == 0)
+			tim = now.tv_usec - cookie->time_entered.tv_usec;
+		scm->time_usec = htonl(tim);
+		sctp_send_operr_to(m, iphlen, op_err, cookie->peers_vtag);
+		return (NULL);
+	}
+	/*
+	 * Now we must see with the lookup address if we have an existing
+	 * asoc. This will only happen if we were in the COOKIE-WAIT state
+	 * and a INIT collided with us and somewhere the peer sent the
+	 * cookie on another address besides the single address our assoc
+	 * had for him. In this case we will have one of the tie-tags set
+	 * at least AND the address field in the cookie can be used to
+	 * look it up.
+	 */
+	to = NULL;
+	if (cookie->addr_type == SCTP_IPV6_ADDRESS) {
+		memset(&sin6, 0, sizeof(sin6));
+		sin6.sin6_family = AF_INET6;
+		sin6.sin6_len = sizeof(sin6);
+		sin6.sin6_port = sh->src_port;
+		sin6.sin6_scope_id = cookie->scope_id;
+		memcpy(&sin6.sin6_addr.s6_addr, cookie->address,
+		       sizeof(sin6.sin6_addr.s6_addr));
+		to = (struct sockaddr *)&sin6;
+	} else if (cookie->addr_type == SCTP_IPV4_ADDRESS) {
+		memset(&sin, 0, sizeof(sin));
+		sin.sin_family = AF_INET;
+		sin.sin_len = sizeof(sin);
+		sin.sin_port = sh->src_port;
+		sin.sin_addr.s_addr = cookie->address[0];
+		to = (struct sockaddr *)&sin;
+	}
+
+	if ((*stcb == NULL) && to) {
+		/* Yep, lets check */
+		*stcb = sctp_findassociation_ep_addr(inp_p, to, netp, localep_sa, NULL);
+		if (*stcb == NULL) {
+			/* We should have only got back the same inp. If we
+			 * got back a different ep we have a problem. The original
+			 * findep got back l_inp and now
+			 */
+			if (l_inp != *inp_p) {
+				printf("Bad problem find_ep got a diff inp then special_locate?\n");
+			}
+		}
+	}
+
+	cookie_len -= SCTP_SIGNATURE_SIZE;
+	if (*stcb == NULL) {
+		/* this is the "normal" case... get a new TCB */
+#ifdef SCTP_DEBUG
+		if (sctp_debug_on & SCTP_DEBUG_INPUT2) {
+			printf("sctp_handle_cookie: processing NEW cookie\n");
+		}
+#endif
+		*stcb = sctp_process_cookie_new(m, iphlen, offset, sh, cookie,
+		    cookie_len, *inp_p, netp, to, &notification);
+		/* now always decrement, since this is the normal
+		 * case.. we had no tcb when we entered.
+		 */
+	} else {
+		/* this is abnormal... cookie-echo on existing TCB */
+#ifdef SCTP_DEBUG
+		if (sctp_debug_on & SCTP_DEBUG_INPUT2) {
+			printf("sctp_handle_cookie: processing EXISTING cookie\n");
+		}
+#endif
+		had_a_existing_tcb = 1;
+		*stcb = sctp_process_cookie_existing(m, iphlen, offset, sh,
+		    cookie, cookie_len, *inp_p, *stcb, *netp, to, &notification);
+	}
+
+	if (*stcb == NULL) {
+		/* still no TCB... must be bad cookie-echo */
+#ifdef SCTP_DEBUG
+		if (sctp_debug_on & SCTP_DEBUG_INPUT2) {
+			printf("handle_cookie_echo: ACK! don't have a TCB!\n");
+		}
+#endif /* SCTP_DEBUG */
+		return (NULL);
+	}
+
+	/*
+	 * Ok, we built an association so confirm the address
+	 * we sent the INIT-ACK to.
+	 */
+	netl = sctp_findnet(*stcb, to);
+        /* This code should in theory NOT run but
+	 */
+	if (netl == NULL) {
+		int ret;
+#ifdef SCTP_DEBUG
+		printf("TSNH! Huh, why do I need to add this address here?\n");
+#endif
+		ret = sctp_add_remote_addr(*stcb, to, 0, 100);
+		netl = sctp_findnet(*stcb, to);
+	}
+	if (netl) {
+		if (netl->dest_state &  SCTP_ADDR_UNCONFIRMED) {
+			netl->dest_state &= ~SCTP_ADDR_UNCONFIRMED;
+			sctp_set_primary_addr((*stcb), (struct sockaddr *)NULL,
+					      netl);
+			sctp_ulp_notify(SCTP_NOTIFY_INTERFACE_CONFIRMED,
+					(*stcb), 0, (void *)netl);
+		}
+	}
+#ifdef SCTP_DEBUG
+	else {
+		printf("Could not add source address for some reason\n");
+	}
+#endif
+
+	if ((*inp_p)->sctp_flags & SCTP_PCB_FLAGS_TCPTYPE) {
+		if (!had_a_existing_tcb ||
+		    (((*inp_p)->sctp_flags & SCTP_PCB_FLAGS_CONNECTED) == 0)) {
+			/*
+			 * If we have a NEW cookie or the connect never reached
+			 * the connected state during collision we must do the
+			 * TCP accept thing.
+			 */
+			struct socket *so, *oso;
+			struct sctp_inpcb *inp;
+			if (notification == SCTP_NOTIFY_ASSOC_RESTART) {
+				/*
+				 * For a restart we will keep the same socket,
+				 * no need to do anything. I THINK!!
+				 */
+				sctp_ulp_notify(notification, *stcb, 0, NULL);
+				return (m);
+			}
+			oso = (*inp_p)->sctp_socket;
+			SCTP_TCB_UNLOCK((*stcb));
+			so = sonewconn(oso, SS_ISCONNECTED);
+			SCTP_INP_WLOCK((*stcb)->sctp_ep);
+			SCTP_TCB_LOCK((*stcb));
+			SCTP_INP_WUNLOCK((*stcb)->sctp_ep);
+			if (so == NULL) {
+				struct mbuf *op_err;
+				/* Too many sockets */
+#ifdef SCTP_DEBUG
+				if (sctp_debug_on & SCTP_DEBUG_INPUT1) {
+					printf("process_cookie_new: no room for another socket!\n");
+				}
+#endif /* SCTP_DEBUG */
+				op_err = sctp_generate_invmanparam(SCTP_CAUSE_OUT_OF_RESC);
+				sctp_abort_association(*inp_p, NULL, m, iphlen,
+				    sh, op_err);
+				sctp_free_assoc(*inp_p, *stcb);
+				return (NULL);
+			}
+			inp = (struct sctp_inpcb *)so->so_pcb;
+			inp->sctp_flags = (SCTP_PCB_FLAGS_TCPTYPE |
+			    SCTP_PCB_FLAGS_CONNECTED |
+			    SCTP_PCB_FLAGS_IN_TCPPOOL |
+			    (SCTP_PCB_COPY_FLAGS & (*inp_p)->sctp_flags) |
+			    SCTP_PCB_FLAGS_DONT_WAKE);
+			inp->sctp_socket = so;
+
+			/*
+			 * Now we must move it from one hash table to another
+			 * and get the tcb in the right place.
+			 */
+			sctp_move_pcb_and_assoc(*inp_p, inp, *stcb);
+
+			/* Switch over to the new guy */
+			*inp_p = inp;
+
+			sctp_timer_start(SCTP_TIMER_TYPE_HEARTBEAT, inp,
+			    *stcb, *netp);
+
+			sctp_ulp_notify(notification, *stcb, 0, NULL);
+			return (m);
+		}
+	}
+	if ((notification) && ((*inp_p)->sctp_flags & SCTP_PCB_FLAGS_UDPTYPE)) {
+		sctp_ulp_notify(notification, *stcb, 0, NULL);
+	}
+	return (m);
+}
+
+static void
+sctp_handle_cookie_ack(struct sctp_cookie_ack_chunk *cp,
+    struct sctp_tcb *stcb, struct sctp_nets *net)
+{
+	/* cp must not be used, others call this without a c-ack :-) */
+	struct sctp_association *asoc;
+
+#ifdef SCTP_DEBUG
+	if (sctp_debug_on & SCTP_DEBUG_INPUT2) {
+		printf("sctp_handle_cookie_ack: handling COOKIE-ACK\n");
+	}
+#endif
+	if (stcb == NULL)
+		return;
+
+	asoc = &stcb->asoc;
+
+	sctp_timer_stop(SCTP_TIMER_TYPE_COOKIE, stcb->sctp_ep, stcb, net);
+
+	/* process according to association state */
+	if (SCTP_GET_STATE(asoc) == SCTP_STATE_COOKIE_ECHOED) {
+		/* state change only needed when I am in right state */
+#ifdef SCTP_DEBUG
+		if (sctp_debug_on & SCTP_DEBUG_INPUT2) {
+			printf("moving to OPEN state\n");
+		}
+#endif
+		if (asoc->state & SCTP_STATE_SHUTDOWN_PENDING) {
+			asoc->state = SCTP_STATE_OPEN | SCTP_STATE_SHUTDOWN_PENDING;
+		} else {
+			asoc->state = SCTP_STATE_OPEN;
+		}
+
+		/* update RTO */
+		if (asoc->overall_error_count == 0) {
+			net->RTO = sctp_calculate_rto(stcb, asoc, net,
+			    &asoc->time_entered);
+		}
+		SCTP_GETTIME_TIMEVAL(&asoc->time_entered);
+		sctp_ulp_notify(SCTP_NOTIFY_ASSOC_UP, stcb, 0, NULL);
+		if ((stcb->sctp_ep->sctp_flags & SCTP_PCB_FLAGS_TCPTYPE) ||
+		    (stcb->sctp_ep->sctp_flags & SCTP_PCB_FLAGS_IN_TCPPOOL)) {
+			stcb->sctp_ep->sctp_flags |= SCTP_PCB_FLAGS_CONNECTED;
+			soisconnected(stcb->sctp_ep->sctp_socket);
+		}
+		sctp_timer_start(SCTP_TIMER_TYPE_HEARTBEAT, stcb->sctp_ep,
+		    stcb, net);
+		/* since we did not send a HB make sure we don't double things */
+		net->hb_responded = 1;
+
+		if (stcb->asoc.sctp_autoclose_ticks &&
+		    (stcb->sctp_ep->sctp_flags & SCTP_PCB_FLAGS_AUTOCLOSE)) {
+			sctp_timer_start(SCTP_TIMER_TYPE_AUTOCLOSE,
+			    stcb->sctp_ep, stcb, NULL);
+		}
+
+		/*
+		 * set ASCONF timer if ASCONFs are pending and allowed
+		 * (eg. addresses changed when init/cookie echo in flight)
+		 */
+		if ((stcb->sctp_ep->sctp_flags & SCTP_PCB_FLAGS_DO_ASCONF) &&
+		    (stcb->asoc.peer_supports_asconf) &&
+		    (!TAILQ_EMPTY(&stcb->asoc.asconf_queue))) {
+			sctp_timer_start(SCTP_TIMER_TYPE_ASCONF,
+			    stcb->sctp_ep, stcb,
+			    stcb->asoc.primary_destination);
+		}
+
+	}
+	/* Toss the cookie if I can */
+	sctp_toss_old_cookies(asoc);
+	if (!TAILQ_EMPTY(&asoc->sent_queue)) {
+		/* Restart the timer if we have pending data */
+		struct sctp_tmit_chunk *chk;
+		chk = TAILQ_FIRST(&asoc->sent_queue);
+		if (chk) {
+			sctp_timer_start(SCTP_TIMER_TYPE_SEND, stcb->sctp_ep,
+			    stcb, chk->whoTo);
+		}
+	}
+
+}
+
+static void
+sctp_handle_ecn_echo(struct sctp_ecne_chunk *cp,
+    struct sctp_tcb *stcb)
+{
+	struct sctp_nets *net;
+	struct sctp_tmit_chunk *lchk;
+	u_int32_t tsn;
+	if (ntohs(cp->ch.chunk_length) != sizeof(struct sctp_ecne_chunk)) {
+		return;
+	}
+	sctp_pegs[SCTP_ECNE_RCVD]++;
+	tsn = ntohl(cp->tsn);
+	/* ECN Nonce stuff: need a resync and disable the nonce sum check */
+	/* Also we make sure we disable the nonce_wait */
+	lchk = TAILQ_FIRST(&stcb->asoc.send_queue);
+	if (lchk == NULL) {
+		stcb->asoc.nonce_resync_tsn = stcb->asoc.sending_seq;
+	} else {
+		stcb->asoc.nonce_resync_tsn = lchk->rec.data.TSN_seq;
+	}
+	stcb->asoc.nonce_wait_for_ecne = 0;
+	stcb->asoc.nonce_sum_check = 0;
+
+	/* Find where it was sent, if possible */
+	net = NULL;
+	lchk = TAILQ_FIRST(&stcb->asoc.sent_queue);
+	while (lchk) {
+		if (lchk->rec.data.TSN_seq == tsn) {
+			net = lchk->whoTo;
+			break;
+		}
+		if (compare_with_wrap(lchk->rec.data.TSN_seq, tsn, MAX_SEQ))
+			break;
+		lchk = TAILQ_NEXT(lchk, sctp_next);
+	}
+	if (net == NULL)
+		/* default is we use the primary */
+		net = stcb->asoc.primary_destination;
+
+	if (compare_with_wrap(tsn, stcb->asoc.last_cwr_tsn, MAX_TSN)) {
+#ifdef SCTP_CWND_LOGGING
+		int old_cwnd;
+#endif
+#ifdef SCTP_CWND_LOGGING
+		old_cwnd = net->cwnd;
+#endif
+		sctp_pegs[SCTP_CWR_PERFO]++;
+		net->ssthresh = net->cwnd / 2;
+		if (net->ssthresh < net->mtu) {
+			net->ssthresh = net->mtu;
+			/* here back off the timer as well, to slow us down */
+			net->RTO <<= 2;
+		}
+		net->cwnd = net->ssthresh;
+#ifdef SCTP_CWND_LOGGING
+		sctp_log_cwnd(net, (net->cwnd-old_cwnd), SCTP_CWND_LOG_FROM_SAT);
+#endif
+		/* we reduce once every RTT. So we will only lower
+		 * cwnd at the next sending seq i.e. the resync_tsn.
+		 */
+		stcb->asoc.last_cwr_tsn = stcb->asoc.nonce_resync_tsn;
+	}
+	/*
+	 * We always send a CWR this way if our previous one was lost
+	 * our peer will get an update, or if it is not time again
+	 * to reduce we still get the cwr to the peer.
+	 */
+	sctp_send_cwr(stcb, net, tsn);
+}
+
+static void
+sctp_handle_ecn_cwr(struct sctp_cwr_chunk *cp, struct sctp_tcb *stcb)
+{
+	/* Here we get a CWR from the peer. We must look in
+	 * the outqueue and make sure that we have a covered
+	 * ECNE in teh control chunk part. If so remove it.
+	 */
+	struct sctp_tmit_chunk *chk;
+	struct sctp_ecne_chunk *ecne;
+
+	TAILQ_FOREACH(chk, &stcb->asoc.control_send_queue, sctp_next) {
+		if (chk->rec.chunk_id != SCTP_ECN_ECHO) {
+			continue;
+		}
+		/* Look for and remove if it is the right TSN. Since
+		 * there is only ONE ECNE on the control queue at
+		 * any one time we don't need to worry about more than
+		 * one!
+		 */
+		ecne = mtod(chk->data, struct sctp_ecne_chunk *);
+		if (compare_with_wrap(ntohl(cp->tsn), ntohl(ecne->tsn),
+		    MAX_TSN) || (cp->tsn == ecne->tsn)) {
+			/* this covers this ECNE, we can remove it */
+			TAILQ_REMOVE(&stcb->asoc.control_send_queue, chk,
+			    sctp_next);
+			if (chk->data) {
+				sctp_m_freem(chk->data);
+				chk->data = NULL;
+			}
+			stcb->asoc.ctrl_queue_cnt--;
+			sctp_free_remote_addr(chk->whoTo);
+			SCTP_ZONE_FREE(sctppcbinfo.ipi_zone_chunk, chk);
+			sctppcbinfo.ipi_count_chunk--;
+			if ((int)sctppcbinfo.ipi_count_chunk < 0) {
+				panic("Chunk count is negative");
+			}
+			sctppcbinfo.ipi_gencnt_chunk++;
+			break;
+		}
+	}
+}
+
+static void
+sctp_handle_shutdown_complete(struct sctp_shutdown_complete_chunk *cp,
+    struct sctp_tcb *stcb, struct sctp_nets *net)
+{
+	struct sctp_association *asoc;
+
+#ifdef SCTP_DEBUG
+	if (sctp_debug_on & SCTP_DEBUG_INPUT2) {
+		printf("sctp_handle_shutdown_complete: handling SHUTDOWN-COMPLETE\n");
+	}
+#endif
+	if (stcb == NULL)
+		return;
+
+	asoc = &stcb->asoc;
+	/* process according to association state */
+	if (SCTP_GET_STATE(asoc) != SCTP_STATE_SHUTDOWN_ACK_SENT) {
+		/* unexpected SHUTDOWN-COMPLETE... so ignore... */
+		return;
+	}
+	/* notify upper layer protocol */
+	sctp_ulp_notify(SCTP_NOTIFY_ASSOC_DOWN, stcb, 0, NULL);
+	if ((stcb->sctp_ep->sctp_flags & SCTP_PCB_FLAGS_TCPTYPE) ||
+	    (stcb->sctp_ep->sctp_flags & SCTP_PCB_FLAGS_IN_TCPPOOL)) {
+		stcb->sctp_ep->sctp_flags &= ~SCTP_PCB_FLAGS_CONNECTED;
+		stcb->sctp_ep->sctp_socket->so_snd.sb_cc = 0;
+		stcb->sctp_ep->sctp_socket->so_snd.sb_mbcnt = 0;
+		soisdisconnected(stcb->sctp_ep->sctp_socket);
+	}
+	/* are the queues empty? they should be */
+	if (!TAILQ_EMPTY(&asoc->send_queue) ||
+	    !TAILQ_EMPTY(&asoc->sent_queue) ||
+	    !TAILQ_EMPTY(&asoc->out_wheel)) {
+		sctp_report_all_outbound(stcb);
+	}
+	/* stop the timer */
+	sctp_timer_stop(SCTP_TIMER_TYPE_SHUTDOWN, stcb->sctp_ep, stcb, net);
+	/* free the TCB */
+	sctp_free_assoc(stcb->sctp_ep, stcb);
+	return;
+}
+
+static int
+process_chunk_drop(struct sctp_tcb *stcb, struct sctp_chunk_desc *desc,
+    struct sctp_nets *net, u_int8_t flg)
+{
+	switch (desc->chunk_type) {
+	case SCTP_DATA:
+		/* find the tsn to resend (possibly */
+	{
+		u_int32_t tsn;
+		struct sctp_tmit_chunk *tp1;
+		tsn = ntohl(desc->tsn_ifany);
+		tp1 = TAILQ_FIRST(&stcb->asoc.sent_queue);
+		while (tp1) {
+			if (tp1->rec.data.TSN_seq == tsn) {
+				/* found it */
+				break;
+			}
+			if (compare_with_wrap(tp1->rec.data.TSN_seq, tsn,
+					      MAX_TSN)) {
+				/* not found */
+				tp1 = NULL;
+				break;
+			}
+			tp1 = TAILQ_NEXT(tp1, sctp_next);
+		}
+		if (tp1 == NULL) {
+			/* Do it the other way */
+			sctp_pegs[SCTP_PDRP_DNFND]++;
+			tp1 = TAILQ_FIRST(&stcb->asoc.sent_queue);
+			while (tp1) {
+				if (tp1->rec.data.TSN_seq == tsn) {
+					/* found it */
+					break;
+				}
+				tp1 = TAILQ_NEXT(tp1, sctp_next);
+			}
+		}
+		if (tp1 == NULL) {
+			sctp_pegs[SCTP_PDRP_TSNNF]++;
+		}
+		if ((tp1) && (tp1->sent < SCTP_DATAGRAM_ACKED)) {
+			u_int8_t *ddp;
+			if (((tp1->rec.data.state_flags & SCTP_WINDOW_PROBE) == SCTP_WINDOW_PROBE) &&
+			    ((flg & SCTP_FROM_MIDDLE_BOX) == 0)) {
+				sctp_pegs[SCTP_PDRP_DIWNP]++;
+				return (0);
+			}
+			if (stcb->asoc.peers_rwnd == 0 &&
+			    (flg & SCTP_FROM_MIDDLE_BOX)) {
+				sctp_pegs[SCTP_PDRP_DIZRW]++;
+				return (0);
+			}
+			ddp = (u_int8_t *)(mtod(tp1->data, caddr_t) +
+			    sizeof(struct sctp_data_chunk));
+			{
+				unsigned int iii;
+				for (iii = 0; iii < sizeof(desc->data_bytes);
+				    iii++) {
+					if (ddp[iii] != desc->data_bytes[iii]) {
+						sctp_pegs[SCTP_PDRP_BADD]++;
+						return (-1);
+					}
+				}
+			}
+			if (tp1->sent != SCTP_DATAGRAM_RESEND) {
+				stcb->asoc.sent_queue_retran_cnt++;
+			}
+			/* We zero out the nonce so resync not needed */
+			tp1->rec.data.ect_nonce = 0;
+
+			if (tp1->do_rtt) {
+				/*
+				 * this guy had a RTO calculation pending on it,
+				 * cancel it
+				 */
+				tp1->whoTo->rto_pending = 0;
+				tp1->do_rtt = 0;
+			}
+			sctp_pegs[SCTP_PDRP_MARK]++;
+			tp1->sent = SCTP_DATAGRAM_RESEND;
+			/*
+			 * mark it as if we were doing a FR, since we
+			 * will be getting gap ack reports behind the
+			 * info from the router.
+			 */
+ 			tp1->rec.data.doing_fast_retransmit = 1;
+			/*
+			 * mark the tsn with what sequences can cause a new FR.
+			 */
+			if (TAILQ_EMPTY(&stcb->asoc.send_queue) ) {
+				tp1->rec.data.fast_retran_tsn = stcb->asoc.sending_seq;
+			} else {
+				tp1->rec.data.fast_retran_tsn = (TAILQ_FIRST(&stcb->asoc.send_queue))->rec.data.TSN_seq;
+			}
+
+			/* restart the timer */
+			sctp_timer_stop(SCTP_TIMER_TYPE_SEND, stcb->sctp_ep,
+			    stcb, tp1->whoTo);
+			sctp_timer_start(SCTP_TIMER_TYPE_SEND, stcb->sctp_ep,
+			    stcb, tp1->whoTo);
+
+			/* fix counts and things */
+			tp1->whoTo->flight_size -= tp1->send_size;
+			if (tp1->whoTo->flight_size < 0) {
+				tp1->whoTo->flight_size = 0;
+			}
+			stcb->asoc.total_flight -= tp1->book_size;
+			if (stcb->asoc.total_flight < 0) {
+				stcb->asoc.total_flight = 0;
+			}
+			stcb->asoc.total_flight_count--;
+			if (stcb->asoc.total_flight_count < 0) {
+				stcb->asoc.total_flight_count = 0;
+			}
+			tp1->snd_count--;
+		}
+		{
+			/* audit code */
+			unsigned int audit;
+			audit = 0;
+			TAILQ_FOREACH(tp1, &stcb->asoc.sent_queue, sctp_next) {
+				if (tp1->sent == SCTP_DATAGRAM_RESEND)
+					audit++;
+			}
+			TAILQ_FOREACH(tp1, &stcb->asoc.control_send_queue,
+			    sctp_next) {
+				if (tp1->sent == SCTP_DATAGRAM_RESEND)
+					audit++;
+			}
+			if (audit != stcb->asoc.sent_queue_retran_cnt) {
+				printf("**Local Audit finds cnt:%d asoc cnt:%d\n",
+				    audit, stcb->asoc.sent_queue_retran_cnt);
+#ifndef SCTP_AUDITING_ENABLED
+				stcb->asoc.sent_queue_retran_cnt = audit;
+#endif
+			}
+		}
+	}
+	break;
+	case SCTP_ASCONF:
+	{
+		struct sctp_tmit_chunk *asconf;
+		TAILQ_FOREACH(asconf, &stcb->asoc.control_send_queue,
+		    sctp_next) {
+			if (asconf->rec.chunk_id == SCTP_ASCONF) {
+				break;
+			}
+		}
+		if (asconf) {
+			if (asconf->sent != SCTP_DATAGRAM_RESEND)
+				stcb->asoc.sent_queue_retran_cnt++;
+			asconf->sent = SCTP_DATAGRAM_RESEND;
+			asconf->snd_count--;
+		}
+	}
+	break;
+	case SCTP_INITIATION:
+		/* resend the INIT */
+		stcb->asoc.dropped_special_cnt++;
+		if (stcb->asoc.dropped_special_cnt < SCTP_RETRY_DROPPED_THRESH) {
+			/*
+			 * If we can get it in, in a few attempts we do this,
+			 * otherwise we let the timer fire.
+			 */
+			sctp_timer_stop(SCTP_TIMER_TYPE_INIT, stcb->sctp_ep,
+			    stcb, net);
+			sctp_send_initiate(stcb->sctp_ep, stcb);
+		}
+		break;
+	case SCTP_SELECTIVE_ACK:
+		/* resend the sack */
+		sctp_send_sack(stcb);
+		break;
+	case SCTP_HEARTBEAT_REQUEST:
+		/* resend a demand HB */
+		sctp_send_hb(stcb, 1, net);
+		break;
+	case SCTP_SHUTDOWN:
+#ifdef SCTP_DEBUG
+		if (sctp_debug_on & SCTP_DEBUG_OUTPUT4) {
+			printf("%s:%d sends a shutdown\n",
+			       __FILE__,
+			       __LINE__
+				);
+		}
+#endif
+		sctp_send_shutdown(stcb, net);
+		break;
+	case SCTP_SHUTDOWN_ACK:
+		sctp_send_shutdown_ack(stcb, net);
+		break;
+	case SCTP_COOKIE_ECHO:
+	{
+		struct sctp_tmit_chunk *cookie;
+		cookie = NULL;
+		TAILQ_FOREACH(cookie, &stcb->asoc.control_send_queue,
+		    sctp_next) {
+			if (cookie->rec.chunk_id == SCTP_COOKIE_ECHO) {
+				break;
+			}
+		}
+		if (cookie) {
+			if (cookie->sent != SCTP_DATAGRAM_RESEND)
+				stcb->asoc.sent_queue_retran_cnt++;
+			cookie->sent = SCTP_DATAGRAM_RESEND;
+			sctp_timer_stop(SCTP_TIMER_TYPE_COOKIE, stcb->sctp_ep, stcb, net);
+		}
+	}
+	break;
+	case SCTP_COOKIE_ACK:
+		sctp_send_cookie_ack(stcb);
+		break;
+	case SCTP_ASCONF_ACK:
+		/* resend last asconf ack */
+		sctp_send_asconf_ack(stcb, 1);
+		break;
+	case SCTP_FORWARD_CUM_TSN:
+		send_forward_tsn(stcb, &stcb->asoc);
+		break;
+		/* can't do anything with these */
+	case SCTP_PACKET_DROPPED:
+	case SCTP_INITIATION_ACK:	/* this should not happen */
+	case SCTP_HEARTBEAT_ACK:
+	case SCTP_ABORT_ASSOCIATION:
+	case SCTP_OPERATION_ERROR:
+	case SCTP_SHUTDOWN_COMPLETE:
+	case SCTP_ECN_ECHO:
+	case SCTP_ECN_CWR:
+	default:
+		break;
+	}
+	return (0);
+}
+
+static void
+sctp_reset_in_stream(struct sctp_tcb *stcb,
+    struct sctp_stream_reset_response *resp, int number_entries)
+{
+	int i;
+	uint16_t *list, temp;
+
+        /* We set things to 0xffff since this is the last delivered
+	 * sequence and we will be sending in 0 after the reset.
+	 */
+
+	if (resp->reset_flags & SCTP_RESET_PERFORMED) {
+		if (number_entries) {
+			list = resp->list_of_streams;
+			for (i = 0; i < number_entries; i++) {
+				temp = ntohs(list[i]);
+				list[i] = temp;
+				if (list[i] >= stcb->asoc.streamincnt) {
+					printf("Invalid stream in-stream reset %d\n", list[i]);
+					continue;
+				}
+				stcb->asoc.strmin[(list[i])].last_sequence_delivered = 0xffff;
+			}
+		} else {
+			list = NULL;
+			for (i = 0; i < stcb->asoc.streamincnt; i++) {
+				stcb->asoc.strmin[i].last_sequence_delivered = 0xffff;
+			}
+		}
+		sctp_ulp_notify(SCTP_NOTIFY_STR_RESET_RECV, stcb, number_entries, (void *)list);
+	}
+}
+
+static void
+sctp_clean_up_stream_reset(struct sctp_tcb *stcb)
+{
+	struct sctp_tmit_chunk *chk, *nchk;
+	struct sctp_association *asoc;
+
+	asoc = &stcb->asoc;
+
+	for (chk = TAILQ_FIRST(&asoc->control_send_queue);
+	    chk; chk = nchk) {
+		nchk = TAILQ_NEXT(chk, sctp_next);
+		if (chk->rec.chunk_id == SCTP_STREAM_RESET) {
+			struct sctp_stream_reset_req *strreq;
+			strreq = mtod(chk->data, struct sctp_stream_reset_req *);
+			if (strreq->sr_req.ph.param_type == ntohs(SCTP_STR_RESET_RESPONSE)) {
+				/* we only clean up the request */
+				continue;
+			} else if (strreq->sr_req.ph.param_type != ntohs(SCTP_STR_RESET_REQUEST)) {
+				printf("TSNH, an unknown stream reset request is in queue %x\n",
+				       (u_int)ntohs(strreq->sr_req.ph.param_type));
+				continue;
+			}
+			sctp_timer_stop(SCTP_TIMER_TYPE_STRRESET, stcb->sctp_ep, stcb, chk->whoTo);
+			TAILQ_REMOVE(&asoc->control_send_queue,
+				     chk,
+				     sctp_next);
+			if (chk->data) {
+				sctp_m_freem(chk->data);
+				chk->data = NULL;
+			}
+			asoc->ctrl_queue_cnt--;
+			sctp_free_remote_addr(chk->whoTo);
+			SCTP_ZONE_FREE(sctppcbinfo.ipi_zone_chunk, chk);
+			sctppcbinfo.ipi_count_chunk--;
+			if ((int)sctppcbinfo.ipi_count_chunk < 0) {
+				panic("Chunk count is negative");
+			}
+			sctppcbinfo.ipi_gencnt_chunk++;
+			/* we can only have one of these so we break */
+			break;
+		}
+	}
+}
+
+
+void
+sctp_handle_stream_reset_response(struct sctp_tcb *stcb,
+	struct sctp_stream_reset_response *resp)
+{
+ 	uint32_t seq, tsn;
+ 	int number_entries, param_length;
+
+ 	param_length = ntohs(resp->ph.param_length);
+ 	seq = ntohl(resp->reset_req_seq_resp);
+	if (seq == stcb->asoc.str_reset_seq_out) {
+ 		sctp_clean_up_stream_reset(stcb);
+ 		stcb->asoc.str_reset_seq_out++;
+ 		stcb->asoc.stream_reset_outstanding = 0;
+ 		tsn = ntohl(resp->reset_at_tsn);
+ 		number_entries = (param_length - sizeof(struct sctp_stream_reset_response))/sizeof(uint16_t);
+ 		tsn--;
+ 		if ((tsn == stcb->asoc.cumulative_tsn) ||
+ 		    (compare_with_wrap(stcb->asoc.cumulative_tsn, tsn, MAX_TSN))) {
+ 			/* no problem we are good to go */
+ 			sctp_reset_in_stream(stcb, resp, number_entries);
+ 		} else {
+ 			/* So, we have a stream reset but there
+ 			 * is pending data. We need to copy
+ 			 * out the stream_reset and then queue
+ 			 * any data = or > resp->reset_at_tsn
+ 			 */
+ 			if (stcb->asoc.pending_reply != NULL) {
+ 				/* FIX ME FIX ME
+ 				 * This IS WRONG. We need
+ 				 * to queue each of these up
+ 				 * and only release the chunks
+ 				 * for each reset that the cum-ack
+ 				 * goes by. This is a short cut.
+ 				 */
+ 				FREE(stcb->asoc.pending_reply, M_PCB);
+ 			}
+ 			MALLOC(stcb->asoc.pending_reply, struct sctp_stream_reset_response *, param_length,
+ 			       M_PCB, M_NOWAIT);
+ 			memcpy(stcb->asoc.pending_reply, resp, param_length);
+ 		}
+
+  	} else {
+ 		/* duplicate */
+#ifdef SCTP_DEBUG
+ 		printf("Duplicate old stream reset resp next:%x this one:%x\n",
+ 		       stcb->asoc.str_reset_seq_out, seq);
+#endif
+	}
+}
+
+
+static void
+sctp_handle_stream_reset(struct sctp_tcb *stcb, struct sctp_stream_reset_req *sr_req)
+{
+ 	int chk_length, param_len;
+ 	struct sctp_paramhdr *ph;
+ 	/* now it may be a reset or a reset-response */
+ 	struct sctp_stream_reset_request *req;
+ 	struct sctp_stream_reset_response *resp;
+ 	chk_length = ntohs(sr_req->ch.chunk_length);
+
+ 	ph = (struct sctp_paramhdr *)&sr_req->sr_req;
+ 	while ((size_t)chk_length >= sizeof(struct sctp_stream_reset_request)) {
+ 		param_len = ntohs(ph->param_length);
+ 		if (ntohs(ph->param_type) == SCTP_STR_RESET_REQUEST) {
+ 			/* this will send the ACK and do the reset if needed */
+ 			req = (struct sctp_stream_reset_request *)ph;
+ 			sctp_send_str_reset_ack(stcb, req);
+ 		} else if (ntohs(ph->param_type) == SCTP_STR_RESET_RESPONSE) {
+ 			/* Now here is a tricky one. We reset our receive side
+ 			 * of the streams. But what happens if the peers
+ 			 * next sending TSN is NOT equal to 1 minus our cumack?
+ 			 * And if his cumack is not equal to our next one out - 1
+ 			 * we have another problem if this is receprical.
+ 			 */
+ 			resp = (struct sctp_stream_reset_response *)ph;
+ 			sctp_handle_stream_reset_response(stcb, resp);
+ 		}
+ 		ph = (struct sctp_paramhdr *)((caddr_t)ph + SCTP_SIZE32(param_len));
+ 		chk_length -= SCTP_SIZE32(param_len);
+  	}
+}
+
+/*
+ * Handle a router or endpoints report of a packet loss, there
+ * are two ways to handle this, either we get the whole packet
+ * and must disect it ourselves (possibly with truncation and
+ * or corruption) or it is a summary from a middle box that did
+ * the disectting for us.
+ */
+static void
+sctp_handle_packet_dropped(struct sctp_pktdrop_chunk *cp,
+    struct sctp_tcb *stcb, struct sctp_nets *net)
+{
+	u_int32_t bottle_bw, on_queue;
+	u_int16_t trunc_len;
+	unsigned int chlen;
+	unsigned int at;
+	struct sctp_chunk_desc desc;
+	struct sctp_chunkhdr *ch;
+
+	chlen = ntohs(cp->ch.chunk_length);
+	chlen -= sizeof(struct sctp_pktdrop_chunk);
+	/* XXX possible chlen underflow */
+	if (chlen == 0) {
+		ch = NULL;
+		if (cp->ch.chunk_flags & SCTP_FROM_MIDDLE_BOX)
+			sctp_pegs[SCTP_PDRP_BWRPT]++;
+	} else {
+		ch = (struct sctp_chunkhdr *)(cp->data + sizeof(struct sctphdr));
+		chlen -= sizeof(struct sctphdr);
+		/* XXX possible chlen underflow */
+		memset(&desc, 0, sizeof(desc));
+	}
+
+	/* first update a rwnd possibly */
+	if ((cp->ch.chunk_flags & SCTP_FROM_MIDDLE_BOX) == 0) {
+		/* From a peer, we get a rwnd report */
+		u_int32_t a_rwnd;
+
+		sctp_pegs[SCTP_PDRP_FEHOS]++;
+
+		bottle_bw = ntohl(cp->bottle_bw);
+		on_queue =  ntohl(cp->current_onq);
+		if (bottle_bw && on_queue) {
+			/* a rwnd report is in here */
+			if (bottle_bw > on_queue)
+				a_rwnd = bottle_bw - on_queue;
+			else
+				a_rwnd = 0;
+
+			if (a_rwnd <= 0)
+				stcb->asoc.peers_rwnd =  0;
+			else {
+				if (a_rwnd > stcb->asoc.total_flight) {
+					stcb->asoc.peers_rwnd =
+					    a_rwnd - stcb->asoc.total_flight;
+				} else {
+					stcb->asoc.peers_rwnd =  0;
+				}
+				if (stcb->asoc.peers_rwnd <
+				    stcb->sctp_ep->sctp_ep.sctp_sws_sender) {
+					/* SWS sender side engages */
+					stcb->asoc.peers_rwnd = 0;
+				}
+			}
+		}
+	} else {
+		sctp_pegs[SCTP_PDRP_FMBOX]++;
+	}
+	trunc_len = (u_int16_t)ntohs(cp->trunc_len);
+	/* now the chunks themselves */
+	while ((ch != NULL) && (chlen >= sizeof(struct sctp_chunkhdr))) {
+		desc.chunk_type = ch->chunk_type;
+		/* get amount we need to move */
+		at = ntohs(ch->chunk_length);
+		if (at < sizeof(struct sctp_chunkhdr)) {
+			/* corrupt chunk, maybe at the end? */
+			sctp_pegs[SCTP_PDRP_CRUPT]++;
+			break;
+		}
+		if (trunc_len == 0) {
+			/* we are supposed to have all of it */
+			if (at > chlen) {
+				/* corrupt skip it */
+				sctp_pegs[SCTP_PDRP_CRUPT]++;
+				break;
+			}
+		} else {
+			/* is there enough of it left ? */
+			if (desc.chunk_type == SCTP_DATA) {
+				if (chlen < (sizeof(struct sctp_data_chunk) +
+					     sizeof(desc.data_bytes))) {
+					break;
+				}
+			} else {
+				if (chlen < sizeof(struct sctp_chunkhdr)) {
+					break;
+				}
+			}
+		}
+		if (desc.chunk_type == SCTP_DATA) {
+			/* can we get out the tsn? */
+			if ((cp->ch.chunk_flags & SCTP_FROM_MIDDLE_BOX))
+				sctp_pegs[SCTP_PDRP_MB_DA]++;
+
+			if (chlen >= (sizeof(struct sctp_data_chunk) + sizeof(u_int32_t)) ) {
+				/* yep */
+				struct sctp_data_chunk *dcp;
+				u_int8_t  *ddp;
+				unsigned int iii;
+				dcp = (struct sctp_data_chunk *)ch;
+				ddp = (u_int8_t *)(dcp + 1);
+				for (iii = 0; iii < sizeof(desc.data_bytes); iii++) {
+					desc.data_bytes[iii] = ddp[iii];
+				}
+				desc.tsn_ifany = dcp->dp.tsn;
+			} else {
+				/* nope we are done. */
+				sctp_pegs[SCTP_PDRP_NEDAT]++;
+				break;
+			}
+		} else {
+			if ((cp->ch.chunk_flags & SCTP_FROM_MIDDLE_BOX))
+				sctp_pegs[SCTP_PDRP_MB_CT]++;
+		}
+
+		if (process_chunk_drop(stcb, &desc, net, cp->ch.chunk_flags)) {
+			sctp_pegs[SCTP_PDRP_PDBRK]++;
+			break;
+		}
+		if (SCTP_SIZE32(at) > chlen) {
+			break;
+		}
+		chlen -= SCTP_SIZE32(at);
+		if (chlen < sizeof(struct sctp_chunkhdr)) {
+			/* done, none left */
+			break;
+		}
+		ch = (struct sctp_chunkhdr *)((caddr_t)ch + SCTP_SIZE32(at));
+	}
+
+	/* now middle boxes in sat networks get a cwnd bump */
+	if ((cp->ch.chunk_flags & SCTP_FROM_MIDDLE_BOX) &&
+	    (stcb->asoc.sat_t3_loss_recovery == 0) &&
+	    (stcb->asoc.sat_network)) {
+		/*
+		 * This is debateable but for sat networks it makes sense
+		 * Note if a T3 timer has went off, we will prohibit any
+		 * changes to cwnd until we exit the t3 loss recovery.
+		 */
+		u_int32_t bw_avail;
+		int rtt, incr;
+#ifdef SCTP_CWND_LOGGING
+		int old_cwnd=net->cwnd;
+#endif
+		/* need real RTT for this calc */
+		rtt = ((net->lastsa >> 2) + net->lastsv) >> 1;
+		/* get bottle neck bw */
+		bottle_bw = ntohl(cp->bottle_bw);
+		/* and whats on queue */
+		on_queue =  ntohl(cp->current_onq);
+		/*
+		 * adjust the on-queue if our flight is more it could be
+		 * that the router has not yet gotten data "in-flight" to it
+		 */
+ 		if (on_queue < net->flight_size)
+			on_queue = net->flight_size;
+
+		/* calculate the available space */
+		bw_avail = (bottle_bw*rtt)/1000;
+		if (bw_avail > bottle_bw) {
+			/*
+			 * Cap the growth to no more than the bottle neck.
+			 * This can happen as RTT slides up due to queues.
+			 * It also means if you have more than a 1 second
+			 * RTT with a empty queue you will be limited to
+			 * the bottle_bw per second no matter if
+			 * other points have 1/2 the RTT and you could
+			 * get more out...
+			 */
+			bw_avail = bottle_bw;
+		}
+
+		if (on_queue > bw_avail) {
+			/*
+			 * No room for anything else don't allow anything
+			 * else to be "added to the fire".
+			 */
+			int seg_inflight, seg_onqueue, my_portion;
+			net->partial_bytes_acked = 0;
+
+			/* how much are we over queue size? */
+			incr = on_queue - bw_avail;
+			if (stcb->asoc.seen_a_sack_this_pkt) {
+				/* undo any cwnd adjustment that
+				 * the sack might have made
+				 */
+				net->cwnd = net->prev_cwnd;
+			}
+
+			/* Now how much of that is mine? */
+			seg_inflight = net->flight_size / net->mtu;
+			seg_onqueue = on_queue / net->mtu;
+			my_portion = (incr * seg_inflight)/seg_onqueue;
+
+			/* Have I made an adjustment already */
+			if (net->cwnd > net->flight_size) {
+				/* for this flight I made an adjustment
+				 * we need to decrease the portion by a share
+				 * our previous adjustment.
+				 */
+				int diff_adj;
+				diff_adj = net->cwnd - net->flight_size;
+				if (diff_adj > my_portion)
+					my_portion = 0;
+				else
+					my_portion -= diff_adj;
+			}
+
+			/* back down to the previous cwnd (assume
+			 * we have had a sack before this packet). minus
+			 * what ever portion of the overage is my fault.
+			 */
+			net->cwnd -= my_portion;
+
+			/* we will NOT back down more than 1 MTU */
+			if (net->cwnd <= net->mtu) {
+				net->cwnd = net->mtu;
+			}
+			/* force into CA */
+			net->ssthresh = net->cwnd - 1;
+		} else {
+			/*
+			 * Take 1/4 of the space left or
+			 * max burst up .. whichever is less.
+			 */
+			incr = min((bw_avail - on_queue) >> 2,
+			    (int)stcb->asoc.max_burst * (int)net->mtu);
+			net->cwnd += incr;
+		}
+		if (net->cwnd > bw_avail) {
+			/* We can't exceed the pipe size */
+			net->cwnd = bw_avail;
+		}
+		if (net->cwnd < net->mtu) {
+			/* We always have 1 MTU */
+			net->cwnd = net->mtu;
+		}
+#ifdef SCTP_CWND_LOGGING
+		if (net->cwnd - old_cwnd != 0) {
+			/* log only changes */
+			sctp_log_cwnd(net, (net->cwnd - old_cwnd),
+			    SCTP_CWND_LOG_FROM_SAT);
+		}
+#endif
+	}
+}
+
+extern int sctp_strict_init;
+
+/*
+ * handles all control chunks in a packet
+ * inputs:
+ * - m: mbuf chain, assumed to still contain IP/SCTP header
+ * - stcb: is the tcb found for this packet
+ * - offset: offset into the mbuf chain to first chunkhdr
+ * - length: is the length of the complete packet
+ * outputs:
+ * - length: modified to remaining length after control processing
+ * - netp: modified to new sctp_nets after cookie-echo processing
+ * - return NULL to discard the packet (ie. no asoc, bad packet,...)
+ *   otherwise return the tcb for this packet
+ */
+static struct sctp_tcb *
+sctp_process_control(struct mbuf *m, int iphlen, int *offset, int length,
+    struct sctphdr *sh, struct sctp_chunkhdr *ch, struct sctp_inpcb *inp,
+    struct sctp_tcb *stcb, struct sctp_nets **netp, int *fwd_tsn_seen)
+{
+	struct sctp_association *asoc;
+	u_int32_t vtag_in;
+	int num_chunks = 0;	/* number of control chunks processed */
+	int chk_length;
+	int ret;
+
+	/*
+	 * How big should this be, and should it be alloc'd?
+	 * Lets try the d-mtu-ceiling for now (2k) and that should
+	 * hopefully work ... until we get into jumbo grams and such..
+	 */
+	u_int8_t chunk_buf[DEFAULT_CHUNK_BUFFER];
+	struct sctp_tcb *locked_tcb = stcb;
+
+#ifdef SCTP_DEBUG
+	if (sctp_debug_on & SCTP_DEBUG_INPUT1) {
+		printf("sctp_process_control: iphlen=%u, offset=%u, length=%u stcb:%p\n",
+		       iphlen, *offset, length, stcb);
+	}
+#endif /* SCTP_DEBUG */
+
+	/* validate chunk header length... */
+	if (ntohs(ch->chunk_length) < sizeof(*ch)) {
+		return (NULL);
+	}
+
+	/*
+	 * validate the verification tag
+	 */
+#ifdef SCTP_DEBUG
+	if (sctp_debug_on & SCTP_DEBUG_INPUT3) {
+		printf("sctp_process_control: validating vtags\n");
+	}
+#endif /* SCTP_DEBUG */
+	vtag_in = ntohl(sh->v_tag);
+	if (ch->chunk_type == SCTP_INITIATION) {
+		if (vtag_in != 0) {
+			/* protocol error- silently discard... */
+#ifdef SCTP_DEBUG
+			if (sctp_debug_on & SCTP_DEBUG_INPUT3) {
+				printf("sctp_process_control: INIT with vtag != 0\n");
+			}
+#endif /* SCTP_DEBUG */
+			sctp_pegs[SCTP_BAD_VTAGS]++;
+			if (locked_tcb)
+				SCTP_TCB_UNLOCK(locked_tcb);
+			return (NULL);
+		}
+	} else if (ch->chunk_type != SCTP_COOKIE_ECHO) {
+		/*
+		 * first check if it's an ASCONF with an unknown src addr
+		 * we need to look inside to find the association
+		 */
+		if (ch->chunk_type == SCTP_ASCONF && stcb == NULL) {
+			stcb = sctp_findassociation_ep_asconf(m, iphlen,
+			    *offset, sh, &inp, netp);
+		}
+		if (stcb == NULL) {
+			/* no association, so it's out of the blue... */
+			sctp_handle_ootb(m, iphlen, *offset, sh, inp, NULL);
+#ifdef SCTP_DEBUG
+			if (sctp_debug_on & SCTP_DEBUG_INPUT3) {
+				printf("sctp_process_control: handling OOTB packet, chunk type=%xh\n",
+				       ch->chunk_type);
+			}
+#endif /* SCTP_DEBUG */
+			*offset = length;
+			if (locked_tcb)
+				SCTP_TCB_UNLOCK(locked_tcb);
+			return (NULL);
+		}
+		asoc = &stcb->asoc;
+		/* ABORT and SHUTDOWN can use either v_tag... */
+		if ((ch->chunk_type == SCTP_ABORT_ASSOCIATION) ||
+		    (ch->chunk_type == SCTP_SHUTDOWN_COMPLETE) ||
+		    (ch->chunk_type == SCTP_PACKET_DROPPED)) {
+			if ((vtag_in == asoc->my_vtag) ||
+			    ((ch->chunk_flags & SCTP_HAD_NO_TCB) &&
+			     (vtag_in == asoc->peer_vtag))) {
+				/* this is valid */
+			} else {
+				/* drop this packet... */
+				sctp_pegs[SCTP_BAD_VTAGS]++;
+				if (locked_tcb)
+					SCTP_TCB_UNLOCK(locked_tcb);
+				return (NULL);
+			}
+		} else if (ch->chunk_type == SCTP_SHUTDOWN_ACK) {
+			if (vtag_in != asoc->my_vtag) {
+				/*
+				 * this could be a stale SHUTDOWN-ACK or the
+				 * peer never got the SHUTDOWN-COMPLETE and
+				 * is still hung; we have started a new asoc
+				 * but it won't complete until the shutdown is
+				 * completed
+				 */
+				if (locked_tcb)
+					SCTP_TCB_UNLOCK(locked_tcb);
+				sctp_handle_ootb(m, iphlen, *offset, sh, inp,
+				    NULL);
+				return (NULL);
+			}
+		} else {
+			/* for all other chunks, vtag must match */
+
+			if (vtag_in != asoc->my_vtag) {
+				/* invalid vtag... */
+#ifdef SCTP_DEBUG
+				if (sctp_debug_on & SCTP_DEBUG_INPUT3) {
+					printf("invalid vtag: %xh, expect %xh\n", vtag_in, asoc->my_vtag);
+				}
+#endif /* SCTP_DEBUG */
+				sctp_pegs[SCTP_BAD_VTAGS]++;
+				if (locked_tcb)
+					SCTP_TCB_UNLOCK(locked_tcb);
+				*offset = length;
+				return (NULL);
+			}
+		}
+	}  /* end if !SCTP_COOKIE_ECHO */
+
+#ifdef SCTP_DEBUG
+	if (sctp_debug_on & SCTP_DEBUG_INPUT3) {
+		printf("sctp_process_control: vtags ok, processing ctrl chunks\n");
+	}
+#endif /* SCTP_DEBUG */
+
+	/*
+	 * process all control chunks...
+	 */
+	if (((ch->chunk_type == SCTP_SELECTIVE_ACK) ||
+	    (ch->chunk_type == SCTP_HEARTBEAT_REQUEST)) &&
+	    (SCTP_GET_STATE(&stcb->asoc) == SCTP_STATE_COOKIE_ECHOED)) {
+	    /* implied cookie-ack.. we must have lost the ack */
+	    stcb->asoc.overall_error_count = 0;
+	    sctp_handle_cookie_ack((struct sctp_cookie_ack_chunk *)ch, stcb, *netp);
+	}
+
+	while (IS_SCTP_CONTROL(ch)) {
+		/* validate chunk length */
+		chk_length = ntohs(ch->chunk_length);
+#ifdef SCTP_DEBUG
+		if (sctp_debug_on & SCTP_DEBUG_INPUT2) {
+			printf("sctp_process_control: processing a chunk type=%u, len=%u\n", ch->chunk_type, chk_length);
+		}
+#endif /* SCTP_DEBUG */
+		if ((size_t)chk_length < sizeof(*ch) ||
+		    (*offset + chk_length) > length) {
+#ifdef SCTP_DEBUG
+			if (sctp_debug_on & SCTP_DEBUG_INPUT3) {
+				printf("sctp_process_control: chunk length invalid! *offset:%u, chk_length:%u > length:%u\n",
+				    *offset, length, chk_length);
+			}
+#endif /* SCTP_DEBUG */
+			*offset = length;
+			if (locked_tcb)
+				SCTP_TCB_UNLOCK(locked_tcb);
+			return (NULL);
+		}
+
+		/*
+		 * INIT-ACK only gets the init ack "header" portion only
+		 * because we don't have to process the peer's COOKIE.
+		 * All others get a complete chunk.
+		 */
+		if (ch->chunk_type == SCTP_INITIATION_ACK) {
+			/* get an init-ack chunk */
+			ch = (struct sctp_chunkhdr *)sctp_m_getptr(m, *offset,
+			    sizeof(struct sctp_init_ack), chunk_buf);
+			if (ch == NULL) {
+				*offset = length;
+				if (locked_tcb)
+					SCTP_TCB_UNLOCK(locked_tcb);
+				return (NULL);
+			}
+		} else {
+			/* get a complete chunk... */
+			if ((size_t)chk_length > sizeof(chunk_buf)) {
+				struct mbuf *oper;
+				struct sctp_paramhdr *phdr;
+				oper = NULL;
+				MGETHDR(oper, MB_DONTWAIT, MT_HEADER);
+				if (oper) {
+					/* pre-reserve some space */
+					oper->m_data +=
+					    sizeof(struct sctp_chunkhdr);
+					phdr =
+					    mtod(oper, struct sctp_paramhdr *);
+					phdr->param_type =
+					    htons(SCTP_CAUSE_OUT_OF_RESC);
+					phdr->param_length =
+					    htons(sizeof(struct sctp_paramhdr));
+					sctp_queue_op_err(stcb, oper);
+				}
+				if (locked_tcb)
+					SCTP_TCB_UNLOCK(locked_tcb);
+				return (NULL);
+			}
+			ch = (struct sctp_chunkhdr *)sctp_m_getptr(m, *offset,
+			    chk_length, chunk_buf);
+			if (ch == NULL) {
+				printf("sctp_process_control: Can't get the all data....\n");
+				*offset = length;
+				if (locked_tcb)
+					SCTP_TCB_UNLOCK(locked_tcb);
+				return (NULL);
+			}
+
+		}
+		num_chunks++;
+		/* Save off the last place we got a control from */
+		if ((*netp) && stcb) {
+			stcb->asoc.last_control_chunk_from = *netp;
+		}
+#ifdef SCTP_AUDITING_ENABLED
+		sctp_audit_log(0xB0, ch->chunk_type);
+#endif
+		switch (ch->chunk_type) {
+		case SCTP_INITIATION:
+			/* must be first and only chunk */
+#ifdef SCTP_DEBUG
+			if (sctp_debug_on & SCTP_DEBUG_INPUT3) {
+				printf("SCTP_INIT\n");
+			}
+#endif /* SCTP_DEBUG */
+			if (inp->sctp_flags & SCTP_PCB_FLAGS_SOCKET_GONE) {
+				/* We are not interested anymore */
+				if (locked_tcb)
+					SCTP_TCB_UNLOCK(locked_tcb);
+				if (LIST_FIRST(&inp->sctp_asoc_list) == NULL) {
+					/* finish the job now */
+					sctp_inpcb_free(inp, 1);
+				}
+				*offset = length;
+				return (NULL);
+			}
+			if ((num_chunks > 1) ||
+			    (sctp_strict_init && (length - *offset > SCTP_SIZE32(chk_length)))) {
+				*offset = length;
+				if (locked_tcb)
+					SCTP_TCB_UNLOCK(locked_tcb);
+				return (NULL);
+			}
+			if ((stcb != NULL) &&
+			    (SCTP_GET_STATE(&stcb->asoc) ==
+			    SCTP_STATE_SHUTDOWN_ACK_SENT)) {
+				sctp_send_shutdown_ack(stcb,
+				    stcb->asoc.primary_destination);
+				*offset = length;
+				if (locked_tcb)
+					SCTP_TCB_UNLOCK(locked_tcb);
+				return (NULL);
+			}
+			sctp_handle_init(m, iphlen, *offset, sh,
+			    (struct sctp_init_chunk *)ch, inp, stcb, *netp);
+			*offset = length;
+			if (locked_tcb)
+				SCTP_TCB_UNLOCK(locked_tcb);
+			return (NULL);
+			break;
+		case SCTP_INITIATION_ACK:
+			/* must be first and only chunk */
+#ifdef SCTP_DEBUG
+			if (sctp_debug_on & SCTP_DEBUG_INPUT3) {
+				printf("SCTP_INIT-ACK\n");
+			}
+#endif /* SCTP_DEBUG */
+			if (inp->sctp_flags & SCTP_PCB_FLAGS_SOCKET_GONE) {
+				/* We are not interested anymore */
+				if (locked_tcb)
+					SCTP_TCB_UNLOCK(locked_tcb);
+				*offset = length;
+				if (stcb) {
+					sctp_free_assoc(inp, stcb);
+				} else {
+					if (LIST_FIRST(&inp->sctp_asoc_list) == NULL) {
+						/* finish the job now */
+						sctp_inpcb_free(inp, 1);
+					}
+				}
+				return (NULL);
+			}
+			if ((num_chunks > 1) ||
+			    (sctp_strict_init && (length - *offset > SCTP_SIZE32(chk_length)))) {
+#ifdef SCTP_DEBUG
+				if (sctp_debug_on & SCTP_DEBUG_INPUT3) {
+					printf("Length is %d rounded chk_length:%d .. dropping\n",
+					    length - *offset,
+					    SCTP_SIZE32(chk_length));
+				}
+#endif
+				*offset = length;
+				if (locked_tcb)
+					SCTP_TCB_UNLOCK(locked_tcb);
+				return (NULL);
+			}
+			ret = sctp_handle_init_ack(m, iphlen, *offset, sh,
+			    (struct sctp_init_ack_chunk *)ch, stcb, *netp);
+			/*
+			 * Special case, I must call the output routine
+			 * to get the cookie echoed
+			 */
+			if ((stcb) && ret == 0)
+				sctp_chunk_output(stcb->sctp_ep, stcb, 2);
+			*offset = length;
+#ifdef SCTP_DEBUG
+			if (sctp_debug_on & SCTP_DEBUG_INPUT3) {
+				printf("All done INIT-ACK processing\n");
+			}
+#endif
+			if (locked_tcb)
+				SCTP_TCB_UNLOCK(locked_tcb);
+			return (NULL);
+			break;
+		case SCTP_SELECTIVE_ACK:
+#ifdef SCTP_DEBUG
+			if (sctp_debug_on & SCTP_DEBUG_INPUT3) {
+				printf("SCTP_SACK\n");
+			}
+#endif /* SCTP_DEBUG */
+			sctp_pegs[SCTP_PEG_SACKS_SEEN]++;
+			{
+				int abort_now = 0;
+				stcb->asoc.seen_a_sack_this_pkt = 1;
+				sctp_handle_sack((struct sctp_sack_chunk *)ch,
+				    stcb, *netp, &abort_now);
+				if (abort_now) {
+					/* ABORT signal from sack processing */
+					*offset = length;
+					return (NULL);
+				}
+			}
+			break;
+		case SCTP_HEARTBEAT_REQUEST:
+#ifdef SCTP_DEBUG
+			if (sctp_debug_on & SCTP_DEBUG_INPUT3) {
+				printf("SCTP_HEARTBEAT\n");
+			}
+#endif /* SCTP_DEBUG */
+			sctp_pegs[SCTP_HB_RECV]++;
+			sctp_send_heartbeat_ack(stcb, m, *offset, chk_length,
+			    *netp);
+
+			/* He's alive so give him credit */
+			stcb->asoc.overall_error_count = 0;
+			break;
+		case SCTP_HEARTBEAT_ACK:
+#ifdef SCTP_DEBUG
+			if (sctp_debug_on & SCTP_DEBUG_INPUT3) {
+				printf("SCTP_HEARTBEAT-ACK\n");
+			}
+#endif /* SCTP_DEBUG */
+
+			/* He's alive so give him credit */
+			stcb->asoc.overall_error_count = 0;
+
+			sctp_pegs[SCTP_HB_ACK_RECV]++;
+			sctp_handle_heartbeat_ack((struct sctp_heartbeat_chunk *)ch,
+			    stcb, *netp);
+			break;
+		case SCTP_ABORT_ASSOCIATION:
+#ifdef SCTP_DEBUG
+			if (sctp_debug_on & SCTP_DEBUG_INPUT3) {
+				printf("SCTP_ABORT\n");
+			}
+#endif /* SCTP_DEBUG */
+			sctp_handle_abort((struct sctp_abort_chunk *)ch,
+			    stcb, *netp);
+			*offset = length;
+			return (NULL);
+			break;
+		case SCTP_SHUTDOWN:
+#ifdef SCTP_DEBUG
+			if (sctp_debug_on & SCTP_DEBUG_INPUT3) {
+				printf("SCTP_SHUTDOWN\n");
+			}
+#endif /* SCTP_DEBUG */
+                       {
+			       int abort_flag = 0;
+			       sctp_handle_shutdown((struct sctp_shutdown_chunk *)ch,
+				   stcb, *netp, &abort_flag);
+			       if (abort_flag) {
+				       *offset = length;
+				       return (NULL);
+			       }
+		       }
+			break;
+		case SCTP_SHUTDOWN_ACK:
+#ifdef SCTP_DEBUG
+			if (sctp_debug_on & SCTP_DEBUG_INPUT3) {
+				printf("SCTP_SHUTDOWN-ACK\n");
+			}
+#endif /* SCTP_DEBUG */
+			sctp_handle_shutdown_ack((struct sctp_shutdown_ack_chunk *)ch, stcb, *netp);
+			*offset = length;
+			return (NULL);
+			break;
+		case SCTP_OPERATION_ERROR:
+#ifdef SCTP_DEBUG
+			if (sctp_debug_on & SCTP_DEBUG_INPUT3) {
+				printf("SCTP_OP-ERR\n");
+			}
+#endif /* SCTP_DEBUG */
+			if (sctp_handle_error(ch, stcb, *netp) < 0) {
+				*offset = length;
+				return (NULL);
+			}
+			break;
+		case SCTP_COOKIE_ECHO:
+#ifdef SCTP_DEBUG
+			if (sctp_debug_on & SCTP_DEBUG_INPUT3) {
+				printf("SCTP_COOKIE-ECHO stcb is %p\n", stcb);
+			}
+#endif /* SCTP_DEBUG */
+			if (inp->sctp_flags & SCTP_PCB_FLAGS_SOCKET_GONE) {
+				/* We are not interested anymore */
+				*offset = length;
+				if (stcb) {
+					sctp_free_assoc(inp, stcb);
+				} else {
+					if (LIST_FIRST(&inp->sctp_asoc_list) == NULL) {
+						/* finish the job now */
+						sctp_inpcb_free(inp, 1);
+					}
+				}
+				return (NULL);
+			}
+			/*
+			 * First are we accepting?
+			 * We do this again here since it is possible
+			 * that a previous endpoint WAS listening responded to
+			 * a INIT-ACK and then closed. We opened and bound..
+			 * and are now no longer listening.
+			 */
+			printf("flags: %x qlimit: %d\n", inp->sctp_flags, inp->sctp_socket->so_qlimit);
+			if (((inp->sctp_flags & SCTP_PCB_FLAGS_ACCEPTING) == 0) ||
+			    (inp->sctp_socket->so_qlimit == 0)) {
+				sctp_abort_association(inp, stcb, m, iphlen, sh,
+				    NULL);
+				*offset = length;
+				return (NULL);
+			} else if (inp->sctp_flags & SCTP_PCB_FLAGS_ACCEPTING) {
+				/* we are accepting so check limits like TCP */
+				if (inp->sctp_socket->so_qlen >
+				    inp->sctp_socket->so_qlimit) {
+					/* no space */
+					struct mbuf *oper;
+					struct sctp_paramhdr *phdr;
+					oper = NULL;
+					MGETHDR(oper, MB_DONTWAIT, MT_HEADER);
+					if (oper) {
+						oper->m_len =
+						    oper->m_pkthdr.len =
+						    sizeof(struct sctp_paramhdr);
+						phdr = mtod(oper,
+						    struct sctp_paramhdr *);
+						phdr->param_type =
+						    htons(SCTP_CAUSE_OUT_OF_RESC);
+						phdr->param_length =
+						    htons(sizeof(struct sctp_paramhdr));
+					}
+					sctp_abort_association(inp, stcb, m,
+					    iphlen, sh, oper);
+					*offset = length;
+					return (NULL);
+				}
+			}
+			{
+				struct mbuf *ret_buf;
+				ret_buf = sctp_handle_cookie_echo(m, iphlen,
+				    *offset, sh,
+				    (struct sctp_cookie_echo_chunk *)ch, &inp,
+				    &stcb, netp);
+#ifdef SCTP_DEBUG
+				if (sctp_debug_on & SCTP_DEBUG_INPUT3) {
+					printf("ret_buf:%p length:%d off:%d\n",
+					    ret_buf, length, *offset);
+				}
+#endif /* SCTP_DEBUG */
+
+				if (ret_buf == NULL) {
+					if (locked_tcb) {
+						SCTP_TCB_UNLOCK(locked_tcb);
+					}
+#ifdef SCTP_DEBUG
+					if (sctp_debug_on & SCTP_DEBUG_INPUT3) {
+						printf("GAK, null buffer\n");
+					}
+#endif /* SCTP_DEBUG */
+					*offset = length;
+					return (NULL);
+				}
+				if (!TAILQ_EMPTY(&stcb->asoc.sent_queue)) {
+					/*
+					 * Restart the timer if we have pending
+					 * data
+					 */
+					struct sctp_tmit_chunk *chk;
+					chk = TAILQ_FIRST(&stcb->asoc.sent_queue);
+					if (chk) {
+						sctp_timer_start(SCTP_TIMER_TYPE_SEND,
+						    stcb->sctp_ep, stcb,
+						    chk->whoTo);
+					}
+				}
+			}
+			break;
+		case SCTP_COOKIE_ACK:
+#ifdef SCTP_DEBUG
+			if (sctp_debug_on & SCTP_DEBUG_INPUT3) {
+				printf("SCTP_COOKIE-ACK\n");
+			}
+#endif /* SCTP_DEBUG */
+
+			if (inp->sctp_flags & SCTP_PCB_FLAGS_SOCKET_GONE) {
+				/* We are not interested anymore */
+				sctp_free_assoc(inp, stcb);
+				*offset = length;
+				return (NULL);
+			}
+			/* He's alive so give him credit */
+			stcb->asoc.overall_error_count = 0;
+			sctp_handle_cookie_ack((struct sctp_cookie_ack_chunk *)ch,
+			    stcb, *netp);
+			break;
+		case SCTP_ECN_ECHO:
+#ifdef SCTP_DEBUG
+			if (sctp_debug_on & SCTP_DEBUG_INPUT3) {
+				printf("SCTP_ECN-ECHO\n");
+			}
+#endif /* SCTP_DEBUG */
+			/* He's alive so give him credit */
+			stcb->asoc.overall_error_count = 0;
+			sctp_handle_ecn_echo((struct sctp_ecne_chunk *)ch,
+			    stcb);
+			break;
+		case SCTP_ECN_CWR:
+#ifdef SCTP_DEBUG
+			if (sctp_debug_on & SCTP_DEBUG_INPUT3) {
+				printf("SCTP_ECN-CWR\n");
+			}
+#endif /* SCTP_DEBUG */
+			/* He's alive so give him credit */
+			stcb->asoc.overall_error_count = 0;
+
+			sctp_handle_ecn_cwr((struct sctp_cwr_chunk *)ch, stcb);
+			break;
+		case SCTP_SHUTDOWN_COMPLETE:
+#ifdef SCTP_DEBUG
+			if (sctp_debug_on & SCTP_DEBUG_INPUT3) {
+				printf("SCTP_SHUTDOWN-COMPLETE\n");
+			}
+#endif /* SCTP_DEBUG */
+			/* must be first and only chunk */
+			if ((num_chunks > 1) ||
+			    (length - *offset > SCTP_SIZE32(chk_length))) {
+				*offset = length;
+				if (locked_tcb)
+					SCTP_TCB_UNLOCK(locked_tcb);
+
+				return (NULL);
+			}
+			sctp_handle_shutdown_complete((struct sctp_shutdown_complete_chunk *)ch,
+			    stcb, *netp);
+			*offset = length;
+			return (NULL);
+			break;
+		case SCTP_ASCONF:
+#ifdef SCTP_DEBUG
+			if (sctp_debug_on & SCTP_DEBUG_INPUT3) {
+				printf("SCTP_ASCONF\n");
+			}
+#endif /* SCTP_DEBUG */
+			/* He's alive so give him credit */
+			stcb->asoc.overall_error_count = 0;
+
+			sctp_handle_asconf(m, *offset,
+			    (struct sctp_asconf_chunk *)ch, stcb, *netp);
+			break;
+		case SCTP_ASCONF_ACK:
+#ifdef SCTP_DEBUG
+			if (sctp_debug_on & SCTP_DEBUG_INPUT3) {
+				printf("SCTP_ASCONF-ACK\n");
+			}
+#endif /* SCTP_DEBUG */
+			/* He's alive so give him credit */
+			stcb->asoc.overall_error_count = 0;
+
+			sctp_handle_asconf_ack(m, *offset,
+			    (struct sctp_asconf_ack_chunk *)ch, stcb, *netp);
+			break;
+		case SCTP_FORWARD_CUM_TSN:
+#ifdef SCTP_DEBUG
+			if (sctp_debug_on & SCTP_DEBUG_INPUT3) {
+				printf("SCTP_FWD-TSN\n");
+			}
+#endif /* SCTP_DEBUG */
+			/* He's alive so give him credit */
+                        {
+				int abort_flag = 0;
+				stcb->asoc.overall_error_count = 0;
+				*fwd_tsn_seen = 1;
+				sctp_handle_forward_tsn(stcb,
+				    (struct sctp_forward_tsn_chunk *)ch, &abort_flag);
+				if (abort_flag) {
+					*offset = length;
+					return (NULL);
+				} else {
+					stcb->asoc.overall_error_count = 0;
+				}
+
+                        }
+			break;
+		case SCTP_STREAM_RESET:
+#ifdef SCTP_DEBUG
+			if (sctp_debug_on & SCTP_DEBUG_INPUT3) {
+				printf("SCTP_STREAM_RESET\n");
+			}
+#endif /* SCTP_DEBUG */
+			ch = (struct sctp_chunkhdr *)sctp_m_getptr(m, *offset,
+			    chk_length, chunk_buf);
+			if (stcb->asoc.peer_supports_strreset == 0) {
+				/* hmm, peer should have annonced this, but
+				 * we will turn it on since he is sending us
+				 * a stream reset.
+				 */
+				stcb->asoc.peer_supports_strreset = 1;
+ 			}
+			sctp_handle_stream_reset(stcb, (struct sctp_stream_reset_req *)ch);
+			break;
+		case SCTP_PACKET_DROPPED:
+#ifdef SCTP_DEBUG
+			if (sctp_debug_on & SCTP_DEBUG_INPUT3) {
+				printf("SCTP_PACKET_DROPPED\n");
+			}
+#endif /* SCTP_DEBUG */
+			/* re-get it all please */
+			ch = (struct sctp_chunkhdr *)sctp_m_getptr(m, *offset,
+			    chk_length, chunk_buf);
+
+			sctp_handle_packet_dropped((struct sctp_pktdrop_chunk *)ch,
+			    stcb, *netp);
+
+
+			break;
+		default:
+			/* it's an unknown chunk! */
+			if ((ch->chunk_type & 0x40) && (stcb != NULL)) {
+				struct mbuf *mm;
+				struct sctp_paramhdr *phd;
+				MGETHDR(mm, MB_DONTWAIT, MT_HEADER);
+				if (mm) {
+					phd = mtod(mm, struct sctp_paramhdr *);
+					/* We cheat and use param type since we
+					 * did not bother to define a error
+					 * cause struct.
+					 * They are the same basic format with
+					 * different names.
+					 */
+					phd->param_type =
+					    htons(SCTP_CAUSE_UNRECOG_CHUNK);
+					phd->param_length =
+					    htons(chk_length + sizeof(*phd));
+					mm->m_len = sizeof(*phd);
+					mm->m_next = sctp_m_copym(m, *offset,
+					    SCTP_SIZE32(chk_length),
+					    MB_DONTWAIT);
+					if (mm->m_next) {
+						mm->m_pkthdr.len =
+						    SCTP_SIZE32(chk_length) +
+						    sizeof(*phd);
+						sctp_queue_op_err(stcb, mm);
+					} else {
+						sctp_m_freem(mm);
+#ifdef SCTP_DEBUG
+						if (sctp_debug_on &
+						    SCTP_DEBUG_INPUT1) {
+							printf("Gak can't copy the chunk into operr %d bytes\n",
+							    chk_length);
+						}
+#endif
+					}
+				}
+#ifdef SCTP_DEBUG
+				else {
+					if (sctp_debug_on & SCTP_DEBUG_INPUT3) {
+						printf("Gak can't mgethdr for op-err of unrec chunk\n");
+					}
+				}
+#endif
+			}
+			if ((ch->chunk_type & 0x80) == 0) {
+				/* discard this packet */
+				*offset = length;
+				return (stcb);
+			} /* else skip this bad chunk and continue... */
+			break;
+		} /* switch (ch->chunk_type) */
+		/* get the next chunk */
+		*offset += SCTP_SIZE32(chk_length);
+		if (*offset >= length) {
+			/* no more data left in the mbuf chain */
+			break;
+		}
+		ch = (struct sctp_chunkhdr *)sctp_m_getptr(m, *offset,
+		    sizeof(struct sctp_chunkhdr), chunk_buf);
+		if (ch == NULL) {
+			if (locked_tcb)
+				SCTP_TCB_UNLOCK(locked_tcb);
+			*offset = length;
+			return (NULL);
+		}
+	} /* while */
+	return (stcb);
+}
+
+
+/*
+ * Process the ECN bits we have something set so
+ * we must look to see if it is ECN(0) or ECN(1) or CE
+ */
+static void
+sctp_process_ecn_marked_a(struct sctp_tcb *stcb, struct sctp_nets *net,
+    u_int8_t ecn_bits)
+{
+	if ((ecn_bits & SCTP_CE_BITS) == SCTP_CE_BITS) {
+		;
+	} else if ((ecn_bits & SCTP_ECT1_BIT) == SCTP_ECT1_BIT) {
+		/*
+		 * we only add to the nonce sum for ECT1, ECT0
+		 * does not change the NS bit (that we have
+		 * yet to find a way to send it yet).
+		 */
+
+		/* ECN Nonce stuff */
+		stcb->asoc.receiver_nonce_sum++;
+		stcb->asoc.receiver_nonce_sum &= SCTP_SACK_NONCE_SUM;
+
+		/*
+		 * Drag up the last_echo point if cumack is larger since we
+		 * don't want the point falling way behind by more than 2^^31
+		 * and then having it be incorrect.
+		 */
+		if (compare_with_wrap(stcb->asoc.cumulative_tsn,
+		    stcb->asoc.last_echo_tsn, MAX_TSN)) {
+			stcb->asoc.last_echo_tsn = stcb->asoc.cumulative_tsn;
+		}
+	} else if ((ecn_bits & SCTP_ECT0_BIT) == SCTP_ECT0_BIT) {
+		/*
+		 * Drag up the last_echo point if cumack is larger since we
+		 * don't want the point falling way behind by more than 2^^31
+		 * and then having it be incorrect.
+		 */
+		if (compare_with_wrap(stcb->asoc.cumulative_tsn,
+		    stcb->asoc.last_echo_tsn, MAX_TSN)) {
+			stcb->asoc.last_echo_tsn = stcb->asoc.cumulative_tsn;
+		}
+	}
+}
+
+static void
+sctp_process_ecn_marked_b(struct sctp_tcb *stcb, struct sctp_nets *net,
+    u_int32_t high_tsn, u_int8_t ecn_bits)
+{
+	if ((ecn_bits & SCTP_CE_BITS) == SCTP_CE_BITS) {
+		/*
+		 * we possibly must notify the sender that a congestion
+		 * window reduction is in order. We do this
+		 * by adding a ECNE chunk to the output chunk
+		 * queue. The incoming CWR will remove this chunk.
+		 */
+		if (compare_with_wrap(high_tsn, stcb->asoc.last_echo_tsn,
+		    MAX_TSN)) {
+			/* Yep, we need to add a ECNE */
+			sctp_send_ecn_echo(stcb, net, high_tsn);
+			stcb->asoc.last_echo_tsn = high_tsn;
+		}
+	}
+}
+
+/*
+ * common input chunk processing (v4 and v6)
+ */
+int
+sctp_common_input_processing(struct mbuf **mm, int iphlen, int offset,
+    int length, struct sctphdr *sh, struct sctp_chunkhdr *ch,
+    struct sctp_inpcb *inp, struct sctp_tcb *stcb, struct sctp_nets *net,
+    u_int8_t ecn_bits)
+{
+	/*
+	 * Control chunk processing
+	 */
+	u_int32_t high_tsn;
+	int fwd_tsn_seen = 0, data_processed = 0;
+	struct mbuf *m = *mm;
+	int abort_flag = 0;
+
+	sctp_pegs[SCTP_DATAGRAMS_RCVD]++;
+#ifdef SCTP_AUDITING_ENABLED
+	sctp_audit_log(0xE0, 1);
+	sctp_auditing(0, inp, stcb, net);
+#endif
+
+#ifdef SCTP_DEBUG
+	if (sctp_debug_on & SCTP_DEBUG_INPUT1) {
+		printf("Ok, Common input processing called, m:%x iphlen:%d offset:%d\n",
+		       (u_int)m, iphlen, offset);
+	}
+#endif /* SCTP_DEBUG */
+	if (IS_SCTP_CONTROL(ch)) {
+		/* process the control portion of the SCTP packet */
+#ifdef SCTP_DEBUG
+		if (sctp_debug_on & SCTP_DEBUG_INPUT1) {
+			printf("Processing control\n");
+		}
+#endif /* SCTP_DEBUG */
+
+		stcb = sctp_process_control(m, iphlen, &offset, length, sh, ch,
+		    inp, stcb, &net, &fwd_tsn_seen);
+	} else {
+		/*
+		 * no control chunks, so pre-process DATA chunks
+		 * (these checks are taken care of by control processing)
+		 */
+#ifdef SCTP_DEBUG
+		if (sctp_debug_on & SCTP_DEBUG_INPUT1) {
+			printf("No control present\n");
+		}
+#endif /* SCTP_DEBUG */
+
+		if (stcb == NULL) {
+			/* out of the blue DATA chunk */
+			sctp_handle_ootb(m, iphlen, offset, sh, inp, NULL);
+			return (1);
+		}
+		if (stcb->asoc.my_vtag != ntohl(sh->v_tag)) {
+			/* v_tag mismatch! */
+			sctp_pegs[SCTP_BAD_VTAGS]++;
+			SCTP_TCB_UNLOCK(stcb);
+			return (1);
+		}
+	}
+	if (stcb == NULL) {
+		/*
+		 * no valid TCB for this packet,
+		 * or we found it's a bad packet while processing control,
+		 * or we're done with this packet (done or skip rest of data),
+		 * so we drop it...
+		 */
+		return (1);
+	}
+#ifdef SCTP_DEBUG
+	if (sctp_debug_on & SCTP_DEBUG_INPUT1) {
+		printf("Ok, control finished time to look for data (%d) offset:%d\n",
+		       length, offset);
+	}
+#endif /* SCTP_DEBUG */
+	/*
+	 * DATA chunk processing
+	 */
+	/* plow through the data chunks while length > offset */
+	stcb->asoc.seen_a_sack_this_pkt = 0;
+
+	if (length > offset) {
+		int retval;
+		/*
+		 * First check to make sure our state is correct.
+		 * We would not get here unless we really did have a
+		 * tag, so we don't abort if this happens, just
+		 * dump the chunk silently.
+		 */
+		switch (SCTP_GET_STATE(&stcb->asoc)) {
+		case SCTP_STATE_COOKIE_ECHOED:
+			/*
+			 * we consider data with valid tags in
+			 * this state shows us the cookie-ack was lost.
+			 * Imply it was there.
+			 */
+			stcb->asoc.overall_error_count = 0;
+			sctp_handle_cookie_ack(
+			    (struct sctp_cookie_ack_chunk *)ch, stcb, net);
+			break;
+		case SCTP_STATE_COOKIE_WAIT:
+			/*
+			 * We consider OOTB any data sent during asoc setup.
+			 */
+			sctp_handle_ootb(m, iphlen, offset, sh, inp, NULL);
+			SCTP_TCB_UNLOCK(stcb);
+			return (1);
+		        break;
+		case SCTP_STATE_EMPTY:	/* should not happen */
+		case SCTP_STATE_INUSE:	/* should not happen */
+		case SCTP_STATE_SHUTDOWN_RECEIVED:  /* This is a peer error */
+		case SCTP_STATE_SHUTDOWN_ACK_SENT:
+		default:
+#ifdef SCTP_DEBUG
+			if (sctp_debug_on & SCTP_DEBUG_INPUT1) {
+				printf("Got data in invalid state %d.. dropping\n", stcb->asoc.state);
+			}
+#endif
+			SCTP_TCB_UNLOCK(stcb);
+			return (1);
+			break;
+		case SCTP_STATE_OPEN:
+		case SCTP_STATE_SHUTDOWN_SENT:
+			break;
+		}
+		/* take care of ECN, part 1. */
+		if (stcb->asoc.ecn_allowed &&
+		    (ecn_bits & (SCTP_ECT0_BIT|SCTP_ECT1_BIT)) ) {
+			sctp_process_ecn_marked_a(stcb, net, ecn_bits);
+		}
+		/* plow through the data chunks while length > offset */
+		retval = sctp_process_data(mm, iphlen, &offset, length, sh,
+		    inp, stcb, net, &high_tsn);
+		if (retval == 2) {
+			/* The association aborted, NO UNLOCK needed
+			 * since the association is destroyed.
+			 */
+			return (0);
+		}
+
+		data_processed = 1;
+		if (retval == 0) {
+			/* take care of ecn part 2. */
+			if (stcb->asoc.ecn_allowed && (ecn_bits & (SCTP_ECT0_BIT|SCTP_ECT1_BIT)) ) {
+				sctp_process_ecn_marked_b(stcb, net, high_tsn, ecn_bits);
+
+			}
+		}
+
+		/*
+		 * Anything important needs to have been m_copy'ed in
+		 * process_data
+		 */
+	}
+	if ((data_processed == 0) && (fwd_tsn_seen)) {
+		int was_a_gap = 0;
+		if (compare_with_wrap(stcb->asoc.highest_tsn_inside_map,
+				      stcb->asoc.cumulative_tsn, MAX_TSN)) {
+			/* there was a gap before this data was processed */
+			was_a_gap = 1;
+		}
+		sctp_sack_check(stcb, 1, was_a_gap, &abort_flag);
+		if (abort_flag) {
+			/* Again, we aborted so NO UNLOCK needed */
+			return (0);
+		}
+	}
+	/* trigger send of any chunks in queue... */
+#ifdef SCTP_AUDITING_ENABLED
+	sctp_audit_log(0xE0, 2);
+	sctp_auditing(1, inp, stcb, net);
+#endif
+#ifdef SCTP_DEBUG
+	if (sctp_debug_on & SCTP_DEBUG_INPUT1) {
+		printf("Check for chunk output prw:%d tqe:%d tf=%d\n",
+		       stcb->asoc.peers_rwnd,
+		       TAILQ_EMPTY(&stcb->asoc.control_send_queue),
+		       stcb->asoc.total_flight);
+	}
+#endif
+	if (stcb->asoc.peers_rwnd > 0 ||
+	    !TAILQ_EMPTY(&stcb->asoc.control_send_queue) ||
+	    (stcb->asoc.peers_rwnd <= 0 && stcb->asoc.total_flight == 0)) {
+#ifdef SCTP_DEBUG
+		if (sctp_debug_on & SCTP_DEBUG_INPUT3) {
+			printf("Calling chunk OUTPUT\n");
+		}
+#endif
+		sctp_chunk_output(inp, stcb, 3);
+#ifdef SCTP_DEBUG
+		if (sctp_debug_on & SCTP_DEBUG_INPUT3) {
+			printf("chunk OUTPUT returns\n");
+		}
+#endif
+	}
+
+#ifdef SCTP_AUDITING_ENABLED
+	sctp_audit_log(0xE0, 3);
+	sctp_auditing(2, inp, stcb, net);
+#endif
+	SCTP_TCB_UNLOCK(stcb);
+	return (0);
+}
+
+#if defined(__OpenBSD__)
+static void
+sctp_saveopt(struct sctp_inpcb *inp, struct mbuf **mp, struct ip *ip,
+    struct mbuf *m)
+{
+	if (inp->ip_inp.inp.inp_flags & INP_RECVDSTADDR) {
+		*mp = sbcreatecontrol((caddr_t) &ip->ip_dst,
+		    sizeof(struct in_addr), IP_RECVDSTADDR, IPPROTO_IP);
+		if (*mp)
+			mp = &(*mp)->m_next;
+	}
+}
+#endif
+
+extern int sctp_no_csum_on_loopback;
+
+#if defined(__FreeBSD__) || defined(__APPLE__)
+void
+sctp_input(m, off)
+	struct mbuf *m;
+	int off;
+#else
+void
+#if __STDC__
+sctp_input(struct mbuf *m, ...)
+#else
+sctp_input(m, va_alist)
+	struct mbuf *m;
+#endif
+#endif
+{
+	int iphlen;
+#ifdef __OpenBSD__
+	int s;
+#endif
+	u_int8_t ecn_bits;
+	struct ip *ip;
+	struct sctphdr *sh;
+	struct sctp_inpcb *inp = NULL;
+	struct mbuf *opts = 0;
+/*#ifdef INET6*/
+/* Don't think this is needed */
+/*	struct ip6_recvpktopts opts6;*/
+/*#endif INET6 */
+
+	u_int32_t check, calc_check;
+	struct sctp_nets *net;
+	struct sctp_tcb *stcb = NULL;
+	struct sctp_chunkhdr *ch;
+	int refcount_up = 0;
+	int length, mlen, offset;
+#if defined(__OpenBSD__) && defined(IPSEC)
+	struct inpcb *i_inp;
+	struct m_tag *mtag;
+	struct tdb_ident *tdbi;
+	struct tdb *tdb;
+	int error;
+#endif
+
+#if !(defined(__FreeBSD__) || defined(__APPLE__))
+#ifdef __DragonFly__
+	__va_list ap;
+	__va_start(ap, m);
+	iphlen = __va_arg(ap, int);
+	__va_end(ap);
+#else
+	va_list ap;
+	va_start(ap, m);
+	iphlen = va_arg(ap, int);
+	va_end(ap);
+#endif
+#else
+	iphlen = off;
+#endif
+	net = NULL;
+	sctp_pegs[SCTP_INPKTS]++;
+#ifdef SCTP_DEBUG
+	if (sctp_debug_on & SCTP_DEBUG_INPUT1) {
+		printf("V4 input gets a packet iphlen:%d pktlen:%d\n", iphlen, m->m_pkthdr.len);
+	}
+#endif
+/*#ifdef INET6*/
+/* Don't think this is needed */
+/*	bzero(&opts6, sizeof(opts6));*/
+/*#endif INET6 */
+
+	/*
+	 * Strip IP options, we don't allow any in or out.
+	 */
+	if ((size_t)iphlen > sizeof(struct ip)) {
+		ip_stripoptions(m);
+		iphlen = sizeof(struct ip);
+	}
+
+	/*
+	 * Get IP, SCTP, and first chunk header together in first mbuf.
+	 */
+	ip = mtod(m, struct ip *);
+	offset = iphlen + sizeof(*sh) + sizeof(*ch);
+	if (m->m_len < offset) {
+		if ((m = m_pullup(m, offset)) == 0) {
+			sctp_pegs[SCTP_HDR_DROPS]++;
+			return;
+		}
+		ip = mtod(m, struct ip *);
+	}
+	sh = (struct sctphdr *)((caddr_t)ip + iphlen);
+	ch = (struct sctp_chunkhdr *)((caddr_t)sh + sizeof(*sh));
+
+	/* SCTP does not allow broadcasts or multicasts */
+#if defined(__NetBSD__) || defined(__OpenBSD__)
+	if (IN_MULTICAST(ip->ip_dst.s_addr))
+#else
+	if (IN_MULTICAST(ntohl(ip->ip_dst.s_addr)))
+#endif
+	{
+		sctp_pegs[SCTP_IN_MCAST]++;
+		goto bad;
+	}
+	if (in_broadcast(ip->ip_dst, m->m_pkthdr.rcvif)) {
+		sctp_pegs[SCTP_IN_MCAST]++;
+		goto bad;
+	}
+
+	/* destination port of 0 is illegal, based on RFC2960. */
+	if (sh->dest_port == 0) {
+	        sctp_pegs[SCTP_HDR_DROPS]++;
+		goto bad;
+	}
+
+	/* validate SCTP checksum */
+	if ((sctp_no_csum_on_loopback == 0) ||
+	    (m->m_pkthdr.rcvif == NULL) ||
+	    (m->m_pkthdr.rcvif->if_type != IFT_LOOP)) {
+		/* we do NOT validate things from the loopback if the
+		 * sysctl is set to 1.
+		 */
+		check = sh->checksum;	/* save incoming checksum */
+		if ((check == 0) && (sctp_no_csum_on_loopback)) {
+			/* special hook for where we got a local address
+			 * somehow routed across a non IFT_LOOP type interface
+			 */
+			if (ip->ip_src.s_addr == ip->ip_dst.s_addr)
+				goto sctp_skip_csum_4;
+		}
+		sh->checksum = 0;		/* prepare for calc */
+		calc_check = sctp_calculate_sum(m, &mlen, iphlen);
+		if (calc_check != check) {
+#ifdef SCTP_DEBUG
+			if (sctp_debug_on & SCTP_DEBUG_INPUT1) {
+				printf("Bad CSUM on SCTP packet calc_check:%x check:%x  m:%x mlen:%d iphlen:%d\n",
+				       calc_check, check, (u_int)m, mlen, iphlen);
+			}
+#endif
+
+			stcb = sctp_findassociation_addr(m, iphlen,
+							 offset - sizeof(*ch),
+							 sh, ch, &inp, &net);
+			if ((inp) && (stcb)) {
+				sctp_send_packet_dropped(stcb, net, m, iphlen,
+							 1);
+				sctp_chunk_output(inp, stcb, 2);
+			} else if ((inp != NULL) && (stcb == NULL)) {
+				refcount_up = 1;
+			}
+			sctp_pegs[SCTP_BAD_CSUM]++;
+			goto bad;
+		}
+		sh->checksum = calc_check;
+	} else {
+	sctp_skip_csum_4:
+		mlen = m->m_pkthdr.len;
+	}
+	/* validate mbuf chain length with IP payload length */
+#if defined(__NetBSD__) || defined(__OpenBSD__)
+	/* Open BSD gives us the len in network order, fix it */
+	NTOHS(ip->ip_len);
+#endif
+	if (mlen < (ip->ip_len - iphlen)) {
+	        sctp_pegs[SCTP_HDR_DROPS]++;
+		goto bad;
+	}
+
+	/*
+	 * Locate pcb and tcb for datagram
+	 * sctp_findassociation_addr() wants IP/SCTP/first chunk header...
+	 */
+#ifdef SCTP_DEBUG
+	if (sctp_debug_on & SCTP_DEBUG_INPUT1) {
+		printf("V4 find association\n");
+	}
+#endif
+
+	stcb = sctp_findassociation_addr(m, iphlen, offset - sizeof(*ch),
+	    sh, ch, &inp, &net);
+	/* inp's ref-count increased && stcb locked */
+	if (inp == NULL) {
+		struct sctp_init_chunk *init_chk, chunk_buf;
+
+		sctp_pegs[SCTP_NOPORTS]++;
+#ifdef ICMP_BANDLIM
+		/*
+		 * we use the bandwidth limiting to protect against
+		 * sending too many ABORTS all at once. In this case
+		 * these count the same as an ICMP message.
+		 */
+		if (badport_bandlim(0) < 0)
+			goto bad;
+#endif /* ICMP_BANDLIM */
+#ifdef SCTP_DEBUG
+		if (sctp_debug_on & SCTP_DEBUG_INPUT1) {
+			printf("Sending a ABORT from packet entry!\n");
+		}
+#endif
+		if (ch->chunk_type == SCTP_INITIATION) {
+			/* we do a trick here to get the INIT tag,
+			 * dig in and get the tag from the INIT and
+			 * put it in the common header.
+			 */
+			init_chk = (struct sctp_init_chunk *)sctp_m_getptr(m,
+			    iphlen + sizeof(*sh), sizeof(*init_chk),
+			    (u_int8_t *)&chunk_buf);
+			if (init_chk != NULL)
+				sh->v_tag = init_chk->init.initiate_tag;
+		}
+		sctp_send_abort(m, iphlen, sh, 0, NULL);
+		goto bad;
+	} else if (stcb == NULL) {
+		refcount_up = 1;
+	}
+#ifdef IPSEC
+	/*
+	 * I very much doubt any of the IPSEC stuff will work but I have
+	 * no idea, so I will leave it in place.
+	 */
+
+#ifdef __OpenBSD__
+	/* FIX ME: this don't work... :) */
+	{
+   	    /* Find most recent IPsec tag */
+	    i_inp = &inp->ip_inp.inp;
+	    mtag = m_tag_find(m, PACKET_TAG_IPSEC_IN_DONE, NULL);
+	    s = splnet();
+	    if (mtag != NULL) {
+		    tdbi = (struct tdb_ident *)(mtag + 1);
+		    tdb = gettdb(tdbi->spi, &tdbi->dst, tdbi->proto);
+	    } else
+		    tdb = NULL;
+	    ipsp_spd_lookup(m, af, iphlen, &error, IPSP_DIRECTION_IN,
+			    tdb, i_inp);
+	    if (error) {
+		    splx(s);
+		    sctp_pegs[SCTP_HDR_DROPS]++;
+		    goto bad;
+	    }
+
+	    /* Latch SA */
+	    if (i_inp->inp_tdb_in != tdb) {
+		if (tdb) {
+		    tdb_add_inp(tdb, i_inp, 1);
+		    if (i_inp->inp_ipo == NULL) {
+			i_inp->inp_ipo = ipsec_add_policy(i_inp, af,
+							IPSP_DIRECTION_OUT);
+			if (i_inp->inp_ipo == NULL) {
+			    splx(s);
+			    sctp_pegs[SCTP_HDR_DROPS]++;
+			    goto bad;
+			}
+		    }
+		    if (i_inp->inp_ipo->ipo_dstid == NULL &&
+			tdb->tdb_srcid != NULL) {
+			i_inp->inp_ipo->ipo_dstid = tdb->tdb_srcid;
+			tdb->tdb_srcid->ref_count++;
+		    }
+		    if (i_inp->inp_ipsec_remotecred == NULL &&
+			tdb->tdb_remote_cred != NULL) {
+			i_inp->inp_ipsec_remotecred =
+			    tdb->tdb_remote_cred;
+			tdb->tdb_remote_cred->ref_count++;
+		    }
+		    if (i_inp->inp_ipsec_remoteauth == NULL &&
+			tdb->tdb_remote_auth != NULL) {
+			i_inp->inp_ipsec_remoteauth =
+			    tdb->tdb_remote_auth;
+			tdb->tdb_remote_auth->ref_count++;
+		    }
+		} else { /* Just reset */
+		    TAILQ_REMOVE(&i_inp->inp_tdb_in->tdb_inp_in, i_inp,
+				 inp_tdb_in_next);
+		    i_inp->inp_tdb_in = NULL;
+		}
+	    }
+	    splx(s);
+	}
+#else
+	if (ipsec4_in_reject_so(m, inp->ip_inp.inp.inp_socket)) {
+		ipsecstat.in_polvio++;
+		sctp_pegs[SCTP_HDR_DROPS]++;
+		goto bad;
+	}
+#endif
+#endif /* IPSEC */
+
+	/*
+	 * Construct sockaddr format source address.
+	 * Stuff source address and datagram in user buffer.
+	 */
+	if ((inp->ip_inp.inp.inp_flags & INP_CONTROLOPTS)
+#ifndef __OpenBSD__
+	    || (inp->sctp_socket->so_options & SO_TIMESTAMP)
+#endif
+		) {
+#ifdef __OpenBSD__
+		sctp_saveopt(inp, &opts, ip, m);
+#else
+		ip_savecontrol((struct inpcb *)inp, &opts, ip, m);
+#endif
+	}
+
+	/*
+	 * common chunk processing
+	 */
+#if defined(__FreeBSD__)  || defined(__APPLE__) || defined(__DragonFly__)
+	length = ip->ip_len + iphlen;
+#else
+	length = ip->ip_len - (ip->ip_hl << 2) + iphlen;
+#endif
+	offset -= sizeof(struct sctp_chunkhdr);
+
+	ecn_bits = ip->ip_tos;
+	crit_enter();
+	sctp_common_input_processing(&m, iphlen, offset, length, sh, ch,
+	    inp, stcb, net, ecn_bits);
+	/* inp's ref-count reduced && stcb unlocked */
+	crit_exit();
+	if (m) {
+		sctp_m_freem(m);
+	}
+	if (opts)
+		sctp_m_freem(opts);
+
+	if ((inp) && (refcount_up)) {
+		/* reduce ref-count */
+		SCTP_INP_WLOCK(inp);
+		SCTP_INP_DECR_REF(inp);
+		SCTP_INP_WUNLOCK(inp);
+	}
+
+	return;
+bad:
+	if (stcb)
+		SCTP_TCB_UNLOCK(stcb);
+
+	if ((inp) && (refcount_up)) {
+		/* reduce ref-count */
+		SCTP_INP_WLOCK(inp);
+		SCTP_INP_DECR_REF(inp);
+		SCTP_INP_WUNLOCK(inp);
+	}
+
+	if (m) {
+		sctp_m_freem(m);
+	}
+	if (opts)
+		sctp_m_freem(opts);
+	return;
+}
Index: sys/netinet/sctp_input.h
===================================================================
RCS file: sys/netinet/sctp_input.h
diff -N sys/netinet/sctp_input.h
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ sys/netinet/sctp_input.h	18 Jun 2005 16:05:48 -0000
@@ -0,0 +1,44 @@
+/*	$KAME: sctp_input.h,v 1.5 2004/08/17 04:06:17 itojun Exp $	*/
+
+#ifndef __sctp_input_h__
+#define __sctp_input_h__
+
+/*
+ * Copyright (C) 2002 Cisco Systems Inc,
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the project nor the names of its contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#if defined(_KERNEL) || defined(KERNEL)
+int sctp_common_input_processing(struct mbuf **, int, int, int,
+    struct sctphdr *, struct sctp_chunkhdr *, struct sctp_inpcb *,
+    struct sctp_tcb *, struct sctp_nets *, u_int8_t);
+
+void
+sctp_handle_stream_reset_response(struct sctp_tcb *, struct sctp_stream_reset_response *resp);
+
+#endif
+#endif
Index: sys/netinet/sctp_output.c
===================================================================
RCS file: sys/netinet/sctp_output.c
diff -N sys/netinet/sctp_output.c
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ sys/netinet/sctp_output.c	18 Jun 2005 16:05:50 -0000
@@ -0,0 +1,10459 @@
+/*	$KAME: sctp_output.c,v 1.46 2005/03/06 16:04:17 itojun Exp $	*/
+
+/*
+ * Copyright (C) 2002, 2003, 2004 Cisco Systems Inc,
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the project nor the names of its contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#if !(defined(__OpenBSD__) || defined (__APPLE__))
+#include "opt_ipsec.h"
+#endif
+#if defined(__FreeBSD__) || defined(__DragonFly__)
+#include "opt_compat.h"
+#include "opt_inet6.h"
+#include "opt_inet.h"
+#endif
+#if defined(__NetBSD__)
+#include "opt_inet.h"
+#endif
+#ifdef __APPLE__
+#include <sctp.h>
+#elif !defined(__OpenBSD__)
+#include "opt_sctp.h"
+#endif
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/malloc.h>
+#include <sys/mbuf.h>
+#ifndef __OpenBSD__
+#include <sys/domain.h>
+#endif
+#include <sys/protosw.h>
+#include <sys/socket.h>
+#include <sys/socketvar.h>
+#include <sys/proc.h>
+#include <sys/kernel.h>
+#include <sys/sysctl.h>
+#include <sys/resourcevar.h>
+#include <sys/uio.h>
+#ifdef INET6
+#include <sys/domain.h>
+#endif
+#include <sys/thread2.h>
+
+#if (defined(__FreeBSD__) && __FreeBSD_version >= 500000)
+#include <sys/limits.h>
+#else
+#include <machine/limits.h>
+#endif
+#include <machine/cpu.h>
+
+#include <net/if.h>
+#include <net/if_types.h>
+
+#if defined(__FreeBSD__) || defined(__DragonFly__)
+#include <net/if_var.h>
+#endif
+
+#include <net/route.h>
+
+#include <netinet/in.h>
+#include <netinet/in_systm.h>
+#include <netinet/ip.h>
+#include <netinet/in_pcb.h>
+#include <netinet/in_var.h>
+#include <netinet/ip_var.h>
+
+#ifdef INET6
+#include <netinet/ip6.h>
+#include <netinet6/ip6_var.h>
+#include <netinet6/scope6_var.h>
+#include <netinet6/nd6.h>
+
+#if defined(__FreeBSD__) || defined(__NetBSD__) || defined(__DragonFly__)
+#include <netinet6/in6_pcb.h>
+#elif defined(__OpenBSD__)
+#include <netinet/in_pcb.h>
+#endif
+
+#include <netinet/icmp6.h>
+
+#endif /* INET6 */
+
+#include <net/net_osdep.h>
+
+#if defined(HAVE_NRL_INPCB) || defined(__FreeBSD__) || defined(__DragonFly__)
+#ifndef in6pcb
+#define in6pcb		inpcb
+#endif
+#endif
+
+#include <netinet/sctp_pcb.h>
+
+#ifdef IPSEC
+#ifndef __OpenBSD__
+#include <netinet6/ipsec.h>
+#include <netkey/key.h>
+#else
+#undef IPSEC
+#endif
+#endif /* IPSEC */
+
+#include <netinet/sctp_var.h>
+#include <netinet/sctp_header.h>
+#include <netinet/sctputil.h>
+#include <netinet/sctp_pcb.h>
+#include <netinet/sctp_output.h>
+#include <netinet/sctp_uio.h>
+#include <netinet/sctputil.h>
+#include <netinet/sctp_hashdriver.h>
+#include <netinet/sctp_timer.h>
+#include <netinet/sctp_asconf.h>
+#include <netinet/sctp_indata.h>
+
+#ifdef SCTP_DEBUG
+extern uint32_t sctp_debug_on;
+#endif
+
+extern int sctp_peer_chunk_oh;
+
+static int
+sctp_find_cmsg(int c_type, void *data, struct mbuf *control, int cpsize)
+{
+	struct cmsghdr cmh;
+	int tlen, at;
+
+	tlen = control->m_len;
+	at = 0;
+	/*
+	 * Independent of how many mbufs, find the c_type inside the control
+	 * structure and copy out the data.
+	 */
+	while (at < tlen) {
+		if ((tlen-at) < (int)CMSG_ALIGN(sizeof(cmh))) {
+			/* not enough room for one more we are done. */
+			return (0);
+		}
+		m_copydata(control, at, sizeof(cmh), (caddr_t)&cmh);
+		if ((cmh.cmsg_len + at) > tlen) {
+			/*
+			 * this is real messed up since there is not enough
+			 * data here to cover the cmsg header. We are done.
+			 */
+			return (0);
+		}
+		if ((cmh.cmsg_level == IPPROTO_SCTP) &&
+		    (c_type == cmh.cmsg_type)) {
+			/* found the one we want, copy it out */
+			at += CMSG_ALIGN(sizeof(struct cmsghdr));
+			if ((int)(cmh.cmsg_len - CMSG_ALIGN(sizeof(struct cmsghdr))) < cpsize) {
+				/*
+				 * space of cmsg_len after header not
+				 * big enough
+				 */
+				return (0);
+			}
+			m_copydata(control, at, cpsize, data);
+			return (1);
+		 } else {
+			at += CMSG_ALIGN(cmh.cmsg_len);
+			if (cmh.cmsg_len == 0) {
+				break;
+			}
+		}
+	}
+	/* not found */
+	return (0);
+}
+
+static struct mbuf *
+sctp_add_addr_to_mbuf(struct mbuf *m, struct ifaddr *ifa)
+{
+	struct sctp_paramhdr *parmh;
+	struct mbuf *mret;
+	int len;
+	if (ifa->ifa_addr->sa_family == AF_INET) {
+		len = sizeof(struct sctp_ipv4addr_param);
+	} else if (ifa->ifa_addr->sa_family == AF_INET6) {
+		len = sizeof(struct sctp_ipv6addr_param);
+	} else {
+		/* unknown type */
+		return (m);
+	}
+
+	if (M_TRAILINGSPACE(m) >= len) {
+		/* easy side we just drop it on the end */
+		parmh = (struct sctp_paramhdr *)(m->m_data + m->m_len);
+		mret = m;
+	} else {
+		/* Need more space */
+		mret = m;
+		while (mret->m_next != NULL) {
+			mret = mret->m_next;
+		}
+		MGET(mret->m_next, MB_DONTWAIT, MT_DATA);
+		if (mret->m_next == NULL) {
+			/* We are hosed, can't add more addresses */
+			return (m);
+		}
+		mret = mret->m_next;
+		parmh = mtod(mret, struct sctp_paramhdr *);
+	}
+	/* now add the parameter */
+	if (ifa->ifa_addr->sa_family == AF_INET) {
+		struct sctp_ipv4addr_param *ipv4p;
+		struct sockaddr_in *sin;
+		sin = (struct sockaddr_in *)ifa->ifa_addr;
+		ipv4p = (struct sctp_ipv4addr_param *)parmh;
+		parmh->param_type = htons(SCTP_IPV4_ADDRESS);
+		parmh->param_length = htons(len);
+		ipv4p->addr = sin->sin_addr.s_addr;
+		mret->m_len += len;
+	} else if (ifa->ifa_addr->sa_family == AF_INET6) {
+		struct sctp_ipv6addr_param *ipv6p;
+		struct sockaddr_in6 *sin6;
+		sin6 = (struct sockaddr_in6 *)ifa->ifa_addr;
+		ipv6p = (struct sctp_ipv6addr_param *)parmh;
+		parmh->param_type = htons(SCTP_IPV6_ADDRESS);
+		parmh->param_length = htons(len);
+		memcpy(ipv6p->addr, &sin6->sin6_addr,
+		    sizeof(ipv6p->addr));
+		/* clear embedded scope in the address */
+		in6_clearscope((struct in6_addr *)ipv6p->addr);
+		mret->m_len += len;
+	} else {
+		return (m);
+	}
+	return (mret);
+}
+
+
+
+static struct mbuf *
+sctp_add_cookie(struct sctp_inpcb *inp, struct mbuf *init, int init_offset,
+    struct mbuf *initack, int initack_offset, struct sctp_state_cookie *stc_in)
+{
+	struct mbuf *copy_init, *copy_initack, *m_at, *sig, *mret;
+	struct sctp_state_cookie *stc;
+	struct sctp_paramhdr *ph;
+	uint8_t *signature;
+	int sig_offset;
+	uint16_t cookie_sz;
+
+	mret = NULL;
+
+	MGET(mret, MB_DONTWAIT, MT_DATA);
+	if (mret == NULL) {
+		return (NULL);
+	}
+	copy_init = sctp_m_copym(init, init_offset, M_COPYALL, MB_DONTWAIT);
+	if (copy_init == NULL) {
+		sctp_m_freem(mret);
+		return (NULL);
+	}
+	copy_initack = sctp_m_copym(initack, initack_offset, M_COPYALL,
+	    MB_DONTWAIT);
+	if (copy_initack == NULL) {
+		sctp_m_freem(mret);
+		sctp_m_freem(copy_init);
+		return (NULL);
+	}
+	/* easy side we just drop it on the end */
+	ph = mtod(mret, struct sctp_paramhdr *);
+	mret->m_len = sizeof(struct sctp_state_cookie) +
+	    sizeof(struct sctp_paramhdr);
+	stc = (struct sctp_state_cookie *)((caddr_t)ph +
+	    sizeof(struct sctp_paramhdr));
+	ph->param_type = htons(SCTP_STATE_COOKIE);
+	ph->param_length = 0;	/* fill in at the end */
+	/* Fill in the stc cookie data */
+	*stc = *stc_in;
+
+	/* tack the INIT and then the INIT-ACK onto the chain */
+	cookie_sz = 0;
+	m_at = mret;
+	for (m_at = mret; m_at; m_at = m_at->m_next) {
+		cookie_sz += m_at->m_len;
+		if (m_at->m_next == NULL) {
+			m_at->m_next = copy_init;
+			break;
+		}
+	}
+
+	for (m_at = copy_init; m_at; m_at = m_at->m_next) {
+		cookie_sz += m_at->m_len;
+		if (m_at->m_next == NULL) {
+			m_at->m_next = copy_initack;
+			break;
+		}
+	}
+
+	for (m_at = copy_initack; m_at; m_at = m_at->m_next) {
+		cookie_sz += m_at->m_len;
+		if (m_at->m_next == NULL) {
+			break;
+		}
+	}
+	MGET(sig, MB_DONTWAIT, MT_DATA);
+	if (sig == NULL) {
+		/* no space */
+		sctp_m_freem(mret);
+		sctp_m_freem(copy_init);
+		sctp_m_freem(copy_initack);
+		return (NULL);
+	}
+	sig->m_len = 0;
+	m_at->m_next = sig;
+	sig_offset = 0;
+	signature = (uint8_t *)(mtod(sig, caddr_t) + sig_offset);
+	/* Time to sign the cookie */
+	sctp_hash_digest_m((char *)inp->sctp_ep.secret_key[
+	    (int)(inp->sctp_ep.current_secret_number)],
+	    SCTP_SECRET_SIZE, mret, sizeof(struct sctp_paramhdr),
+	    (uint8_t *)signature);
+	sig->m_len += SCTP_SIGNATURE_SIZE;
+	cookie_sz += SCTP_SIGNATURE_SIZE;
+
+	ph->param_length = htons(cookie_sz);
+	return (mret);
+}
+
+
+static struct sockaddr_in *
+sctp_is_v4_ifa_addr_prefered (struct ifaddr *ifa, uint8_t loopscope, uint8_t ipv4_scope, uint8_t *sin_loop, uint8_t *sin_local)
+{
+	struct sockaddr_in *sin;
+	/*
+	 * Here we determine if its a prefered address. A
+	 * prefered address means it is the same scope or
+	 * higher scope then the destination.
+	 *  L = loopback, P = private, G = global
+	 * -----------------------------------------
+	 *  src    |      dest     |    result
+	 *-----------------------------------------
+	 *   L     |       L       |    yes
+	 *-----------------------------------------
+	 *   P     |       L       |    yes
+	 *-----------------------------------------
+	 *   G     |       L       |    yes
+	 *-----------------------------------------
+	 *   L     |       P       |    no
+	 *-----------------------------------------
+	 *   P     |       P       |    yes
+	 *-----------------------------------------
+	 *   G     |       P       |    no
+	 *-----------------------------------------
+	 *   L     |       G       |    no
+	 *-----------------------------------------
+	 *   P     |       G       |    no
+	 *-----------------------------------------
+	 *   G     |       G       |    yes
+	 *-----------------------------------------
+	 */
+
+	if (ifa->ifa_addr->sa_family != AF_INET) {
+		/* forget non-v4 */
+		return (NULL);
+	}
+	/* Ok the address may be ok */
+	sin = (struct sockaddr_in *)ifa->ifa_addr;
+	if (sin->sin_addr.s_addr == 0) {
+		return (NULL);
+	}
+	*sin_local = *sin_loop = 0;
+	if ((ifa->ifa_ifp->if_type == IFT_LOOP) ||
+	    (IN4_ISLOOPBACK_ADDRESS(&sin->sin_addr))) {
+		*sin_loop = 1;
+		*sin_local = 1;
+	}
+	if ((IN4_ISPRIVATE_ADDRESS(&sin->sin_addr))) {
+		*sin_local = 1;
+	}
+	if (!loopscope && *sin_loop) {
+		/* Its a loopback address and we don't have loop scope */
+		return (NULL);
+	}
+	if (!ipv4_scope && *sin_local) {
+		/* Its a private address, and we don't have private address scope */
+		return (NULL);
+	}
+	if (((ipv4_scope == 0) && (loopscope == 0)) && (*sin_local)) {
+		/* its a global src and a private dest */
+		return (NULL);
+	}
+	/* its a prefered address */
+	return (sin);
+}
+
+static struct sockaddr_in *
+sctp_is_v4_ifa_addr_acceptable (struct ifaddr *ifa, uint8_t loopscope, uint8_t ipv4_scope, uint8_t *sin_loop, uint8_t *sin_local)
+{
+	struct sockaddr_in *sin;
+	/*
+	 * Here we determine if its a acceptable address. A
+	 * acceptable address means it is the same scope or
+	 * higher scope but we can allow for NAT which means
+	 * its ok to have a global dest and a private src.
+	 *
+	 *  L = loopback, P = private, G = global
+	 * -----------------------------------------
+	 *  src    |      dest     |    result
+	 *-----------------------------------------
+	 *   L     |       L       |    yes
+	 *-----------------------------------------
+	 *   P     |       L       |    yes
+	 *-----------------------------------------
+	 *   G     |       L       |    yes
+	 *-----------------------------------------
+	 *   L     |       P       |    no
+	 *-----------------------------------------
+	 *   P     |       P       |    yes
+	 *-----------------------------------------
+	 *   G     |       P       |    yes - probably this won't work.
+	 *-----------------------------------------
+	 *   L     |       G       |    no
+	 *-----------------------------------------
+	 *   P     |       G       |    yes
+	 *-----------------------------------------
+	 *   G     |       G       |    yes
+	 *-----------------------------------------
+	 */
+
+	if (ifa->ifa_addr->sa_family != AF_INET) {
+		/* forget non-v4 */
+		return (NULL);
+	}
+	/* Ok the address may be ok */
+	sin = (struct sockaddr_in *)ifa->ifa_addr;
+	if (sin->sin_addr.s_addr == 0) {
+		return (NULL);
+	}
+	*sin_local = *sin_loop = 0;
+	if ((ifa->ifa_ifp->if_type == IFT_LOOP) ||
+	    (IN4_ISLOOPBACK_ADDRESS(&sin->sin_addr))) {
+		*sin_loop = 1;
+		*sin_local = 1;
+	}
+	if ((IN4_ISPRIVATE_ADDRESS(&sin->sin_addr))) {
+		*sin_local = 1;
+	}
+	if (!loopscope && *sin_loop) {
+		/* Its a loopback address and we don't have loop scope */
+		return (NULL);
+	}
+	/* its an acceptable address */
+	return (sin);
+}
+
+/*
+ * This treats the address list on the ep as a restricted list
+ * (negative list). If a the passed address is listed, then
+ * the address is NOT allowed on the association.
+ */
+int
+sctp_is_addr_restricted(struct sctp_tcb *stcb, struct sockaddr *addr)
+{
+	struct sctp_laddr *laddr;
+#ifdef SCTP_DEBUG
+	int cnt=0;
+#endif
+	if (stcb == NULL) {
+		/* There are no restrictions, no TCB :-) */
+		return (0);
+	}
+#ifdef SCTP_DEBUG
+	LIST_FOREACH(laddr, &stcb->asoc.sctp_local_addr_list, sctp_nxt_addr) {
+		cnt++;
+	}
+	if (sctp_debug_on & SCTP_DEBUG_OUTPUT4) {
+		printf("There are %d addresses on the restricted list\n", cnt);
+	}
+	cnt = 0;
+#endif
+	LIST_FOREACH(laddr, &stcb->asoc.sctp_local_addr_list, sctp_nxt_addr) {
+		if (laddr->ifa == NULL) {
+#ifdef SCTP_DEBUG
+			if (sctp_debug_on & SCTP_DEBUG_OUTPUT1) {
+				printf("Help I have fallen and I can't get up!\n");
+			}
+#endif
+			continue;
+		}
+#ifdef SCTP_DEBUG
+		if (sctp_debug_on & SCTP_DEBUG_OUTPUT4) {
+			cnt++;
+			printf("Restricted address[%d]:", cnt);
+			sctp_print_address(laddr->ifa->ifa_addr);
+		}
+#endif
+		if (sctp_cmpaddr(addr, laddr->ifa->ifa_addr) == 1) {
+			/* Yes it is on the list */
+			return (1);
+		}
+	}
+	return (0);
+}
+
+static int
+sctp_is_addr_in_ep(struct sctp_inpcb *inp, struct ifaddr *ifa)
+{
+	struct sctp_laddr *laddr;
+
+	if (ifa == NULL)
+		return (0);
+	LIST_FOREACH(laddr, &inp->sctp_addr_list, sctp_nxt_addr) {
+		if (laddr->ifa == NULL) {
+#ifdef SCTP_DEBUG
+			if (sctp_debug_on & SCTP_DEBUG_OUTPUT1) {
+				printf("Help I have fallen and I can't get up!\n");
+			}
+#endif
+			continue;
+		}
+		if (laddr->ifa->ifa_addr == NULL)
+			continue;
+		if (laddr->ifa == ifa)
+			/* same pointer */
+			return (1);
+		if (laddr->ifa->ifa_addr->sa_family != ifa->ifa_addr->sa_family) {
+			/* skip non compatible address comparison */
+			continue;
+		}
+		if (sctp_cmpaddr(ifa->ifa_addr, laddr->ifa->ifa_addr) == 1) {
+			/* Yes it is restricted */
+			return (1);
+		}
+	}
+	return (0);
+}
+
+
+
+static struct in_addr
+sctp_choose_v4_boundspecific_inp(struct sctp_inpcb *inp,
+				 struct rtentry *rt,
+				 uint8_t ipv4_scope,
+				 uint8_t loopscope)
+{
+	struct in_addr ans;
+	struct sctp_laddr *laddr;
+	struct sockaddr_in *sin;
+	struct ifnet *ifn;
+	struct ifaddr *ifa;
+	uint8_t sin_loop, sin_local;
+
+	/* first question, is the ifn we will emit on
+	 * in our list, if so, we want that one.
+	 */
+	ifn = rt->rt_ifp;
+	if (ifn) {
+		/* is a prefered one on the interface we route out? */
+		TAILQ_FOREACH(ifa, &ifn->if_addrlist, ifa_list) {
+			sin = sctp_is_v4_ifa_addr_prefered (ifa, loopscope, ipv4_scope, &sin_loop, &sin_local);
+			if (sin == NULL)
+				continue;
+			if (sctp_is_addr_in_ep(inp, ifa)) {
+				return (sin->sin_addr);
+			}
+		}
+		/* is an acceptable one on the interface we route out? */
+		TAILQ_FOREACH(ifa, &ifn->if_addrlist, ifa_list) {
+			sin = sctp_is_v4_ifa_addr_acceptable (ifa, loopscope, ipv4_scope, &sin_loop, &sin_local);
+			if (sin == NULL)
+				continue;
+			if (sctp_is_addr_in_ep(inp, ifa)) {
+				return (sin->sin_addr);
+			}
+		}
+	}
+	/* ok, what about a prefered address in the inp */
+	for (laddr = LIST_FIRST(&inp->sctp_addr_list);
+	     laddr && (laddr != inp->next_addr_touse);
+	     laddr = LIST_NEXT(laddr, sctp_nxt_addr)) {
+		if (laddr->ifa == NULL) {
+			/* address has been removed */
+			continue;
+		}
+		sin = sctp_is_v4_ifa_addr_prefered (laddr->ifa, loopscope, ipv4_scope, &sin_loop, &sin_local);
+		if (sin == NULL)
+			continue;
+		return (sin->sin_addr);
+
+	}
+	/* ok, what about an acceptable address in the inp */
+	for (laddr = LIST_FIRST(&inp->sctp_addr_list);
+	     laddr && (laddr != inp->next_addr_touse);
+	     laddr = LIST_NEXT(laddr, sctp_nxt_addr)) {
+		if (laddr->ifa == NULL) {
+			/* address has been removed */
+			continue;
+		}
+		sin = sctp_is_v4_ifa_addr_acceptable (laddr->ifa, loopscope, ipv4_scope, &sin_loop, &sin_local);
+		if (sin == NULL)
+			continue;
+		return (sin->sin_addr);
+
+	}
+
+	/* no address bound can be a source for the destination we are in trouble */
+#ifdef SCTP_DEBUG
+	if (sctp_debug_on & SCTP_DEBUG_OUTPUT1) {
+		printf("Src address selection for EP, no acceptable src address found for address\n");
+	}
+#endif
+	memset(&ans, 0, sizeof(ans));
+	return (ans);
+}
+
+
+
+static struct in_addr
+sctp_choose_v4_boundspecific_stcb(struct sctp_inpcb *inp,
+				  struct sctp_tcb *stcb,
+				  struct sctp_nets *net,
+				  struct rtentry *rt,
+ 			          uint8_t ipv4_scope,
+				  uint8_t loopscope,
+				  int non_asoc_addr_ok)
+{
+	/*
+	 * Here we have two cases, bound all asconf
+	 * allowed. bound all asconf not allowed.
+	 *
+	 */
+	struct sctp_laddr *laddr, *starting_point;
+	struct in_addr ans;
+	struct ifnet *ifn;
+	struct ifaddr *ifa;
+	uint8_t sin_loop, sin_local, start_at_beginning=0;
+	struct sockaddr_in *sin;
+
+	/* first question, is the ifn we will emit on
+	 * in our list, if so, we want that one.
+	 */
+	ifn = rt->rt_ifp;
+
+ 	if (inp->sctp_flags & SCTP_PCB_FLAGS_DO_ASCONF) {
+		/*
+		 * Here we use the list of addresses on the endpoint. Then
+		 * the addresses listed on the "restricted" list is just that,
+		 * address that have not been added and can't be used (unless
+		 * the non_asoc_addr_ok is set).
+		 */
+#ifdef SCTP_DEBUG
+		if (sctp_debug_on & SCTP_DEBUG_OUTPUT1) {
+			printf("Have a STCB - asconf allowed, not bound all have a netgative list\n");
+		}
+#endif
+		/* first question, is the ifn we will emit on
+		 * in our list, if so, we want that one.
+		 */
+		if (ifn) {
+			/* first try for an prefered address on the ep */
+			TAILQ_FOREACH(ifa, &ifn->if_addrlist, ifa_list) {
+				if (sctp_is_addr_in_ep(inp, ifa)) {
+					sin = sctp_is_v4_ifa_addr_prefered (ifa, loopscope, ipv4_scope, &sin_loop, &sin_local);
+					if (sin == NULL)
+						continue;
+					if ((non_asoc_addr_ok == 0) &&
+					    (sctp_is_addr_restricted(stcb, (struct sockaddr *)sin))) {
+						/* on the no-no list */
+						continue;
+					}
+					return (sin->sin_addr);
+				}
+			}
+			/* next try for an acceptable address on the ep */
+			TAILQ_FOREACH(ifa, &ifn->if_addrlist, ifa_list) {
+				if (sctp_is_addr_in_ep(inp, ifa)) {
+					sin = sctp_is_v4_ifa_addr_acceptable (ifa, loopscope, ipv4_scope, &sin_loop, &sin_local);
+					if (sin == NULL)
+						continue;
+					if ((non_asoc_addr_ok == 0) &&
+					    (sctp_is_addr_restricted(stcb, (struct sockaddr *)sin))) {
+						/* on the no-no list */
+						continue;
+					}
+					return (sin->sin_addr);
+				}
+			}
+
+		}
+		/* if we can't find one like that then we must
+		 * look at all addresses bound to pick one at
+		 * first prefereable then secondly acceptable.
+		 */
+		starting_point = stcb->asoc.last_used_address;
+	sctpv4_from_the_top:
+		if (stcb->asoc.last_used_address == NULL) {
+			start_at_beginning=1;
+			stcb->asoc.last_used_address = LIST_FIRST(&inp->sctp_addr_list);
+		}
+		/* search beginning with the last used address */
+		for (laddr = stcb->asoc.last_used_address; laddr;
+		     laddr = LIST_NEXT(laddr, sctp_nxt_addr)) {
+			if (laddr->ifa == NULL) {
+				/* address has been removed */
+				continue;
+			}
+			sin = sctp_is_v4_ifa_addr_prefered (laddr->ifa, loopscope, ipv4_scope, &sin_loop, &sin_local);
+			if (sin == NULL)
+				continue;
+			if ((non_asoc_addr_ok == 0) &&
+			    (sctp_is_addr_restricted(stcb, (struct sockaddr *)sin))) {
+				/* on the no-no list */
+				continue;
+			}
+			return (sin->sin_addr);
+
+		}
+		if (start_at_beginning == 0) {
+			stcb->asoc.last_used_address = NULL;
+			goto sctpv4_from_the_top;
+		}
+		/* now try for any higher scope than the destination */
+		stcb->asoc.last_used_address = starting_point;
+		start_at_beginning = 0;
+	sctpv4_from_the_top2:
+		if (stcb->asoc.last_used_address == NULL) {
+			start_at_beginning=1;
+			stcb->asoc.last_used_address = LIST_FIRST(&inp->sctp_addr_list);
+		}
+		/* search beginning with the last used address */
+		for (laddr = stcb->asoc.last_used_address; laddr;
+		     laddr = LIST_NEXT(laddr, sctp_nxt_addr)) {
+			if (laddr->ifa == NULL) {
+				/* address has been removed */
+				continue;
+			}
+			sin = sctp_is_v4_ifa_addr_acceptable (laddr->ifa, loopscope, ipv4_scope, &sin_loop, &sin_local);
+			if (sin == NULL)
+				continue;
+			if ((non_asoc_addr_ok == 0) &&
+			    (sctp_is_addr_restricted(stcb, (struct sockaddr *)sin))) {
+				/* on the no-no list */
+				continue;
+			}
+			return (sin->sin_addr);
+		}
+		if (start_at_beginning == 0) {
+			stcb->asoc.last_used_address = NULL;
+			goto sctpv4_from_the_top2;
+		}
+	} else {
+		/*
+		 * Here we have an address list on the association, thats the
+		 * only valid source addresses that we can use.
+		 */
+#ifdef SCTP_DEBUG
+		if (sctp_debug_on & SCTP_DEBUG_OUTPUT1) {
+			printf("Have a STCB - no asconf allowed, not bound all have a postive list\n");
+		}
+#endif
+		/* First look at all addresses for one that is on
+		 * the interface we route out
+		 */
+		LIST_FOREACH(laddr, &stcb->asoc.sctp_local_addr_list,
+			     sctp_nxt_addr) {
+			if (laddr->ifa == NULL) {
+				/* address has been removed */
+				continue;
+			}
+			sin = sctp_is_v4_ifa_addr_prefered (laddr->ifa, loopscope, ipv4_scope, &sin_loop, &sin_local);
+			if (sin == NULL)
+				continue;
+			/* first question, is laddr->ifa an address associated with the emit interface */
+			if (ifn) {
+				TAILQ_FOREACH(ifa, &ifn->if_addrlist, ifa_list) {
+					if (laddr->ifa == ifa) {
+						sin = (struct sockaddr_in *)laddr->ifa->ifa_addr;
+						return (sin->sin_addr);
+					}
+					if (sctp_cmpaddr(ifa->ifa_addr, laddr->ifa->ifa_addr) == 1) {
+						sin = (struct sockaddr_in *)laddr->ifa->ifa_addr;
+						return (sin->sin_addr);
+					}
+				}
+			}
+		}
+		/* what about an acceptable one on the interface? */
+		LIST_FOREACH(laddr, &stcb->asoc.sctp_local_addr_list,
+			     sctp_nxt_addr) {
+			if (laddr->ifa == NULL) {
+				/* address has been removed */
+				continue;
+			}
+			sin = sctp_is_v4_ifa_addr_acceptable (laddr->ifa, loopscope, ipv4_scope, &sin_loop, &sin_local);
+			if (sin == NULL)
+				continue;
+			/* first question, is laddr->ifa an address associated with the emit interface */
+			if (ifn) {
+				TAILQ_FOREACH(ifa, &ifn->if_addrlist, ifa_list) {
+					if (laddr->ifa == ifa) {
+						sin = (struct sockaddr_in *)laddr->ifa->ifa_addr;
+						return (sin->sin_addr);
+					}
+					if (sctp_cmpaddr(ifa->ifa_addr, laddr->ifa->ifa_addr) == 1) {
+						sin = (struct sockaddr_in *)laddr->ifa->ifa_addr;
+						return (sin->sin_addr);
+					}
+				}
+			}
+		}
+		/* ok, next one that is preferable in general */
+		LIST_FOREACH(laddr, &stcb->asoc.sctp_local_addr_list,
+			     sctp_nxt_addr) {
+			if (laddr->ifa == NULL) {
+				/* address has been removed */
+				continue;
+			}
+			sin = sctp_is_v4_ifa_addr_prefered (laddr->ifa, loopscope, ipv4_scope, &sin_loop, &sin_local);
+			if (sin == NULL)
+				continue;
+			return (sin->sin_addr);
+		}
+
+		/* last, what about one that is acceptable */
+		LIST_FOREACH(laddr, &stcb->asoc.sctp_local_addr_list,
+			     sctp_nxt_addr) {
+			if (laddr->ifa == NULL) {
+				/* address has been removed */
+				continue;
+			}
+			sin = sctp_is_v4_ifa_addr_acceptable (laddr->ifa, loopscope, ipv4_scope, &sin_loop, &sin_local);
+			if (sin == NULL)
+				continue;
+			return (sin->sin_addr);
+		}
+	}
+	memset(&ans, 0, sizeof(ans));
+	return (ans);
+}
+
+static struct sockaddr_in *
+sctp_select_v4_nth_prefered_addr_from_ifn_boundall (struct ifnet *ifn, struct sctp_tcb *stcb, int non_asoc_addr_ok,
+						    uint8_t loopscope, uint8_t ipv4_scope, int cur_addr_num)
+{
+	struct ifaddr *ifa;
+	struct sockaddr_in *sin;
+	uint8_t sin_loop, sin_local;
+	int num_eligible_addr = 0;
+	TAILQ_FOREACH(ifa, &ifn->if_addrlist, ifa_list) {
+		sin = sctp_is_v4_ifa_addr_prefered (ifa, loopscope, ipv4_scope, &sin_loop, &sin_local);
+		if (sin == NULL)
+			continue;
+		if (stcb) {
+			if ((non_asoc_addr_ok == 0) && sctp_is_addr_restricted(stcb, (struct sockaddr *)sin)) {
+				/* It is restricted for some reason.. probably
+				 * not yet added.
+				 */
+				continue;
+			}
+		}
+		if (cur_addr_num == num_eligible_addr) {
+			return (sin);
+		}
+	}
+	return (NULL);
+}
+
+
+static int
+sctp_count_v4_num_prefered_boundall (struct ifnet *ifn, struct sctp_tcb *stcb, int non_asoc_addr_ok,
+				     uint8_t loopscope, uint8_t ipv4_scope, uint8_t *sin_loop, uint8_t *sin_local)
+{
+	struct ifaddr *ifa;
+	struct sockaddr_in *sin;
+	int num_eligible_addr = 0;
+
+	TAILQ_FOREACH(ifa, &ifn->if_addrlist, ifa_list) {
+		sin = sctp_is_v4_ifa_addr_prefered (ifa, loopscope, ipv4_scope, sin_loop, sin_local);
+		if (sin == NULL)
+			continue;
+		if (stcb) {
+			if ((non_asoc_addr_ok == 0) && sctp_is_addr_restricted(stcb, (struct sockaddr *)sin)) {
+				/* It is restricted for some reason.. probably
+				 * not yet added.
+				 */
+				continue;
+			}
+		}
+		num_eligible_addr++;
+	}
+	return (num_eligible_addr);
+
+}
+
+static struct in_addr
+sctp_choose_v4_boundall(struct sctp_inpcb *inp,
+			struct sctp_tcb *stcb,
+			struct sctp_nets *net,
+			struct rtentry *rt,
+			uint8_t ipv4_scope,
+			uint8_t loopscope,
+			int non_asoc_addr_ok)
+{
+	int cur_addr_num=0, num_prefered=0;
+	uint8_t sin_loop, sin_local;
+	struct ifnet *ifn;
+	struct sockaddr_in *sin;
+	struct in_addr ans;
+	struct ifaddr *ifa;
+	/*
+	 * For v4 we can use (in boundall) any address in the association. If
+	 * non_asoc_addr_ok is set we can use any address (at least in theory).
+	 * So we look for prefered addresses first. If we find one, we use it.
+	 * Otherwise we next try to get an address on the interface, which we
+	 * should be able to do (unless non_asoc_addr_ok is false and we are
+	 * routed out that way). In these cases where we can't use the address
+	 * of the interface we go through all the ifn's looking for an address
+	 * we can use and fill that in. Punting means we send back address
+	 * 0, which will probably cause problems actually since then IP will
+	 * fill in the address of the route ifn, which means we probably already
+	 * rejected it.. i.e. here comes an abort :-<.
+	 */
+	ifn = rt->rt_ifp;
+	if (net) {
+		cur_addr_num = net->indx_of_eligible_next_to_use;
+	}
+	if (ifn == NULL) {
+ 		goto bound_all_v4_plan_c;
+	}
+	num_prefered = sctp_count_v4_num_prefered_boundall (ifn, stcb, non_asoc_addr_ok, loopscope, ipv4_scope, &sin_loop, &sin_local);
+#ifdef SCTP_DEBUG
+	if (sctp_debug_on & SCTP_DEBUG_OUTPUT1) {
+		printf("Found %d prefered source addresses\n", num_prefered);
+	}
+#endif
+	if (num_prefered == 0) {
+		/* no eligible addresses, we must use some other
+		 * interface address if we can find one.
+		 */
+ 		goto bound_all_v4_plan_b;
+	}
+	/* Ok we have num_eligible_addr set with how many we can use,
+	 * this may vary from call to call due to addresses being deprecated etc..
+	 */
+	if (cur_addr_num >= num_prefered) {
+		cur_addr_num = 0;
+	}
+	/* select the nth address from the list (where cur_addr_num is the nth) and
+	 * 0 is the first one, 1 is the second one etc...
+	 */
+#ifdef SCTP_DEBUG
+	if (sctp_debug_on & SCTP_DEBUG_OUTPUT1) {
+		printf("cur_addr_num:%d\n", cur_addr_num);
+	}
+#endif
+	sin = sctp_select_v4_nth_prefered_addr_from_ifn_boundall (ifn, stcb, non_asoc_addr_ok, loopscope,
+								   ipv4_scope, cur_addr_num);
+
+	/* if sin is NULL something changed??, plan_a now */
+	if (sin) {
+		return (sin->sin_addr);
+	}
+
+	/*
+	 * plan_b: Look at the interface that we emit on
+	 *         and see if we can find an acceptable address.
+	 */
+ bound_all_v4_plan_b:
+	TAILQ_FOREACH(ifa, &ifn->if_addrlist, ifa_list) {
+		sin = sctp_is_v4_ifa_addr_acceptable (ifa, loopscope, ipv4_scope, &sin_loop, &sin_local);
+		if (sin == NULL)
+			continue;
+		if (stcb) {
+			if ((non_asoc_addr_ok == 0) && sctp_is_addr_restricted(stcb, (struct sockaddr *)sin)) {
+				/* It is restricted for some reason.. probably
+				 * not yet added.
+				 */
+				continue;
+			}
+		}
+		return (sin->sin_addr);
+	}
+	/*
+	 * plan_c: Look at all interfaces and find a prefered
+	 *         address. If we reache here we are in trouble I think.
+	 */
+ bound_all_v4_plan_c:
+	for (ifn = TAILQ_FIRST(&ifnet);
+	     ifn && (ifn != inp->next_ifn_touse);
+	     ifn=TAILQ_NEXT(ifn, if_list)) {
+		if (loopscope == 0 && ifn->if_type == IFT_LOOP) {
+			/* wrong base scope */
+			continue;
+		}
+		if (ifn == rt->rt_ifp)
+			/* already looked at this guy */
+			continue;
+		num_prefered = sctp_count_v4_num_prefered_boundall (ifn, stcb, non_asoc_addr_ok,
+								    loopscope, ipv4_scope, &sin_loop, &sin_local);
+#ifdef SCTP_DEBUG
+		if (sctp_debug_on & SCTP_DEBUG_OUTPUT1) {
+			printf("Found ifn:%x %d prefered source addresses\n", (u_int)ifn, num_prefered);
+		}
+#endif
+		if (num_prefered == 0) {
+			/*
+			 * None on this interface.
+			 */
+			continue;
+		}
+		/* Ok we have num_eligible_addr set with how many we can use,
+		 * this may vary from call to call due to addresses being deprecated etc..
+		 */
+		if (cur_addr_num >= num_prefered) {
+			cur_addr_num = 0;
+		}
+		sin = sctp_select_v4_nth_prefered_addr_from_ifn_boundall (ifn, stcb, non_asoc_addr_ok, loopscope,
+									  ipv4_scope, cur_addr_num);
+		if (sin == NULL)
+			continue;
+		return (sin->sin_addr);
+
+	}
+
+	/*
+	 * plan_d: We are in deep trouble. No prefered address on
+	 *         any interface. And the emit interface does not
+	 *         even have an acceptable address. Take anything
+	 *         we can get! If this does not work we are
+	 *         probably going to emit a packet that will
+	 *         illicit an ABORT, falling through.
+	 */
+
+	for (ifn = TAILQ_FIRST(&ifnet);
+	     ifn && (ifn != inp->next_ifn_touse);
+	     ifn=TAILQ_NEXT(ifn, if_list)) {
+		if (loopscope == 0 && ifn->if_type == IFT_LOOP) {
+			/* wrong base scope */
+			continue;
+		}
+		if (ifn == rt->rt_ifp)
+			/* already looked at this guy */
+			continue;
+
+		TAILQ_FOREACH(ifa, &ifn->if_addrlist, ifa_list) {
+			sin = sctp_is_v4_ifa_addr_acceptable (ifa, loopscope, ipv4_scope, &sin_loop, &sin_local);
+			if (sin == NULL)
+				continue;
+			if (stcb) {
+				if ((non_asoc_addr_ok == 0) && sctp_is_addr_restricted(stcb, (struct sockaddr *)sin)) {
+					/* It is restricted for some reason.. probably
+					 * not yet added.
+					 */
+					continue;
+				}
+			}
+			return (sin->sin_addr);
+		}
+	}
+	/*
+	 * Ok we can find NO address to source from that is
+	 * not on our negative list. It is either the special
+	 * ASCONF case where we are sourceing from a intf that
+	 * has been ifconfig'd to a different address (i.e.
+	 * it holds a ADD/DEL/SET-PRIM and the proper lookup
+	 * address. OR we are hosed, and this baby is going
+	 * to abort the association.
+	 */
+	if (non_asoc_addr_ok) {
+		return (((struct sockaddr_in *)(rt->rt_ifa->ifa_addr))->sin_addr);
+	} else {
+		memset(&ans, 0, sizeof(ans));
+		return (ans);
+	}
+}
+
+
+
+/* tcb may be NULL */
+struct in_addr
+sctp_ipv4_source_address_selection(struct sctp_inpcb *inp,
+    struct sctp_tcb *stcb, struct route *ro, struct sctp_nets *net,
+    int non_asoc_addr_ok)
+{
+	struct in_addr ans;
+	struct sockaddr_in *to = (struct sockaddr_in *)&ro->ro_dst;
+	uint8_t ipv4_scope, loopscope;
+	/*
+	 * Rules:
+	 * - Find the route if needed, cache if I can.
+	 * - Look at interface address in route, Is it
+	 *   in the bound list. If so we have the best source.
+	 * - If not we must rotate amongst the addresses.
+	 *
+	 * Cavets and issues
+	 *
+	 * Do we need to pay attention to scope. We can have
+	 * a private address or a global address we are sourcing
+	 * or sending to. So if we draw it out
+	 *      source     *      dest   *  result
+	 *  ------------------------------------------
+	 *  a   Private    *     Global  *  NAT?
+	 *  ------------------------------------------
+	 *  b   Private    *     Private *  No problem
+	 *  ------------------------------------------
+	 *  c   Global     *     Private *  Huh, How will this work?
+	 *  ------------------------------------------
+	 *  d   Global     *     Global  *  No Problem
+	 *  ------------------------------------------
+	 *
+	 * And then we add to that what happens if there are multiple
+	 * addresses assigned to an interface. Remember the ifa on a
+	 * ifn is a linked list of addresses. So one interface can
+	 * have more than one IPv4 address. What happens if we
+	 * have both a private and a global address? Do we then
+	 * use context of destination to sort out which one is
+	 * best? And what about NAT's sending P->G may get you
+	 * a NAT translation, or should you select the G thats
+	 * on the interface in preference.
+	 *
+	 * Decisions:
+	 *
+	 *  - count the number of addresses on the interface.
+	 *  - if its one, no problem except case <c>. For <a>
+	 *    we will assume a NAT out there.
+	 *  - if there are more than one, then we need to worry
+	 *    about scope P or G. We should prefer G -> G and
+	 *    P -> P if possible. Then as a secondary fall back
+	 *    to mixed types G->P being a last ditch one.
+	 *  - The above all works for bound all, but bound
+	 *    specific we need to use the same concept but instead
+	 *    only consider the bound addresses. If the bound set
+	 *    is NOT assigned to the interface then we must use
+	 *    rotation amongst them.
+	 *
+	 * Notes: For v4, we can always punt and let ip_output
+	 * decide by sending back a source of 0.0.0.0
+	 */
+
+	if (ro->ro_rt == NULL) {
+		/*
+		 * Need a route to cache.
+		 *
+		 */
+#if defined(__FreeBSD__) || defined(__APPLE__) || defined(__DragonFly__)
+		rtalloc_ign(ro, 0UL);
+#else
+		rtalloc(ro);
+#endif
+        }
+	if (ro->ro_rt == NULL) {
+		/* No route to host .. punt */
+		memset(&ans, 0, sizeof(ans));
+		return (ans);
+	}
+	/* Setup our scopes */
+	if (stcb) {
+		ipv4_scope = stcb->asoc.ipv4_local_scope;
+		loopscope = stcb->asoc.loopback_scope;
+	} else {
+		/* Scope based on outbound address */
+		if ((IN4_ISPRIVATE_ADDRESS(&to->sin_addr))) {
+			ipv4_scope = 1;
+			loopscope = 0;
+		} else if (IN4_ISLOOPBACK_ADDRESS(&to->sin_addr)) {
+			ipv4_scope = 1;
+			loopscope = 1;
+		} else {
+			ipv4_scope = 0;
+			loopscope = 0;
+		}
+	}
+#ifdef SCTP_DEBUG
+	if (sctp_debug_on & SCTP_DEBUG_OUTPUT1) {
+		printf("Scope setup loop:%d ipv4_scope:%d\n",
+		       loopscope, ipv4_scope);
+	}
+#endif
+	if (inp->sctp_flags & SCTP_PCB_FLAGS_BOUNDALL) {
+		/*
+		 * When bound to all if the address list is set
+		 * it is a negative list. Addresses being added
+		 * by asconf.
+		 */
+		return (sctp_choose_v4_boundall(inp, stcb, net, ro->ro_rt,
+		    ipv4_scope, loopscope, non_asoc_addr_ok));
+        }
+	/*
+ 	 * Three possiblities here:
+	 *
+	 * a) stcb is NULL, which means we operate only from
+	 *    the list of addresses (ifa's) bound to the assoc and
+	 *    we care not about the list.
+	 * b) stcb is NOT-NULL, which means we have an assoc structure and
+	 *    auto-asconf is on. This means that the list of addresses is
+         *    a NOT list. We use the list from the inp, but any listed address
+	 *    in our list is NOT yet added. However if the non_asoc_addr_ok is
+	 *    set we CAN use an address NOT available (i.e. being added). Its
+	 *    a negative list.
+	 * c) stcb is NOT-NULL, which means we have an assoc structure and
+	 *    auto-asconf is off. This means that the list of addresses is
+         *    the ONLY addresses I can use.. its positive.
+	 *
+	 *    Note we collapse b & c into the same function just like in
+	 *    the v6 address selection.
+	 */
+	if (stcb) {
+		return (sctp_choose_v4_boundspecific_stcb(inp, stcb, net,
+		    ro->ro_rt, ipv4_scope, loopscope, non_asoc_addr_ok));
+	} else {
+		return (sctp_choose_v4_boundspecific_inp(inp, ro->ro_rt,
+		    ipv4_scope, loopscope));
+	}
+	/* this should not be reached */
+	memset(&ans, 0, sizeof(ans));
+	return (ans);
+}
+
+
+
+static struct sockaddr_in6 *
+sctp_is_v6_ifa_addr_acceptable (struct ifaddr *ifa, int loopscope, int loc_scope, int *sin_loop, int *sin_local)
+{
+	struct in6_ifaddr *ifa6;
+	struct sockaddr_in6 *sin6;
+
+	if (ifa->ifa_addr->sa_family != AF_INET6) {
+		/* forget non-v6 */
+		return (NULL);
+	}
+	ifa6 = (struct in6_ifaddr *)ifa;
+	/* ok to use deprecated addresses? */
+	if (!ip6_use_deprecated) {
+		if (IFA6_IS_DEPRECATED(ifa6)) {
+			/* can't use this type */
+			return (NULL);
+		}
+	}
+	/* are we ok, with the current state of this address? */
+	if (ifa6->ia6_flags &
+	    (IN6_IFF_DETACHED | IN6_IFF_NOTREADY | IN6_IFF_ANYCAST)) {
+		/* Can't use these types */
+		return (NULL);
+	}
+	/* Ok the address may be ok */
+	sin6 = (struct sockaddr_in6 *)ifa->ifa_addr;
+	*sin_local = *sin_loop = 0;
+	if ((ifa->ifa_ifp->if_type == IFT_LOOP) ||
+	    (IN6_IS_ADDR_LOOPBACK(&sin6->sin6_addr))) {
+		*sin_loop = 1;
+	}
+	if (!loopscope && *sin_loop) {
+		/* Its a loopback address and we don't have loop scope */
+		return (NULL);
+	}
+	if (IN6_IS_ADDR_UNSPECIFIED(&sin6->sin6_addr)) {
+		/* we skip unspecifed addresses */
+		return (NULL);
+	}
+
+	if (IN6_IS_ADDR_LINKLOCAL(&sin6->sin6_addr)) {
+		*sin_local = 1;
+	}
+	if (!loc_scope && *sin_local) {
+		/* Its a link local address, and we don't have link local scope */
+		return (NULL);
+	}
+	return (sin6);
+}
+
+
+static struct sockaddr_in6 *
+sctp_choose_v6_boundspecific_stcb(struct sctp_inpcb *inp,
+				  struct sctp_tcb *stcb,
+				  struct sctp_nets *net,
+				  struct rtentry *rt,
+ 			          uint8_t loc_scope,
+				  uint8_t loopscope,
+				  int non_asoc_addr_ok)
+{
+	/*
+	 *   Each endpoint has a list of local addresses associated
+	 *   with it. The address list is either a "negative list" i.e.
+	 *   those addresses that are NOT allowed to be used as a source OR
+	 *   a "postive list" i.e. those addresses that CAN be used.
+	 *
+	 *   Its a negative list if asconf is allowed. What we do
+	 *   in this case is use the ep address list BUT we have
+	 *   to cross check it against the negative list.
+	 *
+	 *   In the case where NO asconf is allowed, we have just
+	 *   a straight association level list that we must use to
+	 *   find a source address.
+	 */
+	struct sctp_laddr *laddr, *starting_point;
+	struct sockaddr_in6 *sin6;
+	int sin_loop, sin_local;
+	int start_at_beginning=0;
+	struct ifnet *ifn;
+	struct ifaddr *ifa;
+
+	ifn = rt->rt_ifp;
+	if (inp->sctp_flags & SCTP_PCB_FLAGS_DO_ASCONF) {
+#ifdef SCTP_DEBUG
+		if (sctp_debug_on & SCTP_DEBUG_OUTPUT1) {
+			printf("Have a STCB - asconf allowed, not bound all have a netgative list\n");
+		}
+#endif
+		/* first question, is the ifn we will emit on
+		 * in our list, if so, we want that one.
+		 */
+		if (ifn) {
+			TAILQ_FOREACH(ifa, &ifn->if_addrlist, ifa_list) {
+				if (sctp_is_addr_in_ep(inp, ifa)) {
+					sin6 = sctp_is_v6_ifa_addr_acceptable (ifa, loopscope, loc_scope, &sin_loop, &sin_local);
+					if (sin6 == NULL)
+						continue;
+					if ((non_asoc_addr_ok == 0) &&
+					    (sctp_is_addr_restricted(stcb, (struct sockaddr *)sin6))) {
+						/* on the no-no list */
+						continue;
+					}
+					return (sin6);
+				}
+			}
+		}
+		starting_point = stcb->asoc.last_used_address;
+		/* First try for matching scope */
+	sctp_from_the_top:
+		if (stcb->asoc.last_used_address == NULL) {
+			start_at_beginning=1;
+			stcb->asoc.last_used_address = LIST_FIRST(&inp->sctp_addr_list);
+		}
+		/* search beginning with the last used address */
+		for (laddr = stcb->asoc.last_used_address; laddr;
+		     laddr = LIST_NEXT(laddr, sctp_nxt_addr)) {
+			if (laddr->ifa == NULL) {
+				/* address has been removed */
+				continue;
+			}
+			sin6 = sctp_is_v6_ifa_addr_acceptable (laddr->ifa, loopscope, loc_scope, &sin_loop, &sin_local);
+			if (sin6 == NULL)
+				continue;
+			if ((non_asoc_addr_ok == 0) && (sctp_is_addr_restricted(stcb, (struct sockaddr *)sin6))) {
+				/* on the no-no list */
+				continue;
+			}
+			/* is it of matching scope ? */
+			if ((loopscope == 0) &&
+			    (loc_scope == 0) &&
+			    (sin_loop == 0) &&
+			    (sin_local == 0)) {
+				/* all of global scope we are ok with it */
+				return (sin6);
+			}
+			if (loopscope && sin_loop)
+				/* both on the loopback, thats ok */
+				return (sin6);
+			if (loc_scope && sin_local)
+				/* both local scope */
+				return (sin6);
+
+		}
+		if (start_at_beginning == 0) {
+			stcb->asoc.last_used_address = NULL;
+			goto sctp_from_the_top;
+		}
+		/* now try for any higher scope than the destination */
+		stcb->asoc.last_used_address = starting_point;
+		start_at_beginning = 0;
+	sctp_from_the_top2:
+		if (stcb->asoc.last_used_address == NULL) {
+			start_at_beginning=1;
+			stcb->asoc.last_used_address = LIST_FIRST(&inp->sctp_addr_list);
+		}
+		/* search beginning with the last used address */
+		for (laddr = stcb->asoc.last_used_address; laddr;
+		     laddr = LIST_NEXT(laddr, sctp_nxt_addr)) {
+			if (laddr->ifa == NULL) {
+				/* address has been removed */
+				continue;
+			}
+			sin6 = sctp_is_v6_ifa_addr_acceptable (laddr->ifa, loopscope, loc_scope, &sin_loop, &sin_local);
+			if (sin6 == NULL)
+				continue;
+			if ((non_asoc_addr_ok == 0) && (sctp_is_addr_restricted(stcb, (struct sockaddr *)sin6))) {
+				/* on the no-no list */
+				continue;
+			}
+			return (sin6);
+		}
+		if (start_at_beginning == 0) {
+			stcb->asoc.last_used_address = NULL;
+			goto sctp_from_the_top2;
+		}
+	} else {
+#ifdef SCTP_DEBUG
+		if (sctp_debug_on & SCTP_DEBUG_OUTPUT1) {
+			printf("Have a STCB - no asconf allowed, not bound all have a postive list\n");
+		}
+#endif
+		/* First try for interface output match */
+		LIST_FOREACH(laddr, &stcb->asoc.sctp_local_addr_list,
+			     sctp_nxt_addr) {
+			if (laddr->ifa == NULL) {
+				/* address has been removed */
+				continue;
+			}
+			sin6 = sctp_is_v6_ifa_addr_acceptable (laddr->ifa, loopscope, loc_scope, &sin_loop, &sin_local);
+			if (sin6 == NULL)
+				continue;
+			/* first question, is laddr->ifa an address associated with the emit interface */
+			if (ifn) {
+				TAILQ_FOREACH(ifa, &ifn->if_addrlist, ifa_list) {
+					if (laddr->ifa == ifa) {
+						sin6 = (struct sockaddr_in6 *)laddr->ifa->ifa_addr;
+						return (sin6);
+					}
+					if (sctp_cmpaddr(ifa->ifa_addr, laddr->ifa->ifa_addr) == 1) {
+						sin6 = (struct sockaddr_in6 *)laddr->ifa->ifa_addr;
+						return (sin6);
+					}
+				}
+			}
+		}
+		/* Next try for matching scope */
+		LIST_FOREACH(laddr, &stcb->asoc.sctp_local_addr_list,
+			     sctp_nxt_addr) {
+			if (laddr->ifa == NULL) {
+				/* address has been removed */
+				continue;
+			}
+			sin6 = sctp_is_v6_ifa_addr_acceptable (laddr->ifa, loopscope, loc_scope, &sin_loop, &sin_local);
+			if (sin6 == NULL)
+				continue;
+
+			if ((loopscope == 0) &&
+			    (loc_scope == 0) &&
+			    (sin_loop == 0) &&
+			    (sin_local == 0)) {
+				/* all of global scope we are ok with it */
+				return (sin6);
+			}
+			if (loopscope && sin_loop)
+				/* both on the loopback, thats ok */
+				return (sin6);
+			if (loc_scope && sin_local)
+				/* both local scope */
+				return (sin6);
+		}
+		/* ok, now try for a higher scope in the source address */
+		/* First try for matching scope */
+		LIST_FOREACH(laddr, &stcb->asoc.sctp_local_addr_list,
+			     sctp_nxt_addr) {
+			if (laddr->ifa == NULL) {
+				/* address has been removed */
+				continue;
+			}
+			sin6 = sctp_is_v6_ifa_addr_acceptable (laddr->ifa, loopscope, loc_scope, &sin_loop, &sin_local);
+			if (sin6 == NULL)
+				continue;
+			return (sin6);
+		}
+	}
+	return (NULL);
+}
+
+static struct sockaddr_in6 *
+sctp_choose_v6_boundspecific_inp(struct sctp_inpcb *inp,
+				 struct rtentry *rt,
+				 uint8_t loc_scope,
+				 uint8_t loopscope)
+{
+	/*
+	 * Here we are bound specific and have only
+	 * an inp. We must find an address that is bound
+	 * that we can give out as a src address. We
+	 * prefer two addresses of same scope if we can
+	 * find them that way.
+	 */
+	struct sctp_laddr *laddr;
+	struct sockaddr_in6 *sin6;
+	struct ifnet *ifn;
+	struct ifaddr *ifa;
+	int sin_loop, sin_local;
+
+	/* first question, is the ifn we will emit on
+	 * in our list, if so, we want that one.
+	 */
+
+	ifn = rt->rt_ifp;
+	if (ifn) {
+		TAILQ_FOREACH(ifa, &ifn->if_addrlist, ifa_list) {
+			sin6 = sctp_is_v6_ifa_addr_acceptable (ifa, loopscope, loc_scope, &sin_loop, &sin_local);
+			if (sin6 == NULL)
+				continue;
+			if (sctp_is_addr_in_ep(inp, ifa)) {
+				return (sin6);
+			}
+		}
+	}
+	for (laddr = LIST_FIRST(&inp->sctp_addr_list);
+	     laddr && (laddr != inp->next_addr_touse);
+	     laddr = LIST_NEXT(laddr, sctp_nxt_addr)) {
+		if (laddr->ifa == NULL) {
+			/* address has been removed */
+			continue;
+		}
+		sin6 = sctp_is_v6_ifa_addr_acceptable (laddr->ifa, loopscope, loc_scope, &sin_loop, &sin_local);
+		if (sin6 == NULL)
+			continue;
+
+		if ((loopscope == 0) &&
+		    (loc_scope == 0) &&
+		    (sin_loop == 0) &&
+		    (sin_local == 0)) {
+			/* all of global scope we are ok with it */
+			return (sin6);
+		}
+		if (loopscope && sin_loop)
+			/* both on the loopback, thats ok */
+			return (sin6);
+		if (loc_scope && sin_local)
+			/* both local scope */
+			return (sin6);
+
+	}
+	/* if we reach here, we could not find two addresses
+	 * of the same scope to give out. Lets look for any higher level
+	 * scope for a source address.
+	 */
+	for (laddr = LIST_FIRST(&inp->sctp_addr_list);
+	     laddr && (laddr != inp->next_addr_touse);
+	     laddr = LIST_NEXT(laddr, sctp_nxt_addr)) {
+		if (laddr->ifa == NULL) {
+			/* address has been removed */
+			continue;
+		}
+		sin6 = sctp_is_v6_ifa_addr_acceptable (laddr->ifa, loopscope, loc_scope, &sin_loop, &sin_local);
+		if (sin6 == NULL)
+			continue;
+		return (sin6);
+	}
+	/* no address bound can be a source for the destination */
+#ifdef SCTP_DEBUG
+	if (sctp_debug_on & SCTP_DEBUG_OUTPUT1) {
+		printf("Src address selection for EP, no acceptable src address found for address\n");
+	}
+#endif
+	return (NULL);
+}
+
+
+static struct sockaddr_in6 *
+sctp_select_v6_nth_addr_from_ifn_boundall (struct ifnet *ifn, struct sctp_tcb *stcb, int non_asoc_addr_ok, uint8_t loopscope,
+					   uint8_t loc_scope, int cur_addr_num, int match_scope)
+{
+	struct ifaddr *ifa;
+	struct sockaddr_in6 *sin6;
+	int sin_loop, sin_local;
+	int num_eligible_addr = 0;
+
+	TAILQ_FOREACH(ifa, &ifn->if_addrlist, ifa_list) {
+		sin6 = sctp_is_v6_ifa_addr_acceptable (ifa, loopscope, loc_scope, &sin_loop, &sin_local);
+		if (sin6 == NULL)
+			continue;
+		if (stcb) {
+			if ((non_asoc_addr_ok == 0) && sctp_is_addr_restricted(stcb, (struct sockaddr *)sin6)) {
+				/* It is restricted for some reason.. probably
+				 * not yet added.
+				 */
+				continue;
+			}
+		}
+		if (match_scope) {
+			/* Here we are asked to match scope if possible */
+			if (loopscope && sin_loop)
+				/* src and destination are loopback scope */
+				return (sin6);
+			if (loc_scope && sin_local)
+				/* src and destination are local scope */
+				return (sin6);
+			if ((loopscope == 0) &&
+			    (loc_scope == 0)  &&
+			    (sin_loop == 0) &&
+			    (sin_local == 0)) {
+				/* src and destination are global scope */
+				return (sin6);
+			}
+			continue;
+		}
+		if (num_eligible_addr == cur_addr_num) {
+			/* this is it */
+			return (sin6);
+		}
+		num_eligible_addr++;
+	}
+	return (NULL);
+}
+
+
+static int
+sctp_count_v6_num_eligible_boundall (struct ifnet *ifn, struct sctp_tcb *stcb,
+				     int non_asoc_addr_ok, uint8_t loopscope, uint8_t loc_scope)
+{
+	struct ifaddr *ifa;
+	struct sockaddr_in6 *sin6;
+	int num_eligible_addr = 0;
+	int sin_loop, sin_local;
+
+	TAILQ_FOREACH(ifa, &ifn->if_addrlist, ifa_list) {
+		sin6 = sctp_is_v6_ifa_addr_acceptable (ifa, loopscope, loc_scope, &sin_loop, &sin_local);
+		if (sin6 == NULL)
+			continue;
+		if (stcb) {
+			if ((non_asoc_addr_ok == 0) && sctp_is_addr_restricted(stcb, (struct sockaddr *)sin6)) {
+				/* It is restricted for some reason.. probably
+				 * not yet added.
+				 */
+				continue;
+			}
+		}
+		num_eligible_addr++;
+	}
+	return (num_eligible_addr);
+}
+
+
+static struct sockaddr_in6 *
+sctp_choose_v6_boundall(struct sctp_inpcb *inp,
+			struct sctp_tcb *stcb,
+			struct sctp_nets *net,
+			struct rtentry *rt,
+			uint8_t loc_scope,
+			uint8_t loopscope,
+			int non_asoc_addr_ok)
+{
+	/* Ok, we are bound all SO any address
+	 * is ok to use as long as it is NOT in the negative
+	 * list.
+	 */
+	int num_eligible_addr;
+	int cur_addr_num=0;
+	int started_at_beginning=0;
+	int match_scope_prefered;
+	/* first question is, how many eligible addresses are
+	 * there for the destination ifn that we are using that
+	 * are within the proper scope?
+	 */
+	struct ifnet *ifn;
+	struct sockaddr_in6 *sin6;
+
+	ifn = rt->rt_ifp;
+	if (net) {
+		cur_addr_num = net->indx_of_eligible_next_to_use;
+	}
+	if (cur_addr_num == 0) {
+		match_scope_prefered = 1;
+	} else {
+		match_scope_prefered = 0;
+	}
+	num_eligible_addr = sctp_count_v6_num_eligible_boundall (ifn, stcb, non_asoc_addr_ok, loopscope, loc_scope);
+#ifdef SCTP_DEBUG
+	if (sctp_debug_on & SCTP_DEBUG_OUTPUT1) {
+		printf("Found %d eligible source addresses\n", num_eligible_addr);
+	}
+#endif
+	if (num_eligible_addr == 0) {
+		/* no eligible addresses, we must use some other
+		 * interface address if we can find one.
+		 */
+ 		goto bound_all_v6_plan_b;
+	}
+	/* Ok we have num_eligible_addr set with how many we can use,
+	 * this may vary from call to call due to addresses being deprecated etc..
+	 */
+	if (cur_addr_num >= num_eligible_addr) {
+		cur_addr_num = 0;
+	}
+	/* select the nth address from the list (where cur_addr_num is the nth) and
+	 * 0 is the first one, 1 is the second one etc...
+	 */
+#ifdef SCTP_DEBUG
+	if (sctp_debug_on & SCTP_DEBUG_OUTPUT1) {
+		printf("cur_addr_num:%d match_scope_prefered:%d select it\n",
+		       cur_addr_num, match_scope_prefered);
+	}
+#endif
+	sin6 = sctp_select_v6_nth_addr_from_ifn_boundall (ifn, stcb, non_asoc_addr_ok, loopscope,
+							  loc_scope, cur_addr_num, match_scope_prefered);
+	if (match_scope_prefered && (sin6 == NULL)) {
+		/* retry without the preference for matching scope */
+#ifdef SCTP_DEBUG
+	if (sctp_debug_on & SCTP_DEBUG_OUTPUT1) {
+		printf("retry with no match_scope_prefered\n");
+	}
+#endif
+		sin6 = sctp_select_v6_nth_addr_from_ifn_boundall (ifn, stcb, non_asoc_addr_ok, loopscope,
+								  loc_scope, cur_addr_num, 0);
+	}
+	if (sin6) {
+#ifdef SCTP_DEBUG
+		if (sctp_debug_on & SCTP_DEBUG_OUTPUT1) {
+			printf("Selected address %d ifn:%x for the route\n", cur_addr_num, (u_int)ifn);
+		}
+#endif
+		if (net) {
+			/* store so we get the next one */
+			if (cur_addr_num < 255)
+				net->indx_of_eligible_next_to_use = cur_addr_num + 1;
+			else
+				net->indx_of_eligible_next_to_use = 0;
+		}
+		return (sin6);
+	}
+	num_eligible_addr = 0;
+ bound_all_v6_plan_b:
+	/* ok, if we reach here we either fell through
+	 * due to something changing during an interupt (unlikely)
+	 * or we have NO eligible source addresses for the ifn
+	 * of the route (most likely). We must look at all the other
+	 * interfaces EXCEPT rt->rt_ifp and do the same game.
+	 */
+#ifdef SCTP_DEBUG
+	if (sctp_debug_on & SCTP_DEBUG_OUTPUT1) {
+		printf("bound-all Plan B\n");
+	}
+#endif
+	if (inp->next_ifn_touse == NULL) {
+		started_at_beginning=1;
+		inp->next_ifn_touse = TAILQ_FIRST(&ifnet);
+#ifdef SCTP_DEBUG
+		if (sctp_debug_on & SCTP_DEBUG_OUTPUT1) {
+			printf("Start at first IFN:%x\n", (u_int)inp->next_ifn_touse);
+		}
+#endif
+	} else {
+		inp->next_ifn_touse = TAILQ_NEXT(inp->next_ifn_touse, if_list);
+#ifdef SCTP_DEBUG
+		if (sctp_debug_on & SCTP_DEBUG_OUTPUT1) {
+			printf("Resume at IFN:%x\n", (u_int)inp->next_ifn_touse);
+		}
+#endif
+		if (inp->next_ifn_touse == NULL) {
+#ifdef SCTP_DEBUG
+			if (sctp_debug_on & SCTP_DEBUG_OUTPUT1) {
+				printf("IFN Resets\n");
+			}
+#endif
+			started_at_beginning=1;
+			inp->next_ifn_touse = TAILQ_FIRST(&ifnet);
+		}
+	}
+	for (ifn = inp->next_ifn_touse; ifn;
+	     ifn = TAILQ_NEXT(ifn, if_list)) {
+		if (loopscope == 0 && ifn->if_type == IFT_LOOP) {
+			/* wrong base scope */
+			continue;
+		}
+		if (loc_scope && (ifn->if_index != loc_scope)) {
+			/* by definition the scope (from to->sin6_scopeid)
+			 * must match that of the interface. If not then
+			 * we could pick a wrong scope for the address.
+			 * Ususally we don't hit plan-b since the route
+			 * handles this. However we can hit plan-b when
+			 * we send to local-host so the route is the
+			 * loopback interface, but the destination is a
+			 * link local.
+			 */
+			continue;
+		}
+		if (ifn == rt->rt_ifp) {
+			/* already looked at this guy */
+			continue;
+		}
+		/* Address rotation will only work when we are not
+		 * rotating sourced interfaces and are using the interface
+		 * of the route. We would need to have a per interface index
+		 * in order to do proper rotation.
+		 */
+		num_eligible_addr = sctp_count_v6_num_eligible_boundall (ifn, stcb, non_asoc_addr_ok, loopscope, loc_scope);
+#ifdef SCTP_DEBUG
+		if (sctp_debug_on & SCTP_DEBUG_OUTPUT1) {
+			printf("IFN:%x has %d eligible\n", (u_int)ifn, num_eligible_addr);
+		}
+#endif
+		if (num_eligible_addr == 0) {
+			/* none we can use */
+			continue;
+		}
+		/* Ok we have num_eligible_addr set with how many we can use,
+		 * this may vary from call to call due to addresses being deprecated etc..
+		 */
+		inp->next_ifn_touse = ifn;
+
+		/* select the first one we can find with perference for matching scope.
+		 */
+		sin6 = sctp_select_v6_nth_addr_from_ifn_boundall (ifn, stcb, non_asoc_addr_ok, loopscope, loc_scope, 0, 1);
+		if (sin6 == NULL) {
+			/* can't find one with matching scope how about a source with higher
+			 * scope
+			 */
+ 			sin6 = sctp_select_v6_nth_addr_from_ifn_boundall (ifn, stcb, non_asoc_addr_ok, loopscope, loc_scope, 0, 0);
+			if (sin6 == NULL)
+				/* Hmm, can't find one in the interface now */
+				continue;
+		}
+#ifdef SCTP_DEBUG
+		if (sctp_debug_on & SCTP_DEBUG_OUTPUT1) {
+			printf("Selected the %d'th address of ifn:%x\n",
+			       cur_addr_num,
+			       (u_int)ifn);
+		}
+#endif
+		return (sin6);
+	}
+	if (started_at_beginning == 0) {
+		/* we have not been through all of them yet, force
+		 * us to go through them all.
+		 */
+#ifdef SCTP_DEBUG
+		if (sctp_debug_on & SCTP_DEBUG_OUTPUT1) {
+			printf("Force a recycle\n");
+		}
+#endif
+		inp->next_ifn_touse = NULL;
+		goto bound_all_v6_plan_b;
+	}
+	return (NULL);
+
+}
+
+/* stcb and net may be NULL */
+struct in6_addr
+sctp_ipv6_source_address_selection(struct sctp_inpcb *inp,
+    struct sctp_tcb *stcb, struct route *ro, struct sctp_nets *net,
+    int non_asoc_addr_ok)
+{
+	struct in6_addr ans;
+	struct sockaddr_in6 *rt_addr;
+	uint8_t loc_scope, loopscope;
+	struct sockaddr_in6 *to = (struct sockaddr_in6 *)&ro->ro_dst;
+
+	/*
+	 * This routine is tricky standard v6 src address
+	 * selection cannot take into account what we have
+	 * bound etc, so we can't use it.
+	 *
+	 * Instead here is what we must do:
+	 * 1) Make sure we have a route, if we
+	 *    don't have a route we can never reach the peer.
+	 * 2) Once we have a route, determine the scope of the
+	 *     route. Link local, loopback or global.
+	 * 3) Next we divide into three types. Either we
+	 *    are bound all.. which means we want to use
+	 *    one of the addresses of the interface we are
+	 *    going out. <or>
+	 * 4a) We have not stcb, which means we are using the
+	 *    specific addresses bound on an inp, in this
+	 *    case we are similar to the stcb case (4b below)
+	 *    accept the list is always a positive list.<or>
+	 * 4b) We are bound specific with a stcb, which means we have a
+	 *    list of bound addresses and we must see if the
+	 *    ifn of the route is actually one of the bound addresses.
+	 *    If not, then we must rotate addresses amongst properly
+	 *    scoped bound addresses, if so we use the address
+	 *    of the interface.
+	 * 5) Always, no matter which path we take through the above
+	 *    we must be sure the source address we use is allowed to
+	 *    be used. I.e.  IN6_IFF_DETACHED, IN6_IFF_NOTREADY, and IN6_IFF_ANYCAST
+	 *    addresses cannot be used.
+	 * 6) Addresses that are deprecated MAY be used
+	 * 		if (!ip6_use_deprecated) {
+	 *                    if (IFA6_IS_DEPRECATED(ifa6)) {
+	 *	                  skip the address
+  	 *	              }
+	 *	        }
+ 	 */
+
+	/*** 1> determine route, if not already done */
+	if (ro->ro_rt == NULL) {
+		/*
+		 * Need a route to cache.
+		 */
+#ifndef SCOPEDROUTING
+		int scope_save;
+		scope_save = to->sin6_scope_id;
+		to->sin6_scope_id = 0;
+#endif
+
+#if defined(__FreeBSD__) || defined(__APPLE__) || defined(__DragonFly__)
+		rtalloc_ign(ro, 0UL);
+#else
+		rtalloc(ro);
+#endif
+#ifndef SCOPEDROUTING
+		to->sin6_scope_id = scope_save;
+#endif
+	}
+	if (ro->ro_rt == NULL) {
+		/*
+		 * no route to host. this packet is going no-where.
+		 * We probably should make sure we arrange to send back
+		 * an error.
+		 */
+#ifdef SCTP_DEBUG
+		if (sctp_debug_on & SCTP_DEBUG_OUTPUT1) {
+			printf("No route to host, this packet cannot be sent!\n");
+		}
+#endif
+		memset(&ans, 0, sizeof(ans));
+		return (ans);
+	}
+
+	/*** 2a> determine scope for outbound address/route */
+	loc_scope = loopscope = 0;
+	/*
+	 * We base our scope on the outbound packet scope and route,
+	 * NOT the TCB (if there is one). This way in local scope we will only
+	 * use a local scope src address when we send to a local address.
+	 */
+
+	if (IN6_IS_ADDR_LOOPBACK(&to->sin6_addr)) {
+		/* If the route goes to the loopback address OR
+		 * the address is a loopback address, we are loopback
+		 * scope.
+		 */
+#ifdef SCTP_DEBUG
+		if (sctp_debug_on & SCTP_DEBUG_OUTPUT1) {
+			printf("Loopback scope is set\n");
+		}
+#endif
+		loc_scope = 0;
+		loopscope = 1;
+		if (net != NULL) {
+			/* mark it as local */
+			net->addr_is_local = 1;
+		}
+
+	} else if (IN6_IS_ADDR_LINKLOCAL(&to->sin6_addr)) {
+#ifdef SCTP_DEBUG
+		if (sctp_debug_on & SCTP_DEBUG_OUTPUT1) {
+			printf("Link local scope is set, id:%d\n", to->sin6_scope_id);
+		}
+#endif
+		if (to->sin6_scope_id)
+			loc_scope = to->sin6_scope_id;
+		else {
+			loc_scope = 1;
+		}
+		loopscope = 0;
+	} else {
+#ifdef SCTP_DEBUG
+		if (sctp_debug_on & SCTP_DEBUG_OUTPUT1) {
+			printf("Global scope is set\n");
+		}
+#endif
+	}
+
+	/* now, depending on which way we are bound we call the appropriate
+	 * routine to do steps 3-6
+	 */
+#ifdef SCTP_DEBUG
+	if (sctp_debug_on & SCTP_DEBUG_OUTPUT1) {
+		printf("Destination address:");
+		sctp_print_address((struct sockaddr *)to);
+	}
+#endif
+
+	if (inp->sctp_flags & SCTP_PCB_FLAGS_BOUNDALL) {
+#ifdef SCTP_DEBUG
+		if (sctp_debug_on & SCTP_DEBUG_OUTPUT1) {
+			printf("Calling bound-all src addr selection for v6\n");
+		}
+#endif
+		rt_addr = sctp_choose_v6_boundall(inp, stcb, net, ro->ro_rt, loc_scope, loopscope, non_asoc_addr_ok);
+	} else {
+#ifdef SCTP_DEBUG
+		if (sctp_debug_on & SCTP_DEBUG_OUTPUT1) {
+			printf("Calling bound-specific src addr selection for v6\n");
+		}
+#endif
+		if (stcb)
+			rt_addr = sctp_choose_v6_boundspecific_stcb(inp, stcb, net, ro->ro_rt, loc_scope, loopscope,  non_asoc_addr_ok);
+		else
+			/* we can't have a non-asoc address since we have no association */
+			rt_addr = sctp_choose_v6_boundspecific_inp(inp,  ro->ro_rt, loc_scope, loopscope);
+	}
+	if (rt_addr == NULL) {
+		/* no suitable address? */
+		struct in6_addr in6;
+#ifdef SCTP_DEBUG
+		if (sctp_debug_on & SCTP_DEBUG_OUTPUT1) {
+			printf("V6 packet will reach dead-end no suitable src address\n");
+		}
+#endif
+		memset(&in6, 0, sizeof(in6));
+		return (in6);
+	}
+#ifdef SCTP_DEBUG
+	if (sctp_debug_on & SCTP_DEBUG_OUTPUT1) {
+		printf("Source address selected is:");
+		sctp_print_address((struct sockaddr *)rt_addr);
+	}
+#endif
+	return (rt_addr->sin6_addr);
+}
+
+static uint8_t
+sctp_get_ect(struct sctp_tcb *stcb,
+	     struct sctp_tmit_chunk *chk)
+{
+	uint8_t this_random;
+
+	/* Huh? */
+	if (sctp_ecn == 0)
+		return (0);
+
+	if (sctp_ecn_nonce == 0)
+		/* no nonce, always return ECT0 */
+		return (SCTP_ECT0_BIT);
+
+	if (stcb->asoc.peer_supports_ecn_nonce == 0) {
+		/* Peer does NOT support it, so we send a ECT0 only */
+ 		return (SCTP_ECT0_BIT);
+	}
+
+	if (chk == NULL)
+	   return (SCTP_ECT0_BIT);
+
+	if (((stcb->asoc.hb_random_idx == 3) &&
+	     (stcb->asoc.hb_ect_randombit > 7)) ||
+	     (stcb->asoc.hb_random_idx > 3)) {
+		uint32_t rndval;
+		rndval = sctp_select_initial_TSN(&stcb->sctp_ep->sctp_ep);
+		memcpy(stcb->asoc.hb_random_values, &rndval,
+		       sizeof(stcb->asoc.hb_random_values));
+		this_random = stcb->asoc.hb_random_values[0];
+		stcb->asoc.hb_random_idx = 0;
+		stcb->asoc.hb_ect_randombit = 0;
+	} else {
+		if (stcb->asoc.hb_ect_randombit > 7) {
+		  stcb->asoc.hb_ect_randombit = 0;
+		  stcb->asoc.hb_random_idx++;
+		}
+		this_random = stcb->asoc.hb_random_values[stcb->asoc.hb_random_idx];
+	}
+	if ((this_random >> stcb->asoc.hb_ect_randombit) & 0x01) {
+		if (chk != NULL)
+			/* ECN Nonce stuff */
+			chk->rec.data.ect_nonce = SCTP_ECT1_BIT;
+		stcb->asoc.hb_ect_randombit++;
+		return (SCTP_ECT1_BIT);
+	} else {
+		stcb->asoc.hb_ect_randombit++;
+		return (SCTP_ECT0_BIT);
+	}
+}
+
+extern int sctp_no_csum_on_loopback;
+
+static int
+sctp_lowlevel_chunk_output(struct sctp_inpcb *inp,
+			   struct sctp_tcb *stcb,    /* may be NULL */
+			   struct sctp_nets *net,
+			   struct sockaddr *to,
+			   struct mbuf *m,
+			   int nofragment_flag,
+			   int ecn_ok,
+			   struct sctp_tmit_chunk *chk,
+			   int out_of_asoc_ok)
+	/* nofragment_flag to tell if IP_DF should be set (IPv4 only) */
+{
+	/*
+	 * Given a mbuf chain (via m_next) that holds a packet header
+	 * WITH a SCTPHDR but no IP header, endpoint inp and sa structure.
+	 * - calculate SCTP checksum and fill in
+	 * - prepend a IP address header
+	 * - if boundall use INADDR_ANY
+	 * - if boundspecific do source address selection
+	 * - set fragmentation option for ipV4
+	 * - On return from IP output, check/adjust mtu size
+	 * - of output interface and smallest_mtu size as well.
+	 */
+	struct sctphdr *sctphdr;
+	int o_flgs;
+	uint32_t csum;
+	int ret;
+	unsigned int have_mtu;
+	struct route *ro;
+
+	if ((net) && (net->dest_state & SCTP_ADDR_OUT_OF_SCOPE)) {
+		sctp_m_freem(m);
+		return (EFAULT);
+	}
+	if ((m->m_flags & M_PKTHDR) == 0) {
+#ifdef SCTP_DEBUG
+		if (sctp_debug_on & SCTP_DEBUG_OUTPUT1) {
+			printf("Software error: sctp_lowlevel_chunk_output() called with non pkthdr!\n");
+		}
+#endif
+		sctp_m_freem(m);
+		return (EFAULT);
+	}
+	/* Calculate the csum and fill in the length of the packet */
+	sctphdr = mtod(m, struct sctphdr *);
+	have_mtu = 0;
+	if (sctp_no_csum_on_loopback &&
+	     (stcb) &&
+	     (stcb->asoc.loopback_scope)) {
+		sctphdr->checksum = 0;
+		m->m_pkthdr.len = sctp_calculate_len(m);
+	} else {
+		sctphdr->checksum = 0;
+		csum = sctp_calculate_sum(m, &m->m_pkthdr.len, 0);
+		sctphdr->checksum = csum;
+	}
+	if (to->sa_family == AF_INET) {
+		struct ip *ip;
+		struct route iproute;
+		M_PREPEND(m, sizeof(struct ip), MB_DONTWAIT);
+		if (m == NULL) {
+			/* failed to prepend data, give up */
+			return (ENOMEM);
+		}
+		ip = mtod(m, struct ip *);
+		ip->ip_v = IPVERSION;
+		ip->ip_hl = (sizeof(struct ip) >> 2);
+		if (nofragment_flag) {
+#if defined(WITH_CONVERT_IP_OFF) || defined(__FreeBSD__) || defined(__DragonFly__)
+#if defined( __OpenBSD__) || defined(__NetBSD__)
+			/* OpenBSD has WITH_CONVERT_IP_OFF defined?? */
+			ip->ip_off = htons(IP_DF);
+#else
+			ip->ip_off = IP_DF;
+#endif
+#else
+			ip->ip_off = htons(IP_DF);
+#endif
+		} else
+			ip->ip_off = 0;
+
+/* FreeBSD and Apple have RANDOM_IP_ID switch */
+#if defined(RANDOM_IP_ID) || defined(__NetBSD__) || defined(__OpenBSD__)
+		ip->ip_id = htons(ip_randomid());
+#else
+		ip->ip_id = htons(ip_id++);
+#endif
+
+#if defined(__FreeBSD__) || defined(__APPLE__) || defined(__DragonFly__)
+		ip->ip_ttl = inp->ip_inp.inp.inp_ip_ttl;
+#else
+		ip->ip_ttl = inp->inp_ip_ttl;
+#endif
+#if defined(__OpenBSD__) || defined(__NetBSD__)
+		ip->ip_len = htons(m->m_pkthdr.len);
+#else
+		ip->ip_len = m->m_pkthdr.len;
+#endif
+		if (stcb) {
+			if ((stcb->asoc.ecn_allowed) && ecn_ok) {
+				/* Enable ECN */
+#if defined(__FreeBSD__) || defined (__APPLE__) || defined(__DragonFly__)
+				ip->ip_tos = (u_char)((inp->ip_inp.inp.inp_ip_tos & 0x000000fc) |
+						      sctp_get_ect(stcb, chk));
+#elif defined(__NetBSD__)
+				ip->ip_tos = (u_char)((inp->ip_inp.inp.inp_ip.ip_tos & 0x000000fc) |
+						      sctp_get_ect(stcb, chk));
+#else
+				ip->ip_tos = (u_char)((inp->inp_ip_tos & 0x000000fc) |
+						      sctp_get_ect(stcb, chk));
+#endif
+			} else {
+				/* No ECN */
+#if defined(__FreeBSD__) || defined(__APPLE__) || defined(__DragonFly__)
+				ip->ip_tos = inp->ip_inp.inp.inp_ip_tos;
+#elif defined(__NetBSD__)
+				ip->ip_tos = inp->ip_inp.inp.inp_ip.ip_tos;
+#else
+				ip->ip_tos = inp->inp_ip_tos;
+#endif
+			}
+		} else {
+			/* no association at all */
+#if defined(__FreeBSD__) || defined(__APPLE__) || defined(__DragonFly__)
+			ip->ip_tos = inp->ip_inp.inp.inp_ip_tos;
+#else
+			ip->ip_tos = inp->inp_ip_tos;
+#endif
+		}
+		ip->ip_p = IPPROTO_SCTP;
+		ip->ip_sum = 0;
+		if (net == NULL) {
+			ro = &iproute;
+			memset(&iproute, 0, sizeof(iproute));
+			memcpy(&ro->ro_dst, to, to->sa_len);
+		} else {
+			ro = (struct route *)&net->ro;
+		}
+		/* Now the address selection part */
+		ip->ip_dst.s_addr = ((struct sockaddr_in *)to)->sin_addr.s_addr;
+
+		/* call the routine to select the src address */
+		if (net) {
+			if (net->src_addr_selected == 0) {
+				/* Cache the source address */
+				((struct sockaddr_in *)&net->ro._s_addr)->sin_addr = sctp_ipv4_source_address_selection(inp,
+				    stcb,
+				    ro, net, out_of_asoc_ok);
+				if (ro->ro_rt)
+					net->src_addr_selected = 1;
+			}
+			ip->ip_src = ((struct sockaddr_in *)&net->ro._s_addr)->sin_addr;
+		} else {
+			ip->ip_src = sctp_ipv4_source_address_selection(inp,
+			    stcb, ro, net, out_of_asoc_ok);
+		}
+		/*
+		 * If source address selection fails and we find no route then
+		 * the ip_ouput should fail as well with a NO_ROUTE_TO_HOST
+		 * type error. We probably should catch that somewhere and
+		 * abort the association right away (assuming this is an INIT
+		 * being sent).
+		 */
+		if ((ro->ro_rt == NULL)) {
+			/*
+			 * src addr selection failed to find a route (or valid
+			 * source addr), so we can't get there from here!
+			 */
+#ifdef SCTP_DEBUG
+			if (sctp_debug_on & SCTP_DEBUG_OUTPUT1) {
+				printf("low_level_output: dropped v4 packet- no valid source addr\n");
+				printf("Destination was %x\n", (u_int)(ntohl(ip->ip_dst.s_addr)));
+			}
+#endif /* SCTP_DEBUG */
+			if (net) {
+				if ((net->dest_state & SCTP_ADDR_REACHABLE) && stcb)
+					sctp_ulp_notify(SCTP_NOTIFY_INTERFACE_DOWN,
+							stcb,
+							SCTP_FAILED_THRESHOLD,
+							(void *)net);
+				net->dest_state &= ~SCTP_ADDR_REACHABLE;
+				net->dest_state |= SCTP_ADDR_NOT_REACHABLE;
+				if (stcb) {
+					if (net == stcb->asoc.primary_destination) {
+						/* need a new primary */
+						struct sctp_nets *alt;
+						alt = sctp_find_alternate_net(stcb, net);
+						if (alt != net) {
+							if (sctp_set_primary_addr(stcb,
+									      (struct sockaddr *)NULL,
+										 alt) == 0) {
+								net->dest_state |= SCTP_ADDR_WAS_PRIMARY;
+								net->src_addr_selected = 0;
+							}
+						}
+					}
+				}
+			}
+			sctp_m_freem(m);
+			return (EHOSTUNREACH);
+		} else {
+			have_mtu = ro->ro_rt->rt_ifp->if_mtu;
+		}
+
+		o_flgs = (IP_RAWOUTPUT | (inp->sctp_socket->so_options & (SO_DONTROUTE | SO_BROADCAST)));
+#ifdef SCTP_DEBUG
+		if (sctp_debug_on & SCTP_DEBUG_OUTPUT3) {
+			printf("Calling ipv4 output routine from low level src addr:%x\n",
+			       (u_int)(ntohl(ip->ip_src.s_addr)));
+			printf("Destination is %x\n", (u_int)(ntohl(ip->ip_dst.s_addr)));
+			printf("RTP route is %p through\n", ro->ro_rt);
+		}
+#endif
+		if ((have_mtu) && (net) && (have_mtu > net->mtu)) {
+			ro->ro_rt->rt_ifp->if_mtu = net->mtu;
+		}
+		ret = ip_output(m, inp->ip_inp.inp.inp_options,
+				ro, o_flgs, inp->ip_inp.inp.inp_moptions
+#if defined(__OpenBSD__) || (defined(__FreeBSD__) && __FreeBSD_version >= 480000) \
+    || defined(__DragonFly__)
+				, (struct inpcb *)NULL
+#endif
+#if defined(__NetBSD__)
+                                ,(struct socket *)inp->sctp_socket
+#endif
+
+);
+		if ((ro->ro_rt) && (have_mtu) && (net) && (have_mtu > net->mtu)) {
+			ro->ro_rt->rt_ifp->if_mtu = have_mtu;
+		}
+		sctp_pegs[SCTP_DATAGRAMS_SENT]++;
+#ifdef SCTP_DEBUG
+		if (sctp_debug_on & SCTP_DEBUG_OUTPUT3) {
+			printf("Ip output returns %d\n", ret);
+		}
+#endif
+		if (net == NULL) {
+			/* free tempy routes */
+			if (ro->ro_rt)
+				RTFREE(ro->ro_rt);
+		} else {
+			/* PMTU check versus smallest asoc MTU goes here */
+			if (ro->ro_rt != NULL) {
+				if (ro->ro_rt->rt_rmx.rmx_mtu &&
+				    (stcb->asoc.smallest_mtu > ro->ro_rt->rt_rmx.rmx_mtu)) {
+					sctp_mtu_size_reset(inp, &stcb->asoc,
+					    ro->ro_rt->rt_rmx.rmx_mtu);
+				}
+			} else {
+				/* route was freed */
+				net->src_addr_selected = 0;
+			}
+		}
+		return (ret);
+	}
+#ifdef INET6
+	else if (to->sa_family == AF_INET6) {
+		struct ip6_hdr *ip6h;
+#ifdef NEW_STRUCT_ROUTE
+		struct route ip6route;
+#else
+		struct route_in6 ip6route;
+#endif
+		struct ifnet *ifp;
+		u_char flowTop;
+		uint16_t flowBottom;
+		u_char tosBottom, tosTop;
+		struct sockaddr_in6 *sin6, tmp, *lsa6, lsa6_tmp;
+		struct sockaddr_in6 lsa6_storage;
+		int prev_scope=0;
+		int error;
+		u_short prev_port=0;
+
+		M_PREPEND(m, sizeof(struct ip6_hdr), MB_DONTWAIT);
+		if (m == NULL) {
+			/* failed to prepend data, give up */
+			return (ENOMEM);
+		}
+		ip6h = mtod(m, struct ip6_hdr *);
+
+		/*
+		 * We assume here that inp_flow is in host byte order within
+		 * the TCB!
+		 */
+		flowBottom = ((struct in6pcb *)inp)->in6p_flowinfo & 0x0000ffff;
+		flowTop = ((((struct in6pcb *)inp)->in6p_flowinfo & 0x000f0000) >> 16);
+
+		tosTop = (((((struct in6pcb *)inp)->in6p_flowinfo & 0xf0) >> 4) | IPV6_VERSION);
+
+		/* protect *sin6 from overwrite */
+		sin6 = (struct sockaddr_in6 *)to;
+		tmp = *sin6;
+		sin6 = &tmp;
+
+		/* KAME hack: embed scopeid */
+#if defined(SCTP_BASE_FREEBSD) || defined(__APPLE__) || defined(__DragonFly__)
+		if (in6_embedscope(&sin6->sin6_addr, sin6, NULL, NULL) != 0)
+#else
+		if (in6_embedscope(&sin6->sin6_addr, sin6) != 0)
+#endif
+			return (EINVAL);
+		if (net == NULL) {
+			memset(&ip6route, 0, sizeof(ip6route));
+			ro = (struct route *)&ip6route;
+			memcpy(&ro->ro_dst, sin6, sin6->sin6_len);
+		} else {
+			ro = (struct route *)&net->ro;
+		}
+		if (stcb != NULL) {
+			if ((stcb->asoc.ecn_allowed) && ecn_ok) {
+				/* Enable ECN */
+				tosBottom = (((((struct in6pcb *)inp)->in6p_flowinfo & 0x0c) | sctp_get_ect(stcb, chk)) << 4);
+			} else {
+				/* No ECN */
+				tosBottom = ((((struct in6pcb *)inp)->in6p_flowinfo & 0x0c) << 4);
+			}
+		} else {
+			/* we could get no asoc if it is a O-O-T-B packet */
+			tosBottom = ((((struct in6pcb *)inp)->in6p_flowinfo & 0x0c) << 4);
+		}
+		ip6h->ip6_flow = htonl(((tosTop << 24) | ((tosBottom|flowTop) << 16) | flowBottom));
+		ip6h->ip6_nxt = IPPROTO_SCTP;
+		ip6h->ip6_plen = m->m_pkthdr.len;
+		ip6h->ip6_dst = sin6->sin6_addr;
+
+		/*
+		 * Add SRC address selection here:
+		 * we can only reuse to a limited degree the kame src-addr-sel,
+		 * since we can try their selection but it may not be bound.
+		 */
+		bzero(&lsa6_tmp, sizeof(lsa6_tmp));
+		lsa6_tmp.sin6_family = AF_INET6;
+		lsa6_tmp.sin6_len = sizeof(lsa6_tmp);
+		lsa6 = &lsa6_tmp;
+		if (net) {
+			if (net->src_addr_selected == 0) {
+				/* Cache the source address */
+				((struct sockaddr_in6 *)&net->ro._s_addr)->sin6_addr = sctp_ipv6_source_address_selection(inp,
+				    stcb, ro, net, out_of_asoc_ok);
+
+				if (ro->ro_rt)
+					net->src_addr_selected = 1;
+			}
+			lsa6->sin6_addr = ((struct sockaddr_in6 *)&net->ro._s_addr)->sin6_addr;
+		} else {
+			lsa6->sin6_addr = sctp_ipv6_source_address_selection(
+			    inp, stcb, ro, net, out_of_asoc_ok);
+		}
+		lsa6->sin6_port = inp->sctp_lport;
+
+		if ((ro->ro_rt ==  NULL)) {
+			/*
+			 * src addr selection failed to find a route (or valid
+			 * source addr), so we can't get there from here!
+			 */
+#ifdef SCTP_DEBUG
+			if (sctp_debug_on & SCTP_DEBUG_OUTPUT1) {
+				printf("low_level_output: dropped v6 pkt- no valid source addr\n");
+			}
+#endif
+			sctp_m_freem(m);
+			if (net) {
+				if ((net->dest_state & SCTP_ADDR_REACHABLE) && stcb)
+					sctp_ulp_notify(SCTP_NOTIFY_INTERFACE_DOWN,
+							stcb,
+							SCTP_FAILED_THRESHOLD,
+							(void *)net);
+				net->dest_state &= ~SCTP_ADDR_REACHABLE;
+				net->dest_state |= SCTP_ADDR_NOT_REACHABLE;
+				if (stcb) {
+					if (net == stcb->asoc.primary_destination) {
+						/* need a new primary */
+						struct sctp_nets *alt;
+						alt = sctp_find_alternate_net(stcb, net);
+						if (alt != net) {
+							if (sctp_set_primary_addr(stcb,
+									      (struct sockaddr *)NULL,
+										 alt) == 0) {
+								net->dest_state |= SCTP_ADDR_WAS_PRIMARY;
+								net->src_addr_selected = 0;
+							}
+						}
+					}
+				}
+			}
+			return (EHOSTUNREACH);
+		}
+
+#ifndef SCOPEDROUTING
+		/*
+		 * XXX: sa6 may not have a valid sin6_scope_id in
+		 * the non-SCOPEDROUTING case.
+		 */
+		bzero(&lsa6_storage, sizeof(lsa6_storage));
+		lsa6_storage.sin6_family = AF_INET6;
+		lsa6_storage.sin6_len = sizeof(lsa6_storage);
+		if ((error = in6_recoverscope(&lsa6_storage, &lsa6->sin6_addr,
+					      NULL)) != 0) {
+			sctp_m_freem(m);
+			return (error);
+		}
+		/* XXX */
+		lsa6_storage.sin6_addr = lsa6->sin6_addr;
+		lsa6_storage.sin6_port = inp->sctp_lport;
+		lsa6 = &lsa6_storage;
+#endif /* SCOPEDROUTING */
+		ip6h->ip6_src = lsa6->sin6_addr;
+
+		/*
+		 * We set the hop limit now since there is a good chance that
+		 * our ro pointer is now filled
+		 */
+		ip6h->ip6_hlim = in6_selecthlim((struct in6pcb *)&inp->ip_inp.inp,
+						(ro ?
+						 (ro->ro_rt ? (ro->ro_rt->rt_ifp) : (NULL)) :
+						 (NULL)));
+		o_flgs = 0;
+		ifp = ro->ro_rt->rt_ifp;
+#ifdef SCTP_DEBUG
+		if (sctp_debug_on & SCTP_DEBUG_OUTPUT3) {
+			/* Copy to be sure something bad is not happening */
+			sin6->sin6_addr = ip6h->ip6_dst;
+			lsa6->sin6_addr = ip6h->ip6_src;
+
+			printf("Calling ipv6 output routine from low level\n");
+			printf("src: ");
+			sctp_print_address((struct sockaddr *)lsa6);
+			printf("dst: ");
+			sctp_print_address((struct sockaddr *)sin6);
+		}
+#endif /* SCTP_DEBUG */
+		if (net) {
+			sin6 = (struct sockaddr_in6 *)&net->ro._l_addr;
+			/* preserve the port and scope for link local send */
+			prev_scope = sin6->sin6_scope_id;
+			prev_port = sin6->sin6_port;
+		}
+		ret = ip6_output(m, ((struct in6pcb *)inp)->in6p_outputopts,
+#ifdef NEW_STRUCT_ROUTE
+				 ro,
+#else
+				 (struct route_in6 *)ro,
+#endif
+				 o_flgs,
+				 ((struct in6pcb *)inp)->in6p_moptions,
+#if defined(__NetBSD__)
+				 (struct socket *)inp->sctp_socket,
+#endif
+				 &ifp
+#if (defined(__FreeBSD__) && __FreeBSD_version >= 480000) || defined(__DragonFly__)
+		    , NULL
+#endif
+			);
+		if (net) {
+			/* for link local this must be done */
+			sin6->sin6_scope_id = prev_scope;
+			sin6->sin6_port = prev_port;
+		}
+#ifdef SCTP_DEBUG
+		if (sctp_debug_on & SCTP_DEBUG_OUTPUT3) {
+			printf("return from send is %d\n", ret);
+		}
+#endif /* SCTP_DEBUG_OUTPUT */
+		sctp_pegs[SCTP_DATAGRAMS_SENT]++;
+		if (net == NULL) {
+			/* Now if we had a temp route free it */
+			if (ro->ro_rt) {
+				RTFREE(ro->ro_rt);
+			}
+		} else {
+			/* PMTU check versus smallest asoc MTU goes here */
+			if (ro->ro_rt == NULL) {
+				/* Route was freed */
+				net->src_addr_selected = 0;
+			}
+			if (ro->ro_rt != NULL) {
+				if (ro->ro_rt->rt_rmx.rmx_mtu &&
+				    (stcb->asoc.smallest_mtu > ro->ro_rt->rt_rmx.rmx_mtu)) {
+					sctp_mtu_size_reset(inp,
+							    &stcb->asoc,
+							    ro->ro_rt->rt_rmx.rmx_mtu);
+				}
+			} else if (ifp) {
+#if (defined(SCTP_BASE_FREEBSD) &&  __FreeBSD_version < 500000) || defined(__APPLE__)
+#define ND_IFINFO(ifp) (&nd_ifinfo[ifp->if_index])
+#endif /* SCTP_BASE_FREEBSD */
+				if (ND_IFINFO(ifp)->linkmtu &&
+				    (stcb->asoc.smallest_mtu > ND_IFINFO(ifp)->linkmtu)) {
+					sctp_mtu_size_reset(inp,
+							    &stcb->asoc,
+							    ND_IFINFO(ifp)->linkmtu);
+				}
+			}
+		}
+		return (ret);
+	}
+#endif
+	else {
+#ifdef SCTP_DEBUG
+		if (sctp_debug_on & SCTP_DEBUG_OUTPUT1) {
+			printf("Unknown protocol (TSNH) type %d\n", ((struct sockaddr *)to)->sa_family);
+		}
+#endif
+		sctp_m_freem(m);
+		return (EFAULT);
+	}
+}
+
+static
+int sctp_is_address_in_scope(struct ifaddr *ifa,
+ 			     int ipv4_addr_legal,
+			     int ipv6_addr_legal,
+			     int loopback_scope,
+			     int ipv4_local_scope,
+			     int local_scope,
+			     int site_scope)
+{
+	if ((loopback_scope == 0) &&
+	    (ifa->ifa_ifp) &&
+	    (ifa->ifa_ifp->if_type == IFT_LOOP)) {
+		/* skip loopback if not in scope *
+		 */
+		return (0);
+	}
+	if ((ifa->ifa_addr->sa_family == AF_INET) && ipv4_addr_legal) {
+		struct sockaddr_in *sin;
+		sin = (struct sockaddr_in *)ifa->ifa_addr;
+		if (sin->sin_addr.s_addr == 0) {
+			/* not in scope , unspecified */
+			return (0);
+		}
+		if ((ipv4_local_scope == 0) &&
+		    (IN4_ISPRIVATE_ADDRESS(&sin->sin_addr))) {
+			/* private address not in scope */
+			return (0);
+		}
+	} else if ((ifa->ifa_addr->sa_family == AF_INET6) && ipv6_addr_legal) {
+		struct sockaddr_in6 *sin6;
+		struct in6_ifaddr *ifa6;
+
+		ifa6 = (struct in6_ifaddr *)ifa;
+		/* ok to use deprecated addresses? */
+		if (!ip6_use_deprecated) {
+			if (ifa6->ia6_flags &
+			    IN6_IFF_DEPRECATED) {
+				return (0);
+			}
+		}
+		if (ifa6->ia6_flags &
+		    (IN6_IFF_DETACHED |
+		     IN6_IFF_ANYCAST |
+		     IN6_IFF_NOTREADY)) {
+			return (0);
+		}
+		sin6 = (struct sockaddr_in6 *)ifa->ifa_addr;
+		if (IN6_IS_ADDR_UNSPECIFIED(&sin6->sin6_addr)) {
+			/* skip unspecifed addresses */
+			return (0);
+		}
+		if (/*(local_scope == 0) && */
+		    (IN6_IS_ADDR_LINKLOCAL(&sin6->sin6_addr))) {
+			return (0);
+		}
+		if ((site_scope == 0) &&
+		    (IN6_IS_ADDR_SITELOCAL(&sin6->sin6_addr))) {
+			return (0);
+		}
+	} else {
+		return (0);
+	}
+	return (1);
+}
+
+
+void
+sctp_send_initiate(struct sctp_inpcb *inp, struct sctp_tcb *stcb)
+{
+	struct mbuf *m, *m_at, *m_last;
+	struct sctp_nets *net;
+	struct sctp_init_msg *initm;
+	struct sctp_supported_addr_param *sup_addr;
+	struct sctp_ecn_supported_param *ecn;
+	struct sctp_prsctp_supported_param *prsctp;
+	struct sctp_ecn_nonce_supported_param *ecn_nonce;
+	struct sctp_supported_chunk_types_param *pr_supported;
+	int cnt_inits_to=0;
+	int padval, ret;
+
+	/* INIT's always go to the primary (and usually ONLY address) */
+	m_last = NULL;
+	net = stcb->asoc.primary_destination;
+	if (net == NULL) {
+		net = TAILQ_FIRST(&stcb->asoc.nets);
+		if (net == NULL) {
+			/* TSNH */
+			return;
+		}
+		/* we confirm any address we send an INIT to */
+		net->dest_state &= ~SCTP_ADDR_UNCONFIRMED;
+		sctp_set_primary_addr(stcb, NULL, net);
+	} else {
+		/* we confirm any address we send an INIT to */
+		net->dest_state &= ~SCTP_ADDR_UNCONFIRMED;
+	}
+#ifdef SCTP_DEBUG
+	if (sctp_debug_on & SCTP_DEBUG_OUTPUT4) {
+		printf("Sending INIT to ");
+		sctp_print_address ((struct sockaddr *)&net->ro._l_addr);
+	}
+#endif
+	if (((struct sockaddr *)&(net->ro._l_addr))->sa_family == AF_INET6) {
+		/* special hook, if we are sending to link local
+		 * it will not show up in our private address count.
+		 */
+ 		struct sockaddr_in6 *sin6l;
+		sin6l = &net->ro._l_addr.sin6;
+		if (IN6_IS_ADDR_LINKLOCAL(&sin6l->sin6_addr))
+			cnt_inits_to = 1;
+	}
+	if (callout_pending(&net->rxt_timer.timer)) {
+		/* This case should not happen */
+		return;
+	}
+	/* start the INIT timer */
+	if (sctp_timer_start(SCTP_TIMER_TYPE_INIT, inp, stcb, net)) {
+		/* we are hosed since I can't start the INIT timer? */
+		return;
+	}
+	MGETHDR(m, MB_DONTWAIT, MT_HEADER);
+	if (m == NULL) {
+		/* No memory, INIT timer will re-attempt. */
+		return;
+	}
+	/* make it into a M_EXT */
+	MCLGET(m, MB_DONTWAIT);
+	if ((m->m_flags & M_EXT) != M_EXT) {
+		/* Failed to get cluster buffer */
+		sctp_m_freem(m);
+		return;
+	}
+	m->m_data += SCTP_MIN_OVERHEAD;
+	m->m_len = sizeof(struct sctp_init_msg);
+	/* Now lets put the SCTP header in place */
+	initm = mtod(m, struct sctp_init_msg *);
+	initm->sh.src_port = inp->sctp_lport;
+	initm->sh.dest_port = stcb->rport;
+	initm->sh.v_tag = 0;
+	initm->sh.checksum = 0;	/* calculate later */
+	/* now the chunk header */
+	initm->msg.ch.chunk_type = SCTP_INITIATION;
+	initm->msg.ch.chunk_flags = 0;
+	/* fill in later from mbuf we build */
+	initm->msg.ch.chunk_length = 0;
+	/* place in my tag */
+	initm->msg.init.initiate_tag = htonl(stcb->asoc.my_vtag);
+	/* set up some of the credits. */
+	initm->msg.init.a_rwnd = htonl(max(inp->sctp_socket->so_rcv.sb_hiwat,
+	    SCTP_MINIMAL_RWND));
+
+	initm->msg.init.num_outbound_streams = htons(stcb->asoc.pre_open_streams);
+	initm->msg.init.num_inbound_streams = htons(stcb->asoc.max_inbound_streams);
+	initm->msg.init.initial_tsn = htonl(stcb->asoc.init_seq_number);
+	/* now the address restriction */
+	sup_addr = (struct sctp_supported_addr_param *)((caddr_t)initm +
+	    sizeof(*initm));
+	sup_addr->ph.param_type = htons(SCTP_SUPPORTED_ADDRTYPE);
+	/* we support 2 types IPv6/IPv4 */
+	sup_addr->ph.param_length = htons(sizeof(*sup_addr) +
+					  sizeof(uint16_t));
+	sup_addr->addr_type[0] = htons(SCTP_IPV4_ADDRESS);
+	sup_addr->addr_type[1] = htons(SCTP_IPV6_ADDRESS);
+	m->m_len += sizeof(*sup_addr) + sizeof(uint16_t);
+
+/*	if (inp->sctp_flags & SCTP_PCB_FLAGS_ADAPTIONEVNT) {*/
+	if (inp->sctp_ep.adaption_layer_indicator) {
+		struct sctp_adaption_layer_indication *ali;
+		ali = (struct sctp_adaption_layer_indication *)(
+		    (caddr_t)sup_addr + sizeof(*sup_addr) + sizeof(uint16_t));
+		ali->ph.param_type = htons(SCTP_ULP_ADAPTION);
+		ali->ph.param_length = htons(sizeof(*ali));
+		ali->indication = ntohl(inp->sctp_ep.adaption_layer_indicator);
+		m->m_len += sizeof(*ali);
+		ecn = (struct sctp_ecn_supported_param *)((caddr_t)ali +
+		    sizeof(*ali));
+	} else {
+		ecn = (struct sctp_ecn_supported_param *)((caddr_t)sup_addr +
+		    sizeof(*sup_addr) + sizeof(uint16_t));
+	}
+
+	/* now any cookie time extensions */
+	if (stcb->asoc.cookie_preserve_req) {
+		struct sctp_cookie_perserve_param *cookie_preserve;
+		cookie_preserve = (struct sctp_cookie_perserve_param *)(ecn);
+		cookie_preserve->ph.param_type = htons(SCTP_COOKIE_PRESERVE);
+		cookie_preserve->ph.param_length = htons(
+		    sizeof(*cookie_preserve));
+		cookie_preserve->time = htonl(stcb->asoc.cookie_preserve_req);
+		m->m_len += sizeof(*cookie_preserve);
+		ecn = (struct sctp_ecn_supported_param *)(
+		    (caddr_t)cookie_preserve + sizeof(*cookie_preserve));
+		stcb->asoc.cookie_preserve_req = 0;
+	}
+
+	/* ECN parameter */
+	if (sctp_ecn == 1) {
+		ecn->ph.param_type = htons(SCTP_ECN_CAPABLE);
+		ecn->ph.param_length = htons(sizeof(*ecn));
+		m->m_len += sizeof(*ecn);
+		prsctp = (struct sctp_prsctp_supported_param *)((caddr_t)ecn +
+		    sizeof(*ecn));
+	} else {
+		prsctp = (struct sctp_prsctp_supported_param *)((caddr_t)ecn);
+	}
+	/* And now tell the peer we do pr-sctp */
+	prsctp->ph.param_type = htons(SCTP_PRSCTP_SUPPORTED);
+	prsctp->ph.param_length = htons(sizeof(*prsctp));
+	m->m_len += sizeof(*prsctp);
+
+
+	/* And now tell the peer we do all the extensions */
+	pr_supported = (struct sctp_supported_chunk_types_param *)((caddr_t)prsctp +
+	   sizeof(*prsctp));
+
+	pr_supported->ph.param_type = htons(SCTP_SUPPORTED_CHUNK_EXT);
+	pr_supported->ph.param_length = htons(sizeof(*pr_supported) + SCTP_EXT_COUNT);
+	pr_supported->chunk_types[0] = SCTP_ASCONF;
+	pr_supported->chunk_types[1] = SCTP_ASCONF_ACK;
+	pr_supported->chunk_types[2] = SCTP_FORWARD_CUM_TSN;
+	pr_supported->chunk_types[3] = SCTP_PACKET_DROPPED;
+	pr_supported->chunk_types[4] = SCTP_STREAM_RESET;
+	pr_supported->chunk_types[5] = 0; /* pad */
+	pr_supported->chunk_types[6] = 0; /* pad */
+	pr_supported->chunk_types[7] = 0; /* pad */
+
+	m->m_len += (sizeof(*pr_supported) + SCTP_EXT_COUNT + SCTP_PAD_EXT_COUNT);
+	/* ECN nonce: And now tell the peer we support ECN nonce */
+
+	if (sctp_ecn_nonce) {
+		ecn_nonce = (struct sctp_ecn_nonce_supported_param *)((caddr_t)pr_supported +
+		    sizeof(*pr_supported) + SCTP_EXT_COUNT + SCTP_PAD_EXT_COUNT);
+		ecn_nonce->ph.param_type = htons(SCTP_ECN_NONCE_SUPPORTED);
+		ecn_nonce->ph.param_length = htons(sizeof(*ecn_nonce));
+		m->m_len += sizeof(*ecn_nonce);
+	}
+
+	m_at = m;
+	/* now the addresses */
+	if (inp->sctp_flags & SCTP_PCB_FLAGS_BOUNDALL) {
+		struct ifnet *ifn;
+		struct ifaddr *ifa;
+		int cnt;
+
+		cnt = cnt_inits_to;
+ 		TAILQ_FOREACH(ifn, &ifnet, if_list) {
+			if ((stcb->asoc.loopback_scope == 0) &&
+			    (ifn->if_type == IFT_LOOP)) {
+				/*
+				 * Skip loopback devices if loopback_scope
+				 * not set
+				 */
+				continue;
+			}
+			TAILQ_FOREACH(ifa, &ifn->if_addrlist, ifa_list) {
+				if (sctp_is_address_in_scope(ifa,
+				    stcb->asoc.ipv4_addr_legal,
+				    stcb->asoc.ipv6_addr_legal,
+				    stcb->asoc.loopback_scope,
+				    stcb->asoc.ipv4_local_scope,
+				    stcb->asoc.local_scope,
+				    stcb->asoc.site_scope) == 0) {
+					continue;
+				}
+				cnt++;
+			}
+		}
+		if (cnt > 1) {
+			TAILQ_FOREACH(ifn, &ifnet, if_list) {
+				if ((stcb->asoc.loopback_scope == 0) &&
+				    (ifn->if_type == IFT_LOOP)) {
+					/*
+					 * Skip loopback devices if loopback_scope
+					 * not set
+					 */
+					continue;
+				}
+				TAILQ_FOREACH(ifa, &ifn->if_addrlist, ifa_list) {
+					if (sctp_is_address_in_scope(ifa,
+					    stcb->asoc.ipv4_addr_legal,
+					    stcb->asoc.ipv6_addr_legal,
+					    stcb->asoc.loopback_scope,
+					    stcb->asoc.ipv4_local_scope,
+					    stcb->asoc.local_scope,
+					    stcb->asoc.site_scope) == 0) {
+						continue;
+					}
+					m_at = sctp_add_addr_to_mbuf(m_at, ifa);
+				}
+			}
+		}
+	} else {
+		struct sctp_laddr *laddr;
+		int cnt;
+		cnt = cnt_inits_to;
+		/* First, how many ? */
+		LIST_FOREACH(laddr, &inp->sctp_addr_list, sctp_nxt_addr) {
+			if (laddr->ifa == NULL) {
+				continue;
+			}
+			if (laddr->ifa->ifa_addr == NULL)
+				continue;
+			if (sctp_is_address_in_scope(laddr->ifa,
+			    stcb->asoc.ipv4_addr_legal,
+			    stcb->asoc.ipv6_addr_legal,
+			    stcb->asoc.loopback_scope,
+			    stcb->asoc.ipv4_local_scope,
+			    stcb->asoc.local_scope,
+			    stcb->asoc.site_scope) == 0) {
+				continue;
+			}
+			cnt++;
+		}
+		/* To get through a NAT we only list addresses if
+		 * we have more than one. That way if you just
+		 * bind a single address we let the source of the init
+		 * dictate our address.
+		 */
+		if (cnt > 1) {
+			LIST_FOREACH(laddr, &inp->sctp_addr_list, sctp_nxt_addr) {
+				if (laddr->ifa == NULL) {
+					continue;
+				}
+				if (laddr->ifa->ifa_addr == NULL) {
+					continue;
+				}
+
+				if (sctp_is_address_in_scope(laddr->ifa,
+				    stcb->asoc.ipv4_addr_legal,
+				    stcb->asoc.ipv6_addr_legal,
+				    stcb->asoc.loopback_scope,
+				    stcb->asoc.ipv4_local_scope,
+				    stcb->asoc.local_scope,
+				    stcb->asoc.site_scope) == 0) {
+					continue;
+				}
+				m_at = sctp_add_addr_to_mbuf(m_at, laddr->ifa);
+			}
+		}
+	}
+	/* calulate the size and update pkt header and chunk header */
+	m->m_pkthdr.len = 0;
+	for (m_at = m; m_at; m_at = m_at->m_next) {
+		if (m_at->m_next == NULL)
+			m_last = m_at;
+		m->m_pkthdr.len += m_at->m_len;
+	}
+	initm->msg.ch.chunk_length = htons((m->m_pkthdr.len -
+	    sizeof(struct sctphdr)));
+	/* We pass 0 here to NOT set IP_DF if its IPv4, we
+	 * ignore the return here since the timer will drive
+	 * a retranmission.
+	 */
+
+	/* I don't expect this to execute but we will be safe here */
+	padval = m->m_pkthdr.len % 4;
+	if ((padval) && (m_last)) {
+		/* The compiler worries that m_last may not be
+		 * set even though I think it is impossible :->
+		 * however we add m_last here just in case.
+		 */
+		int ret;
+		ret = sctp_add_pad_tombuf(m_last, (4-padval));
+		if (ret) {
+			/* Houston we have a problem, no space */
+			sctp_m_freem(m);
+			return;
+		}
+		m->m_pkthdr.len += padval;
+	}
+#ifdef SCTP_DEBUG
+	if (sctp_debug_on & SCTP_DEBUG_OUTPUT4) {
+		printf("Calling lowlevel output stcb:%x net:%x\n",
+		       (u_int)stcb, (u_int)net);
+	}
+#endif
+	ret = sctp_lowlevel_chunk_output(inp, stcb, net,
+		  (struct sockaddr *)&net->ro._l_addr, m, 0, 0, NULL, 0);
+#ifdef SCTP_DEBUG
+	if (sctp_debug_on & SCTP_DEBUG_OUTPUT4) {
+		printf("Low level output returns %d\n", ret);
+	}
+#endif
+	sctp_timer_start(SCTP_TIMER_TYPE_INIT, inp, stcb, net);
+	SCTP_GETTIME_TIMEVAL(&net->last_sent_time);
+}
+
+struct mbuf *
+sctp_arethere_unrecognized_parameters(struct mbuf *in_initpkt,
+    int param_offset, int *abort_processing, struct sctp_chunkhdr *cp)
+{
+	/* Given a mbuf containing an INIT or INIT-ACK
+	 * with the param_offset being equal to the
+	 * beginning of the params i.e. (iphlen + sizeof(struct sctp_init_msg)
+	 * parse through the parameters to the end of the mbuf verifying
+	 * that all parameters are known.
+	 *
+	 * For unknown parameters build and return a mbuf with
+	 * UNRECOGNIZED_PARAMETER errors. If the flags indicate
+	 * to stop processing this chunk stop, and set *abort_processing
+	 * to 1.
+	 *
+	 * By having param_offset be pre-set to where parameters begin
+	 * it is hoped that this routine may be reused in the future
+	 * by new features.
+	 */
+	struct sctp_paramhdr *phdr, params;
+
+	struct mbuf *mat, *op_err;
+	char tempbuf[2048];
+	int at, limit, pad_needed;
+	uint16_t ptype, plen;
+	int err_at;
+
+	*abort_processing = 0;
+	mat = in_initpkt;
+	err_at = 0;
+	limit = ntohs(cp->chunk_length) - sizeof(struct sctp_init_chunk);
+#ifdef SCTP_DEBUG
+	if (sctp_debug_on & SCTP_DEBUG_OUTPUT4) {
+		printf("Limit is %d bytes\n", limit);
+	}
+#endif
+	at = param_offset;
+	op_err = NULL;
+
+	phdr = sctp_get_next_param(mat, at, &params, sizeof(params));
+	while ((phdr != NULL) && ((size_t)limit >= sizeof(struct sctp_paramhdr))) {
+		ptype = ntohs(phdr->param_type);
+		plen = ntohs(phdr->param_length);
+		limit -= SCTP_SIZE32(plen);
+		if (plen < sizeof(struct sctp_paramhdr)) {
+#ifdef SCTP_DEBUG
+	if (sctp_debug_on & SCTP_DEBUG_OUTPUT4) {
+			printf("sctp_output.c:Impossible length in parameter < %d\n", plen);
+	}
+#endif
+			*abort_processing = 1;
+			break;
+		}
+		/* All parameters for all chunks that we
+		 * know/understand are listed here. We process
+		 * them other places and make appropriate
+		 * stop actions per the upper bits. However
+		 * this is the generic routine processor's can
+		 * call to get back an operr.. to either incorporate (init-ack)
+		 * or send.
+		 */
+		if ((ptype == SCTP_HEARTBEAT_INFO) ||
+		    (ptype == SCTP_IPV4_ADDRESS) ||
+		    (ptype == SCTP_IPV6_ADDRESS) ||
+		    (ptype == SCTP_STATE_COOKIE) ||
+		    (ptype == SCTP_UNRECOG_PARAM) ||
+		    (ptype == SCTP_COOKIE_PRESERVE) ||
+		    (ptype == SCTP_SUPPORTED_ADDRTYPE) ||
+		    (ptype == SCTP_PRSCTP_SUPPORTED) ||
+		    (ptype == SCTP_ADD_IP_ADDRESS) ||
+		    (ptype == SCTP_DEL_IP_ADDRESS) ||
+		    (ptype == SCTP_ECN_CAPABLE) ||
+		    (ptype == SCTP_ULP_ADAPTION) ||
+		    (ptype == SCTP_ERROR_CAUSE_IND) ||
+		    (ptype == SCTP_SET_PRIM_ADDR) ||
+		    (ptype == SCTP_SUCCESS_REPORT) ||
+		    (ptype == SCTP_ULP_ADAPTION) ||
+		    (ptype == SCTP_SUPPORTED_CHUNK_EXT) ||
+		    (ptype == SCTP_ECN_NONCE_SUPPORTED)
+			) {
+			/* no skip it */
+			at += SCTP_SIZE32(plen);
+		} else if (ptype == SCTP_HOSTNAME_ADDRESS) {
+			/* We can NOT handle HOST NAME addresses!! */
+#ifdef SCTP_DEBUG
+	if (sctp_debug_on & SCTP_DEBUG_OUTPUT4) {
+		printf("Can't handle hostname addresses.. abort processing\n");
+	}
+#endif
+			*abort_processing = 1;
+			if (op_err == NULL) {
+				/* Ok need to try to get a mbuf */
+				MGETHDR(op_err, MB_DONTWAIT, MT_DATA);
+				if (op_err) {
+					op_err->m_len = 0;
+					op_err->m_pkthdr.len = 0;
+					/* pre-reserve space for ip and sctp header  and chunk hdr*/
+					op_err->m_data += sizeof(struct ip6_hdr);
+					op_err->m_data += sizeof(struct sctphdr);
+					op_err->m_data += sizeof(struct sctp_chunkhdr);
+				}
+			}
+			if (op_err) {
+				/* If we have space */
+				struct sctp_paramhdr s;
+				if (err_at % 4) {
+					u_int32_t cpthis=0;
+					pad_needed = 4 - (err_at % 4);
+					m_copyback(op_err, err_at, pad_needed, (caddr_t)&cpthis);
+					err_at += pad_needed;
+				}
+				s.param_type = htons(SCTP_CAUSE_UNRESOLV_ADDR);
+				s.param_length = htons(sizeof(s) + plen);
+				m_copyback(op_err, err_at, sizeof(s), (caddr_t)&s);
+				err_at += sizeof(s);
+				phdr = sctp_get_next_param(mat, at, (struct sctp_paramhdr *)tempbuf, plen);
+				if (phdr == NULL) {
+					sctp_m_freem(op_err);
+					/* we are out of memory but we
+					 * still need to have a look at what to
+					 * do (the system is in trouble though).
+					 */
+					return (NULL);
+				}
+				m_copyback(op_err, err_at, plen, (caddr_t)phdr);
+				err_at += plen;
+			}
+			return (op_err);
+		} else {
+			/* we do not recognize the parameter
+			 * figure out what we do.
+			 */
+#ifdef SCTP_DEBUG
+			if (sctp_debug_on & SCTP_DEBUG_OUTPUT4) {
+				printf("Got parameter type %x - unknown\n",
+				       (u_int)ptype);
+			}
+#endif
+			if ((ptype & 0x4000) == 0x4000) {
+				/* Report bit is set?? */
+#ifdef SCTP_DEBUG
+				if (sctp_debug_on & SCTP_DEBUG_OUTPUT4) {
+					printf("Report bit is set\n");
+				}
+#endif
+				if (op_err == NULL) {
+					/* Ok need to try to get an mbuf */
+					MGETHDR(op_err, MB_DONTWAIT, MT_DATA);
+					if (op_err) {
+						op_err->m_len = 0;
+						op_err->m_pkthdr.len = 0;
+						op_err->m_data += sizeof(struct ip6_hdr);
+						op_err->m_data += sizeof(struct sctphdr);
+						op_err->m_data += sizeof(struct sctp_chunkhdr);
+					}
+				}
+				if (op_err) {
+					/* If we have space */
+					struct sctp_paramhdr s;
+					if (err_at % 4) {
+						u_int32_t cpthis=0;
+						pad_needed = 4 - (err_at % 4);
+						m_copyback(op_err, err_at, pad_needed, (caddr_t)&cpthis);
+						err_at += pad_needed;
+ 					}
+					s.param_type = htons(SCTP_UNRECOG_PARAM);
+					s.param_length = htons(sizeof(s) + plen);
+					m_copyback(op_err, err_at, sizeof(s), (caddr_t)&s);
+					err_at += sizeof(s);
+					if (plen > sizeof(tempbuf)) {
+						plen = sizeof(tempbuf);
+					}
+					phdr = sctp_get_next_param(mat, at, (struct sctp_paramhdr *)tempbuf, plen);
+					if (phdr == NULL) {
+						sctp_m_freem(op_err);
+						/* we are out of memory but we
+						 * still need to have a look at what to
+						 * do (the system is in trouble though).
+						 */
+						goto more_processing;
+					}
+					m_copyback(op_err, err_at, plen, (caddr_t)phdr);
+					err_at += plen;
+				}
+			}
+		more_processing:
+			if ((ptype & 0x8000) == 0x0000) {
+#ifdef SCTP_DEBUG
+				if (sctp_debug_on & SCTP_DEBUG_OUTPUT4) {
+					printf("Abort bit is now setting1\n");
+				}
+#endif
+				return (op_err);
+			} else {
+				/* skip this chunk and continue processing */
+				at += SCTP_SIZE32(plen);
+			}
+
+		}
+		phdr = sctp_get_next_param(mat, at, &params, sizeof(params));
+	}
+	return (op_err);
+}
+
+static int
+sctp_are_there_new_addresses(struct sctp_association *asoc,
+    struct mbuf *in_initpkt, int iphlen, int offset)
+{
+	/*
+	 * Given a INIT packet, look through the packet to verify that
+	 * there are NO new addresses. As we go through the parameters
+	 * add reports of any un-understood parameters that require an
+	 * error.  Also we must return (1) to drop the packet if we see
+	 * a un-understood parameter that tells us to drop the chunk.
+	 */
+	struct sockaddr_in sin4, *sa4;
+	struct sockaddr_in6 sin6, *sa6;
+	struct sockaddr *sa_touse;
+	struct sockaddr *sa;
+	struct sctp_paramhdr *phdr, params;
+	struct ip *iph;
+	struct mbuf *mat;
+	uint16_t ptype, plen;
+	int err_at;
+	uint8_t fnd;
+	struct sctp_nets *net;
+
+	memset(&sin4, 0, sizeof(sin4));
+	memset(&sin6, 0, sizeof(sin6));
+	sin4.sin_family = AF_INET;
+	sin4.sin_len = sizeof(sin4);
+	sin6.sin6_family = AF_INET6;
+	sin6.sin6_len = sizeof(sin6);
+
+	sa_touse = NULL;
+	/* First what about the src address of the pkt ? */
+	iph = mtod(in_initpkt, struct ip *);
+	if (iph->ip_v == IPVERSION) {
+		/* source addr is IPv4 */
+		sin4.sin_addr = iph->ip_src;
+		sa_touse = (struct sockaddr *)&sin4;
+	} else if (iph->ip_v == (IPV6_VERSION >> 4)) {
+		/* source addr is IPv6 */
+		struct ip6_hdr *ip6h;
+		ip6h = mtod(in_initpkt, struct ip6_hdr *);
+		sin6.sin6_addr = ip6h->ip6_src;
+		sa_touse = (struct sockaddr *)&sin6;
+	} else {
+		return (1);
+	}
+
+	fnd = 0;
+	TAILQ_FOREACH(net, &asoc->nets, sctp_next) {
+		sa = (struct sockaddr *)&net->ro._l_addr;
+		if (sa->sa_family == sa_touse->sa_family) {
+			if (sa->sa_family == AF_INET) {
+			    sa4 = (struct sockaddr_in *)sa;
+				if (sa4->sin_addr.s_addr ==
+				    sin4.sin_addr.s_addr) {
+					fnd = 1;
+					break;
+				}
+			} else if (sa->sa_family == AF_INET6) {
+				sa6 = (struct sockaddr_in6 *)sa;
+				if (SCTP6_ARE_ADDR_EQUAL(&sa6->sin6_addr,
+				    &sin6.sin6_addr)) {
+					fnd = 1;
+					break;
+				}
+			}
+		}
+	}
+	if (fnd == 0) {
+		/* New address added! no need to look futher. */
+		return (1);
+	}
+	/* Ok so far lets munge through the rest of the packet */
+	mat = in_initpkt;
+	err_at = 0;
+	sa_touse = NULL;
+	offset += sizeof(struct sctp_init_chunk);
+	phdr = sctp_get_next_param(mat, offset, &params, sizeof(params));
+	while (phdr) {
+		ptype = ntohs(phdr->param_type);
+		plen = ntohs(phdr->param_length);
+		if (ptype == SCTP_IPV4_ADDRESS) {
+			struct sctp_ipv4addr_param *p4, p4_buf;
+
+			phdr = sctp_get_next_param(mat, offset,
+			    (struct sctp_paramhdr *)&p4_buf, sizeof(p4_buf));
+			if (plen != sizeof(struct sctp_ipv4addr_param) ||
+			    phdr == NULL) {
+                                return (1);
+                        }
+			p4 = (struct sctp_ipv4addr_param *)phdr;
+			sin4.sin_addr.s_addr = p4->addr;
+			sa_touse = (struct sockaddr *)&sin4;
+		} else if (ptype == SCTP_IPV6_ADDRESS) {
+			struct sctp_ipv6addr_param *p6, p6_buf;
+
+			phdr = sctp_get_next_param(mat, offset,
+			    (struct sctp_paramhdr *)&p6_buf, sizeof(p6_buf));
+			if (plen != sizeof(struct sctp_ipv6addr_param) ||
+			    phdr == NULL) {
+                                return (1);
+                        }
+			p6 = (struct sctp_ipv6addr_param *)phdr;
+			memcpy((caddr_t)&sin6.sin6_addr, p6->addr,
+			    sizeof(p6->addr));
+			sa_touse = (struct sockaddr *)&sin4;
+		}
+
+		if (sa_touse) {
+			/* ok, sa_touse points to one to check */
+			fnd = 0;
+			TAILQ_FOREACH(net, &asoc->nets, sctp_next) {
+				sa = (struct sockaddr *)&net->ro._l_addr;
+				if (sa->sa_family != sa_touse->sa_family) {
+					continue;
+				}
+				if (sa->sa_family == AF_INET) {
+					sa4 = (struct sockaddr_in *)sa;
+					if (sa4->sin_addr.s_addr ==
+					    sin4.sin_addr.s_addr) {
+						fnd = 1;
+						break;
+					}
+				} else if (sa->sa_family == AF_INET6) {
+					sa6 = (struct sockaddr_in6 *)sa;
+					if (SCTP6_ARE_ADDR_EQUAL(
+					    &sa6->sin6_addr, &sin6.sin6_addr)) {
+						fnd = 1;
+						break;
+					}
+				}
+			}
+			if (!fnd) {
+				/* New addr added! no need to look further */
+				return (1);
+			}
+		}
+		offset += SCTP_SIZE32(plen);
+		phdr = sctp_get_next_param(mat, offset, &params, sizeof(params));
+	}
+	return (0);
+}
+
+/*
+ * Given a MBUF chain that was sent into us containing an
+ * INIT. Build a INIT-ACK with COOKIE and send back.
+ * We assume that the in_initpkt has done a pullup to
+ * include IPv6/4header, SCTP header and initial part of
+ * INIT message (i.e. the struct sctp_init_msg).
+ */
+void
+sctp_send_initiate_ack(struct sctp_inpcb *inp, struct sctp_tcb *stcb,
+    struct mbuf *init_pkt, int iphlen, int offset, struct sctphdr *sh,
+    struct sctp_init_chunk *init_chk)
+{
+	struct sctp_association *asoc;
+	struct mbuf *m, *m_at, *m_tmp, *m_cookie, *op_err, *m_last;
+	struct sctp_init_msg *initackm_out;
+	struct sctp_ecn_supported_param *ecn;
+	struct sctp_prsctp_supported_param *prsctp;
+	struct sctp_ecn_nonce_supported_param *ecn_nonce;
+	struct sctp_supported_chunk_types_param *pr_supported;
+	struct sockaddr_storage store;
+	struct sockaddr_in *sin;
+	struct sockaddr_in6 *sin6;
+	struct route *ro;
+	struct ip *iph;
+	struct ip6_hdr *ip6;
+	struct sockaddr *to;
+	struct sctp_state_cookie stc;
+	struct sctp_nets *net=NULL;
+	int cnt_inits_to=0;
+	uint16_t his_limit, i_want;
+	int abort_flag, padval, sz_of;
+
+	if (stcb) {
+		asoc = &stcb->asoc;
+	} else {
+		asoc = NULL;
+	}
+	m_last = NULL;
+	if ((asoc != NULL) &&
+	    (SCTP_GET_STATE(asoc) != SCTP_STATE_COOKIE_WAIT) &&
+	    (sctp_are_there_new_addresses(asoc, init_pkt, iphlen, offset))) {
+		/* new addresses, out of here in non-cookie-wait states */
+		/*
+		 * Send a ABORT, we don't add the new address error clause though
+		 * we even set the T bit and copy in the 0 tag.. this looks no
+		 * different than if no listner was present.
+		 */
+		sctp_send_abort(init_pkt, iphlen, sh, 0, NULL);
+		return;
+	}
+	abort_flag = 0;
+	op_err = sctp_arethere_unrecognized_parameters(init_pkt,
+	    (offset+sizeof(struct sctp_init_chunk)),
+	    &abort_flag, (struct sctp_chunkhdr *)init_chk);
+	if (abort_flag) {
+		sctp_send_abort(init_pkt, iphlen, sh, init_chk->init.initiate_tag, op_err);
+		return;
+	}
+	MGETHDR(m, MB_DONTWAIT, MT_HEADER);
+	if (m == NULL) {
+		/* No memory, INIT timer will re-attempt. */
+		if (op_err)
+			sctp_m_freem(op_err);
+		return;
+	}
+	MCLGET(m, MB_DONTWAIT);
+	if ((m->m_flags & M_EXT) != M_EXT) {
+		/* Failed to get cluster buffer */
+		if (op_err)
+			sctp_m_freem(op_err);
+		sctp_m_freem(m);
+		return;
+	}
+	m->m_data += SCTP_MIN_OVERHEAD;
+	m->m_pkthdr.rcvif = 0;
+	m->m_len = sizeof(struct sctp_init_msg);
+
+	/* the time I built cookie */
+	SCTP_GETTIME_TIMEVAL(&stc.time_entered);
+
+	/* populate any tie tags */
+	if (asoc != NULL) {
+		/* unlock before tag selections */
+		SCTP_TCB_UNLOCK(stcb);
+		if (asoc->my_vtag_nonce == 0)
+			asoc->my_vtag_nonce = sctp_select_a_tag(inp);
+		stc.tie_tag_my_vtag = asoc->my_vtag_nonce;
+
+		if (asoc->peer_vtag_nonce == 0)
+			asoc->peer_vtag_nonce = sctp_select_a_tag(inp);
+		stc.tie_tag_peer_vtag = asoc->peer_vtag_nonce;
+
+		stc.cookie_life = asoc->cookie_life;
+		net = asoc->primary_destination;
+		/* now we must relock */
+		SCTP_INP_RLOCK(inp);
+		/* we may be in trouble here if the inp got freed
+		 * most likely this set of tests will protect
+		 * us but there is a chance not.
+		 */
+		if (inp->sctp_flags & (SCTP_PCB_FLAGS_SOCKET_GONE|SCTP_PCB_FLAGS_SOCKET_ALLGONE)) {
+			if (op_err)
+				sctp_m_freem(op_err);
+			sctp_m_freem(m);
+			sctp_send_abort(init_pkt, iphlen, sh, 0, NULL);
+			return;
+		}
+		SCTP_TCB_LOCK(stcb);
+		SCTP_INP_RUNLOCK(stcb->sctp_ep);
+	} else {
+		stc.tie_tag_my_vtag = 0;
+		stc.tie_tag_peer_vtag = 0;
+		/* life I will award this cookie */
+		stc.cookie_life = inp->sctp_ep.def_cookie_life;
+	}
+
+	/* copy in the ports for later check */
+	stc.myport = sh->dest_port;
+	stc.peerport = sh->src_port;
+
+	/*
+	 * If we wanted to honor cookie life extentions, we would add
+	 * to stc.cookie_life. For now we should NOT honor any extension
+	 */
+	stc.site_scope = stc.local_scope = stc.loopback_scope = 0;
+	if (inp->sctp_flags & SCTP_PCB_FLAGS_BOUND_V6) {
+		struct inpcb *in_inp;
+		/* Its a V6 socket */
+		in_inp = (struct inpcb *)inp;
+		stc.ipv6_addr_legal = 1;
+		/* Now look at the binding flag to see if V4 will be legal */
+		if (
+#if defined(__FreeBSD__) || defined(__APPLE__) || defined(__DragonFly__)
+		    (in_inp->inp_flags & IN6P_IPV6_V6ONLY)
+#elif defined(__OpenBSD__)
+		    (0)	/* For openbsd we do dual bind only */
+#else
+		    (((struct in6pcb *)in_inp)->in6p_flags & IN6P_IPV6_V6ONLY)
+#endif
+		    == 0) {
+			stc.ipv4_addr_legal = 1;
+		} else {
+			/* V4 addresses are NOT legal on the association */
+			stc.ipv4_addr_legal = 0;
+		}
+	} else {
+		/* Its a V4 socket, no - V6 */
+		stc.ipv4_addr_legal = 1;
+		stc.ipv6_addr_legal = 0;
+	}
+
+#ifdef SCTP_DONT_DO_PRIVADDR_SCOPE
+	stc.ipv4_scope = 1;
+#else
+	stc.ipv4_scope = 0;
+#endif
+	/* now for scope setup */
+	memset((caddr_t)&store, 0, sizeof(store));
+	sin = (struct sockaddr_in *)&store;
+	sin6 = (struct sockaddr_in6 *)&store;
+	if (net == NULL) {
+		to = (struct sockaddr *)&store;
+		iph = mtod(init_pkt, struct ip *);
+		if (iph->ip_v == IPVERSION) {
+			struct in_addr addr;
+			struct route iproute;
+
+			sin->sin_family = AF_INET;
+			sin->sin_len = sizeof(struct sockaddr_in);
+			sin->sin_port = sh->src_port;
+			sin->sin_addr = iph->ip_src;
+			/* lookup address */
+			stc.address[0] = sin->sin_addr.s_addr;
+			stc.address[1] = 0;
+			stc.address[2] = 0;
+			stc.address[3] = 0;
+			stc.addr_type = SCTP_IPV4_ADDRESS;
+			/* local from address */
+			memset(&iproute, 0, sizeof(iproute));
+			ro = &iproute;
+			memcpy(&ro->ro_dst, sin, sizeof(*sin));
+			addr = sctp_ipv4_source_address_selection(inp, NULL,
+			    ro, NULL, 0);
+			if (ro->ro_rt) {
+				RTFREE(ro->ro_rt);
+			}
+			stc.laddress[0] = addr.s_addr;
+			stc.laddress[1] = 0;
+			stc.laddress[2] = 0;
+			stc.laddress[3] = 0;
+			stc.laddr_type = SCTP_IPV4_ADDRESS;
+			/* scope_id is only for v6 */
+			stc.scope_id = 0;
+#ifndef SCTP_DONT_DO_PRIVADDR_SCOPE
+			if (IN4_ISPRIVATE_ADDRESS(&sin->sin_addr)) {
+				stc.ipv4_scope = 1;
+			}
+#else
+			stc.ipv4_scope = 1;
+#endif /* SCTP_DONT_DO_PRIVADDR_SCOPE */
+			/* Must use the address in this case */
+			if (sctp_is_address_on_local_host((struct sockaddr *)sin)) {
+				stc.loopback_scope = 1;
+				stc.ipv4_scope = 1;
+				stc.site_scope = 1;
+				stc.local_scope = 1;
+			}
+		} else if (iph->ip_v == (IPV6_VERSION >> 4)) {
+			struct in6_addr addr;
+#ifdef NEW_STRUCT_ROUTE
+                        struct route iproute6;
+#else
+                        struct route_in6 iproute6;
+#endif
+			ip6 = mtod(init_pkt, struct ip6_hdr *);
+			sin6->sin6_family = AF_INET6;
+			sin6->sin6_len = sizeof(struct sockaddr_in6);
+			sin6->sin6_port = sh->src_port;
+			sin6->sin6_addr = ip6->ip6_src;
+			/* lookup address */
+			memcpy(&stc.address, &sin6->sin6_addr,
+			    sizeof(struct in6_addr));
+			sin6->sin6_scope_id = 0;
+			stc.addr_type = SCTP_IPV6_ADDRESS;
+			stc.scope_id = 0;
+			if (sctp_is_address_on_local_host((struct sockaddr *)sin6)) {
+				stc.loopback_scope = 1;
+				stc.local_scope = 1;
+				stc.site_scope = 1;
+				stc.ipv4_scope = 1;
+			} else if (IN6_IS_ADDR_LINKLOCAL(&sin6->sin6_addr)) {
+				/*
+				 * If the new destination is a LINK_LOCAL
+				 * we must have common both site and local
+				 * scope. Don't set local scope though since
+				 * we must depend on the source to be added
+				 * implicitly. We cannot assure just because
+				 * we share one link that all links are common.
+				 */
+				stc.local_scope = 0;
+				stc.site_scope = 1;
+ 				stc.ipv4_scope = 1;
+				/* we start counting for the private
+				 * address stuff at 1. since the link
+				 * local we source from won't show
+				 * up in our scoped cou8nt.
+				 */
+				cnt_inits_to=1;
+				/* pull out the scope_id from incoming pkt */
+				(void)in6_recoverscope(sin6, &ip6->ip6_src,
+				    init_pkt->m_pkthdr.rcvif);
+#if defined(SCTP_BASE_FREEBSD) || defined(__APPLE__) || defined(__DragonFly__)
+				in6_embedscope(&sin6->sin6_addr, sin6, NULL,
+				    NULL);
+#else
+				in6_embedscope(&sin6->sin6_addr, sin6);
+#endif
+				stc.scope_id = sin6->sin6_scope_id;
+			} else if (IN6_IS_ADDR_SITELOCAL(&sin6->sin6_addr)) {
+				/*
+				 * If the new destination is SITE_LOCAL
+				 * then we must have site scope in common.
+				 */
+				stc.site_scope = 1;
+			}
+			/* local from address */
+			memset(&iproute6, 0, sizeof(iproute6));
+			ro = (struct route *)&iproute6;
+			memcpy(&ro->ro_dst, sin6, sizeof(*sin6));
+			addr = sctp_ipv6_source_address_selection(inp, NULL,
+			    ro, NULL, 0);
+			if (ro->ro_rt) {
+				RTFREE(ro->ro_rt);
+			}
+			memcpy(&stc.laddress, &addr, sizeof(struct in6_addr));
+			stc.laddr_type = SCTP_IPV6_ADDRESS;
+		}
+	} else {
+		/* set the scope per the existing tcb */
+		struct sctp_nets *lnet;
+
+		stc.loopback_scope = asoc->loopback_scope;
+		stc.ipv4_scope = asoc->ipv4_local_scope;
+		stc.site_scope = asoc->site_scope;
+		stc.local_scope = asoc->local_scope;
+		TAILQ_FOREACH(lnet, &asoc->nets, sctp_next) {
+			if (lnet->ro._l_addr.sin6.sin6_family == AF_INET6) {
+				if (IN6_IS_ADDR_LINKLOCAL(&lnet->ro._l_addr.sin6.sin6_addr)) {
+					/* if we have a LL address, start counting
+					 * at 1.
+					 */
+ 					cnt_inits_to = 1;
+				}
+			}
+		}
+
+		/* use the net pointer */
+		to = (struct sockaddr *)&net->ro._l_addr;
+		if (to->sa_family == AF_INET) {
+			sin = (struct sockaddr_in *)to;
+			stc.address[0] = sin->sin_addr.s_addr;
+			stc.address[1] = 0;
+			stc.address[2] = 0;
+			stc.address[3] = 0;
+			stc.addr_type = SCTP_IPV4_ADDRESS;
+			if (net->src_addr_selected == 0) {
+				/* strange case here, the INIT
+				 * should have did the selection.
+				 */
+				net->ro._s_addr.sin.sin_addr =
+				    sctp_ipv4_source_address_selection(inp,
+				    stcb, (struct route *)&net->ro, net, 0);
+				net->src_addr_selected = 1;
+
+			}
+
+			stc.laddress[0] = net->ro._s_addr.sin.sin_addr.s_addr;
+			stc.laddress[1] = 0;
+			stc.laddress[2] = 0;
+			stc.laddress[3] = 0;
+			stc.laddr_type = SCTP_IPV4_ADDRESS;
+		} else if (to->sa_family == AF_INET6) {
+			sin6 = (struct sockaddr_in6 *)to;
+			memcpy(&stc.address, &sin6->sin6_addr,
+			    sizeof(struct in6_addr));
+			stc.addr_type = SCTP_IPV6_ADDRESS;
+			if (net->src_addr_selected == 0) {
+				/* strange case here, the INIT
+				 * should have did the selection.
+				 */
+				net->ro._s_addr.sin6.sin6_addr =
+				    sctp_ipv6_source_address_selection(inp,
+				    stcb, (struct route *)&net->ro, net, 0);
+				net->src_addr_selected = 1;
+			}
+			memcpy(&stc.laddress, &net->ro._l_addr.sin6.sin6_addr,
+			    sizeof(struct in6_addr));
+			stc.laddr_type = SCTP_IPV6_ADDRESS;
+		}
+	}
+	/* Now lets put the SCTP header in place */
+	initackm_out = mtod(m, struct sctp_init_msg *);
+	initackm_out->sh.src_port = inp->sctp_lport;
+	initackm_out->sh.dest_port = sh->src_port;
+	initackm_out->sh.v_tag = init_chk->init.initiate_tag;
+	/* Save it off for quick ref */
+	stc.peers_vtag = init_chk->init.initiate_tag;
+	initackm_out->sh.checksum = 0;	/* calculate later */
+	/* who are we */
+	strncpy(stc.identification, SCTP_VERSION_STRING,
+	   min(strlen(SCTP_VERSION_STRING), sizeof(stc.identification)));
+	/* now the chunk header */
+	initackm_out->msg.ch.chunk_type = SCTP_INITIATION_ACK;
+	initackm_out->msg.ch.chunk_flags = 0;
+	/* fill in later from mbuf we build */
+	initackm_out->msg.ch.chunk_length = 0;
+	/* place in my tag */
+	if ((asoc != NULL) &&
+	    ((SCTP_GET_STATE(asoc) == SCTP_STATE_COOKIE_WAIT) ||
+	     (SCTP_GET_STATE(asoc) == SCTP_STATE_COOKIE_ECHOED))) {
+		/* re-use the v-tags and init-seq here */
+		initackm_out->msg.init.initiate_tag = htonl(asoc->my_vtag);
+		initackm_out->msg.init.initial_tsn = htonl(asoc->init_seq_number);
+	} else {
+		initackm_out->msg.init.initiate_tag = htonl(sctp_select_a_tag(inp));
+		/* get a TSN to use too */
+		initackm_out->msg.init.initial_tsn = htonl(sctp_select_initial_TSN(&inp->sctp_ep));
+	}
+	/* save away my tag to */
+	stc.my_vtag = initackm_out->msg.init.initiate_tag;
+
+	/* set up some of the credits. */
+	initackm_out->msg.init.a_rwnd = htonl(max(inp->sctp_socket->so_rcv.sb_hiwat, SCTP_MINIMAL_RWND));
+	/* set what I want */
+	his_limit = ntohs(init_chk->init.num_inbound_streams);
+	/* choose what I want */
+	if (asoc != NULL) {
+		if (asoc->streamoutcnt > inp->sctp_ep.pre_open_stream_count) {
+			i_want = asoc->streamoutcnt;
+		} else {
+			i_want = inp->sctp_ep.pre_open_stream_count;
+		}
+	} else {
+		i_want = inp->sctp_ep.pre_open_stream_count;
+	}
+	if (his_limit < i_want) {
+		/* I Want more :< */
+		initackm_out->msg.init.num_outbound_streams = init_chk->init.num_inbound_streams;
+	} else {
+		/* I can have what I want :> */
+		initackm_out->msg.init.num_outbound_streams = htons(i_want);
+	}
+	/* tell him his limt. */
+	initackm_out->msg.init.num_inbound_streams =
+	    htons(inp->sctp_ep.max_open_streams_intome);
+	/* setup the ECN pointer */
+
+/*	if (inp->sctp_flags & SCTP_PCB_FLAGS_ADAPTIONEVNT) {*/
+	if (inp->sctp_ep.adaption_layer_indicator) {
+		struct sctp_adaption_layer_indication *ali;
+		ali = (struct sctp_adaption_layer_indication *)(
+		    (caddr_t)initackm_out + sizeof(*initackm_out));
+		ali->ph.param_type = htons(SCTP_ULP_ADAPTION);
+		ali->ph.param_length = htons(sizeof(*ali));
+		ali->indication = ntohl(inp->sctp_ep.adaption_layer_indicator);
+		m->m_len += sizeof(*ali);
+		ecn = (struct sctp_ecn_supported_param *)((caddr_t)ali +
+		    sizeof(*ali));
+	} else {
+		ecn = (struct sctp_ecn_supported_param*)(
+		    (caddr_t)initackm_out + sizeof(*initackm_out));
+	}
+
+	/* ECN parameter */
+	if (sctp_ecn == 1) {
+		ecn->ph.param_type = htons(SCTP_ECN_CAPABLE);
+		ecn->ph.param_length = htons(sizeof(*ecn));
+		m->m_len += sizeof(*ecn);
+
+		prsctp = (struct sctp_prsctp_supported_param *)((caddr_t)ecn +
+		    sizeof(*ecn));
+	} else {
+		prsctp = (struct sctp_prsctp_supported_param *)((caddr_t)ecn);
+	}
+	/* And now tell the peer we do  pr-sctp */
+	prsctp->ph.param_type = htons(SCTP_PRSCTP_SUPPORTED);
+	prsctp->ph.param_length = htons(sizeof(*prsctp));
+	m->m_len += sizeof(*prsctp);
+
+
+	/* And now tell the peer we do all the extensions */
+	pr_supported = (struct sctp_supported_chunk_types_param *)((caddr_t)prsctp +
+	   sizeof(*prsctp));
+
+	pr_supported->ph.param_type = htons(SCTP_SUPPORTED_CHUNK_EXT);
+	pr_supported->ph.param_length = htons(sizeof(*pr_supported) + SCTP_EXT_COUNT);
+	pr_supported->chunk_types[0] = SCTP_ASCONF;
+	pr_supported->chunk_types[1] = SCTP_ASCONF_ACK;
+	pr_supported->chunk_types[2] = SCTP_FORWARD_CUM_TSN;
+	pr_supported->chunk_types[3] = SCTP_PACKET_DROPPED;
+	pr_supported->chunk_types[4] = SCTP_STREAM_RESET;
+	pr_supported->chunk_types[5] = 0; /* pad */
+	pr_supported->chunk_types[6] = 0; /* pad */
+	pr_supported->chunk_types[7] = 0; /* pad */
+
+	m->m_len += (sizeof(*pr_supported) + SCTP_EXT_COUNT + SCTP_PAD_EXT_COUNT);
+	if (sctp_ecn_nonce) {
+		/* ECN nonce: And now tell the peer we support ECN nonce */
+		ecn_nonce = (struct sctp_ecn_nonce_supported_param *)((caddr_t)pr_supported +
+		     sizeof(*pr_supported) + SCTP_EXT_COUNT + SCTP_PAD_EXT_COUNT);
+		ecn_nonce->ph.param_type = htons(SCTP_ECN_NONCE_SUPPORTED);
+		ecn_nonce->ph.param_length = htons(sizeof(*ecn_nonce));
+		m->m_len += sizeof(*ecn_nonce);
+	}
+
+	m_at = m;
+	/* now the addresses */
+	if (inp->sctp_flags & SCTP_PCB_FLAGS_BOUNDALL) {
+		struct ifnet *ifn;
+		struct ifaddr *ifa;
+		int cnt = cnt_inits_to;
+
+		TAILQ_FOREACH(ifn, &ifnet, if_list) {
+			if ((stc.loopback_scope == 0) &&
+			    (ifn->if_type == IFT_LOOP)) {
+				/*
+				 * Skip loopback devices if loopback_scope
+				 * not set
+				 */
+				continue;
+			}
+			TAILQ_FOREACH(ifa, &ifn->if_addrlist, ifa_list) {
+				if (sctp_is_address_in_scope(ifa,
+				    stc.ipv4_addr_legal, stc.ipv6_addr_legal,
+				    stc.loopback_scope, stc.ipv4_scope,
+				    stc.local_scope, stc.site_scope) == 0) {
+					continue;
+				}
+				cnt++;
+			}
+		}
+		if (cnt > 1) {
+			TAILQ_FOREACH(ifn, &ifnet, if_list) {
+				if ((stc.loopback_scope == 0) &&
+				    (ifn->if_type == IFT_LOOP)) {
+					/*
+					 * Skip loopback devices if
+					 * loopback_scope not set
+					 */
+					continue;
+				}
+				TAILQ_FOREACH(ifa, &ifn->if_addrlist, ifa_list) {
+					if (sctp_is_address_in_scope(ifa,
+					    stc.ipv4_addr_legal,
+					    stc.ipv6_addr_legal,
+					    stc.loopback_scope, stc.ipv4_scope,
+					    stc.local_scope, stc.site_scope) == 0) {
+						continue;
+					}
+					m_at = sctp_add_addr_to_mbuf(m_at, ifa);
+				}
+			}
+		}
+	} else {
+		struct sctp_laddr *laddr;
+		int cnt;
+		cnt = cnt_inits_to;
+		/* First, how many ? */
+		LIST_FOREACH(laddr, &inp->sctp_addr_list, sctp_nxt_addr) {
+			if (laddr->ifa == NULL) {
+				continue;
+			}
+			if (laddr->ifa->ifa_addr == NULL)
+				continue;
+			if (sctp_is_address_in_scope(laddr->ifa,
+			    stc.ipv4_addr_legal, stc.ipv6_addr_legal,
+			    stc.loopback_scope, stc.ipv4_scope,
+			    stc.local_scope, stc.site_scope) == 0) {
+				continue;
+			}
+			cnt++;
+		}
+		/* If we bind a single address only we won't list
+		 * any. This way you can get through a NAT
+		 */
+		if (cnt > 1) {
+			LIST_FOREACH(laddr, &inp->sctp_addr_list, sctp_nxt_addr) {
+				if (laddr->ifa == NULL) {
+#ifdef SCTP_DEBUG
+					if (sctp_debug_on & SCTP_DEBUG_OUTPUT1) {
+						printf("Help I have fallen and I can't get up!\n");
+					}
+#endif
+					continue;
+				}
+				if (laddr->ifa->ifa_addr == NULL)
+					continue;
+				if (sctp_is_address_in_scope(laddr->ifa,
+				    stc.ipv4_addr_legal, stc.ipv6_addr_legal,
+				    stc.loopback_scope, stc.ipv4_scope,
+				    stc.local_scope, stc.site_scope) == 0) {
+					continue;
+				}
+				m_at = sctp_add_addr_to_mbuf(m_at, laddr->ifa);
+			}
+		}
+	}
+
+	/* tack on the operational error if present */
+	if (op_err) {
+		if (op_err->m_pkthdr.len % 4) {
+			/* must add a pad to the param */
+			u_int32_t cpthis=0;
+			int padlen;
+			padlen = 4 - (op_err->m_pkthdr.len % 4);
+			m_copyback(op_err, op_err->m_pkthdr.len, padlen, (caddr_t)&cpthis);
+		}
+		while (m_at->m_next != NULL) {
+			m_at = m_at->m_next;
+		}
+		m_at->m_next = op_err;
+		while (m_at->m_next != NULL) {
+			m_at = m_at->m_next;
+		}
+	}
+	/* Get total size of init packet */
+	sz_of = SCTP_SIZE32(ntohs(init_chk->ch.chunk_length));
+	/* pre-calulate the size and update pkt header and chunk header */
+	m->m_pkthdr.len = 0;
+	for (m_tmp = m; m_tmp; m_tmp = m_tmp->m_next) {
+		m->m_pkthdr.len += m_tmp->m_len;
+		if (m_tmp->m_next == NULL) {
+			/* m_tmp should now point to last one */
+			break;
+		}
+	}
+	/*
+	 * Figure now the size of the cookie. We know the size of the
+	 * INIT-ACK. The Cookie is going to be the size of INIT, INIT-ACK,
+	 * COOKIE-STRUCTURE and SIGNATURE.
+	 */
+
+	/*
+	 * take our earlier INIT calc and add in the sz we just calculated
+	 * minus the size of the sctphdr (its not included in chunk size
+	 */
+
+	/* add once for the INIT-ACK */
+	sz_of += (m->m_pkthdr.len - sizeof(struct sctphdr));
+
+	/* add a second time for the INIT-ACK in the cookie */
+	sz_of += (m->m_pkthdr.len - sizeof(struct sctphdr));
+
+	/* Now add the cookie header and cookie message struct */
+	sz_of += sizeof(struct sctp_state_cookie_param);
+	/* ...and add the size of our signature */
+	sz_of += SCTP_SIGNATURE_SIZE;
+	initackm_out->msg.ch.chunk_length = htons(sz_of);
+
+	/* Now we must build a cookie */
+	m_cookie = sctp_add_cookie(inp, init_pkt, offset, m,
+	    sizeof(struct sctphdr), &stc);
+	if (m_cookie == NULL) {
+		/* memory problem */
+		sctp_m_freem(m);
+		return;
+	}
+	/* Now append the cookie to the end and update the space/size */
+	m_tmp->m_next = m_cookie;
+
+	/*
+	 * We pass 0 here to NOT set IP_DF if its IPv4, we ignore the
+	 * return here since the timer will drive a retranmission.
+	 */
+	padval = m->m_pkthdr.len % 4;
+	if ((padval) && (m_last)) {
+		/* see my previous comments on m_last */
+		int ret;
+		ret = sctp_add_pad_tombuf(m_last, (4-padval));
+		if (ret) {
+			/* Houston we have a problem, no space */
+			sctp_m_freem(m);
+			return;
+		}
+		m->m_pkthdr.len += padval;
+	}
+	sctp_lowlevel_chunk_output(inp, NULL, NULL, to, m, 0, 0, NULL, 0);
+}
+
+
+static void
+sctp_insert_on_wheel(struct sctp_association *asoc,
+		     struct sctp_stream_out *strq)
+{
+	struct sctp_stream_out *stre, *strn;
+	stre = TAILQ_FIRST(&asoc->out_wheel);
+	if (stre == NULL) {
+		/* only one on wheel */
+		TAILQ_INSERT_HEAD(&asoc->out_wheel, strq, next_spoke);
+		return;
+	}
+	for (; stre; stre = strn) {
+		strn = TAILQ_NEXT(stre, next_spoke);
+		if (stre->stream_no > strq->stream_no) {
+			TAILQ_INSERT_BEFORE(stre, strq, next_spoke);
+			return;
+		} else if (stre->stream_no == strq->stream_no) {
+			/* huh, should not happen */
+			return;
+		} else if (strn == NULL) {
+			/* next one is null */
+			TAILQ_INSERT_AFTER(&asoc->out_wheel, stre, strq,
+					   next_spoke);
+		}
+	}
+}
+
+static void
+sctp_remove_from_wheel(struct sctp_association *asoc,
+		       struct sctp_stream_out *strq)
+{
+	/* take off and then setup so we know it is not on the wheel */
+	TAILQ_REMOVE(&asoc->out_wheel, strq, next_spoke);
+	strq->next_spoke.tqe_next = NULL;
+	strq->next_spoke.tqe_prev = NULL;
+}
+
+
+static void
+sctp_prune_prsctp(struct sctp_tcb *stcb,
+		  struct sctp_association *asoc,
+		  struct sctp_sndrcvinfo *srcv,
+		  int dataout
+	)
+{
+	int freed_spc=0;
+	struct sctp_tmit_chunk *chk, *nchk;
+	if ((asoc->peer_supports_prsctp) && (asoc->sent_queue_cnt_removeable > 0)) {
+		TAILQ_FOREACH(chk, &asoc->sent_queue, sctp_next) {
+			/*
+			 * Look for chunks marked with the PR_SCTP
+			 * flag AND the buffer space flag. If the one
+			 * being sent is equal or greater priority then
+			 * purge the old one and free some space.
+			 */
+			if ((chk->flags & (SCTP_PR_SCTP_ENABLED |
+					   SCTP_PR_SCTP_BUFFER)) ==
+			    (SCTP_PR_SCTP_ENABLED|SCTP_PR_SCTP_BUFFER)) {
+				/*
+				 * This one is PR-SCTP AND buffer space
+				 * limited type
+				 */
+				if (chk->rec.data.timetodrop.tv_sec >= (long)srcv->sinfo_timetolive) {
+					/* Lower numbers equates to
+					 * higher priority so if the
+					 * one we are looking at has a
+					 * larger or equal priority we
+					 * want to drop the data and
+					 * NOT retransmit it.
+					 */
+					if (chk->data) {
+						/* We release the
+						 * book_size if the
+						 * mbuf is here
+						 */
+						int ret_spc;
+						int cause;
+						if (chk->sent > SCTP_DATAGRAM_UNSENT)
+							cause = SCTP_RESPONSE_TO_USER_REQ|SCTP_NOTIFY_DATAGRAM_SENT;
+						else
+							cause = SCTP_RESPONSE_TO_USER_REQ|SCTP_NOTIFY_DATAGRAM_UNSENT;
+						ret_spc  = sctp_release_pr_sctp_chunk(stcb, chk,
+										      cause,
+										      &asoc->sent_queue);
+						freed_spc += ret_spc;
+						if (freed_spc >= dataout) {
+							return;
+						}
+					} /* if chunk was present */
+				} /* if of sufficent priority */
+			} /* if chunk has enabled */
+		} /* tailqforeach */
+
+		chk = TAILQ_FIRST(&asoc->send_queue);
+		while (chk) {
+			nchk = TAILQ_NEXT(chk, sctp_next);
+			/* Here we must move to the sent queue and mark */
+			if ((chk->flags & (SCTP_PR_SCTP_ENABLED |
+					   SCTP_PR_SCTP_BUFFER)) ==
+			    (SCTP_PR_SCTP_ENABLED|SCTP_PR_SCTP_BUFFER)) {
+				if (chk->rec.data.timetodrop.tv_sec >= (long)srcv->sinfo_timetolive) {
+					if (chk->data) {
+						/* We release the
+						 * book_size if the
+						 * mbuf is here
+						 */
+						int ret_spc;
+						ret_spc  = sctp_release_pr_sctp_chunk(stcb, chk,
+						    SCTP_RESPONSE_TO_USER_REQ|SCTP_NOTIFY_DATAGRAM_UNSENT,
+						    &asoc->send_queue);
+
+						freed_spc += ret_spc;
+						if (freed_spc >= dataout) {
+							return;
+						}
+					} /* end if chk->data */
+				} /* end if right class */
+			} /* end if chk pr-sctp */
+			chk = nchk;
+		} /* end while (chk) */
+	} /* if enabled in asoc */
+}
+
+static void
+sctp_prepare_chunk(struct sctp_tmit_chunk *template,
+		   struct sctp_tcb *stcb,
+		   struct sctp_sndrcvinfo *srcv,
+		   struct sctp_stream_out *strq,
+		   struct sctp_nets *net)
+{
+	bzero(template, sizeof(struct sctp_tmit_chunk));
+	template->sent = SCTP_DATAGRAM_UNSENT;
+	if ((stcb->asoc.peer_supports_prsctp) &&
+	    (srcv->sinfo_flags & (MSG_PR_SCTP_TTL|MSG_PR_SCTP_BUF)) &&
+	    (srcv->sinfo_timetolive > 0)
+		) {
+		/* If:
+		 *  Peer supports PR-SCTP
+		 *  The flags is set against this send for PR-SCTP
+		 *  And timetolive is a postive value, zero is reserved
+		 *     to mean a reliable send for both buffer/time
+		 *     related one.
+		 */
+		if (srcv->sinfo_flags & MSG_PR_SCTP_BUF) {
+			/*
+			 * Time to live is a priority stored in tv_sec
+			 * when doing the buffer drop thing.
+			 */
+			template->rec.data.timetodrop.tv_sec = srcv->sinfo_timetolive;
+		} else {
+			struct timeval tv;
+
+			SCTP_GETTIME_TIMEVAL(&template->rec.data.timetodrop);
+			tv.tv_sec = srcv->sinfo_timetolive / 1000;
+			tv.tv_usec = (srcv->sinfo_timetolive * 1000) % 1000000;
+#ifndef __FreeBSD__
+			timeradd(&template->rec.data.timetodrop, &tv,
+			    &template->rec.data.timetodrop);
+#else
+			timevaladd(&template->rec.data.timetodrop, &tv);
+#endif
+		}
+	}
+	if ((srcv->sinfo_flags & MSG_UNORDERED) == 0) {
+		template->rec.data.stream_seq = strq->next_sequence_sent;
+	} else {
+		template->rec.data.stream_seq = 0;
+	}
+	template->rec.data.TSN_seq = 0;	/* not yet assigned */
+
+	template->rec.data.stream_number = srcv->sinfo_stream;
+	template->rec.data.payloadtype = srcv->sinfo_ppid;
+	template->rec.data.context = srcv->sinfo_context;
+	template->rec.data.doing_fast_retransmit = 0;
+	template->rec.data.ect_nonce = 0;   /* ECN Nonce */
+
+	if (srcv->sinfo_flags & MSG_ADDR_OVER) {
+		template->whoTo = net;
+	} else {
+		if (stcb->asoc.primary_destination)
+			template->whoTo = stcb->asoc.primary_destination;
+		else {
+			/* TSNH */
+			template->whoTo = net;
+		}
+	}
+	/* the actual chunk flags */
+	if (srcv->sinfo_flags & MSG_UNORDERED) {
+		template->rec.data.rcv_flags = SCTP_DATA_UNORDERED;
+	} else {
+		template->rec.data.rcv_flags = 0;
+	}
+	/* no flags yet, FRAGMENT_OK goes here */
+	template->flags = 0;
+	/* PR sctp flags */
+	if (stcb->asoc.peer_supports_prsctp) {
+		if (srcv->sinfo_timetolive > 0) {
+			/*
+			 * We only set the flag if timetolive (or
+			 * priority) was set to a positive number.
+			 * Zero is reserved specifically to be
+			 * EXCLUDED and sent reliable.
+			 */
+			if (srcv->sinfo_flags & MSG_PR_SCTP_TTL) {
+				template->flags |= SCTP_PR_SCTP_ENABLED;
+			}
+			if (srcv->sinfo_flags & MSG_PR_SCTP_BUF) {
+				template->flags |= SCTP_PR_SCTP_BUFFER;
+			}
+		}
+	}
+	template->asoc = &stcb->asoc;
+}
+
+
+int
+sctp_get_frag_point(struct sctp_tcb *stcb,
+		    struct sctp_association *asoc)
+{
+	int siz, ovh;
+
+	/* For endpoints that have both 6 and 4 addresses
+	 * we must reserver room for the 6 ip header, for
+	 * those that are only dealing with V4 we use
+	 * a larger frag point.
+	 */
+ 	if (stcb->sctp_ep->sctp_flags & SCTP_PCB_FLAGS_BOUND_V6) {
+		ovh = SCTP_MED_OVERHEAD;
+	} else {
+		ovh = SCTP_MED_V4_OVERHEAD;
+	}
+
+	if (stcb->sctp_ep->sctp_frag_point > asoc->smallest_mtu)
+		siz = asoc->smallest_mtu - ovh;
+	else
+		siz = (stcb->sctp_ep->sctp_frag_point - ovh);
+/*
+  if (siz > (MCLBYTES-sizeof(struct sctp_data_chunk))) { */
+		/* A data chunk MUST fit in a cluster */
+/*		siz = (MCLBYTES - sizeof(struct sctp_data_chunk));*/
+/*	}*/
+
+	if (siz % 4) {
+		/* make it an even word boundary please */
+		siz -= (siz % 4);
+	}
+	return (siz);
+}
+extern unsigned int sctp_max_chunks_on_queue;
+
+#define   SBLOCKWAIT(f)   (((f)&MSG_DONTWAIT) ? M_NOWAIT : M_WAITOK)
+
+static int
+sctp_msg_append(struct sctp_tcb *stcb,
+		struct sctp_nets *net,
+		struct mbuf *m,
+		struct sctp_sndrcvinfo *srcv,
+		int flags)
+{
+	struct socket *so;
+	struct sctp_association *asoc;
+	struct sctp_stream_out *strq;
+	struct sctp_tmit_chunk *chk;
+	struct sctpchunk_listhead tmp;
+	struct sctp_tmit_chunk template;
+	struct mbuf *n, *mnext;
+	struct mbuf *mm;
+	unsigned int dataout, siz;
+	int mbcnt = 0;
+	int mbcnt_e = 0;
+	int error = 0;
+
+	if ((stcb == NULL) || (net == NULL) || (m == NULL) || (srcv == NULL)) {
+		/* Software fault, you blew it on the call */
+#ifdef SCTP_DEBUG
+		if (sctp_debug_on & SCTP_DEBUG_OUTPUT1) {
+			printf("software error in sctp_msg_append:1\n");
+			printf("stcb:%p net:%p m:%p srcv:%p\n",
+			       stcb, net, m, srcv);
+		}
+#endif
+		if (m)
+			sctp_m_freem(m);
+		return (EFAULT);
+	}
+	so = stcb->sctp_socket;
+	asoc = &stcb->asoc;
+	if (srcv->sinfo_flags & MSG_ABORT) {
+		if ((SCTP_GET_STATE(asoc) != SCTP_STATE_COOKIE_WAIT) &&
+		    (SCTP_GET_STATE(asoc) != SCTP_STATE_COOKIE_ECHOED)) {
+			/* It has to be up before we abort */
+			/* how big is the user initiated abort? */
+			if ((m->m_flags & M_PKTHDR) && (m->m_pkthdr.len)) {
+				dataout = m->m_pkthdr.len;
+			} else {
+				/* we must count */
+				dataout = 0;
+				for (n = m; n; n = n->m_next) {
+					dataout += n->m_len;
+				}
+			}
+			M_PREPEND(m, sizeof(struct sctp_paramhdr), MB_DONTWAIT);
+			if (m) {
+				struct sctp_paramhdr *ph;
+				m->m_len = sizeof(struct sctp_paramhdr) + dataout;
+				ph = mtod(m, struct sctp_paramhdr *);
+				ph->param_type = htons(SCTP_CAUSE_USER_INITIATED_ABT);
+				ph->param_length = htons(m->m_len);
+			}
+			sctp_abort_an_association(stcb->sctp_ep, stcb, SCTP_RESPONSE_TO_USER_REQ, m);
+			m = NULL;
+		} else {
+			/* Only free if we don't send an abort */
+			;
+		}
+		goto out;
+	}
+	if ((SCTP_GET_STATE(asoc) == SCTP_STATE_SHUTDOWN_SENT) ||
+	    (SCTP_GET_STATE(asoc) == SCTP_STATE_SHUTDOWN_ACK_SENT) ||
+	    (SCTP_GET_STATE(asoc) == SCTP_STATE_SHUTDOWN_RECEIVED) ||
+	    (asoc->state & SCTP_STATE_SHUTDOWN_PENDING)) {
+		/* got data while shutting down */
+		error = ECONNRESET;
+		goto out;
+	}
+
+	if (srcv->sinfo_stream >= asoc->streamoutcnt) {
+		/* Invalid stream number */
+		error = EINVAL;
+		goto out;
+	}
+	if (asoc->strmout == NULL) {
+		/* huh? software error */
+#ifdef SCTP_DEBUG
+		if (sctp_debug_on & SCTP_DEBUG_OUTPUT1) {
+			printf("software error in sctp_msg_append:2\n");
+		}
+#endif
+		error = EFAULT;
+		goto out;
+	}
+	strq = &asoc->strmout[srcv->sinfo_stream];
+	/* how big is it ? */
+	if ((m->m_flags & M_PKTHDR) && (m->m_pkthdr.len)) {
+		dataout = m->m_pkthdr.len;
+	} else {
+		/* we must count */
+		dataout = 0;
+		for (n = m; n; n = n->m_next) {
+			dataout += n->m_len;
+		}
+	}
+#ifdef SCTP_DEBUG
+	if (sctp_debug_on & SCTP_DEBUG_OUTPUT1) {
+		printf("Attempt to send out %d bytes\n",
+		       dataout);
+	}
+#endif
+
+	/* lock the socket buf */
+	SOCKBUF_LOCK(&so->so_snd);
+	error = sblock(&so->so_snd, SBLOCKWAIT(flags));
+	if (error)
+		goto out_locked;
+
+	if (dataout > so->so_snd.sb_hiwat) {
+		/* It will NEVER fit */
+		error = EMSGSIZE;
+		goto release;
+	}
+	if ((srcv->sinfo_flags & MSG_EOF) &&
+	    (stcb->sctp_ep->sctp_flags & SCTP_PCB_FLAGS_UDPTYPE) &&
+	    (dataout == 0)
+		) {
+		goto zap_by_it_all;
+	}
+	if ((so->so_snd.sb_hiwat <
+	     (dataout + asoc->total_output_queue_size)) ||
+	    (asoc->chunks_on_out_queue > sctp_max_chunks_on_queue) ||
+	    (asoc->total_output_mbuf_queue_size >
+	     so->so_snd.sb_mbmax)
+		) {
+		/* XXX Buffer space hunt for data to skip */
+		if (asoc->peer_supports_prsctp) {
+			sctp_prune_prsctp(stcb, asoc, srcv, dataout);
+		}
+		while ((so->so_snd.sb_hiwat <
+		    (dataout + asoc->total_output_queue_size)) ||
+		    (asoc->chunks_on_out_queue > sctp_max_chunks_on_queue) ||
+		    (asoc->total_output_mbuf_queue_size >
+		    so->so_snd.sb_mbmax)) {
+			struct sctp_inpcb *inp;
+			/* Now did we free up enough room? */
+			if ((so->so_state & SS_NBIO)
+#if defined(__FreeBSD__) && __FreeBSD_version >= 500000
+			    || (flags & MSG_NBIO)
+#endif
+				) {
+				/* Non-blocking io in place */
+				error = EWOULDBLOCK;
+				goto release;
+			}
+			/*
+			 * We store off a pointer to the endpoint.
+			 * Since on return from this we must check to
+			 * see if an so_error is set. If so we may have
+			 * been reset and our stcb destroyed. Returning
+			 * an error will cause the correct error return
+			 * through and fix this all.
+			 */
+			inp = stcb->sctp_ep;
+			/*
+			 * Not sure how else to do this since
+			 * the level we suspended at is not
+			 * known deep down where we are. I will
+			 * drop to spl0() so that others can
+			 * get in.
+			 */
+
+			inp->sctp_tcb_at_block = (void *)stcb;
+			inp->error_on_block = 0;
+			sbunlock(&so->so_snd);
+			error = sbwait(&so->so_snd);
+			/*
+			 * XXX: This is ugly but I have
+			 * recreated most of what goes on to
+			 * block in the sb. UGHH
+			 * May want to add the bit about being
+			 * no longer connected.. but this then
+			 * further dooms the UDP model NOT to
+			 * allow this.
+			 */
+			inp->sctp_tcb_at_block = 0;
+			if (inp->error_on_block)
+				error = inp->error_on_block;
+			if (so->so_error)
+				error = so->so_error;
+			if (error) {
+				goto out_locked;
+			}
+			error = sblock(&so->so_snd, M_WAITOK);
+			if (error)
+				goto out_locked;
+			/* Otherwise we cycle back and recheck
+			 * the space
+			 */
+#if defined(__FreeBSD__) && __FreeBSD_version >= 502115
+			if (so->so_rcv.sb_state & SBS_CANTSENDMORE) {
+#else
+			if (so->so_state & SS_CANTSENDMORE) {
+#endif
+				error = EPIPE;
+				goto release;
+			}
+			if (so->so_error) {
+				error = so->so_error;
+				goto release;
+			}
+		}
+	}
+	/* If we have a packet header fix it if it was broke */
+	if (m->m_flags & M_PKTHDR) {
+		m->m_pkthdr.len = dataout;
+	}
+	/* use the smallest one, user set value or
+	 * smallest mtu of the asoc
+	 */
+	siz = sctp_get_frag_point(stcb, asoc);
+	SOCKBUF_UNLOCK(&so->so_snd);
+	if ((dataout) && (dataout <= siz)) {
+		/* Fast path */
+		chk = (struct sctp_tmit_chunk *)SCTP_ZONE_GET(sctppcbinfo.ipi_zone_chunk);
+		if (chk == NULL) {
+			error = ENOMEM;
+			SOCKBUF_LOCK(&so->so_snd);
+			goto release;
+		}
+		sctp_prepare_chunk(chk, stcb, srcv, strq, net);
+		chk->whoTo->ref_count++;
+		chk->rec.data.rcv_flags |= SCTP_DATA_NOT_FRAG;
+
+		/* no flags yet, FRAGMENT_OK goes here */
+		sctppcbinfo.ipi_count_chunk++;
+		sctppcbinfo.ipi_gencnt_chunk++;
+		asoc->chunks_on_out_queue++;
+		chk->data = m;
+		m = NULL;
+		/* Total in the MSIZE */
+		for (mm = chk->data; mm; mm = mm->m_next) {
+			mbcnt += MSIZE;
+			if (mm->m_flags & M_EXT) {
+				mbcnt += chk->data->m_ext.ext_size;
+			}
+		}
+		/* fix up the send_size if it is not present */
+		chk->send_size = dataout;
+		chk->book_size = chk->send_size;
+		chk->mbcnt = mbcnt;
+		/* ok, we are commited */
+		if ((srcv->sinfo_flags & MSG_UNORDERED) == 0) {
+			/* bump the ssn if we are unordered. */
+			strq->next_sequence_sent++;
+		}
+		chk->data->m_nextpkt = 0;
+		asoc->stream_queue_cnt++;
+		TAILQ_INSERT_TAIL(&strq->outqueue, chk, sctp_next);
+		/* now check if this stream is on the wheel */
+		if ((strq->next_spoke.tqe_next == NULL) &&
+		    (strq->next_spoke.tqe_prev == NULL)) {
+			/* Insert it on the wheel since it is not
+			 * on it currently
+			 */
+			sctp_insert_on_wheel(asoc, strq);
+		}
+	} else if ((dataout) && (dataout > siz)) {
+		/* Slow path */
+		if ((stcb->sctp_ep->sctp_flags & SCTP_PCB_FLAGS_NO_FRAGMENT) &&
+		    (dataout > siz)) {
+			error = EMSGSIZE;
+			SOCKBUF_LOCK(&so->so_snd);
+			goto release;
+		}
+		/* setup the template */
+		sctp_prepare_chunk(&template, stcb, srcv, strq, net);
+
+		n = m;
+		while (dataout > siz) {
+			/*
+			 * We can wait since this is called from the user
+			 * send side
+			 */
+			n->m_nextpkt = m_split(n, siz, MB_WAIT);
+			if (n->m_nextpkt == NULL) {
+				error = EFAULT;
+				SOCKBUF_LOCK(&so->so_snd);
+				goto release;
+			}
+			dataout -= siz;
+			n = n->m_nextpkt;
+		}
+		/*
+		 * ok, now we have a chain on m where m->m_nextpkt points to
+		 * the next chunk and m/m->m_next chain is the piece to send.
+		 * We must go through the chains and thread them on to
+		 * sctp_tmit_chunk chains and place them all on the stream
+		 * queue, breaking the m->m_nextpkt pointers as we go.
+		 */
+		n = m;
+		TAILQ_INIT(&tmp);
+		while (n) {
+			/*
+			 * first go through and allocate a sctp_tmit chunk
+			 * for each chunk piece
+			 */
+			chk = (struct sctp_tmit_chunk *)SCTP_ZONE_GET(sctppcbinfo.ipi_zone_chunk);
+			if (chk == NULL) {
+				/*
+				 * ok we must spin through and dump anything
+				 * we have allocated and then jump to the
+				 * no_membad
+				 */
+				chk = TAILQ_FIRST(&tmp);
+				while (chk) {
+					TAILQ_REMOVE(&tmp, chk, sctp_next);
+					SCTP_ZONE_FREE(sctppcbinfo.ipi_zone_chunk, chk);
+					sctppcbinfo.ipi_count_chunk--;
+					asoc->chunks_on_out_queue--;
+					if ((int)sctppcbinfo.ipi_count_chunk < 0) {
+						panic("Chunk count is negative");
+					}
+					sctppcbinfo.ipi_gencnt_chunk++;
+					chk = TAILQ_FIRST(&tmp);
+				}
+				error = ENOMEM;
+				SOCKBUF_LOCK(&so->so_snd);
+				goto release;
+			}
+			sctppcbinfo.ipi_count_chunk++;
+			asoc->chunks_on_out_queue++;
+
+			sctppcbinfo.ipi_gencnt_chunk++;
+			*chk = template;
+			chk->whoTo->ref_count++;
+			chk->data = n;
+			/* Total in the MSIZE */
+			mbcnt_e = 0;
+			for (mm = chk->data; mm; mm = mm->m_next) {
+				mbcnt_e += MSIZE;
+				if (mm->m_flags & M_EXT) {
+					mbcnt_e += chk->data->m_ext.ext_size;
+				}
+			}
+			/* now fix the chk->send_size */
+			if (chk->data->m_flags & M_PKTHDR) {
+				chk->send_size = chk->data->m_pkthdr.len;
+			} else {
+				struct mbuf *nn;
+				chk->send_size = 0;
+				for (nn = chk->data; nn; nn = nn->m_next) {
+					chk->send_size += nn->m_len;
+				}
+			}
+			chk->book_size = chk->send_size;
+			chk->mbcnt = mbcnt_e;
+			mbcnt += mbcnt_e;
+			if (chk->flags & SCTP_PR_SCTP_BUFFER) {
+				asoc->sent_queue_cnt_removeable++;
+			}
+			n = n->m_nextpkt;
+			TAILQ_INSERT_TAIL(&tmp, chk, sctp_next);
+		}
+		m = NULL;
+		/* now that we have enough space for all de-couple the
+		 * chain of mbufs by going through our temp array
+		 * and breaking the pointers.
+		 */
+		/* ok, we are commited */
+		if ((srcv->sinfo_flags & MSG_UNORDERED) == 0) {
+			/* bump the ssn if we are unordered. */
+			strq->next_sequence_sent++;
+		}
+		/* Mark the first/last flags. This will
+		 * result int a 3 for a single item on the list
+		 */
+		chk = TAILQ_FIRST(&tmp);
+		chk->rec.data.rcv_flags |= SCTP_DATA_FIRST_FRAG;
+		chk = TAILQ_LAST(&tmp, sctpchunk_listhead);
+		chk->rec.data.rcv_flags |= SCTP_DATA_LAST_FRAG;
+		/* now break any chains on the queue and
+		 * move it to the streams actual queue.
+		 */
+		chk = TAILQ_FIRST(&tmp);
+		while (chk) {
+			chk->data->m_nextpkt = 0;
+			TAILQ_REMOVE(&tmp, chk, sctp_next);
+			asoc->stream_queue_cnt++;
+			TAILQ_INSERT_TAIL(&strq->outqueue, chk, sctp_next);
+			chk = TAILQ_FIRST(&tmp);
+		}
+		/* now check if this stream is on the wheel */
+		if ((strq->next_spoke.tqe_next == NULL) &&
+		    (strq->next_spoke.tqe_prev == NULL)) {
+			/* Insert it on the wheel since it is not
+			 * on it currently
+			 */
+			sctp_insert_on_wheel(asoc, strq);
+		}
+	}
+	SOCKBUF_LOCK(&so->so_snd);
+	/* has a SHUTDOWN been (also) requested by the user on this asoc? */
+zap_by_it_all:
+
+	if ((srcv->sinfo_flags & MSG_EOF) &&
+	    (stcb->sctp_ep->sctp_flags & SCTP_PCB_FLAGS_UDPTYPE)) {
+
+		int some_on_streamwheel = 0;
+
+		if (!TAILQ_EMPTY(&asoc->out_wheel)) {
+			/* Check to see if some data queued */
+			struct sctp_stream_out *outs;
+			TAILQ_FOREACH(outs, &asoc->out_wheel, next_spoke) {
+				if (!TAILQ_EMPTY(&outs->outqueue)) {
+					some_on_streamwheel = 1;
+					break;
+				}
+			}
+		}
+
+		if (TAILQ_EMPTY(&asoc->send_queue) &&
+		    TAILQ_EMPTY(&asoc->sent_queue) &&
+		    (some_on_streamwheel == 0)) {
+			/* there is nothing queued to send, so I'm done... */
+			if ((SCTP_GET_STATE(asoc) != SCTP_STATE_SHUTDOWN_SENT) &&
+			    (SCTP_GET_STATE(asoc) != SCTP_STATE_SHUTDOWN_ACK_SENT)) {
+				/* only send SHUTDOWN the first time through */
+#ifdef SCTP_DEBUG
+				if (sctp_debug_on & SCTP_DEBUG_OUTPUT4) {
+					printf("%s:%d sends a shutdown\n",
+					       __FILE__,
+					       __LINE__
+						);
+				}
+#endif
+				sctp_send_shutdown(stcb, stcb->asoc.primary_destination);
+				asoc->state = SCTP_STATE_SHUTDOWN_SENT;
+				sctp_timer_start(SCTP_TIMER_TYPE_SHUTDOWN, stcb->sctp_ep, stcb,
+						 asoc->primary_destination);
+				sctp_timer_start(SCTP_TIMER_TYPE_SHUTDOWNGUARD, stcb->sctp_ep, stcb,
+						 asoc->primary_destination);
+			}
+		} else {
+			/*
+			 * we still got (or just got) data to send, so set
+			 * SHUTDOWN_PENDING
+			 */
+			/*
+			 * XXX sockets draft says that MSG_EOF should be sent
+			 * with no data.  currently, we will allow user data
+			 * to be sent first and move to SHUTDOWN-PENDING
+			 */
+			asoc->state |= SCTP_STATE_SHUTDOWN_PENDING;
+		}
+	}
+#ifdef SCTP_MBCNT_LOGGING
+	sctp_log_mbcnt(SCTP_LOG_MBCNT_INCREASE,
+		       asoc->total_output_queue_size,
+		       dataout,
+		       asoc->total_output_mbuf_queue_size,
+		       mbcnt);
+#endif
+	asoc->total_output_queue_size += dataout;
+	asoc->total_output_mbuf_queue_size += mbcnt;
+	if ((stcb->sctp_ep->sctp_flags & SCTP_PCB_FLAGS_TCPTYPE) ||
+	    (stcb->sctp_ep->sctp_flags & SCTP_PCB_FLAGS_IN_TCPPOOL)) {
+		so->so_snd.sb_cc += dataout;
+		so->so_snd.sb_mbcnt += mbcnt;
+	}
+
+#ifdef SCTP_DEBUG
+	if (sctp_debug_on & SCTP_DEBUG_OUTPUT2) {
+		printf("++total out:%d total_mbuf_out:%d\n",
+		       (int)asoc->total_output_queue_size,
+		       (int)asoc->total_output_mbuf_queue_size);
+	}
+#endif
+
+release:
+	sbunlock(&so->so_snd);
+out_locked:
+	SOCKBUF_UNLOCK(&so->so_snd);
+out:
+	if (m && m->m_nextpkt) {
+		n = m;
+		while (n) {
+			mnext = n->m_nextpkt;
+			n->m_nextpkt = NULL;
+			sctp_m_freem(n);
+			n = mnext;
+		}
+	} else if (m)
+		sctp_m_freem(m);
+
+	return (error);
+}
+
+static struct mbuf *
+sctp_copy_mbufchain(struct mbuf *clonechain,
+		    struct mbuf *outchain)
+{
+	struct mbuf *appendchain;
+#if defined(__FreeBSD__) || defined(__NetBSD__)
+	/* Supposedly m_copypacket is an optimization, use it if we can */
+	if (clonechain->m_flags & M_PKTHDR) {
+		appendchain = m_copypacket(clonechain, MB_DONTWAIT);
+		sctp_pegs[SCTP_CACHED_SRC]++;
+	} else
+		appendchain = m_copy(clonechain, 0, M_COPYALL);
+#elif defined(__APPLE__)
+	appendchain = sctp_m_copym(clonechain, 0, M_COPYALL, MB_DONTWAIT);
+#else
+	appendchain = m_copy(clonechain, 0, M_COPYALL);
+#endif
+
+	if (appendchain == NULL) {
+		/* error */
+		if (outchain)
+			sctp_m_freem(outchain);
+		return (NULL);
+	}
+	if (outchain) {
+		/* tack on to the end */
+		struct mbuf *m;
+		m = outchain;
+		while (m) {
+			if (m->m_next == NULL) {
+				m->m_next = appendchain;
+				break;
+			}
+			m = m->m_next;
+		}
+		if (outchain->m_flags & M_PKTHDR) {
+			int append_tot;
+			struct mbuf *t;
+			t = appendchain;
+			append_tot = 0;
+			while (t) {
+				append_tot += t->m_len;
+				t = t->m_next;
+			}
+			outchain->m_pkthdr.len += append_tot;
+		}
+		return (outchain);
+	} else {
+		return (appendchain);
+	}
+}
+
+static void
+sctp_sendall_iterator(struct sctp_inpcb *inp, struct sctp_tcb *stcb, void *ptr, u_int32_t val)
+{
+	struct sctp_copy_all *ca;
+	struct mbuf *m;
+	int turned_on_nonblock=0, ret;
+
+	ca = (struct sctp_copy_all *)ptr;
+	if (ca->m == NULL) {
+		return;
+	}
+	if (ca->inp != inp) {
+		/* TSNH */
+		return;
+	}
+	m = sctp_copy_mbufchain(ca->m, NULL);
+	if (m == NULL) {
+		/* can't copy so we are done */
+		ca->cnt_failed++;
+		return;
+	}
+	if ((stcb->sctp_socket->so_state & SS_NBIO) == 0) {
+		/* we have to do this non-blocking */
+		turned_on_nonblock = 1;
+		stcb->sctp_socket->so_state |= SS_NBIO;
+	}
+	ret = sctp_msg_append(stcb, stcb->asoc.primary_destination, m, &ca->sndrcv, 0);
+	if (turned_on_nonblock) {
+		/* we turned on non-blocking so turn it off */
+		stcb->sctp_socket->so_state &= ~SS_NBIO;
+	}
+	if (ret) {
+		ca->cnt_failed++;
+	} else {
+		ca->cnt_sent++;
+	}
+}
+
+static void
+sctp_sendall_completes(void *ptr, u_int32_t val)
+{
+	struct sctp_copy_all *ca;
+	ca = (struct sctp_copy_all *)ptr;
+	/* Do a notify here?
+	 * Kacheong suggests that the notify
+	 * be done at the send time.. so you would
+	 * push up a notification if any send failed.
+	 * Don't know if this is feasable since the
+	 * only failures we have is "memory" related and
+	 * if you cannot get an mbuf to send the data
+	 * you surely can't get an mbuf to send up
+	 * to notify the user you can't send the data :->
+	 */
+
+	/* now free everything */
+	m_freem(ca->m);
+	FREE(ca, M_PCB);
+}
+
+
+#define	MC_ALIGN(m, len) do {						\
+	(m)->m_data += (MCLBYTES - (len)) & ~(sizeof(long) - 1);		\
+} while (0)
+
+
+
+static struct mbuf *
+sctp_copy_out_all(struct uio *uio, int len)
+{
+	struct mbuf *ret, *at;
+	int left, willcpy, cancpy, error;
+
+	MGETHDR(ret, MB_WAIT, MT_HEADER);
+	if (ret == NULL) {
+		/* TSNH */
+		return (NULL);
+	}
+	left = len;
+	ret->m_len = 0;
+	ret->m_pkthdr.len = len;
+	MCLGET(ret, MB_WAIT);
+	if (ret == NULL) {
+		return (NULL);
+	}
+	if ((ret->m_flags & M_EXT) == 0) {
+		m_freem (ret);
+		return (NULL);
+	}
+	cancpy = M_TRAILINGSPACE(ret);
+	willcpy = min(cancpy, left);
+	at = ret;
+	while (left > 0) {
+		/* Align data to the end */
+		MC_ALIGN(at, willcpy);
+		error = uiomove(mtod(at, caddr_t), willcpy, uio);
+		if (error) {
+		err_out_now:
+			m_freem(ret);
+			return (NULL);
+		}
+		at->m_len = willcpy;
+		at->m_nextpkt = at->m_next = 0;
+		left -= willcpy;
+		if (left > 0) {
+			MGET(at->m_next, MB_WAIT, MT_DATA);
+			if (at->m_next == NULL) {
+				goto err_out_now;
+			}
+			at = at->m_next;
+			at->m_len = 0;
+			MCLGET(at, MB_WAIT);
+			if (at == NULL) {
+				goto err_out_now;
+			}
+			if ((at->m_flags & M_EXT) == 0) {
+				goto err_out_now;
+			}
+			cancpy = M_TRAILINGSPACE(at);
+			willcpy = min(cancpy, left);
+		}
+	}
+	return (ret);
+}
+
+static int
+sctp_sendall (struct sctp_inpcb *inp, struct uio *uio, struct mbuf *m, struct sctp_sndrcvinfo *srcv)
+{
+	int ret;
+	struct sctp_copy_all *ca;
+	MALLOC(ca, struct sctp_copy_all *,
+	       sizeof(struct sctp_copy_all), M_PCB, MB_WAIT);
+	if (ca == NULL) {
+		m_freem(m);
+		return (ENOMEM);
+	}
+	memset (ca, 0, sizeof(struct sctp_copy_all));
+
+	ca->inp = inp;
+	ca->sndrcv = *srcv;
+	/* take off the sendall flag, it would
+	 * be bad if we failed to do this  :-0
+	 */
+ 	ca->sndrcv.sinfo_flags &= ~MSG_SENDALL;
+
+	/* get length and mbuf chain */
+	if (uio) {
+		ca->sndlen = uio->uio_resid;
+		ca->m = sctp_copy_out_all(uio, ca->sndlen);
+		if (ca->m == NULL) {
+			FREE(ca, M_PCB);
+			return (ENOMEM);
+		}
+	} else {
+		if ((m->m_flags & M_PKTHDR) == 0) {
+			struct mbuf *mat;
+			mat = m;
+			ca->sndlen = 0;
+			while(m) {
+				ca->sndlen += m->m_len;
+				m = m->m_next;
+			}
+		} else {
+			ca->sndlen = m->m_pkthdr.len;
+		}
+		ca->m = m;
+	}
+
+	ret = sctp_initiate_iterator(sctp_sendall_iterator, SCTP_PCB_ANY_FLAGS, SCTP_ASOC_ANY_STATE,
+				     (void *)ca, 0, sctp_sendall_completes, inp);
+	if (ret) {
+#ifdef SCTP_DEBUG
+		printf("Failed to initate iterator to takeover associations\n");
+#endif
+		FREE(ca, M_PCB);
+		return (EFAULT);
+
+	}
+	return (0);
+}
+
+
+void
+sctp_toss_old_cookies(struct sctp_association *asoc)
+{
+	struct sctp_tmit_chunk *chk, *nchk;
+	chk = TAILQ_FIRST(&asoc->control_send_queue);
+	while (chk) {
+		nchk = TAILQ_NEXT(chk, sctp_next);
+		if (chk->rec.chunk_id == SCTP_COOKIE_ECHO) {
+			TAILQ_REMOVE(&asoc->control_send_queue, chk, sctp_next);
+			if (chk->data) {
+				sctp_m_freem(chk->data);
+				chk->data = NULL;
+			}
+			asoc->ctrl_queue_cnt--;
+			if (chk->whoTo)
+				sctp_free_remote_addr(chk->whoTo);
+			SCTP_ZONE_FREE(sctppcbinfo.ipi_zone_chunk, chk);
+			sctppcbinfo.ipi_count_chunk--;
+			if ((int)sctppcbinfo.ipi_count_chunk < 0) {
+				panic("Chunk count is negative");
+			}
+			sctppcbinfo.ipi_gencnt_chunk++;
+		}
+		chk = nchk;
+	}
+}
+
+void
+sctp_toss_old_asconf(struct sctp_tcb *stcb)
+{
+	struct sctp_association *asoc;
+	struct sctp_tmit_chunk *chk, *chk_tmp;
+
+	asoc = &stcb->asoc;
+	for (chk = TAILQ_FIRST(&asoc->control_send_queue); chk != NULL;
+	     chk = chk_tmp) {
+		/* get next chk */
+		chk_tmp = TAILQ_NEXT(chk, sctp_next);
+		/* find SCTP_ASCONF chunk in queue (only one ever in queue) */
+		if (chk->rec.chunk_id == SCTP_ASCONF) {
+			TAILQ_REMOVE(&asoc->control_send_queue, chk, sctp_next);
+			if (chk->data) {
+				sctp_m_freem(chk->data);
+				chk->data = NULL;
+			}
+			asoc->ctrl_queue_cnt--;
+			if (chk->whoTo)
+				sctp_free_remote_addr(chk->whoTo);
+			SCTP_ZONE_FREE(sctppcbinfo.ipi_zone_chunk, chk);
+			sctppcbinfo.ipi_count_chunk--;
+			if ((int)sctppcbinfo.ipi_count_chunk < 0) {
+				panic("Chunk count is negative");
+			}
+			sctppcbinfo.ipi_gencnt_chunk++;
+		}
+	}
+}
+
+
+static void
+sctp_clean_up_datalist(struct sctp_tcb *stcb,
+		       struct sctp_association *asoc,
+		       struct sctp_tmit_chunk **data_list,
+		       int bundle_at,
+		       struct sctp_nets *net)
+{
+	int i;
+	for (i = 0; i < bundle_at; i++) {
+		/* off of the send queue */
+		if (i) {
+			/* Any chunk NOT 0 you zap the time
+			 * chunk 0 gets zapped or set based on
+			 * if a RTO measurment is needed.
+			 */
+			data_list[i]->do_rtt = 0;
+		}
+		/* record time */
+		data_list[i]->sent_rcv_time = net->last_sent_time;
+		TAILQ_REMOVE(&asoc->send_queue,
+			     data_list[i],
+			     sctp_next);
+		/* on to the sent queue */
+		TAILQ_INSERT_TAIL(&asoc->sent_queue,
+				  data_list[i],
+				  sctp_next);
+		/* This does not lower until the cum-ack passes it */
+		asoc->sent_queue_cnt++;
+		asoc->send_queue_cnt--;
+		if ((asoc->peers_rwnd <= 0) &&
+		    (asoc->total_flight == 0) &&
+		    (bundle_at == 1)) {
+			/* Mark the chunk as being a window probe */
+#ifdef SCTP_DEBUG
+			if (sctp_debug_on & SCTP_DEBUG_OUTPUT1) {
+				printf("WINDOW PROBE SET\n");
+			}
+#endif
+			sctp_pegs[SCTP_WINDOW_PROBES]++;
+			data_list[i]->rec.data.state_flags |= SCTP_WINDOW_PROBE;
+		} else {
+			data_list[i]->rec.data.state_flags &= ~SCTP_WINDOW_PROBE;
+		}
+#ifdef SCTP_AUDITING_ENABLED
+		sctp_audit_log(0xC2, 3);
+#endif
+		data_list[i]->sent = SCTP_DATAGRAM_SENT;
+		data_list[i]->snd_count = 1;
+		net->flight_size += data_list[i]->book_size;
+		asoc->total_flight += data_list[i]->book_size;
+		asoc->total_flight_count++;
+#ifdef SCTP_LOG_RWND
+		sctp_log_rwnd(SCTP_DECREASE_PEER_RWND,
+			      asoc->peers_rwnd , data_list[i]->send_size, sctp_peer_chunk_oh);
+#endif
+		asoc->peers_rwnd = sctp_sbspace_sub(asoc->peers_rwnd,
+						    (u_int32_t)(data_list[i]->send_size + sctp_peer_chunk_oh));
+		if (asoc->peers_rwnd < stcb->sctp_ep->sctp_ep.sctp_sws_sender) {
+			/* SWS sender side engages */
+			asoc->peers_rwnd = 0;
+		}
+	}
+}
+
+static void
+sctp_clean_up_ctl(struct sctp_association *asoc)
+{
+	struct sctp_tmit_chunk *chk, *nchk;
+	for (chk = TAILQ_FIRST(&asoc->control_send_queue);
+	    chk; chk = nchk) {
+		nchk = TAILQ_NEXT(chk, sctp_next);
+		if ((chk->rec.chunk_id == SCTP_SELECTIVE_ACK) ||
+		    (chk->rec.chunk_id == SCTP_HEARTBEAT_REQUEST) ||
+		    (chk->rec.chunk_id == SCTP_HEARTBEAT_ACK) ||
+		    (chk->rec.chunk_id == SCTP_SHUTDOWN) ||
+		    (chk->rec.chunk_id == SCTP_SHUTDOWN_ACK) ||
+		    (chk->rec.chunk_id == SCTP_OPERATION_ERROR) ||
+		    (chk->rec.chunk_id == SCTP_PACKET_DROPPED) ||
+		    (chk->rec.chunk_id == SCTP_COOKIE_ACK) ||
+		    (chk->rec.chunk_id == SCTP_ECN_CWR) ||
+		    (chk->rec.chunk_id == SCTP_ASCONF_ACK)) {
+			/* Stray chunks must be cleaned up */
+		clean_up_anyway:
+			TAILQ_REMOVE(&asoc->control_send_queue, chk, sctp_next);
+			if (chk->data) {
+				sctp_m_freem(chk->data);
+				chk->data = NULL;
+			}
+			asoc->ctrl_queue_cnt--;
+			sctp_free_remote_addr(chk->whoTo);
+			SCTP_ZONE_FREE(sctppcbinfo.ipi_zone_chunk, chk);
+			sctppcbinfo.ipi_count_chunk--;
+			if ((int)sctppcbinfo.ipi_count_chunk < 0) {
+				panic("Chunk count is negative");
+			}
+			sctppcbinfo.ipi_gencnt_chunk++;
+		} else if (chk->rec.chunk_id == SCTP_STREAM_RESET) {
+			struct sctp_stream_reset_req *strreq;
+			/* special handling, we must look into the param */
+			strreq = mtod(chk->data, struct sctp_stream_reset_req *);
+			if (strreq->sr_req.ph.param_type == ntohs(SCTP_STR_RESET_RESPONSE)) {
+				goto clean_up_anyway;
+			}
+		}
+	}
+}
+
+static int
+sctp_move_to_outqueue(struct sctp_tcb *stcb,
+		      struct sctp_stream_out *strq)
+{
+	/* Move from the stream to the send_queue keeping track of the total */
+	struct sctp_association *asoc;
+	int tot_moved = 0;
+	int failed = 0;
+	int padval;
+	struct sctp_tmit_chunk *chk, *nchk;
+	struct sctp_data_chunk *dchkh;
+	struct sctpchunk_listhead tmp;
+	struct mbuf *orig;
+
+	asoc = &stcb->asoc;
+	TAILQ_INIT(&tmp);
+	chk = TAILQ_FIRST(&strq->outqueue);
+	while (chk) {
+		nchk = TAILQ_NEXT(chk, sctp_next);
+		/* now put in the chunk header */
+		orig = chk->data;
+		M_PREPEND(chk->data, sizeof(struct sctp_data_chunk), MB_DONTWAIT);
+		if (chk->data == NULL) {
+			/* HELP */
+			failed++;
+			break;
+		}
+		if (orig != chk->data) {
+			/* A new mbuf was added, account for it */
+			if ((stcb->sctp_ep->sctp_flags & SCTP_PCB_FLAGS_TCPTYPE) ||
+			    (stcb->sctp_ep->sctp_flags & SCTP_PCB_FLAGS_IN_TCPPOOL)) {
+				stcb->sctp_socket->so_snd.sb_mbcnt += MSIZE;
+			}
+#ifdef SCTP_MBCNT_LOGGING
+			sctp_log_mbcnt(SCTP_LOG_MBCNT_INCREASE,
+				       asoc->total_output_queue_size,
+				       0,
+				       asoc->total_output_mbuf_queue_size,
+				       MSIZE);
+#endif
+			stcb->asoc.total_output_mbuf_queue_size += MSIZE;
+			chk->mbcnt += MSIZE;
+		}
+		chk->send_size += sizeof(struct sctp_data_chunk);
+		/* This should NOT have to do anything, but
+		 * I would rather be cautious
+		 */
+		if (!failed && ((size_t)chk->data->m_len < sizeof(struct sctp_data_chunk))) {
+			m_pullup(chk->data, sizeof(struct sctp_data_chunk));
+			if (chk->data == NULL) {
+				failed++;
+				break;
+			}
+		}
+		dchkh = mtod(chk->data, struct sctp_data_chunk *);
+		dchkh->ch.chunk_length = htons(chk->send_size);
+		/* Chunks must be padded to even word boundary */
+		padval = chk->send_size % 4;
+		if (padval) {
+			/* For fragmented messages this should not
+			 * run except possibly on the last chunk
+			 */
+			if (sctp_pad_lastmbuf(chk->data, (4 - padval))) {
+				/* we are in big big trouble no mbufs :< */
+				failed++;
+				break;
+			}
+			chk->send_size += (4 - padval);
+		}
+		/* pull from stream queue */
+		TAILQ_REMOVE(&strq->outqueue, chk, sctp_next);
+		asoc->stream_queue_cnt--;
+		TAILQ_INSERT_TAIL(&tmp, chk, sctp_next);
+		/* add it in to the size of moved chunks */
+		if (chk->rec.data.rcv_flags & SCTP_DATA_LAST_FRAG) {
+			/* we pull only one message */
+			break;
+		}
+		chk = nchk;
+	}
+	if (failed) {
+		/* Gak, we just lost the user message */
+		chk = TAILQ_FIRST(&tmp);
+		while (chk) {
+			nchk = TAILQ_NEXT(chk, sctp_next);
+			TAILQ_REMOVE(&tmp, chk, sctp_next);
+
+			sctp_ulp_notify(SCTP_NOTIFY_DG_FAIL, stcb,
+					(SCTP_NOTIFY_DATAGRAM_UNSENT|SCTP_INTERNAL_ERROR),
+					chk);
+
+			if (chk->data) {
+				sctp_m_freem(chk->data);
+				chk->data = NULL;
+			}
+			if (chk->whoTo) {
+				sctp_free_remote_addr(chk->whoTo);
+				chk->whoTo = NULL;
+			}
+			SCTP_ZONE_FREE(sctppcbinfo.ipi_zone_chunk, chk);
+			sctppcbinfo.ipi_count_chunk--;
+			if ((int)sctppcbinfo.ipi_count_chunk < 0) {
+				panic("Chunk count is negative");
+			}
+			sctppcbinfo.ipi_gencnt_chunk++;
+			chk = nchk;
+		}
+		return (0);
+	}
+	/* now pull them off of temp wheel */
+	chk = TAILQ_FIRST(&tmp);
+	while (chk) {
+		nchk = TAILQ_NEXT(chk, sctp_next);
+		/* insert on send_queue */
+		TAILQ_REMOVE(&tmp, chk, sctp_next);
+		TAILQ_INSERT_TAIL(&asoc->send_queue, chk, sctp_next);
+		asoc->send_queue_cnt++;
+		/* assign TSN */
+		chk->rec.data.TSN_seq = asoc->sending_seq++;
+
+		dchkh = mtod(chk->data, struct sctp_data_chunk *);
+		/* Put the rest of the things in place now. Size
+		 * was done earlier in previous loop prior to
+		 * padding.
+		 */
+		dchkh->ch.chunk_type = SCTP_DATA;
+		dchkh->ch.chunk_flags = chk->rec.data.rcv_flags;
+		dchkh->dp.tsn = htonl(chk->rec.data.TSN_seq);
+		dchkh->dp.stream_id = htons(strq->stream_no);
+		dchkh->dp.stream_sequence = htons(chk->rec.data.stream_seq);
+		dchkh->dp.protocol_id = chk->rec.data.payloadtype;
+		/* total count moved */
+		tot_moved += chk->send_size;
+		chk = nchk;
+	}
+	return (tot_moved);
+}
+
+static void
+sctp_fill_outqueue(struct sctp_tcb *stcb,
+		   struct sctp_nets *net)
+{
+	struct sctp_association *asoc;
+	struct sctp_tmit_chunk *chk;
+	struct sctp_stream_out *strq, *strqn;
+	int mtu_fromwheel, goal_mtu;
+	unsigned int moved, seenend, cnt_mvd=0;
+
+	asoc = &stcb->asoc;
+	/* Attempt to move at least 1 MTU's worth
+	 * onto the wheel for each destination address
+	 */
+	goal_mtu = net->cwnd - net->flight_size;
+	if ((unsigned int)goal_mtu < net->mtu) {
+		goal_mtu = net->mtu;
+	}
+	if (sctp_pegs[SCTP_MOVED_MTU] < (unsigned int)goal_mtu) {
+		sctp_pegs[SCTP_MOVED_MTU] = goal_mtu;
+	}
+	seenend = moved = mtu_fromwheel = 0;
+	if (asoc->last_out_stream == NULL) {
+		strq = asoc->last_out_stream = TAILQ_FIRST(&asoc->out_wheel);
+		if (asoc->last_out_stream == NULL) {
+			/* huh nothing on the wheel, TSNH */
+			return;
+		}
+		goto done_it;
+	}
+	strq = TAILQ_NEXT(asoc->last_out_stream, next_spoke);
+ done_it:
+	if (strq == NULL) {
+		asoc->last_out_stream = TAILQ_FIRST(&asoc->out_wheel);
+	}
+	while (mtu_fromwheel < goal_mtu) {
+		if (strq == NULL) {
+			if (seenend == 0) {
+				seenend = 1;
+				strq = TAILQ_FIRST(&asoc->out_wheel);
+			} else if ((moved == 0) && (seenend)) {
+				/* none left on the wheel */
+				sctp_pegs[SCTP_MOVED_NLEF]++;
+				return;
+			} else if (moved) {
+				/*
+				 * clear the flags and rotate back through
+				 * again
+				 */
+				moved = 0;
+				seenend = 0;
+				strq = TAILQ_FIRST(&asoc->out_wheel);
+			}
+			if (strq == NULL)
+				break;
+			continue;
+		}
+		strqn = TAILQ_NEXT(strq, next_spoke);
+		if ((chk = TAILQ_FIRST(&strq->outqueue)) == NULL) {
+			/* none left on this queue, prune a spoke?  */
+			sctp_remove_from_wheel(asoc, strq);
+			if (strq == asoc->last_out_stream) {
+			    /* the last one we used went off the wheel */
+			    asoc->last_out_stream = NULL;
+			}
+			strq = strqn;
+			continue;
+		}
+		if (chk->whoTo != net) {
+			/* Skip this stream, first one on stream
+			 * does not head to our current destination.
+			 */
+			strq = strqn;
+			continue;
+		}
+		mtu_fromwheel += sctp_move_to_outqueue(stcb, strq);
+		cnt_mvd++;
+		moved++;
+		asoc->last_out_stream = strq;
+		strq = strqn;
+	}
+	sctp_pegs[SCTP_MOVED_MAX]++;
+#ifdef SCTP_DEBUG
+	if (sctp_debug_on & SCTP_DEBUG_OUTPUT3) {
+		printf("Ok we moved %d chunks to send queue\n",
+		       moved);
+	}
+#endif
+	if (sctp_pegs[SCTP_MOVED_QMAX] < cnt_mvd) {
+		sctp_pegs[SCTP_MOVED_QMAX] = cnt_mvd;
+	}
+}
+
+void
+sctp_fix_ecn_echo(struct sctp_association *asoc)
+{
+	struct sctp_tmit_chunk *chk;
+	TAILQ_FOREACH(chk, &asoc->control_send_queue, sctp_next) {
+		if (chk->rec.chunk_id == SCTP_ECN_ECHO) {
+			chk->sent = SCTP_DATAGRAM_UNSENT;
+		}
+	}
+}
+
+static void
+sctp_move_to_an_alt(struct sctp_tcb *stcb,
+		    struct sctp_association *asoc,
+		    struct sctp_nets *net)
+{
+	struct sctp_tmit_chunk *chk;
+	struct sctp_nets *a_net;
+	a_net = sctp_find_alternate_net(stcb, net);
+	if ((a_net != net) &&
+	    ((a_net->dest_state & SCTP_ADDR_REACHABLE) == SCTP_ADDR_REACHABLE)) {
+		/*
+		 * We only proceed if a valid alternate is found that is
+		 * not this one and is reachable. Here we must move all
+		 * chunks queued in the send queue off of the destination
+		 * address to our alternate.
+		 */
+		TAILQ_FOREACH(chk, &asoc->send_queue, sctp_next) {
+			if (chk->whoTo == net) {
+				/* Move the chunk to our alternate */
+				sctp_free_remote_addr(chk->whoTo);
+				chk->whoTo = a_net;
+				a_net->ref_count++;
+			}
+		}
+	}
+}
+
+static int sctp_from_user_send=0;
+
+static int
+sctp_med_chunk_output(struct sctp_inpcb *inp,
+		      struct sctp_tcb *stcb,
+		      struct sctp_association *asoc,
+		      int *num_out,
+		      int *reason_code,
+		      int control_only, int *cwnd_full, int from_where,
+		      struct timeval *now, int *now_filled)
+{
+	/*
+	 * Ok this is the generic chunk service queue.
+	 * we must do the following:
+	 *  - Service the stream queue that is next, moving any message
+	 *    (note I must get a complete message i.e. FIRST/MIDDLE and
+	 *    LAST to the out queue in one pass) and assigning TSN's
+	 *  - Check to see if the cwnd/rwnd allows any output, if so we
+	 *    go ahead and fomulate and send the low level chunks. Making
+	 *    sure to combine any control in the control chunk queue also.
+	 */
+	struct sctp_nets *net;
+	struct mbuf *outchain;
+	struct sctp_tmit_chunk *chk, *nchk;
+	struct sctphdr *shdr;
+	/* temp arrays for unlinking */
+	struct sctp_tmit_chunk *data_list[SCTP_MAX_DATA_BUNDLING];
+	int no_fragmentflg, error;
+	int one_chunk, hbflag;
+	int asconf, cookie, no_out_cnt;
+	int bundle_at, ctl_cnt, no_data_chunks, cwnd_full_ind;
+        unsigned int mtu, r_mtu, omtu;
+	*num_out = 0;
+	cwnd_full_ind = 0;
+	ctl_cnt = no_out_cnt = asconf = cookie = 0;
+	/*
+	 * First lets prime the pump. For each destination, if there
+	 * is room in the flight size, attempt to pull an MTU's worth
+	 * out of the stream queues into the general send_queue
+	 */
+#ifdef SCTP_AUDITING_ENABLED
+	sctp_audit_log(0xC2, 2);
+#endif
+#ifdef SCTP_DEBUG
+	if (sctp_debug_on & SCTP_DEBUG_OUTPUT3) {
+		printf("***********************\n");
+	}
+#endif
+	hbflag = 0;
+	if (control_only)
+		no_data_chunks = 1;
+	else
+		no_data_chunks = 0;
+
+	/* Nothing to possible to send? */
+	if (TAILQ_EMPTY(&asoc->control_send_queue) &&
+	    TAILQ_EMPTY(&asoc->send_queue) &&
+	    TAILQ_EMPTY(&asoc->out_wheel)) {
+#ifdef SCTP_DEBUG
+		if (sctp_debug_on & SCTP_DEBUG_OUTPUT3) {
+			printf("All wheels empty\n");
+		}
+#endif
+		return (0);
+	}
+	if (asoc->peers_rwnd <= 0) {
+		/* No room in peers rwnd */
+		*cwnd_full = 1;
+		*reason_code = 1;
+		if (asoc->total_flight > 0) {
+			/* we are allowed one chunk in flight */
+			no_data_chunks = 1;
+			sctp_pegs[SCTP_RWND_BLOCKED]++;
+		}
+	}
+#ifdef SCTP_DEBUG
+	if (sctp_debug_on & SCTP_DEBUG_OUTPUT3) {
+		printf("Ok we have done the fillup no_data_chunk=%d tf=%d prw:%d\n",
+		       (int)no_data_chunks,
+		       (int)asoc->total_flight, (int)asoc->peers_rwnd);
+	}
+#endif
+	TAILQ_FOREACH(net, &asoc->nets, sctp_next) {
+#ifdef SCTP_DEBUG
+		if (sctp_debug_on & SCTP_DEBUG_OUTPUT3) {
+			printf("net:%p fs:%d  cwnd:%d\n",
+			       net, net->flight_size, net->cwnd);
+		}
+#endif
+		if (net->flight_size >= net->cwnd) {
+			/* skip this network, no room */
+			cwnd_full_ind++;
+#ifdef SCTP_DEBUG
+			if (sctp_debug_on & SCTP_DEBUG_OUTPUT3) {
+				printf("Ok skip fillup->fs:%d > cwnd:%d\n",
+				       net->flight_size,
+				       net->cwnd);
+			}
+#endif
+			sctp_pegs[SCTP_CWND_NOFILL]++;
+			continue;
+		}
+		/*
+		 * spin through the stream queues moving one message and
+		 * assign TSN's as appropriate.
+		 */
+		sctp_fill_outqueue(stcb, net);
+	}
+	*cwnd_full = cwnd_full_ind;
+	/* now service each destination and send out what we can for it */
+#ifdef SCTP_DEBUG
+	if (sctp_debug_on & SCTP_DEBUG_OUTPUT3) {
+		int chk_cnt = 0;
+		TAILQ_FOREACH(chk, &asoc->send_queue, sctp_next) {
+			chk_cnt++;
+		}
+		printf("We have %d chunks on the send_queue\n", chk_cnt);
+		chk_cnt = 0;
+		TAILQ_FOREACH(chk, &asoc->sent_queue, sctp_next) {
+			chk_cnt++;
+		}
+		printf("We have %d chunks on the sent_queue\n", chk_cnt);
+		TAILQ_FOREACH(chk, &asoc->control_send_queue, sctp_next) {
+			chk_cnt++;
+		}
+		printf("We have %d chunks on the control_queue\n", chk_cnt);
+	}
+#endif
+	/* If we have data to send, and DSACK is running, stop it
+	 * and build a SACK to dump on to bundle with output. This
+	 * actually MAY make it so the bundling does not occur if
+	 * the SACK is big but I think this is ok because basic SACK
+	 * space is pre-reserved in our fragmentation size choice.
+	 */
+	if ((TAILQ_FIRST(&asoc->send_queue) != NULL) &&
+	    (no_data_chunks == 0)) {
+		/* We will be sending something */
+		if (callout_pending(&stcb->asoc.dack_timer.timer)) {
+			/* Yep a callout is pending */
+			sctp_timer_stop(SCTP_TIMER_TYPE_RECV,
+					stcb->sctp_ep,
+					stcb, NULL);
+			sctp_send_sack(stcb);
+		}
+	}
+	/* Nothing to send? */
+	if ((TAILQ_FIRST(&asoc->control_send_queue) == NULL) &&
+	    (TAILQ_FIRST(&asoc->send_queue) == NULL)) {
+		return (0);
+	}
+	TAILQ_FOREACH(net, &asoc->nets, sctp_next) {
+		/* how much can we send? */
+		if (net->ref_count < 2) {
+			/* Ref-count of 1 so we cannot have data or control
+			 * queued to this address. Skip it.
+			 */
+ 			continue;
+		}
+		ctl_cnt = bundle_at = 0;
+		outchain = NULL;
+		no_fragmentflg = 1;
+		one_chunk = 0;
+
+		if ((net->ro.ro_rt) && (net->ro.ro_rt->rt_ifp)) {
+			/* if we have a route and an ifp
+			 * check to see if we have room to
+			 * send to this guy
+			 */
+			struct ifnet *ifp;
+			ifp = net->ro.ro_rt->rt_ifp;
+			if ((ifp->if_snd.ifq_len + 2) >= ifp->if_snd.ifq_maxlen) {
+				sctp_pegs[SCTP_IFP_QUEUE_FULL]++;
+#ifdef SCTP_LOG_MAXBURST
+				sctp_log_maxburst(net, ifp->if_snd.ifq_len, ifp->if_snd.ifq_maxlen, SCTP_MAX_IFP_APPLIED);
+  #endif
+				continue;
+			}
+		}
+		if (((struct sockaddr *)&net->ro._l_addr)->sa_family == AF_INET) {
+			mtu = net->mtu - (sizeof(struct ip) + sizeof(struct sctphdr));
+		} else {
+			mtu = net->mtu - (sizeof(struct ip6_hdr) + sizeof(struct sctphdr));
+		}
+		if (mtu > asoc->peers_rwnd) {
+			if (asoc->total_flight > 0) {
+				/* We have a packet in flight somewhere */
+				r_mtu = asoc->peers_rwnd;
+			} else {
+				/* We are always allowed to send one MTU out */
+				one_chunk = 1;
+				r_mtu = mtu;
+			}
+		} else {
+			r_mtu = mtu;
+		}
+#ifdef SCTP_DEBUG
+		if (sctp_debug_on & SCTP_DEBUG_OUTPUT3) {
+			printf("Ok r_mtu is %d mtu is %d for this net:%p one_chunk:%d\n",
+			       r_mtu, mtu, net, one_chunk);
+		}
+#endif
+		/************************/
+		/* Control transmission */
+		/************************/
+		/* Now first lets go through the control queue */
+		for (chk = TAILQ_FIRST(&asoc->control_send_queue);
+		     chk; chk = nchk) {
+			nchk = TAILQ_NEXT(chk, sctp_next);
+			if (chk->whoTo != net) {
+				/*
+				 * No, not sent to the network we are
+				 * looking at
+				 */
+				continue;
+			}
+			if (chk->data == NULL) {
+				continue;
+			}
+			if ((chk->data->m_flags & M_PKTHDR) == 0) {
+				/*
+				 * NOTE: the chk queue MUST have the PKTHDR
+				 * flag set on it with a total in the
+				 * m_pkthdr.len field!! else the chunk will
+				 * ALWAYS be skipped
+				 */
+				continue;
+			}
+			if (chk->sent != SCTP_DATAGRAM_UNSENT) {
+				/*
+				 * It must be unsent. Cookies and ASCONF's
+				 * hang around but there timers will force
+				 * when marked for resend.
+				 */
+				continue;
+			}
+			/* Here we do NOT factor the r_mtu */
+			if ((chk->data->m_pkthdr.len < (int)mtu) ||
+			    (chk->flags & CHUNK_FLAGS_FRAGMENT_OK)) {
+				/*
+				 * We probably should glom the mbuf chain from
+				 * the chk->data for control but the problem
+				 * is it becomes yet one more level of
+				 * tracking to do if for some reason output
+				 * fails. Then I have got to reconstruct the
+				 * merged control chain.. el yucko.. for now
+				 * we take the easy way and do the copy
+				 */
+				outchain = sctp_copy_mbufchain(chk->data,
+							       outchain);
+				if (outchain == NULL) {
+					return (ENOMEM);
+				}
+				/* update our MTU size */
+				mtu -= chk->data->m_pkthdr.len;
+				if (mtu < 0) {
+					mtu = 0;
+				}
+				/* Do clear IP_DF ? */
+				if (chk->flags & CHUNK_FLAGS_FRAGMENT_OK) {
+					no_fragmentflg = 0;
+				}
+				/* Mark things to be removed, if needed */
+				if ((chk->rec.chunk_id == SCTP_SELECTIVE_ACK) ||
+				    (chk->rec.chunk_id == SCTP_HEARTBEAT_REQUEST) ||
+				    (chk->rec.chunk_id == SCTP_HEARTBEAT_ACK) ||
+				    (chk->rec.chunk_id == SCTP_SHUTDOWN) ||
+				    (chk->rec.chunk_id == SCTP_SHUTDOWN_ACK) ||
+				    (chk->rec.chunk_id == SCTP_OPERATION_ERROR) ||
+				    (chk->rec.chunk_id == SCTP_COOKIE_ACK) ||
+				    (chk->rec.chunk_id == SCTP_ECN_CWR) ||
+				    (chk->rec.chunk_id == SCTP_PACKET_DROPPED) ||
+				    (chk->rec.chunk_id == SCTP_ASCONF_ACK)) {
+
+					if (chk->rec.chunk_id == SCTP_HEARTBEAT_REQUEST)
+						hbflag = 1;
+					/* remove these chunks at the end */
+					if (chk->rec.chunk_id == SCTP_SELECTIVE_ACK) {
+						/* turn off the timer */
+						if (callout_pending(&stcb->asoc.dack_timer.timer)) {
+							sctp_timer_stop(SCTP_TIMER_TYPE_RECV,
+									inp, stcb, net);
+						}
+					}
+					ctl_cnt++;
+				} else {
+					/*
+					 * Other chunks, since they have
+					 * timers running (i.e. COOKIE or
+					 * ASCONF) we just "trust" that it
+					 * gets sent or retransmitted.
+					 */
+					ctl_cnt++;
+					if (chk->rec.chunk_id == SCTP_COOKIE_ECHO) {
+						cookie = 1;
+						no_out_cnt = 1;
+					} else if (chk->rec.chunk_id == SCTP_ASCONF) {
+						/*
+						 * set hb flag since we can use
+						 * these for RTO
+						 */
+						hbflag = 1;
+						asconf = 1;
+					}
+					chk->sent = SCTP_DATAGRAM_SENT;
+					chk->snd_count++;
+				}
+				if (mtu == 0) {
+					/*
+					 * Ok we are out of room but we can
+					 * output without effecting the flight
+					 * size since this little guy is a
+					 * control only packet.
+					 */
+					if (asconf) {
+						sctp_timer_start(SCTP_TIMER_TYPE_ASCONF, inp, stcb, net);
+						asconf = 0;
+					}
+					if (cookie) {
+						sctp_timer_start(SCTP_TIMER_TYPE_COOKIE, inp, stcb, net);
+						cookie = 0;
+					}
+					if (outchain->m_len == 0) {
+						/*
+						 * Special case for when you
+						 * get a 0 len mbuf at the
+						 * head due to the lack of a
+						 * MHDR at the beginning.
+						 */
+						outchain->m_len = sizeof(struct sctphdr);
+					} else {
+						M_PREPEND(outchain, sizeof(struct sctphdr), MB_DONTWAIT);
+						if (outchain == NULL) {
+							/* no memory */
+							error = ENOBUFS;
+							goto error_out_again;
+						}
+					}
+					shdr = mtod(outchain, struct sctphdr *);
+					shdr->src_port = inp->sctp_lport;
+					shdr->dest_port = stcb->rport;
+					shdr->v_tag = htonl(stcb->asoc.peer_vtag);
+					shdr->checksum = 0;
+
+					if ((error = sctp_lowlevel_chunk_output(inp, stcb, net,
+										(struct sockaddr *)&net->ro._l_addr,
+										outchain,
+										no_fragmentflg, 0, NULL, asconf))) {
+						if (error == ENOBUFS) {
+							asoc->ifp_had_enobuf = 1;
+						}
+						sctp_pegs[SCTP_DATA_OUT_ERR]++;
+						if (from_where == 0) {
+							sctp_pegs[SCTP_ERROUT_FRM_USR]++;
+						}
+					error_out_again:
+#ifdef SCTP_DEBUG
+						if (sctp_debug_on & SCTP_DEBUG_OUTPUT2) {
+							printf("Gak got ctrl error %d\n", error);
+						}
+#endif
+						/* error, could not output */
+						if (hbflag) {
+#ifdef SCTP_DEBUG
+							if (sctp_debug_on & SCTP_DEBUG_OUTPUT3) {
+								printf("Update HB anyway\n");
+							}
+#endif
+							if (*now_filled == 0) {
+								SCTP_GETTIME_TIMEVAL(&net->last_sent_time);
+								*now_filled = 1;
+								*now = net->last_sent_time;
+							} else {
+								net->last_sent_time = *now;
+							}
+							hbflag = 0;
+						}
+						if (error == EHOSTUNREACH) {
+							/*
+							 * Destination went
+							 * unreachable during
+							 * this send
+							 */
+#ifdef SCTP_DEBUG
+							if (sctp_debug_on & SCTP_DEBUG_OUTPUT3) {
+								printf("Moving data to an alterante\n");
+							}
+#endif
+							sctp_move_to_an_alt(stcb, asoc, net);
+						}
+						sctp_clean_up_ctl (asoc);
+						return (error);
+					} else
+						asoc->ifp_had_enobuf = 0;
+					/* Only HB or ASCONF advances time */
+					if (hbflag) {
+						if (*now_filled == 0) {
+							SCTP_GETTIME_TIMEVAL(&net->last_sent_time);
+							*now_filled = 1;
+							*now = net->last_sent_time;
+						} else {
+							net->last_sent_time = *now;
+						}
+						hbflag = 0;
+					}
+					/*
+					 * increase the number we sent, if a
+					 * cookie is sent we don't tell them
+					 * any was sent out.
+					 */
+					if (!no_out_cnt)
+						*num_out +=  ctl_cnt;
+					/* recalc a clean slate and setup */
+					if (stcb->sctp_ep->sctp_flags & SCTP_PCB_FLAGS_BOUND_V6) {
+						mtu = (net->mtu - SCTP_MIN_OVERHEAD);
+					} else {
+						mtu = (net->mtu - SCTP_MIN_V4_OVERHEAD);
+					}
+					no_fragmentflg = 1;
+				}
+			}
+		}
+		/*********************/
+		/* Data transmission */
+		/*********************/
+		/* now lets add any data within the MTU constraints */
+		if (((struct sockaddr *)&net->ro._l_addr)->sa_family == AF_INET) {
+			omtu = net->mtu - (sizeof(struct ip) + sizeof(struct sctphdr));
+		} else {
+			omtu = net->mtu - (sizeof(struct ip6_hdr) + sizeof(struct sctphdr));
+		}
+
+#ifdef SCTP_DEBUG
+		if (sctp_debug_on & SCTP_DEBUG_OUTPUT3) {
+			printf("Now to data transmission\n");
+		}
+#endif
+
+		if (((asoc->state & SCTP_STATE_OPEN) == SCTP_STATE_OPEN) ||
+		    (cookie)) {
+			for (chk = TAILQ_FIRST(&asoc->send_queue); chk; chk = nchk) {
+				if (no_data_chunks) {
+					/* let only control go out */
+#ifdef SCTP_DEBUG
+					if (sctp_debug_on & SCTP_DEBUG_OUTPUT3) {
+						printf("Either nothing to send or we are full\n");
+					}
+#endif
+					break;
+				}
+				if (net->flight_size >= net->cwnd) {
+					/* skip this net, no room for data */
+#ifdef SCTP_DEBUG
+					if (sctp_debug_on & SCTP_DEBUG_OUTPUT3) {
+						printf("fs:%d > cwnd:%d\n",
+						       net->flight_size, net->cwnd);
+					}
+#endif
+					sctp_pegs[SCTP_CWND_BLOCKED]++;
+					*reason_code = 2;
+					break;
+				}
+				nchk = TAILQ_NEXT(chk, sctp_next);
+				if (chk->whoTo != net) {
+					/* No, not sent to this net */
+#ifdef SCTP_DEBUG
+					if (sctp_debug_on & SCTP_DEBUG_OUTPUT3) {
+						printf("chk->whoTo:%p not %p\n",
+						       chk->whoTo, net);
+
+					}
+#endif
+					continue;
+				}
+#ifdef SCTP_DEBUG
+				if (sctp_debug_on & SCTP_DEBUG_OUTPUT3) {
+					printf("Can we pick up a chunk?\n");
+				}
+#endif
+				if ((chk->send_size > omtu) && ((chk->flags & CHUNK_FLAGS_FRAGMENT_OK) == 0)) {
+					/* strange, we have a chunk that is to bit
+					 * for its destination and yet no fragment ok flag.
+					 * Something went wrong when the PMTU changed...we did
+					 * not mark this chunk for some reason?? I will
+					 * fix it here by letting IP fragment it for now and
+					 * printing a warning. This really should not happen ...
+					 */
+/*#ifdef SCTP_DEBUG*/
+					printf("Warning chunk of %d bytes > mtu:%d and yet PMTU disc missed\n",
+					       chk->send_size, mtu);
+/*#endif*/
+					chk->flags |= CHUNK_FLAGS_FRAGMENT_OK;
+				}
+
+				if (((chk->send_size <= mtu) && (chk->send_size <= r_mtu)) ||
+				    ((chk->flags & CHUNK_FLAGS_FRAGMENT_OK) && (chk->send_size <= asoc->peers_rwnd))) {
+					/* ok we will add this one */
+#ifdef SCTP_DEBUG
+					if (sctp_debug_on & SCTP_DEBUG_OUTPUT3) {
+						printf("Picking up the chunk\n");
+					}
+#endif
+					outchain = sctp_copy_mbufchain(chk->data, outchain);
+					if (outchain == NULL) {
+#ifdef SCTP_DEBUG
+						if (sctp_debug_on & SCTP_DEBUG_OUTPUT3) {
+							printf("Gakk no memory\n");
+						}
+#endif
+						if (!callout_pending(&net->rxt_timer.timer)) {
+							sctp_timer_start(SCTP_TIMER_TYPE_SEND, inp, stcb, net);
+						}
+						return (ENOMEM);
+					}
+					/* upate our MTU size */
+					/* Do clear IP_DF ? */
+					if (chk->flags & CHUNK_FLAGS_FRAGMENT_OK) {
+						no_fragmentflg = 0;
+					}
+					mtu -= chk->send_size;
+					r_mtu -= chk->send_size;
+					data_list[bundle_at++] = chk;
+					if (bundle_at >= SCTP_MAX_DATA_BUNDLING) {
+						mtu = 0;
+						break;
+					}
+					if (mtu <= 0) {
+						mtu = 0;
+						break;
+					}
+					if ((r_mtu <= 0) || one_chunk) {
+						r_mtu = 0;
+						break;
+					}
+				} else {
+					/*
+					 * Must be sent in order of the TSN's
+					 * (on a network)
+					 */
+#ifdef SCTP_DEBUG
+					if (sctp_debug_on & SCTP_DEBUG_OUTPUT3) {
+						printf("ok no more chk:%d > mtu:%d || < r_mtu:%d\n",
+						       chk->send_size, mtu, r_mtu);
+					}
+#endif
+
+					break;
+				}
+			}/* for () */
+		} /* if asoc.state OPEN */
+		/* Is there something to send for this destination? */
+#ifdef SCTP_DEBUG
+		if (sctp_debug_on & SCTP_DEBUG_OUTPUT3) {
+			printf("ok now is chain assembled? %p\n",
+			       outchain);
+		}
+#endif
+
+		if (outchain) {
+			/* We may need to start a control timer or two */
+			if (asconf) {
+				sctp_timer_start(SCTP_TIMER_TYPE_ASCONF, inp, stcb, net);
+				asconf = 0;
+			}
+			if (cookie) {
+				sctp_timer_start(SCTP_TIMER_TYPE_COOKIE, inp, stcb, net);
+				cookie = 0;
+			}
+			/* must start a send timer if data is being sent */
+			if (bundle_at && (!callout_pending(&net->rxt_timer.timer))) {
+				/* no timer running on this destination
+				 * restart it.
+				 */
+#ifdef SCTP_DEBUG
+				if (sctp_debug_on & SCTP_DEBUG_OUTPUT3) {
+					printf("ok lets start a send timer .. we will transmit %p\n",
+					       outchain);
+				}
+#endif
+				sctp_timer_start(SCTP_TIMER_TYPE_SEND, inp, stcb, net);
+			}
+			/* Now send it, if there is anything to send :> */
+			if ((outchain->m_flags & M_PKTHDR) == 0) {
+				struct mbuf *t;
+
+				MGETHDR(t, MB_DONTWAIT, MT_HEADER);
+				if (t == NULL) {
+					sctp_m_freem(outchain);
+					return (ENOMEM);
+				}
+				t->m_next = outchain;
+				t->m_pkthdr.len = 0;
+				t->m_pkthdr.rcvif = 0;
+				t->m_len = 0;
+
+				outchain = t;
+				while (t) {
+					outchain->m_pkthdr.len += t->m_len;
+					t = t->m_next;
+				}
+			}
+			if (outchain->m_len == 0) {
+				/* Special case for when you get a 0 len
+				 * mbuf at the head due to the lack
+				 * of a MHDR at the beginning.
+				 */
+				MH_ALIGN(outchain, sizeof(struct sctphdr));
+				outchain->m_len = sizeof(struct sctphdr);
+			} else {
+				M_PREPEND(outchain, sizeof(struct sctphdr), MB_DONTWAIT);
+				if (outchain == NULL) {
+					/* out of mbufs */
+					error = ENOBUFS;
+					goto errored_send;
+				}
+			}
+			shdr = mtod(outchain, struct sctphdr *);
+			shdr->src_port = inp->sctp_lport;
+			shdr->dest_port = stcb->rport;
+			shdr->v_tag = htonl(stcb->asoc.peer_vtag);
+			shdr->checksum = 0;
+			if ((error = sctp_lowlevel_chunk_output(inp, stcb, net,
+								(struct sockaddr *)&net->ro._l_addr,
+								outchain,
+								no_fragmentflg, bundle_at, data_list[0], asconf))) {
+				/* error, we could not output */
+				if (error == ENOBUFS) {
+					asoc->ifp_had_enobuf = 1;
+				}
+				sctp_pegs[SCTP_DATA_OUT_ERR]++;
+				if (from_where == 0) {
+					sctp_pegs[SCTP_ERROUT_FRM_USR]++;
+				}
+
+			errored_send:
+#ifdef SCTP_DEBUG
+				if (sctp_debug_on & SCTP_DEBUG_OUTPUT3) {
+					printf("Gak send error %d\n", error);
+				}
+#endif
+				if (hbflag) {
+#ifdef SCTP_DEBUG
+					if (sctp_debug_on & SCTP_DEBUG_OUTPUT3) {
+						printf("Update HB time anyway\n");
+					}
+#endif
+					if (*now_filled == 0) {
+						SCTP_GETTIME_TIMEVAL(&net->last_sent_time);
+						*now_filled = 1;
+						*now = net->last_sent_time;
+					} else {
+						net->last_sent_time = *now;
+					}
+					hbflag = 0;
+				}
+				if (error == EHOSTUNREACH) {
+					/*
+					 * Destination went unreachable during
+					 * this send
+					 */
+#ifdef SCTP_DEBUG
+					if (sctp_debug_on & SCTP_DEBUG_OUTPUT3) {
+						printf("Calling the movement routine\n");
+					}
+#endif
+					sctp_move_to_an_alt(stcb, asoc, net);
+				}
+				sctp_clean_up_ctl (asoc);
+				return (error);
+			} else {
+				asoc->ifp_had_enobuf = 0;
+			}
+			if (bundle_at || hbflag) {
+				/* For data/asconf and hb set time */
+				if (*now_filled == 0) {
+					SCTP_GETTIME_TIMEVAL(&net->last_sent_time);
+					*now_filled = 1;
+					*now = net->last_sent_time;
+				} else {
+					net->last_sent_time = *now;
+				}
+			}
+
+			if (!no_out_cnt) {
+				*num_out += (ctl_cnt + bundle_at);
+			}
+			if (bundle_at) {
+				if (!net->rto_pending) {
+					/* setup for a RTO measurement */
+					net->rto_pending = 1;
+					data_list[0]->do_rtt = 1;
+				} else {
+					data_list[0]->do_rtt = 0;
+				}
+				sctp_pegs[SCTP_PEG_TSNS_SENT] += bundle_at;
+				sctp_clean_up_datalist(stcb, asoc, data_list, bundle_at, net);
+			}
+			if (one_chunk) {
+				break;
+			}
+		}
+	}
+	/* At the end there should be no NON timed
+	 * chunks hanging on this queue.
+	 */
+	if ((*num_out == 0) && (*reason_code == 0)) {
+		*reason_code = 3;
+	}
+	sctp_clean_up_ctl (asoc);
+	return (0);
+}
+
+void
+sctp_queue_op_err(struct sctp_tcb *stcb, struct mbuf *op_err)
+{
+	/* Prepend a OPERATIONAL_ERROR chunk header
+	 * and put on the end of the control chunk queue.
+	 */
+	/* Sender had better have gotten a MGETHDR or else
+	 * the control chunk will be forever skipped
+	 */
+	struct sctp_chunkhdr *hdr;
+	struct sctp_tmit_chunk *chk;
+	struct mbuf *mat;
+
+	chk = (struct sctp_tmit_chunk *)SCTP_ZONE_GET(sctppcbinfo.ipi_zone_chunk);
+	if (chk == NULL) {
+		/* no memory */
+		sctp_m_freem(op_err);
+		return;
+	}
+	sctppcbinfo.ipi_count_chunk++;
+	sctppcbinfo.ipi_gencnt_chunk++;
+	M_PREPEND(op_err, sizeof(struct sctp_chunkhdr), MB_DONTWAIT);
+	if (op_err == NULL) {
+		SCTP_ZONE_FREE(sctppcbinfo.ipi_zone_chunk, chk);
+		sctppcbinfo.ipi_count_chunk--;
+		if ((int)sctppcbinfo.ipi_count_chunk < 0) {
+			panic("Chunk count is negative");
+		}
+		sctppcbinfo.ipi_gencnt_chunk++;
+		return;
+	}
+	chk->send_size = 0;
+	mat = op_err;
+	while (mat != NULL) {
+		chk->send_size += mat->m_len;
+		mat = mat->m_next;
+	}
+	chk->rec.chunk_id = SCTP_OPERATION_ERROR;
+	chk->sent = SCTP_DATAGRAM_UNSENT;
+	chk->snd_count = 0;
+	chk->flags = 0;
+	chk->asoc = &stcb->asoc;
+	chk->data = op_err;
+	chk->whoTo = chk->asoc->primary_destination;
+	chk->whoTo->ref_count++;
+	hdr = mtod(op_err, struct sctp_chunkhdr *);
+	hdr->chunk_type = SCTP_OPERATION_ERROR;
+	hdr->chunk_flags = 0;
+	hdr->chunk_length = htons(chk->send_size);
+	TAILQ_INSERT_TAIL(&chk->asoc->control_send_queue,
+			  chk,
+			  sctp_next);
+	chk->asoc->ctrl_queue_cnt++;
+}
+
+int
+sctp_send_cookie_echo(struct mbuf *m,
+		      int offset,
+		      struct sctp_tcb *stcb,
+		      struct sctp_nets *net)
+{
+	/*
+	 * pull out the cookie and put it at the front of the control
+	 * chunk queue.
+	 */
+	int at;
+	struct mbuf *cookie, *mat;
+	struct sctp_paramhdr parm, *phdr;
+	struct sctp_chunkhdr *hdr;
+	struct sctp_tmit_chunk *chk;
+	uint16_t ptype, plen;
+	/* First find the cookie in the param area */
+	cookie = NULL;
+	at = offset + sizeof(struct sctp_init_chunk);
+
+	do {
+		phdr = sctp_get_next_param(m, at, &parm, sizeof(parm));
+		if (phdr == NULL) {
+			return (-3);
+		}
+		ptype = ntohs(phdr->param_type);
+		plen = ntohs(phdr->param_length);
+		if (ptype == SCTP_STATE_COOKIE) {
+			int pad;
+			/* found the cookie */
+			if ((pad = (plen % 4))) {
+				plen += 4 - pad;
+			}
+			cookie = sctp_m_copym(m, at, plen, MB_DONTWAIT);
+			if (cookie == NULL) {
+				/* No memory */
+				return (-2);
+			}
+			break;
+		}
+		at += SCTP_SIZE32(plen);
+	} while (phdr);
+	if (cookie == NULL) {
+		/* Did not find the cookie */
+		return (-3);
+	}
+	/* ok, we got the cookie lets change it into a cookie echo chunk */
+
+	/* first the change from param to cookie */
+	hdr = mtod(cookie, struct sctp_chunkhdr *);
+	hdr->chunk_type = SCTP_COOKIE_ECHO;
+	hdr->chunk_flags = 0;
+	/* now we MUST have a PKTHDR on it */
+	if ((cookie->m_flags & M_PKTHDR) != M_PKTHDR) {
+		/* we hope this happens rarely */
+		MGETHDR(mat, MB_DONTWAIT, MT_HEADER);
+		if (mat == NULL) {
+			sctp_m_freem(cookie);
+			return (-4);
+		}
+		mat->m_len = 0;
+		mat->m_pkthdr.rcvif = 0;
+		mat->m_next = cookie;
+		cookie = mat;
+	}
+	cookie->m_pkthdr.len = plen;
+	/* get the chunk stuff now and place it in the FRONT of the queue */
+	chk = (struct sctp_tmit_chunk *)SCTP_ZONE_GET(sctppcbinfo.ipi_zone_chunk);
+	if (chk == NULL) {
+		/* no memory */
+		sctp_m_freem(cookie);
+		return (-5);
+	}
+	sctppcbinfo.ipi_count_chunk++;
+	sctppcbinfo.ipi_gencnt_chunk++;
+	chk->send_size = cookie->m_pkthdr.len;
+	chk->rec.chunk_id = SCTP_COOKIE_ECHO;
+	chk->sent = SCTP_DATAGRAM_UNSENT;
+	chk->snd_count = 0;
+	chk->flags = 0;
+	chk->asoc = &stcb->asoc;
+	chk->data = cookie;
+	chk->whoTo = chk->asoc->primary_destination;
+	chk->whoTo->ref_count++;
+	TAILQ_INSERT_HEAD(&chk->asoc->control_send_queue, chk, sctp_next);
+	chk->asoc->ctrl_queue_cnt++;
+	return (0);
+}
+
+void
+sctp_send_heartbeat_ack(struct sctp_tcb *stcb,
+			struct mbuf *m,
+			int offset,
+			int chk_length,
+			struct sctp_nets *net)
+{
+	/* take a HB request and make it into a
+	 * HB ack and send it.
+	 */
+	struct mbuf *outchain;
+	struct sctp_chunkhdr *chdr;
+	struct sctp_tmit_chunk *chk;
+
+
+	if (net == NULL)
+		/* must have a net pointer */
+		return;
+
+	outchain = sctp_m_copym(m, offset, chk_length, MB_DONTWAIT);
+	if (outchain == NULL) {
+		/* gak out of memory */
+		return;
+	}
+	chdr = mtod(outchain, struct sctp_chunkhdr *);
+	chdr->chunk_type = SCTP_HEARTBEAT_ACK;
+	chdr->chunk_flags = 0;
+	if ((outchain->m_flags & M_PKTHDR) != M_PKTHDR) {
+		/* should not happen but we are cautious. */
+		struct mbuf *tmp;
+		MGETHDR(tmp, MB_DONTWAIT, MT_HEADER);
+		if (tmp == NULL) {
+			return;
+		}
+		tmp->m_len = 0;
+		tmp->m_pkthdr.rcvif = 0;
+		tmp->m_next = outchain;
+		outchain = tmp;
+	}
+	outchain->m_pkthdr.len = chk_length;
+	if (chk_length % 4) {
+		/* need pad */
+		u_int32_t cpthis=0;
+		int padlen;
+		padlen = 4 - (outchain->m_pkthdr.len % 4);
+		m_copyback(outchain, outchain->m_pkthdr.len, padlen, (caddr_t)&cpthis);
+	}
+	chk = (struct sctp_tmit_chunk *)SCTP_ZONE_GET(sctppcbinfo.ipi_zone_chunk);
+	if (chk == NULL) {
+		/* no memory */
+		sctp_m_freem(outchain);
+		return ;
+	}
+	sctppcbinfo.ipi_count_chunk++;
+	sctppcbinfo.ipi_gencnt_chunk++;
+
+	chk->send_size = chk_length;
+	chk->rec.chunk_id = SCTP_HEARTBEAT_ACK;
+	chk->sent = SCTP_DATAGRAM_UNSENT;
+	chk->snd_count = 0;
+	chk->flags = 0;
+	chk->asoc = &stcb->asoc;
+	chk->data = outchain;
+	chk->whoTo = net;
+	chk->whoTo->ref_count++;
+	TAILQ_INSERT_TAIL(&chk->asoc->control_send_queue, chk, sctp_next);
+	chk->asoc->ctrl_queue_cnt++;
+}
+
+int
+sctp_send_cookie_ack(struct sctp_tcb *stcb) {
+	/* formulate and queue a cookie-ack back to sender */
+	struct mbuf *cookie_ack;
+	struct sctp_chunkhdr *hdr;
+	struct sctp_tmit_chunk *chk;
+
+	cookie_ack = NULL;
+	MGETHDR(cookie_ack, MB_DONTWAIT, MT_HEADER);
+	if (cookie_ack == NULL) {
+		/* no mbuf's */
+		return (-1);
+	}
+ 	cookie_ack->m_data += SCTP_MIN_OVERHEAD;
+	chk = (struct sctp_tmit_chunk *)SCTP_ZONE_GET(sctppcbinfo.ipi_zone_chunk);
+	if (chk == NULL) {
+		/* no memory */
+		sctp_m_freem(cookie_ack);
+		return (-1);
+	}
+	sctppcbinfo.ipi_count_chunk++;
+	sctppcbinfo.ipi_gencnt_chunk++;
+
+	chk->send_size = sizeof(struct sctp_chunkhdr);
+	chk->rec.chunk_id = SCTP_COOKIE_ACK;
+	chk->sent = SCTP_DATAGRAM_UNSENT;
+	chk->snd_count = 0;
+	chk->flags = 0;
+	chk->asoc = &stcb->asoc;
+	chk->data = cookie_ack;
+	if (chk->asoc->last_control_chunk_from != NULL) {
+		chk->whoTo = chk->asoc->last_control_chunk_from;
+	} else {
+		chk->whoTo = chk->asoc->primary_destination;
+	}
+	chk->whoTo->ref_count++;
+	hdr = mtod(cookie_ack, struct sctp_chunkhdr *);
+	hdr->chunk_type = SCTP_COOKIE_ACK;
+	hdr->chunk_flags = 0;
+	hdr->chunk_length = htons(chk->send_size);
+	cookie_ack->m_pkthdr.len = cookie_ack->m_len = chk->send_size;
+	cookie_ack->m_pkthdr.rcvif = 0;
+	TAILQ_INSERT_TAIL(&chk->asoc->control_send_queue, chk, sctp_next);
+	chk->asoc->ctrl_queue_cnt++;
+	return (0);
+}
+
+
+int
+sctp_send_shutdown_ack(struct sctp_tcb *stcb, struct sctp_nets *net)
+{
+	/* formulate and queue a SHUTDOWN-ACK back to the sender */
+	struct mbuf *m_shutdown_ack;
+	struct sctp_shutdown_ack_chunk *ack_cp;
+	struct sctp_tmit_chunk *chk;
+
+	m_shutdown_ack = NULL;
+	MGETHDR(m_shutdown_ack, MB_DONTWAIT, MT_HEADER);
+	if (m_shutdown_ack == NULL) {
+		/* no mbuf's */
+		return (-1);
+	}
+	m_shutdown_ack->m_data += SCTP_MIN_OVERHEAD;
+	chk = (struct sctp_tmit_chunk *)SCTP_ZONE_GET(sctppcbinfo.ipi_zone_chunk);
+	if (chk == NULL) {
+		/* no memory */
+		sctp_m_freem(m_shutdown_ack);
+		return (-1);
+	}
+	sctppcbinfo.ipi_count_chunk++;
+	sctppcbinfo.ipi_gencnt_chunk++;
+
+	chk->send_size = sizeof(struct sctp_chunkhdr);
+	chk->rec.chunk_id = SCTP_SHUTDOWN_ACK;
+	chk->sent = SCTP_DATAGRAM_UNSENT;
+	chk->snd_count = 0;
+	chk->flags = 0;
+	chk->asoc = &stcb->asoc;
+	chk->data = m_shutdown_ack;
+	chk->whoTo = net;
+	net->ref_count++;
+
+	ack_cp = mtod(m_shutdown_ack, struct sctp_shutdown_ack_chunk *);
+	ack_cp->ch.chunk_type = SCTP_SHUTDOWN_ACK;
+	ack_cp->ch.chunk_flags = 0;
+	ack_cp->ch.chunk_length = htons(chk->send_size);
+	m_shutdown_ack->m_pkthdr.len = m_shutdown_ack->m_len = chk->send_size;
+	m_shutdown_ack->m_pkthdr.rcvif = 0;
+	TAILQ_INSERT_TAIL(&chk->asoc->control_send_queue, chk, sctp_next);
+	chk->asoc->ctrl_queue_cnt++;
+	return (0);
+}
+
+int
+sctp_send_shutdown(struct sctp_tcb *stcb, struct sctp_nets *net)
+{
+	/* formulate and queue a SHUTDOWN to the sender */
+	struct mbuf *m_shutdown;
+	struct sctp_shutdown_chunk *shutdown_cp;
+	struct sctp_tmit_chunk *chk;
+
+	m_shutdown = NULL;
+	MGETHDR(m_shutdown, MB_DONTWAIT, MT_HEADER);
+	if (m_shutdown == NULL) {
+		/* no mbuf's */
+		return (-1);
+	}
+	m_shutdown->m_data += SCTP_MIN_OVERHEAD;
+	chk = (struct sctp_tmit_chunk *)SCTP_ZONE_GET(sctppcbinfo.ipi_zone_chunk);
+	if (chk == NULL) {
+		/* no memory */
+		sctp_m_freem(m_shutdown);
+		return (-1);
+	}
+	sctppcbinfo.ipi_count_chunk++;
+	sctppcbinfo.ipi_gencnt_chunk++;
+
+	chk->send_size = sizeof(struct sctp_shutdown_chunk);
+	chk->rec.chunk_id = SCTP_SHUTDOWN;
+	chk->sent = SCTP_DATAGRAM_UNSENT;
+	chk->snd_count = 0;
+	chk->flags = 0;
+	chk->asoc = &stcb->asoc;
+	chk->data = m_shutdown;
+	chk->whoTo = net;
+	net->ref_count++;
+
+	shutdown_cp = mtod(m_shutdown, struct sctp_shutdown_chunk *);
+	shutdown_cp->ch.chunk_type = SCTP_SHUTDOWN;
+	shutdown_cp->ch.chunk_flags = 0;
+	shutdown_cp->ch.chunk_length = htons(chk->send_size);
+	shutdown_cp->cumulative_tsn_ack = htonl(stcb->asoc.cumulative_tsn);
+	m_shutdown->m_pkthdr.len = m_shutdown->m_len = chk->send_size;
+	m_shutdown->m_pkthdr.rcvif = 0;
+	TAILQ_INSERT_TAIL(&chk->asoc->control_send_queue, chk, sctp_next);
+	chk->asoc->ctrl_queue_cnt++;
+
+	if ((stcb->sctp_ep->sctp_flags & SCTP_PCB_FLAGS_TCPTYPE) ||
+	    (stcb->sctp_ep->sctp_flags & SCTP_PCB_FLAGS_IN_TCPPOOL)) {
+		stcb->sctp_ep->sctp_socket->so_snd.sb_cc = 0;
+		soisdisconnecting(stcb->sctp_ep->sctp_socket);
+	}
+	return (0);
+}
+
+int
+sctp_send_asconf(struct sctp_tcb *stcb, struct sctp_nets *net)
+{
+	/*
+	 * formulate and queue an ASCONF to the peer
+	 * ASCONF parameters should be queued on the assoc queue
+	 */
+	struct sctp_tmit_chunk *chk;
+	struct mbuf *m_asconf;
+	struct sctp_asconf_chunk *acp;
+
+
+	/* compose an ASCONF chunk, maximum length is PMTU */
+	m_asconf = sctp_compose_asconf(stcb);
+	if (m_asconf == NULL) {
+		return (-1);
+	}
+	acp = mtod(m_asconf, struct sctp_asconf_chunk *);
+	chk = (struct sctp_tmit_chunk *)SCTP_ZONE_GET(sctppcbinfo.ipi_zone_chunk);
+	if (chk == NULL) {
+		/* no memory */
+		sctp_m_freem(m_asconf);
+		return (-1);
+	}
+	sctppcbinfo.ipi_count_chunk++;
+	sctppcbinfo.ipi_gencnt_chunk++;
+
+	chk->data = m_asconf;
+	chk->send_size = m_asconf->m_pkthdr.len;
+	chk->rec.chunk_id = SCTP_ASCONF;
+	chk->sent = SCTP_DATAGRAM_UNSENT;
+	chk->snd_count = 0;
+	chk->flags = 0;
+	chk->asoc = &stcb->asoc;
+	chk->whoTo = chk->asoc->primary_destination;
+	chk->whoTo->ref_count++;
+	TAILQ_INSERT_TAIL(&chk->asoc->control_send_queue, chk, sctp_next);
+	chk->asoc->ctrl_queue_cnt++;
+	return (0);
+}
+
+int
+sctp_send_asconf_ack(struct sctp_tcb *stcb, uint32_t retrans)
+{
+	/*
+	 * formulate and queue a asconf-ack back to sender
+	 * the asconf-ack must be stored in the tcb
+	 */
+	struct sctp_tmit_chunk *chk;
+	struct mbuf *m_ack;
+
+	/* is there a asconf-ack mbuf chain to send? */
+	if (stcb->asoc.last_asconf_ack_sent == NULL) {
+		return (-1);
+	}
+
+	/* copy the asconf_ack */
+#if defined(__FreeBSD__) || defined(__NetBSD__)
+	/* Supposedly the m_copypacket is a optimzation,
+	 * use it if we can.
+	 */
+	if (stcb->asoc.last_asconf_ack_sent->m_flags & M_PKTHDR) {
+		m_ack = m_copypacket(stcb->asoc.last_asconf_ack_sent, MB_DONTWAIT);
+		sctp_pegs[SCTP_CACHED_SRC]++;
+	} else
+		m_ack = m_copy(stcb->asoc.last_asconf_ack_sent, 0, M_COPYALL);
+#else
+		m_ack = m_copy(stcb->asoc.last_asconf_ack_sent, 0, M_COPYALL);
+#endif
+	if (m_ack == NULL) {
+		/* couldn't copy it */
+
+		return (-1);
+	}
+	chk = (struct sctp_tmit_chunk *)SCTP_ZONE_GET(sctppcbinfo.ipi_zone_chunk);
+	if (chk == NULL) {
+		/* no memory */
+		if (m_ack)
+			sctp_m_freem(m_ack);
+		return (-1);
+	}
+	sctppcbinfo.ipi_count_chunk++;
+	sctppcbinfo.ipi_gencnt_chunk++;
+
+	/* figure out where it goes to */
+	if (retrans) {
+		/* we're doing a retransmission */
+		if (stcb->asoc.used_alt_asconfack > 2) {
+			/* tried alternate nets already, go back */
+			chk->whoTo = NULL;
+		} else {
+			/* need to try and alternate net */
+			chk->whoTo = sctp_find_alternate_net(stcb, stcb->asoc.last_control_chunk_from);
+			stcb->asoc.used_alt_asconfack++;
+		}
+		if (chk->whoTo == NULL) {
+			/* no alternate */
+			if (stcb->asoc.last_control_chunk_from == NULL)
+				chk->whoTo = stcb->asoc.primary_destination;
+			else
+				chk->whoTo = stcb->asoc.last_control_chunk_from;
+			stcb->asoc.used_alt_asconfack = 0;
+		}
+	} else {
+		/* normal case */
+		if (stcb->asoc.last_control_chunk_from == NULL)
+			chk->whoTo = stcb->asoc.primary_destination;
+		else
+			chk->whoTo = stcb->asoc.last_control_chunk_from;
+		stcb->asoc.used_alt_asconfack = 0;
+	}
+	chk->data = m_ack;
+	chk->send_size = m_ack->m_pkthdr.len;
+	chk->rec.chunk_id = SCTP_ASCONF_ACK;
+	chk->sent = SCTP_DATAGRAM_UNSENT;
+	chk->snd_count = 0;
+	chk->flags = 0;
+	chk->asoc = &stcb->asoc;
+	chk->whoTo->ref_count++;
+	TAILQ_INSERT_TAIL(&chk->asoc->control_send_queue, chk, sctp_next);
+	chk->asoc->ctrl_queue_cnt++;
+	return (0);
+}
+
+
+static int
+sctp_chunk_retransmission(struct sctp_inpcb *inp,
+			  struct sctp_tcb *stcb,
+			  struct sctp_association *asoc,
+			  int *cnt_out, struct timeval *now, int *now_filled)
+{
+	/*
+	 * send out one MTU of retransmission.
+	 * If fast_retransmit is happening we ignore the cwnd.
+	 * Otherwise we obey the cwnd and rwnd.
+	 * For a Cookie or Asconf in the control chunk queue we retransmit
+	 * them by themselves.
+	 *
+	 * For data chunks we will pick out the lowest TSN's in the
+	 * sent_queue marked for resend and bundle them all together
+	 * (up to a MTU of destination). The address to send to should
+	 * have been selected/changed where the retransmission was
+	 * marked (i.e. in FR or t3-timeout routines).
+	 */
+	struct sctp_tmit_chunk *data_list[SCTP_MAX_DATA_BUNDLING];
+	struct sctp_tmit_chunk *chk, *fwd;
+	struct mbuf *m;
+	struct sctphdr *shdr;
+	int asconf;
+	struct sctp_nets *net;
+	int no_fragmentflg, bundle_at, cnt_thru;
+	unsigned int mtu;
+	int error, i, one_chunk, fwd_tsn, ctl_cnt, tmr_started;
+
+	tmr_started = ctl_cnt = bundle_at =  error = 0;
+	no_fragmentflg = 1;
+	asconf = 0;
+	fwd_tsn = 0;
+	*cnt_out = 0;
+	fwd = NULL;
+	m = NULL;
+#ifdef SCTP_AUDITING_ENABLED
+	sctp_audit_log(0xC3, 1);
+#endif
+	if (TAILQ_EMPTY(&asoc->sent_queue)) {
+#ifdef SCTP_DEBUG
+		if (sctp_debug_on & SCTP_DEBUG_OUTPUT1) {
+			printf("SCTP hits empty queue with cnt set to %d?\n",
+			       asoc->sent_queue_retran_cnt);
+		}
+#endif
+		asoc->sent_queue_cnt = 0;
+		asoc->sent_queue_cnt_removeable = 0;
+	}
+	TAILQ_FOREACH(chk, &asoc->control_send_queue, sctp_next) {
+		if (chk->sent != SCTP_DATAGRAM_RESEND) {
+			/* we only worry about things marked for resend */
+			continue;
+		}
+		if ((chk->rec.chunk_id == SCTP_COOKIE_ECHO) ||
+		    (chk->rec.chunk_id == SCTP_ASCONF) ||
+		    (chk->rec.chunk_id == SCTP_STREAM_RESET) ||
+		    (chk->rec.chunk_id == SCTP_FORWARD_CUM_TSN)) {
+			if (chk->rec.chunk_id == SCTP_STREAM_RESET) {
+				/* For stream reset we only retran the request
+				 * not the response.
+				 */
+				struct sctp_stream_reset_req *strreq;
+				strreq = mtod(chk->data, struct sctp_stream_reset_req *);
+				if (strreq->sr_req.ph.param_type != ntohs(SCTP_STR_RESET_REQUEST)) {
+					continue;
+				}
+			}
+			ctl_cnt++;
+			if (chk->rec.chunk_id == SCTP_ASCONF) {
+				no_fragmentflg = 1;
+				asconf = 1;
+			}
+			if (chk->rec.chunk_id == SCTP_FORWARD_CUM_TSN) {
+				fwd_tsn = 1;
+				fwd = chk;
+			}
+			m = sctp_copy_mbufchain(chk->data, m);
+			break;
+		}
+	}
+	one_chunk = 0;
+	cnt_thru = 0;
+	/* do we have control chunks to retransmit? */
+	if (m != NULL) {
+		/* Start a timer no matter if we suceed or fail */
+		if (chk->rec.chunk_id == SCTP_COOKIE_ECHO) {
+			sctp_timer_start(SCTP_TIMER_TYPE_COOKIE, inp, stcb, chk->whoTo);
+		} else if (chk->rec.chunk_id == SCTP_ASCONF)
+			sctp_timer_start(SCTP_TIMER_TYPE_ASCONF, inp, stcb, chk->whoTo);
+
+		if (m->m_len == 0) {
+			/* Special case for when you get a 0 len
+			 * mbuf at the head due to the lack
+			 * of a MHDR at the beginning.
+			 */
+			m->m_len = sizeof(struct sctphdr);
+		} else {
+			M_PREPEND(m, sizeof(struct sctphdr), MB_DONTWAIT);
+			if (m == NULL) {
+				return (ENOBUFS);
+			}
+		}
+		shdr = mtod(m, struct sctphdr *);
+		shdr->src_port = inp->sctp_lport;
+		shdr->dest_port = stcb->rport;
+		shdr->v_tag = htonl(stcb->asoc.peer_vtag);
+		shdr->checksum = 0;
+		chk->snd_count++;		/* update our count */
+
+		if ((error = sctp_lowlevel_chunk_output(inp, stcb, chk->whoTo,
+		    (struct sockaddr *)&chk->whoTo->ro._l_addr, m,
+		    no_fragmentflg, 0, NULL, asconf))) {
+			sctp_pegs[SCTP_DATA_OUT_ERR]++;
+			return (error);
+		}
+		/*
+		 *We don't want to mark the net->sent time here since this
+		 * we use this for HB and retrans cannot measure RTT
+		 */
+		/*    SCTP_GETTIME_TIMEVAL(&chk->whoTo->last_sent_time);*/
+		*cnt_out += 1;
+		chk->sent = SCTP_DATAGRAM_SENT;
+		asoc->sent_queue_retran_cnt--;
+		if (asoc->sent_queue_retran_cnt < 0) {
+		    asoc->sent_queue_retran_cnt = 0;
+		}
+		if (fwd_tsn == 0) {
+			return (0);
+		} else {
+			/* Clean up the fwd-tsn list */
+			sctp_clean_up_ctl (asoc);
+			return (0);
+		}
+	}
+	/* Ok, it is just data retransmission we need to do or
+	 * that and a fwd-tsn with it all.
+	 */
+	if (TAILQ_EMPTY(&asoc->sent_queue)) {
+		return (-1);
+	}
+#ifdef SCTP_DEBUG
+	if (sctp_debug_on & SCTP_DEBUG_OUTPUT1) {
+		printf("Normal chunk retransmission cnt:%d\n",
+		       asoc->sent_queue_retran_cnt);
+	}
+#endif
+	if ((SCTP_GET_STATE(asoc) == SCTP_STATE_COOKIE_ECHOED) ||
+	    (SCTP_GET_STATE(asoc) == SCTP_STATE_COOKIE_WAIT)) {
+		/* not yet open, resend the cookie and that is it */
+		return (1);
+	}
+
+
+#ifdef SCTP_AUDITING_ENABLED
+	sctp_auditing(20, inp, stcb, NULL);
+#endif
+	TAILQ_FOREACH(chk, &asoc->sent_queue, sctp_next) {
+		if (chk->sent != SCTP_DATAGRAM_RESEND) {
+			/* No, not sent to this net or not ready for rtx */
+			continue;
+
+		}
+		/* pick up the net */
+		net = chk->whoTo;
+		if (stcb->sctp_ep->sctp_flags & SCTP_PCB_FLAGS_BOUND_V6) {
+			mtu = (net->mtu - SCTP_MIN_OVERHEAD);
+		} else {
+			mtu = net->mtu- SCTP_MIN_V4_OVERHEAD;
+		}
+
+		if ((asoc->peers_rwnd < mtu) && (asoc->total_flight > 0)) {
+			/* No room in peers rwnd */
+			uint32_t tsn;
+			tsn = asoc->last_acked_seq + 1;
+			if (tsn == chk->rec.data.TSN_seq) {
+				/* we make a special exception for this case.
+				 * The peer has no rwnd but is missing the
+				 * lowest chunk.. which is probably what is
+				 * holding up the rwnd.
+				 */
+				goto one_chunk_around;
+			}
+#ifdef SCTP_DEBUG
+			if (sctp_debug_on & SCTP_DEBUG_OUTPUT1) {
+				printf("blocked-peers_rwnd:%d tf:%d\n",
+				       (int)asoc->peers_rwnd,
+				       (int)asoc->total_flight);
+			}
+#endif
+			sctp_pegs[SCTP_RWND_BLOCKED]++;
+			return (1);
+		}
+	one_chunk_around:
+		if (asoc->peers_rwnd < mtu) {
+			one_chunk = 1;
+		}
+#ifdef SCTP_AUDITING_ENABLED
+		sctp_audit_log(0xC3, 2);
+#endif
+		bundle_at = 0;
+		m = NULL;
+		net->fast_retran_ip = 0;
+		if (chk->rec.data.doing_fast_retransmit == 0) {
+			/* if no FR in progress skip destination that
+			 * have flight_size > cwnd.
+			 */
+			if (net->flight_size >= net->cwnd) {
+				sctp_pegs[SCTP_CWND_BLOCKED]++;
+				continue;
+			}
+		} else {
+			/* Mark the destination net to have FR recovery
+			 * limits put on it.
+			 */
+			net->fast_retran_ip = 1;
+		}
+
+		if ((chk->send_size <= mtu) || (chk->flags & CHUNK_FLAGS_FRAGMENT_OK)) {
+			/* ok we will add this one */
+			m = sctp_copy_mbufchain(chk->data, m);
+			if (m == NULL) {
+				return (ENOMEM);
+			}
+			/* upate our MTU size */
+			/* Do clear IP_DF ? */
+			if (chk->flags & CHUNK_FLAGS_FRAGMENT_OK) {
+				no_fragmentflg = 0;
+			}
+			mtu -= chk->send_size;
+			data_list[bundle_at++] = chk;
+			if (one_chunk && (asoc->total_flight <= 0)) {
+				sctp_pegs[SCTP_WINDOW_PROBES]++;
+				chk->rec.data.state_flags |= SCTP_WINDOW_PROBE;
+			}
+		}
+		if (one_chunk == 0) {
+			/* now are there anymore forward from chk to pick up?*/
+			fwd = TAILQ_NEXT(chk, sctp_next);
+			while (fwd) {
+				if (fwd->sent != SCTP_DATAGRAM_RESEND) {
+					/* Nope, not for retran */
+					fwd = TAILQ_NEXT(fwd, sctp_next);
+					continue;
+				}
+				if (fwd->whoTo != net) {
+					/* Nope, not the net in question */
+					fwd = TAILQ_NEXT(fwd, sctp_next);
+					continue;
+				}
+				if (fwd->send_size <= mtu) {
+					m = sctp_copy_mbufchain(fwd->data, m);
+					if (m == NULL) {
+						return (ENOMEM);
+					}
+					/* upate our MTU size */
+					/* Do clear IP_DF ? */
+					if (fwd->flags & CHUNK_FLAGS_FRAGMENT_OK) {
+						no_fragmentflg = 0;
+					}
+					mtu -= fwd->send_size;
+					data_list[bundle_at++] = fwd;
+					if (bundle_at >= SCTP_MAX_DATA_BUNDLING) {
+						break;
+					}
+					fwd = TAILQ_NEXT(fwd, sctp_next);
+				} else {
+					/* can't fit so we are done */
+					break;
+				}
+			}
+		}
+		/* Is there something to send for this destination? */
+		if (m) {
+			/* No matter if we fail/or suceed we should
+			 * start a timer. A failure is like a lost
+			 * IP packet :-)
+			 */
+			if (!callout_pending(&net->rxt_timer.timer)) {
+				/* no timer running on this destination
+				 * restart it.
+				 */
+				sctp_timer_start(SCTP_TIMER_TYPE_SEND, inp, stcb, net);
+				tmr_started = 1;
+			}
+			if (m->m_len == 0) {
+				/* Special case for when you get a 0 len
+				 * mbuf at the head due to the lack
+				 * of a MHDR at the beginning.
+				 */
+				m->m_len = sizeof(struct sctphdr);
+			} else {
+				M_PREPEND(m, sizeof(struct sctphdr), MB_DONTWAIT);
+				if (m == NULL) {
+					return (ENOBUFS);
+				}
+			}
+			shdr = mtod(m, struct sctphdr *);
+			shdr->src_port = inp->sctp_lport;
+			shdr->dest_port = stcb->rport;
+			shdr->v_tag = htonl(stcb->asoc.peer_vtag);
+			shdr->checksum = 0;
+
+			/* Now lets send it, if there is anything to send :> */
+			if ((error = sctp_lowlevel_chunk_output(inp, stcb, net,
+							       (struct sockaddr *)&net->ro._l_addr,
+							       m,
+							       no_fragmentflg, 0, NULL, asconf))) {
+				/* error, we could not output */
+				sctp_pegs[SCTP_DATA_OUT_ERR]++;
+				return (error);
+			}
+			/* For HB's */
+			/*
+			 * We don't want to mark the net->sent time here since
+			 * this we use this for HB and retrans cannot measure
+			 * RTT
+			 */
+			/*      SCTP_GETTIME_TIMEVAL(&net->last_sent_time);*/
+
+			/* For auto-close */
+			cnt_thru++;
+			if (*now_filled == 0) {
+				SCTP_GETTIME_TIMEVAL(&asoc->time_last_sent);
+				*now = asoc->time_last_sent;
+				*now_filled = 1;
+			} else {
+				asoc->time_last_sent = *now;
+			}
+			*cnt_out += bundle_at;
+#ifdef SCTP_AUDITING_ENABLED
+			sctp_audit_log(0xC4, bundle_at);
+#endif
+			for (i = 0; i < bundle_at; i++) {
+				sctp_pegs[SCTP_RETRANTSN_SENT]++;
+				data_list[i]->sent = SCTP_DATAGRAM_SENT;
+				data_list[i]->snd_count++;
+				asoc->sent_queue_retran_cnt--;
+				/* record the time */
+				data_list[i]->sent_rcv_time = asoc->time_last_sent;
+				if (asoc->sent_queue_retran_cnt < 0) {
+				    asoc->sent_queue_retran_cnt = 0;
+				}
+				net->flight_size += data_list[i]->book_size;
+				asoc->total_flight += data_list[i]->book_size;
+				asoc->total_flight_count++;
+
+#ifdef SCTP_LOG_RWND
+				sctp_log_rwnd(SCTP_DECREASE_PEER_RWND,
+					      asoc->peers_rwnd , data_list[i]->send_size, sctp_peer_chunk_oh);
+#endif
+				asoc->peers_rwnd = sctp_sbspace_sub(asoc->peers_rwnd,
+								    (u_int32_t)(data_list[i]->send_size + sctp_peer_chunk_oh));
+				if (asoc->peers_rwnd < stcb->sctp_ep->sctp_ep.sctp_sws_sender) {
+					/* SWS sender side engages */
+					asoc->peers_rwnd = 0;
+				}
+
+				if ((i == 0) &&
+				    (data_list[i]->rec.data.doing_fast_retransmit)) {
+					sctp_pegs[SCTP_FAST_RETRAN]++;
+					if ((data_list[i] == TAILQ_FIRST(&asoc->sent_queue)) &&
+					    (tmr_started == 0)) {
+						/*
+						 * ok we just fast-retrans'd
+						 * the lowest TSN, i.e the
+						 * first on the list. In this
+						 * case we want to give some
+						 * more time to get a SACK
+						 * back without a t3-expiring.
+						 */
+						sctp_timer_stop(SCTP_TIMER_TYPE_SEND, inp, stcb, net);
+						sctp_timer_start(SCTP_TIMER_TYPE_SEND, inp, stcb, net);
+					}
+				}
+			}
+#ifdef SCTP_AUDITING_ENABLED
+			sctp_auditing(21, inp, stcb, NULL);
+#endif
+		} else {
+			/* None will fit */
+			return (1);
+		}
+		if (asoc->sent_queue_retran_cnt <= 0) {
+			/* all done we have no more to retran */
+			asoc->sent_queue_retran_cnt = 0;
+			break;
+		}
+		if (one_chunk) {
+			/* No more room in rwnd */
+			return (1);
+		}
+		/* stop the for loop here. we sent out a packet */
+		break;
+	}
+	return (0);
+}
+
+
+static int
+sctp_timer_validation(struct sctp_inpcb *inp,
+		      struct sctp_tcb *stcb,
+		      struct sctp_association *asoc,
+		      int ret)
+{
+	struct sctp_nets *net;
+	/* Validate that a timer is running somewhere */
+	TAILQ_FOREACH(net, &asoc->nets, sctp_next) {
+		if (callout_pending(&net->rxt_timer.timer)) {
+			/* Here is a timer */
+			return (ret);
+		}
+	}
+	/* Gak, we did not have a timer somewhere */
+#ifdef SCTP_DEBUG
+	if (sctp_debug_on & SCTP_DEBUG_OUTPUT3) {
+		printf("Deadlock avoided starting timer on a dest at retran\n");
+	}
+#endif
+	sctp_timer_start(SCTP_TIMER_TYPE_SEND, inp, stcb, asoc->primary_destination);
+	return (ret);
+}
+
+int
+sctp_chunk_output(struct sctp_inpcb *inp,
+		  struct sctp_tcb *stcb,
+		  int from_where)
+{
+	/* Ok this is the generic chunk service queue.
+	 * we must do the following:
+	 *  - See if there are retransmits pending, if so we
+	 *   	must do these first and return.
+	 *  - Service the stream queue that is next,
+	 *    moving any message (note I must get a complete
+	 *    message i.e. FIRST/MIDDLE and LAST to the out
+	 *    queue in one pass) and assigning TSN's
+	 *  - Check to see if the cwnd/rwnd allows any output, if
+	 *	so we go ahead and fomulate and send the low level
+	 *    chunks. Making sure to combine any control in the
+	 *    control chunk queue also.
+	 */
+	struct sctp_association *asoc;
+	struct sctp_nets *net;
+	int error, num_out, tot_out, ret, reason_code, burst_cnt, burst_limit;
+	struct timeval now;
+	int now_filled=0;
+	int cwnd_full=0;
+	asoc = &stcb->asoc;
+	tot_out = 0;
+	num_out = 0;
+	reason_code = 0;
+	sctp_pegs[SCTP_CALLS_TO_CO]++;
+#ifdef SCTP_DEBUG
+	if (sctp_debug_on & SCTP_DEBUG_OUTPUT3) {
+		printf("in co - retran count:%d\n", asoc->sent_queue_retran_cnt);
+	}
+#endif
+	while (asoc->sent_queue_retran_cnt) {
+		/* Ok, it is retransmission time only, we send out only ONE
+		 * packet with a single call off to the retran code.
+		 */
+		ret = sctp_chunk_retransmission(inp, stcb, asoc, &num_out, &now, &now_filled);
+		if (ret > 0) {
+			/* Can't send anymore */
+#ifdef SCTP_DEBUG
+			if (sctp_debug_on & SCTP_DEBUG_OUTPUT1) {
+				printf("retransmission ret:%d -- full\n", ret);
+			}
+#endif
+			/*
+			 * now lets push out control by calling med-level
+			 * output once. this assures that we WILL send HB's
+			 * if queued too.
+			 */
+			(void)sctp_med_chunk_output(inp, stcb, asoc, &num_out, &reason_code, 1,
+						    &cwnd_full, from_where,
+						    &now, &now_filled);
+#ifdef SCTP_DEBUG
+			if (sctp_debug_on & SCTP_DEBUG_OUTPUT1) {
+				printf("Control send outputs:%d@full\n", num_out);
+			}
+#endif
+#ifdef SCTP_AUDITING_ENABLED
+			sctp_auditing(8, inp, stcb, NULL);
+#endif
+			return (sctp_timer_validation(inp, stcb, asoc, ret));
+		}
+		if (ret < 0) {
+			/*
+			 * The count was off.. retran is not happening so do
+			 * the normal retransmission.
+			 */
+#ifdef SCTP_DEBUG
+			if (sctp_debug_on & SCTP_DEBUG_OUTPUT1) {
+				printf("Done with retrans, none left fill up window\n");
+			}
+#endif
+#ifdef SCTP_AUDITING_ENABLED
+			sctp_auditing(9, inp, stcb, NULL);
+#endif
+			break;
+		}
+		if (from_where == 1) {
+			/* Only one transmission allowed out of a timeout */
+#ifdef SCTP_DEBUG
+			if (sctp_debug_on & SCTP_DEBUG_OUTPUT1) {
+				printf("Only one packet allowed out\n");
+			}
+#endif
+#ifdef SCTP_AUDITING_ENABLED
+			sctp_auditing(10, inp, stcb, NULL);
+#endif
+			/* Push out any control */
+			(void)sctp_med_chunk_output(inp, stcb, asoc, &num_out, &reason_code, 1, &cwnd_full, from_where,
+						    &now, &now_filled);
+			return (ret);
+		}
+		if ((num_out == 0) && (ret == 0)) {
+			/* No more retrans to send */
+			break;
+		}
+	}
+#ifdef SCTP_AUDITING_ENABLED
+	sctp_auditing(12, inp, stcb, NULL);
+#endif
+	/* Check for bad destinations, if they exist move chunks around. */
+	burst_limit = asoc->max_burst;
+	TAILQ_FOREACH(net, &asoc->nets, sctp_next) {
+		if ((net->dest_state & SCTP_ADDR_NOT_REACHABLE) ==
+		    SCTP_ADDR_NOT_REACHABLE) {
+			/*
+			 * if possible move things off of this address
+			 * we still may send below due to the dormant state
+			 * but we try to find an alternate address to send
+			 * to and if we have one we move all queued data on
+			 * the out wheel to this alternate address.
+			 */
+			sctp_move_to_an_alt(stcb, asoc, net);
+		} else {
+			/*
+			if ((asoc->sat_network) || (net->addr_is_local)) {
+				burst_limit = asoc->max_burst * SCTP_SAT_NETWORK_BURST_INCR;
+			}
+			*/
+#ifdef SCTP_DEBUG
+			if (sctp_debug_on & SCTP_DEBUG_OUTPUT3) {
+				printf("examined net:%p burst limit:%d\n", net, asoc->max_burst);
+			}
+#endif
+
+#ifdef SCTP_USE_ALLMAN_BURST
+			if ((net->flight_size+(burst_limit*net->mtu)) < net->cwnd) {
+				if (net->ssthresh < net->cwnd)
+					net->ssthresh = net->cwnd;
+				net->cwnd = (net->flight_size+(burst_limit*net->mtu));
+#ifdef SCTP_LOG_MAXBURST
+				sctp_log_maxburst(net, 0, burst_limit, SCTP_MAX_BURST_APPLIED);
+#endif
+				sctp_pegs[SCTP_MAX_BURST_APL]++;
+			}
+			net->fast_retran_ip = 0;
+#endif
+		}
+
+	}
+	/* Fill up what we can to the destination */
+	burst_cnt = 0;
+	cwnd_full = 0;
+	do {
+#ifdef SCTP_DEBUG
+		if (sctp_debug_on & SCTP_DEBUG_OUTPUT3) {
+			printf("Burst count:%d - call m-c-o\n", burst_cnt);
+		}
+#endif
+		error = sctp_med_chunk_output(inp, stcb, asoc, &num_out,
+					      &reason_code, 0,  &cwnd_full, from_where,
+					      &now, &now_filled);
+		if (error) {
+#ifdef SCTP_DEBUG
+			if (sctp_debug_on & SCTP_DEBUG_OUTPUT1) {
+				printf("Error %d was returned from med-c-op\n", error);
+			}
+#endif
+#ifdef SCTP_LOG_MAXBURST
+			sctp_log_maxburst(asoc->primary_destination, error , burst_cnt, SCTP_MAX_BURST_ERROR_STOP);
+#endif
+			break;
+		}
+#ifdef SCTP_DEBUG
+		if (sctp_debug_on & SCTP_DEBUG_OUTPUT3) {
+			printf("m-c-o put out %d\n", num_out);
+		}
+#endif
+		tot_out += num_out;
+		burst_cnt++;
+	} while (num_out
+#ifndef SCTP_USE_ALLMAN_BURST
+		 &&  (burst_cnt < burst_limit)
+#endif
+		);
+#ifndef SCTP_USE_ALLMAN_BURST
+	if (burst_cnt >= burst_limit) {
+		sctp_pegs[SCTP_MAX_BURST_APL]++;
+ 		asoc->burst_limit_applied = 1;
+#ifdef SCTP_LOG_MAXBURST
+		sctp_log_maxburst(asoc->primary_destination, 0 , burst_cnt, SCTP_MAX_BURST_APPLIED);
+#endif
+ 	} else {
+		asoc->burst_limit_applied = 0;
+ 	}
+#endif
+
+#ifdef SCTP_DEBUG
+	if (sctp_debug_on & SCTP_DEBUG_OUTPUT1) {
+		printf("Ok, we have put out %d chunks\n", tot_out);
+	}
+#endif
+	if (tot_out == 0) {
+		sctp_pegs[SCTP_CO_NODATASNT]++;
+		if (asoc->stream_queue_cnt > 0) {
+			sctp_pegs[SCTP_SOS_NOSNT]++;
+		} else {
+			sctp_pegs[SCTP_NOS_NOSNT]++;
+		}
+		if (asoc->send_queue_cnt > 0) {
+			sctp_pegs[SCTP_SOSE_NOSNT]++;
+		} else {
+			sctp_pegs[SCTP_NOSE_NOSNT]++;
+		}
+	}
+	/* Now we need to clean up the control chunk chain if
+	 * a ECNE is on it. It must be marked as UNSENT again
+	 * so next call will continue to send it until
+	 * such time that we get a CWR, to remove it.
+	 */
+	sctp_fix_ecn_echo(asoc);
+	return (error);
+}
+
+
+int
+sctp_output(inp, m, addr, control, p, flags)
+     struct sctp_inpcb *inp;
+     struct mbuf *m;
+     struct sockaddr *addr;
+     struct mbuf *control;
+#if (defined(__FreeBSD__) && __FreeBSD_version >= 500000) || defined(__DragonFly__)
+     struct thread *p;
+#else
+     struct proc *p;
+#endif
+    int flags;
+{
+	struct inpcb *ip_inp;
+	struct sctp_inpcb *t_inp;
+ 	struct sctp_tcb *stcb;
+	struct sctp_nets *net;
+	struct sctp_association *asoc;
+	int create_lock_applied = 0;
+	int queue_only, error = 0;
+	struct sctp_sndrcvinfo srcv;
+	int un_sent = 0;
+	int use_rcvinfo = 0;
+	t_inp = inp;
+	/*  struct route ro;*/
+
+	crit_enter();
+	queue_only = 0;
+	ip_inp = (struct inpcb *)inp;
+	stcb = NULL;
+	asoc = NULL;
+	net = NULL;
+
+#ifdef SCTP_DEBUG
+	if (sctp_debug_on & SCTP_DEBUG_OUTPUT1) {
+		printf("USR Send BEGINS\n");
+	}
+#endif
+
+	if ((inp->sctp_flags & SCTP_PCB_FLAGS_TCPTYPE) &&
+	    (inp->sctp_flags & SCTP_PCB_FLAGS_ACCEPTING)) {
+		/* The listner can NOT send */
+		if (control) {
+			sctppcbinfo.mbuf_track--;
+			sctp_m_freem(control);
+			control = NULL;
+		}
+		sctp_m_freem(m);
+		crit_exit();
+		return (EFAULT);
+	}
+	/* Can't allow a V6 address on a non-v6 socket */
+	if (addr) {
+		SCTP_ASOC_CREATE_LOCK(inp);
+		if ((inp->sctp_flags & SCTP_PCB_FLAGS_SOCKET_GONE) ||
+		    (inp->sctp_flags & SCTP_PCB_FLAGS_SOCKET_GONE)) {
+			/* Should I really unlock ? */
+			SCTP_ASOC_CREATE_UNLOCK(inp);
+			if (control) {
+				sctppcbinfo.mbuf_track--;
+				sctp_m_freem(control);
+				control = NULL;
+			}
+			sctp_m_freem(m);
+			crit_exit();
+			return (EFAULT);
+		}
+		create_lock_applied = 1;
+		if (((inp->sctp_flags & SCTP_PCB_FLAGS_BOUND_V6) == 0) &&
+		    (addr->sa_family == AF_INET6)) {
+			SCTP_ASOC_CREATE_UNLOCK(inp);
+			if (control) {
+				sctppcbinfo.mbuf_track--;
+				sctp_m_freem(control);
+				control = NULL;
+			}
+			sctp_m_freem(m);
+			crit_exit();
+			return (EINVAL);
+		}
+	}
+	if (control) {
+		sctppcbinfo.mbuf_track++;
+		if (sctp_find_cmsg(SCTP_SNDRCV, (void *)&srcv, control,
+				   sizeof(srcv))) {
+			if (srcv.sinfo_flags & MSG_SENDALL) {
+				/* its a sendall */
+				sctppcbinfo.mbuf_track--;
+				sctp_m_freem(control);
+				crit_exit();
+				if (create_lock_applied) {
+					SCTP_ASOC_CREATE_UNLOCK(inp);
+					create_lock_applied = 0;
+				}
+				return (sctp_sendall(inp, NULL, m, &srcv));
+			}
+			if (srcv.sinfo_assoc_id) {
+				if (inp->sctp_flags & SCTP_PCB_FLAGS_CONNECTED) {
+					SCTP_INP_RLOCK(inp);
+					stcb = LIST_FIRST(&inp->sctp_asoc_list);
+					if (stcb)
+						SCTP_TCB_LOCK(stcb);
+					SCTP_INP_RUNLOCK(inp);
+
+					if (stcb == NULL) {
+						if (create_lock_applied) {
+							SCTP_ASOC_CREATE_UNLOCK(inp);
+							create_lock_applied = 0;
+						}
+						sctppcbinfo.mbuf_track--;
+						sctp_m_freem(control);
+						sctp_m_freem(m);
+						crit_exit();
+						return (ENOTCONN);
+					}
+					net = stcb->asoc.primary_destination;
+				} else {
+					stcb = sctp_findassociation_ep_asocid(inp, srcv.sinfo_assoc_id);
+				}
+				/*
+				 * Question: Should I error here if the
+
+				 * assoc_id is no longer valid?
+				 * i.e. I can't find it?
+				 */
+				if ((stcb) &&
+				    (addr != NULL)) {
+					/* Must locate the net structure */
+					if (addr)
+						net = sctp_findnet(stcb, addr);
+				}
+				if (net == NULL)
+					net = stcb->asoc.primary_destination;
+			}
+			use_rcvinfo = 1;
+		}
+	}
+	if (stcb == NULL) {
+		if (inp->sctp_flags & SCTP_PCB_FLAGS_CONNECTED) {
+			SCTP_INP_RLOCK(inp);
+			stcb = LIST_FIRST(&inp->sctp_asoc_list);
+			if (stcb)
+				SCTP_TCB_LOCK(stcb);
+			SCTP_INP_RUNLOCK(inp);
+			if (stcb == NULL) {
+				crit_exit();
+				if (create_lock_applied) {
+					SCTP_ASOC_CREATE_UNLOCK(inp);
+					create_lock_applied = 0;
+				}
+				if (control) {
+					sctppcbinfo.mbuf_track--;
+					sctp_m_freem(control);
+					control = NULL;
+				}
+				sctp_m_freem(m);
+				return (ENOTCONN);
+			}
+			if (addr == NULL) {
+				net = stcb->asoc.primary_destination;
+			} else {
+				net = sctp_findnet(stcb, addr);
+				if (net == NULL) {
+					net = stcb->asoc.primary_destination;
+				}
+			}
+		} else {
+			if (addr != NULL) {
+				SCTP_INP_WLOCK(inp);
+				SCTP_INP_INCR_REF(inp);
+				SCTP_INP_WUNLOCK(inp);
+				stcb = sctp_findassociation_ep_addr(&t_inp, addr, &net, NULL, NULL);
+				if (stcb == NULL) {
+					SCTP_INP_WLOCK(inp);
+					SCTP_INP_DECR_REF(inp);
+					SCTP_INP_WUNLOCK(inp);
+				}
+			}
+		}
+	}
+	if ((stcb == NULL) &&
+	    (inp->sctp_flags & SCTP_PCB_FLAGS_TCPTYPE)) {
+		if (control) {
+			sctppcbinfo.mbuf_track--;
+			sctp_m_freem(control);
+			control = NULL;
+		}
+		if (create_lock_applied) {
+			SCTP_ASOC_CREATE_UNLOCK(inp);
+			create_lock_applied = 0;
+		}
+		sctp_m_freem(m);
+		crit_exit();
+		return (ENOTCONN);
+	} else if ((stcb == NULL) &&
+		   (addr == NULL)) {
+		if (control) {
+			sctppcbinfo.mbuf_track--;
+			sctp_m_freem(control);
+			control = NULL;
+		}
+		if (create_lock_applied) {
+			SCTP_ASOC_CREATE_UNLOCK(inp);
+			create_lock_applied = 0;
+		}
+		sctp_m_freem(m);
+		crit_exit();
+		return (ENOENT);
+	} else if (stcb == NULL) {
+		/* UDP mode, we must go ahead and start the INIT process */
+		if ((use_rcvinfo) && (srcv.sinfo_flags & MSG_ABORT)) {
+			/* Strange user to do this */
+			if (control) {
+				sctppcbinfo.mbuf_track--;
+				sctp_m_freem(control);
+				control = NULL;
+			}
+			if (create_lock_applied) {
+				SCTP_ASOC_CREATE_UNLOCK(inp);
+				create_lock_applied = 0;
+			}
+			sctp_m_freem(m);
+			crit_exit();
+			return (ENOENT);
+		}
+		stcb = sctp_aloc_assoc(inp, addr, 1, &error, 0);
+		if (stcb == NULL) {
+			if (control) {
+				sctppcbinfo.mbuf_track--;
+				sctp_m_freem(control);
+				control = NULL;
+			}
+			if (create_lock_applied) {
+				SCTP_ASOC_CREATE_UNLOCK(inp);
+				create_lock_applied = 0;
+			}
+			sctp_m_freem(m);
+			crit_exit();
+			return (error);
+		}
+		if (create_lock_applied) {
+			SCTP_ASOC_CREATE_UNLOCK(inp);
+			create_lock_applied = 0;
+		} else {
+			printf("Huh-1, create lock should have been applied!\n");
+		}
+		queue_only = 1;
+		asoc = &stcb->asoc;
+		asoc->state = SCTP_STATE_COOKIE_WAIT;
+		SCTP_GETTIME_TIMEVAL(&asoc->time_entered);
+		if (control) {
+			/* see if a init structure exists in cmsg headers */
+			struct sctp_initmsg initm;
+			int i;
+			if (sctp_find_cmsg(SCTP_INIT, (void *)&initm, control,
+					   sizeof(initm))) {
+				/* we have an INIT override of the default */
+				if (initm.sinit_max_attempts)
+					asoc->max_init_times = initm.sinit_max_attempts;
+				if (initm.sinit_num_ostreams)
+					asoc->pre_open_streams = initm.sinit_num_ostreams;
+				if (initm.sinit_max_instreams)
+					asoc->max_inbound_streams = initm.sinit_max_instreams;
+				if (initm.sinit_max_init_timeo)
+					asoc->initial_init_rto_max = initm.sinit_max_init_timeo;
+			}
+			if (asoc->streamoutcnt < asoc->pre_open_streams) {
+				/* Default is NOT correct */
+#ifdef SCTP_DEBUG
+				if (sctp_debug_on & SCTP_DEBUG_OUTPUT1) {
+					printf("Ok, defout:%d pre_open:%d\n",
+					       asoc->streamoutcnt, asoc->pre_open_streams);
+				}
+#endif
+				FREE(asoc->strmout, M_PCB);
+				asoc->strmout = NULL;
+				asoc->streamoutcnt = asoc->pre_open_streams;
+				MALLOC(asoc->strmout, struct sctp_stream_out *,
+				       asoc->streamoutcnt *
+				       sizeof(struct sctp_stream_out), M_PCB,
+				       MB_WAIT);
+				for (i = 0; i < asoc->streamoutcnt; i++) {
+					/*
+					 * inbound side must be set to 0xffff,
+					 * also NOTE when we get the INIT-ACK
+					 * back (for INIT sender) we MUST
+					 * reduce the count (streamoutcnt) but
+					 * first check if we sent to any of the
+					 * upper streams that were dropped (if
+					 * some were). Those that were dropped
+					 * must be notified to the upper layer
+					 * as failed to send.
+					 */
+					asoc->strmout[i].next_sequence_sent = 0x0;
+					TAILQ_INIT(&asoc->strmout[i].outqueue);
+					asoc->strmout[i].stream_no = i;
+					asoc->strmout[i].next_spoke.tqe_next = 0;
+					asoc->strmout[i].next_spoke.tqe_prev = 0;
+				}
+			}
+		}
+		sctp_send_initiate(inp, stcb);
+		/*
+		 * we may want to dig in after this call and adjust the MTU
+		 * value. It defaulted to 1500 (constant) but the ro structure
+		 * may now have an update and thus we may need to change it
+		 * BEFORE we append the message.
+		 */
+		net = stcb->asoc.primary_destination;
+	} else {
+		if (create_lock_applied) {
+			SCTP_ASOC_CREATE_UNLOCK(inp);
+			create_lock_applied = 0;
+		}
+		asoc = &stcb->asoc;
+		if ((SCTP_GET_STATE(asoc) == SCTP_STATE_COOKIE_WAIT) ||
+		    (SCTP_GET_STATE(asoc) == SCTP_STATE_COOKIE_ECHOED)) {
+			queue_only = 1;
+		}
+		if ((SCTP_GET_STATE(asoc) == SCTP_STATE_SHUTDOWN_SENT) ||
+		    (SCTP_GET_STATE(asoc) == SCTP_STATE_SHUTDOWN_RECEIVED) ||
+		    (SCTP_GET_STATE(asoc) == SCTP_STATE_SHUTDOWN_ACK_SENT) ||
+		    (asoc->state & SCTP_STATE_SHUTDOWN_PENDING)) {
+			if (control) {
+				sctppcbinfo.mbuf_track--;
+				sctp_m_freem(control);
+				control = NULL;
+			}
+			if ((use_rcvinfo) &&
+			    (srcv.sinfo_flags & MSG_ABORT)) {
+				sctp_msg_append(stcb, net, m, &srcv, flags);
+				error = 0;
+			} else {
+				if (m)
+					sctp_m_freem(m);
+				error = ECONNRESET;
+			}
+			crit_exit();
+			SCTP_TCB_UNLOCK(stcb);
+			return (error);
+		}
+	}
+	if (create_lock_applied) {
+		/* we should never hit here with the create lock applied
+		 *
+		 */
+		SCTP_ASOC_CREATE_UNLOCK(inp);
+		create_lock_applied = 0;
+	}
+
+
+	if (use_rcvinfo == 0) {
+		srcv = stcb->asoc.def_send;
+	}
+#ifdef SCTP_DEBUG
+	else {
+		if (sctp_debug_on & SCTP_DEBUG_OUTPUT5) {
+			printf("stream:%d\n", srcv.sinfo_stream);
+			printf("flags:%x\n", (u_int)srcv.sinfo_flags);
+			printf("ppid:%d\n", srcv.sinfo_ppid);
+			printf("context:%d\n", srcv.sinfo_context);
+		}
+	}
+#endif
+	if (control) {
+		sctppcbinfo.mbuf_track--;
+		sctp_m_freem(control);
+		control = NULL;
+	}
+	if (net && ((srcv.sinfo_flags & MSG_ADDR_OVER))) {
+		/* we take the override or the unconfirmed */
+		;
+	} else {
+		net = stcb->asoc.primary_destination;
+	}
+	if ((error = sctp_msg_append(stcb, net, m, &srcv, flags))) {
+		SCTP_TCB_UNLOCK(stcb);
+		crit_exit();
+		return (error);
+	}
+	if (net->flight_size > net->cwnd) {
+		sctp_pegs[SCTP_SENDTO_FULL_CWND]++;
+		queue_only = 1;
+ 	} else if (asoc->ifp_had_enobuf) {
+		sctp_pegs[SCTP_QUEONLY_BURSTLMT]++;
+	 	queue_only = 1;
+ 	} else {
+		un_sent = ((stcb->asoc.total_output_queue_size - stcb->asoc.total_flight) +
+			   ((stcb->asoc.chunks_on_out_queue - stcb->asoc.total_flight_count) * sizeof(struct sctp_data_chunk)) +
+			   SCTP_MED_OVERHEAD);
+
+		if (((inp->sctp_flags & SCTP_PCB_FLAGS_NODELAY) == 0) &&
+		    (stcb->asoc.total_flight > 0) &&
+		    (un_sent < (int)stcb->asoc.smallest_mtu)
+			) {
+
+			/* Ok, Nagle is set on and we have
+			 * data outstanding. Don't send anything
+			 * and let the SACK drive out the data.
+			 */
+			sctp_pegs[SCTP_NAGLE_NOQ]++;
+			queue_only = 1;
+		} else {
+			sctp_pegs[SCTP_NAGLE_OFF]++;
+		}
+	}
+	if ((queue_only == 0) && stcb->asoc.peers_rwnd) {
+		/* we can attempt to send too.*/
+#ifdef SCTP_DEBUG
+		if (sctp_debug_on & SCTP_DEBUG_OUTPUT1) {
+			printf("USR Send calls sctp_chunk_output\n");
+		}
+#endif
+#ifdef SCTP_AUDITING_ENABLED
+		sctp_audit_log(0xC0, 1);
+		sctp_auditing(6, inp, stcb, net);
+#endif
+		sctp_pegs[SCTP_OUTPUT_FRM_SND]++;
+		sctp_chunk_output(inp, stcb, 0);
+#ifdef SCTP_AUDITING_ENABLED
+		sctp_audit_log(0xC0, 2);
+		sctp_auditing(7, inp, stcb, net);
+#endif
+
+	}
+#ifdef SCTP_DEBUG
+	if (sctp_debug_on & SCTP_DEBUG_OUTPUT1) {
+		printf("USR Send complete qo:%d prw:%d\n", queue_only, stcb->asoc.peers_rwnd);
+	}
+#endif
+	SCTP_TCB_UNLOCK(stcb);
+	crit_exit();
+	return (0);
+}
+
+void
+send_forward_tsn(struct sctp_tcb *stcb,
+		 struct sctp_association *asoc)
+{
+	struct sctp_tmit_chunk *chk;
+	struct sctp_forward_tsn_chunk *fwdtsn;
+
+	TAILQ_FOREACH(chk, &asoc->control_send_queue, sctp_next) {
+		if (chk->rec.chunk_id == SCTP_FORWARD_CUM_TSN) {
+			/* mark it to unsent */
+			chk->sent = SCTP_DATAGRAM_UNSENT;
+			chk->snd_count = 0;
+			/* Do we correct its output location? */
+			if (chk->whoTo != asoc->primary_destination) {
+				sctp_free_remote_addr(chk->whoTo);
+				chk->whoTo = asoc->primary_destination;
+				chk->whoTo->ref_count++;
+			}
+			goto sctp_fill_in_rest;
+		}
+	}
+	/* Ok if we reach here we must build one */
+	chk = (struct sctp_tmit_chunk *)SCTP_ZONE_GET(sctppcbinfo.ipi_zone_chunk);
+	if (chk == NULL) {
+		return;
+	}
+	sctppcbinfo.ipi_count_chunk++;
+	sctppcbinfo.ipi_gencnt_chunk++;
+	chk->rec.chunk_id = SCTP_FORWARD_CUM_TSN;
+	chk->asoc = asoc;
+	MGETHDR(chk->data, MB_DONTWAIT, MT_DATA);
+	if (chk->data == NULL) {
+		chk->whoTo->ref_count--;
+		SCTP_ZONE_FREE(sctppcbinfo.ipi_zone_chunk, chk);
+		sctppcbinfo.ipi_count_chunk--;
+		if ((int)sctppcbinfo.ipi_count_chunk < 0) {
+			panic("Chunk count is negative");
+		}
+		sctppcbinfo.ipi_gencnt_chunk++;
+		return;
+	}
+	chk->data->m_data += SCTP_MIN_OVERHEAD;
+	chk->sent = SCTP_DATAGRAM_UNSENT;
+	chk->snd_count = 0;
+	chk->whoTo = asoc->primary_destination;
+	chk->whoTo->ref_count++;
+	TAILQ_INSERT_TAIL(&asoc->control_send_queue, chk, sctp_next);
+	asoc->ctrl_queue_cnt++;
+ sctp_fill_in_rest:
+	/* Here we go through and fill out the part that
+	 * deals with stream/seq of the ones we skip.
+	 */
+	chk->data->m_pkthdr.len = chk->data->m_len = 0;
+	{
+		struct sctp_tmit_chunk *at, *tp1, *last;
+		struct sctp_strseq *strseq;
+		unsigned int cnt_of_space, i, ovh;
+		unsigned int space_needed;
+		unsigned int cnt_of_skipped = 0;
+		TAILQ_FOREACH(at, &asoc->sent_queue, sctp_next) {
+			if (at->sent != SCTP_FORWARD_TSN_SKIP) {
+				/* no more to look at */
+				break;
+			}
+			if (at->rec.data.rcv_flags & SCTP_DATA_UNORDERED) {
+				/* We don't report these */
+				continue;
+			}
+			cnt_of_skipped++;
+		}
+		space_needed = (sizeof(struct sctp_forward_tsn_chunk) +
+				(cnt_of_skipped * sizeof(struct sctp_strseq)));
+		if ((M_TRAILINGSPACE(chk->data) < (int)space_needed) &&
+		    ((chk->data->m_flags & M_EXT) == 0)) {
+			/* Need a M_EXT, get one and move
+			 * fwdtsn to data area.
+			 */
+			MCLGET(chk->data, MB_DONTWAIT);
+		}
+		cnt_of_space = M_TRAILINGSPACE(chk->data);
+
+		if (stcb->sctp_ep->sctp_flags & SCTP_PCB_FLAGS_BOUND_V6) {
+			ovh = SCTP_MIN_OVERHEAD;
+		} else {
+			ovh = SCTP_MIN_V4_OVERHEAD;
+		}
+		if (cnt_of_space > (asoc->smallest_mtu-ovh)) {
+			/* trim to a mtu size */
+			cnt_of_space = asoc->smallest_mtu - ovh;
+		}
+		if (cnt_of_space < space_needed) {
+			/* ok we must trim down the chunk by lowering
+			 * the advance peer ack point.
+			 */
+			cnt_of_skipped = (cnt_of_space-
+					  ((sizeof(struct sctp_forward_tsn_chunk))/
+ 					    sizeof(struct sctp_strseq)));
+			/* Go through and find the TSN that
+			 * will be the one we report.
+			 */
+			at = TAILQ_FIRST(&asoc->sent_queue);
+			for (i = 0; i < cnt_of_skipped; i++) {
+				tp1 = TAILQ_NEXT(at, sctp_next);
+				at = tp1;
+			}
+			last = at;
+			/* last now points to last one I can report, update peer ack point */
+			asoc->advanced_peer_ack_point = last->rec.data.TSN_seq;
+			space_needed -= (cnt_of_skipped * sizeof(struct sctp_strseq));
+		}
+		chk->send_size = space_needed;
+		/* Setup the chunk */
+		fwdtsn = mtod(chk->data, struct sctp_forward_tsn_chunk *);
+		fwdtsn->ch.chunk_length = htons(chk->send_size);
+		fwdtsn->ch.chunk_flags = 0;
+		fwdtsn->ch.chunk_type = SCTP_FORWARD_CUM_TSN;
+		fwdtsn->new_cumulative_tsn = htonl(asoc->advanced_peer_ack_point);
+		chk->send_size = (sizeof(struct sctp_forward_tsn_chunk) +
+				  (cnt_of_skipped * sizeof(struct sctp_strseq)));
+		chk->data->m_pkthdr.len = chk->data->m_len = chk->send_size;
+		fwdtsn++;
+		/* Move pointer to after the fwdtsn and transfer to
+		 * the strseq pointer.
+		 */
+		strseq = (struct sctp_strseq *)fwdtsn;
+		/*
+		 * Now populate the strseq list. This is done blindly
+		 * without pulling out duplicate stream info. This is
+		 * inefficent but won't harm the process since the peer
+		 * will look at these in sequence and will thus release
+		 * anything. It could mean we exceed the PMTU and chop
+		 * off some that we could have included.. but this is
+		 * unlikely (aka 1432/4 would mean 300+ stream seq's would
+		 * have to be reported in one FWD-TSN. With a bit of work
+		 * we can later FIX this to optimize and pull out duplcates..
+		 * but it does add more overhead. So for now... not!
+		 */
+		at = TAILQ_FIRST(&asoc->sent_queue);
+		for (i = 0; i < cnt_of_skipped; i++) {
+			tp1 = TAILQ_NEXT(at, sctp_next);
+			if (at->rec.data.rcv_flags & SCTP_DATA_UNORDERED) {
+				/* We don't report these */
+				i--;
+				at = tp1;
+				continue;
+			}
+			strseq->stream = ntohs(at->rec.data.stream_number);
+			strseq->sequence = ntohs(at->rec.data.stream_seq);
+			strseq++;
+			at = tp1;
+		}
+	}
+	return;
+
+}
+
+void
+sctp_send_sack(struct sctp_tcb *stcb)
+{
+	/*
+	 * Queue up a SACK in the control queue. We must first check to
+	 * see if a SACK is somehow on the control queue. If so, we will
+	 * take and and remove the old one.
+	 */
+	struct sctp_association *asoc;
+	struct sctp_tmit_chunk *chk, *a_chk;
+	struct sctp_sack_chunk *sack;
+	struct sctp_gap_ack_block *gap_descriptor;
+	uint32_t *dup;
+	int start;
+	unsigned int i, maxi, seeing_ones, m_size;
+	unsigned int num_gap_blocks, space;
+
+	start = maxi = 0;
+	seeing_ones = 1;
+	a_chk = NULL;
+	asoc = &stcb->asoc;
+	if (asoc->last_data_chunk_from == NULL) {
+		/* Hmm we never received anything */
+		return;
+	}
+	sctp_set_rwnd(stcb, asoc);
+	TAILQ_FOREACH(chk, &asoc->control_send_queue, sctp_next) {
+		if (chk->rec.chunk_id == SCTP_SELECTIVE_ACK) {
+			/* Hmm, found a sack already on queue, remove it */
+			TAILQ_REMOVE(&asoc->control_send_queue, chk, sctp_next);
+			asoc->ctrl_queue_cnt++;
+			a_chk = chk;
+			if (a_chk->data)
+				sctp_m_freem(a_chk->data);
+			a_chk->data = NULL;
+			sctp_free_remote_addr(a_chk->whoTo);
+			a_chk->whoTo = NULL;
+			break;
+		}
+	}
+	if (a_chk == NULL) {
+		a_chk = (struct sctp_tmit_chunk *)SCTP_ZONE_GET(sctppcbinfo.ipi_zone_chunk);
+		if (a_chk == NULL) {
+			/* No memory so we drop the idea, and set a timer */
+			sctp_timer_stop(SCTP_TIMER_TYPE_RECV,
+					stcb->sctp_ep, stcb, NULL);
+			sctp_timer_start(SCTP_TIMER_TYPE_RECV,
+					 stcb->sctp_ep, stcb, NULL);
+			return;
+		}
+		sctppcbinfo.ipi_count_chunk++;
+		sctppcbinfo.ipi_gencnt_chunk++;
+		a_chk->rec.chunk_id = SCTP_SELECTIVE_ACK;
+	}
+	a_chk->asoc = asoc;
+	a_chk->snd_count = 0;
+	a_chk->send_size = 0;	/* fill in later */
+	a_chk->sent = SCTP_DATAGRAM_UNSENT;
+	m_size = (asoc->mapping_array_size << 3);
+
+	if ((asoc->numduptsns) ||
+	    (asoc->last_data_chunk_from->dest_state & SCTP_ADDR_NOT_REACHABLE)
+		) {
+		/* Ok, we have some duplicates or the destination for the
+		 * sack is unreachable, lets see if we can select an alternate
+		 * than asoc->last_data_chunk_from
+		 */
+		if ((!(asoc->last_data_chunk_from->dest_state &
+		      SCTP_ADDR_NOT_REACHABLE)) &&
+		    (asoc->used_alt_onsack > 2)) {
+			/* We used an alt last time, don't this time */
+			a_chk->whoTo = NULL;
+		} else {
+			asoc->used_alt_onsack++;
+			a_chk->whoTo = sctp_find_alternate_net(stcb, asoc->last_data_chunk_from);
+		}
+		if (a_chk->whoTo == NULL) {
+			/* Nope, no alternate */
+			a_chk->whoTo = asoc->last_data_chunk_from;
+			asoc->used_alt_onsack = 0;
+		}
+	} else {
+		/* No duplicates so we use the last
+		 * place we received data from.
+		 */
+#ifdef SCTP_DEBUG
+		if (asoc->last_data_chunk_from == NULL) {
+			printf("Huh, last_data_chunk_from is null when we want to sack??\n");
+		}
+#endif
+		asoc->used_alt_onsack = 0;
+		a_chk->whoTo = asoc->last_data_chunk_from;
+	}
+	if (a_chk->whoTo)
+		a_chk->whoTo->ref_count++;
+
+	/* Ok now lets formulate a MBUF with our sack */
+	MGETHDR(a_chk->data, MB_DONTWAIT, MT_DATA);
+	if ((a_chk->data == NULL) ||
+	    (a_chk->whoTo == NULL)) {
+		/* rats, no mbuf memory */
+		if (a_chk->data) {
+			/* was a problem with the destination */
+			sctp_m_freem(a_chk->data);
+			a_chk->data = NULL;
+		}
+		a_chk->whoTo->ref_count--;
+		SCTP_ZONE_FREE(sctppcbinfo.ipi_zone_chunk, a_chk);
+		sctppcbinfo.ipi_count_chunk--;
+		if ((int)sctppcbinfo.ipi_count_chunk < 0) {
+			panic("Chunk count is negative");
+		}
+		sctppcbinfo.ipi_gencnt_chunk++;
+		sctp_timer_stop(SCTP_TIMER_TYPE_RECV,
+				stcb->sctp_ep, stcb, NULL);
+		sctp_timer_start(SCTP_TIMER_TYPE_RECV,
+				 stcb->sctp_ep, stcb, NULL);
+		return;
+	}
+	/* First count the number of gap ack blocks we need */
+	if (asoc->highest_tsn_inside_map == asoc->cumulative_tsn) {
+		/* We know if there are none above the cum-ack we
+		 * have everything with NO gaps
+		 */
+		num_gap_blocks = 0;
+	} else {
+		/* Ok we must count how many gaps we
+		 * have.
+		 */
+		num_gap_blocks = 0;
+		if (asoc->highest_tsn_inside_map >= asoc->mapping_array_base_tsn) {
+			maxi = (asoc->highest_tsn_inside_map - asoc->mapping_array_base_tsn);
+		} else {
+			maxi = (asoc->highest_tsn_inside_map  + (MAX_TSN - asoc->mapping_array_base_tsn) + 1);
+		}
+		if (maxi > m_size) {
+			/* impossible but who knows, someone is playing with us  :> */
+#ifdef SCTP_DEBUG
+			printf("GAK maxi:%d  > m_size:%d came out higher than allowed htsn:%u base:%u cumack:%u\n",
+			       maxi,
+			       m_size,
+			       asoc->highest_tsn_inside_map,
+			       asoc->mapping_array_base_tsn,
+			       asoc->cumulative_tsn
+			       );
+#endif
+			num_gap_blocks = 0;
+			goto no_gaps_now;
+		}
+		if (asoc->cumulative_tsn >= asoc->mapping_array_base_tsn) {
+			start = (asoc->cumulative_tsn - asoc->mapping_array_base_tsn);
+		} else {
+			/* Set it so we start at 0 */
+			start = -1;
+		}
+		/* Ok move start up one to look at the NEXT past the cum-ack */
+		start++;
+		for (i = start; i <= maxi; i++) {
+			if (seeing_ones) {
+				/* while seeing ones I must
+				 * transition back to 0 before
+				 * finding the next gap and
+				 * counting the segment.
+				 */
+				if (SCTP_IS_TSN_PRESENT(asoc->mapping_array, i) == 0) {
+					seeing_ones = 0;
+				}
+			} else {
+				if (SCTP_IS_TSN_PRESENT(asoc->mapping_array, i)) {
+					seeing_ones = 1;
+					num_gap_blocks++;
+				}
+			}
+		}
+	no_gaps_now:
+		if (num_gap_blocks == 0) {
+			/*
+			 * Traveled all of the bits and NO one,
+			 * must have reneged
+			 */
+			if (compare_with_wrap(asoc->cumulative_tsn, asoc->highest_tsn_inside_map, MAX_TSN)) {
+			   asoc->highest_tsn_inside_map = asoc->cumulative_tsn;
+#ifdef SCTP_MAP_LOGGING
+			   sctp_log_map(0, 4, asoc->highest_tsn_inside_map, SCTP_MAP_SLIDE_RESULT);
+#endif
+			}
+		}
+	}
+
+	/* Now calculate the space needed */
+	space = (sizeof(struct sctp_sack_chunk) +
+		 (num_gap_blocks * sizeof(struct sctp_gap_ack_block)) +
+		 (asoc->numduptsns * sizeof(int32_t))
+		);
+	if (space > (asoc->smallest_mtu-SCTP_MAX_OVERHEAD)) {
+		/* Reduce the size of the sack to fit */
+		int calc, fit;
+		calc = (asoc->smallest_mtu - SCTP_MAX_OVERHEAD);
+		calc -= sizeof(struct sctp_gap_ack_block);
+		fit = calc/sizeof(struct sctp_gap_ack_block);
+		if (fit > (int)num_gap_blocks) {
+			/* discard some dups */
+			asoc->numduptsns = (fit - num_gap_blocks);
+		} else {
+			/* discard all dups and some gaps */
+			num_gap_blocks = fit;
+			asoc->numduptsns = 0;
+		}
+		/* recalc space */
+		space = (sizeof(struct sctp_sack_chunk) +
+			 (num_gap_blocks * sizeof(struct sctp_gap_ack_block)) +
+			 (asoc->numduptsns * sizeof(int32_t))
+			);
+
+	}
+
+	if ((space+SCTP_MIN_OVERHEAD) > MHLEN) {
+		/* We need a cluster */
+		MCLGET(a_chk->data, MB_DONTWAIT);
+		if ((a_chk->data->m_flags & M_EXT) != M_EXT) {
+			/* can't get a cluster
+			 * give up and try later.
+			 */
+			if (a_chk->data)
+				sctp_m_freem(a_chk->data);
+			a_chk->data = NULL;
+			a_chk->whoTo->ref_count--;
+			SCTP_ZONE_FREE(sctppcbinfo.ipi_zone_chunk, a_chk);
+			sctppcbinfo.ipi_count_chunk--;
+			if ((int)sctppcbinfo.ipi_count_chunk < 0) {
+				panic("Chunk count is negative");
+			}
+			sctppcbinfo.ipi_gencnt_chunk++;
+			sctp_timer_stop(SCTP_TIMER_TYPE_RECV,
+					stcb->sctp_ep, stcb, NULL);
+			sctp_timer_start(SCTP_TIMER_TYPE_RECV,
+					 stcb->sctp_ep, stcb, NULL);
+			return;
+		}
+	}
+
+	/* ok, lets go through and fill it in */
+	a_chk->data->m_data += SCTP_MIN_OVERHEAD;
+	sack = mtod(a_chk->data, struct sctp_sack_chunk *);
+	sack->ch.chunk_type = SCTP_SELECTIVE_ACK;
+	sack->ch.chunk_flags = asoc->receiver_nonce_sum & SCTP_SACK_NONCE_SUM;
+	sack->sack.cum_tsn_ack = htonl(asoc->cumulative_tsn);
+	sack->sack.a_rwnd = htonl(asoc->my_rwnd);
+	asoc->my_last_reported_rwnd = asoc->my_rwnd;
+	sack->sack.num_gap_ack_blks = htons(num_gap_blocks);
+	sack->sack.num_dup_tsns = htons(asoc->numduptsns);
+
+	a_chk->send_size = (sizeof(struct sctp_sack_chunk) +
+			    (num_gap_blocks * sizeof(struct sctp_gap_ack_block)) +
+			    (asoc->numduptsns * sizeof(int32_t)));
+	a_chk->data->m_pkthdr.len = a_chk->data->m_len = a_chk->send_size;
+	sack->ch.chunk_length = htons(a_chk->send_size);
+
+	gap_descriptor = (struct sctp_gap_ack_block *)((caddr_t)sack + sizeof(struct sctp_sack_chunk));
+	seeing_ones = 0;
+	for (i = start; i <= maxi; i++) {
+		if (num_gap_blocks == 0) {
+			break;
+		}
+		if (seeing_ones) {
+			/* while seeing Ones I must
+			 * transition back to 0 before
+			 * finding the next gap
+			 */
+			if (SCTP_IS_TSN_PRESENT(asoc->mapping_array, i) == 0) {
+				gap_descriptor->end = htons(((uint16_t)(i-start)));
+				gap_descriptor++;
+				seeing_ones = 0;
+				num_gap_blocks--;
+			}
+		} else {
+			if (SCTP_IS_TSN_PRESENT(asoc->mapping_array, i)) {
+				gap_descriptor->start = htons(((uint16_t)(i+1-start)));
+				/* advance struct to next pointer */
+				seeing_ones = 1;
+			}
+		}
+	}
+	if (num_gap_blocks) {
+		/* special case where the array is all 1's
+		 * to the end of the array.
+		 */
+		gap_descriptor->end = htons(((uint16_t)((i-start))));
+		gap_descriptor++;
+	}
+	/* now we must add any dups we are going to report. */
+	if (asoc->numduptsns) {
+		dup = (uint32_t *)gap_descriptor;
+		for (i = 0; i < asoc->numduptsns; i++) {
+			*dup = htonl(asoc->dup_tsns[i]);
+			dup++;
+		}
+		asoc->numduptsns = 0;
+	}
+	/* now that the chunk is prepared queue it to the control
+	 * chunk queue.
+	 */
+	TAILQ_INSERT_TAIL(&asoc->control_send_queue, a_chk, sctp_next);
+	asoc->ctrl_queue_cnt++;
+	sctp_pegs[SCTP_PEG_SACKS_SENT]++;
+	return;
+}
+
+void
+sctp_send_abort_tcb(struct sctp_tcb *stcb, struct mbuf *operr)
+{
+	struct mbuf *m_abort;
+	struct sctp_abort_msg *abort_m;
+	int sz;
+	abort_m = NULL;
+	MGETHDR(m_abort, MB_DONTWAIT, MT_HEADER);
+	if (m_abort == NULL) {
+		/* no mbuf's */
+		return;
+	}
+	m_abort->m_data += SCTP_MIN_OVERHEAD;
+	abort_m = mtod(m_abort, struct sctp_abort_msg *);
+	m_abort->m_len = sizeof(struct sctp_abort_msg);
+	m_abort->m_next = operr;
+	sz = 0;
+	if (operr) {
+		struct mbuf *n;
+		n = operr;
+		while (n) {
+			sz += n->m_len;
+			n = n->m_next;
+		}
+	}
+	abort_m->msg.ch.chunk_type = SCTP_ABORT_ASSOCIATION;
+	abort_m->msg.ch.chunk_flags = 0;
+	abort_m->msg.ch.chunk_length = htons(sizeof(struct sctp_abort_chunk) +
+					     sz);
+	abort_m->sh.src_port = stcb->sctp_ep->sctp_lport;
+	abort_m->sh.dest_port = stcb->rport;
+	abort_m->sh.v_tag = htonl(stcb->asoc.peer_vtag);
+	abort_m->sh.checksum = 0;
+	m_abort->m_pkthdr.len = m_abort->m_len + sz;
+	m_abort->m_pkthdr.rcvif = 0;
+	sctp_lowlevel_chunk_output(stcb->sctp_ep, stcb,
+	    stcb->asoc.primary_destination,
+	    (struct sockaddr *)&stcb->asoc.primary_destination->ro._l_addr,
+	    m_abort, 1, 0, NULL, 0);
+}
+
+int
+sctp_send_shutdown_complete(struct sctp_tcb *stcb,
+			    struct sctp_nets *net)
+
+{
+	/* formulate and SEND a SHUTDOWN-COMPLETE */
+	struct mbuf *m_shutdown_comp;
+	struct sctp_shutdown_complete_msg *comp_cp;
+
+	m_shutdown_comp = NULL;
+	MGETHDR(m_shutdown_comp, MB_DONTWAIT, MT_HEADER);
+	if (m_shutdown_comp == NULL) {
+		/* no mbuf's */
+		return (-1);
+	}
+	m_shutdown_comp->m_data += sizeof(struct ip6_hdr);
+	comp_cp = mtod(m_shutdown_comp, struct sctp_shutdown_complete_msg *);
+	comp_cp->shut_cmp.ch.chunk_type = SCTP_SHUTDOWN_COMPLETE;
+	comp_cp->shut_cmp.ch.chunk_flags = 0;
+	comp_cp->shut_cmp.ch.chunk_length = htons(sizeof(struct sctp_shutdown_complete_chunk));
+	comp_cp->sh.src_port = stcb->sctp_ep->sctp_lport;
+	comp_cp->sh.dest_port = stcb->rport;
+	comp_cp->sh.v_tag = htonl(stcb->asoc.peer_vtag);
+	comp_cp->sh.checksum = 0;
+
+	m_shutdown_comp->m_pkthdr.len = m_shutdown_comp->m_len = sizeof(struct sctp_shutdown_complete_msg);
+	m_shutdown_comp->m_pkthdr.rcvif = 0;
+	sctp_lowlevel_chunk_output(stcb->sctp_ep, stcb, net,
+	    (struct sockaddr *)&net->ro._l_addr, m_shutdown_comp,
+	    1, 0, NULL, 0);
+	if ((stcb->sctp_ep->sctp_flags & SCTP_PCB_FLAGS_TCPTYPE) ||
+	    (stcb->sctp_ep->sctp_flags & SCTP_PCB_FLAGS_IN_TCPPOOL)) {
+		stcb->sctp_ep->sctp_flags &= ~SCTP_PCB_FLAGS_CONNECTED;
+		stcb->sctp_ep->sctp_socket->so_snd.sb_cc = 0;
+		soisdisconnected(stcb->sctp_ep->sctp_socket);
+	}
+	return (0);
+}
+
+int
+sctp_send_shutdown_complete2(struct mbuf *m, int iphlen, struct sctphdr *sh)
+{
+	/* formulate and SEND a SHUTDOWN-COMPLETE */
+	struct mbuf *mout;
+	struct ip *iph, *iph_out;
+	struct ip6_hdr *ip6, *ip6_out;
+	int offset_out;
+	struct sctp_shutdown_complete_msg *comp_cp;
+
+	MGETHDR(mout, MB_DONTWAIT, MT_HEADER);
+	if (mout == NULL) {
+		/* no mbuf's */
+		return (-1);
+	}
+	iph = mtod(m, struct ip *);
+	iph_out = NULL;
+	ip6_out = NULL;
+	offset_out = 0;
+	if (iph->ip_v == IPVERSION) {
+		mout->m_len = sizeof(struct ip) +
+		    sizeof(struct sctp_shutdown_complete_msg);
+		mout->m_next = NULL;
+		iph_out = mtod(mout, struct ip *);
+
+		/* Fill in the IP header for the ABORT */
+		iph_out->ip_v = IPVERSION;
+		iph_out->ip_hl = (sizeof(struct ip)/4);
+		iph_out->ip_tos = (u_char)0;
+		iph_out->ip_id = 0;
+		iph_out->ip_off = 0;
+		iph_out->ip_ttl = MAXTTL;
+		iph_out->ip_p = IPPROTO_SCTP;
+		iph_out->ip_src.s_addr = iph->ip_dst.s_addr;
+		iph_out->ip_dst.s_addr = iph->ip_src.s_addr;
+
+		/* let IP layer calculate this */
+		iph_out->ip_sum = 0;
+		offset_out += sizeof(*iph_out);
+		comp_cp = (struct sctp_shutdown_complete_msg *)(
+		    (caddr_t)iph_out + offset_out);
+	} else if (iph->ip_v == (IPV6_VERSION >> 4)) {
+		ip6 = (struct ip6_hdr *)iph;
+		mout->m_len = sizeof(struct ip6_hdr) +
+		    sizeof(struct sctp_shutdown_complete_msg);
+		mout->m_next = NULL;
+		ip6_out = mtod(mout, struct ip6_hdr *);
+
+		/* Fill in the IPv6 header for the ABORT */
+		ip6_out->ip6_flow = ip6->ip6_flow;
+		ip6_out->ip6_hlim = ip6_defhlim;
+		ip6_out->ip6_nxt = IPPROTO_SCTP;
+		ip6_out->ip6_src = ip6->ip6_dst;
+		ip6_out->ip6_dst = ip6->ip6_src;
+ 		ip6_out->ip6_plen = mout->m_len;
+		offset_out += sizeof(*ip6_out);
+		comp_cp = (struct sctp_shutdown_complete_msg *)(
+		    (caddr_t)ip6_out + offset_out);
+	} else {
+		/* Currently not supported. */
+		return (-1);
+	}
+
+	/* Now copy in and fill in the ABORT tags etc. */
+	comp_cp->sh.src_port = sh->dest_port;
+	comp_cp->sh.dest_port = sh->src_port;
+	comp_cp->sh.checksum = 0;
+	comp_cp->sh.v_tag = sh->v_tag;
+	comp_cp->shut_cmp.ch.chunk_flags = SCTP_HAD_NO_TCB;
+	comp_cp->shut_cmp.ch.chunk_type = SCTP_SHUTDOWN_COMPLETE;
+	comp_cp->shut_cmp.ch.chunk_length = htons(sizeof(struct sctp_shutdown_complete_chunk));
+
+	mout->m_pkthdr.len = mout->m_len;
+	/* add checksum */
+	if ((sctp_no_csum_on_loopback) &&
+	   (m->m_pkthdr.rcvif) &&
+	   (m->m_pkthdr.rcvif->if_type == IFT_LOOP)) {
+		comp_cp->sh.checksum =  0;
+	} else {
+		comp_cp->sh.checksum = sctp_calculate_sum(mout, NULL, offset_out);
+	}
+
+	/* zap the rcvif, it should be null */
+	mout->m_pkthdr.rcvif = 0;
+	/* zap the stack pointer to the route */
+	if (iph_out != NULL) {
+		struct route ro;
+
+		bzero(&ro, sizeof ro);
+#ifdef SCTP_DEBUG
+		if (sctp_debug_on & SCTP_DEBUG_OUTPUT2) {
+			printf("sctp_shutdown_complete2 calling ip_output:\n");
+			sctp_print_address_pkt(iph_out, &comp_cp->sh);
+		}
+#endif
+		/* set IPv4 length */
+#if defined(__FreeBSD__)
+		iph_out->ip_len = mout->m_pkthdr.len;
+#else
+		iph_out->ip_len = htons(mout->m_pkthdr.len);
+#endif
+		/* out it goes */
+		ip_output(mout, 0, &ro, IP_RAWOUTPUT, NULL
+#if defined(__OpenBSD__) || (defined(__FreeBSD__) && __FreeBSD_version >= 480000) \
+    || defined(__NetBSD__) || defined(__DragonFly__)
+		    , NULL
+#endif
+		    );
+		/* Free the route if we got one back */
+		if (ro.ro_rt)
+			RTFREE(ro.ro_rt);
+	} else if (ip6_out != NULL) {
+#ifdef NEW_STRUCT_ROUTE
+		struct route ro;
+#else
+		struct route_in6 ro;
+#endif
+
+		bzero(&ro, sizeof(ro));
+#ifdef SCTP_DEBUG
+		if (sctp_debug_on & SCTP_DEBUG_OUTPUT2) {
+			printf("sctp_shutdown_complete2 calling ip6_output:\n");
+			sctp_print_address_pkt((struct ip *)ip6_out,
+			    &comp_cp->sh);
+		}
+#endif
+		ip6_output(mout, NULL, &ro, 0, NULL, NULL
+#if defined(__NetBSD__)
+			   , NULL
+#endif
+#if (defined(__FreeBSD__) && __FreeBSD_version >= 480000) || defined(__DragonFly__)
+			   , NULL
+#endif
+		    );
+		/* Free the route if we got one back */
+		if (ro.ro_rt)
+			RTFREE(ro.ro_rt);
+	}
+	sctp_pegs[SCTP_DATAGRAMS_SENT]++;
+	return (0);
+}
+
+static struct sctp_nets *
+sctp_select_hb_destination(struct sctp_tcb *stcb, struct timeval *now)
+{
+	struct sctp_nets *net, *hnet;
+	int ms_goneby, highest_ms, state_overide=0;
+
+	SCTP_GETTIME_TIMEVAL(now);
+	highest_ms = 0;
+	hnet = NULL;
+	TAILQ_FOREACH(net, &stcb->asoc.nets, sctp_next) {
+		if (
+			((net->dest_state & SCTP_ADDR_NOHB) && ((net->dest_state & SCTP_ADDR_UNCONFIRMED) == 0)) ||
+			(net->dest_state & SCTP_ADDR_OUT_OF_SCOPE)
+			) {
+			/* Skip this guy from consideration if HB is off AND its confirmed*/
+#ifdef SCTP_DEBUG
+			if (sctp_debug_on & SCTP_DEBUG_OUTPUT4) {
+				printf("Skipping net:%p state:%d nohb/out-of-scope\n",
+				       net, net->dest_state);
+			}
+#endif
+			continue;
+		}
+		if (sctp_destination_is_reachable(stcb, (struct sockaddr *)&net->ro._l_addr) == 0) {
+			/* skip this dest net from consideration */
+#ifdef SCTP_DEBUG
+			if (sctp_debug_on & SCTP_DEBUG_OUTPUT4) {
+				printf("Skipping net:%p reachable NOT\n",
+				       net);
+			}
+#endif
+			continue;
+		}
+		if (net->last_sent_time.tv_sec) {
+			/* Sent to so we subtract */
+			ms_goneby = (now->tv_sec - net->last_sent_time.tv_sec) * 1000;
+		} else
+			/* Never been sent to */
+			ms_goneby = 0x7fffffff;
+#ifdef SCTP_DEBUG
+		if (sctp_debug_on & SCTP_DEBUG_OUTPUT4) {
+			printf("net:%p ms_goneby:%d\n",
+			       net, ms_goneby);
+		}
+#endif
+		/* When the address state is unconfirmed but still considered reachable, we
+		 * HB at a higher rate. Once it goes confirmed OR reaches the "unreachable"
+		 * state, thenw we cut it back to HB at a more normal pace.
+		 */
+		if ((net->dest_state & (SCTP_ADDR_UNCONFIRMED|SCTP_ADDR_NOT_REACHABLE)) == SCTP_ADDR_UNCONFIRMED) {
+			state_overide = 1;
+		} else {
+			state_overide = 0;
+		}
+
+		if ((((unsigned int)ms_goneby >= net->RTO) || (state_overide)) &&
+		    (ms_goneby > highest_ms)) {
+			highest_ms = ms_goneby;
+			hnet = net;
+#ifdef SCTP_DEBUG
+			if (sctp_debug_on & SCTP_DEBUG_OUTPUT4) {
+				printf("net:%p is the new high\n",
+				       net);
+			}
+#endif
+		}
+	}
+	if (hnet &&
+	   ((hnet->dest_state & (SCTP_ADDR_UNCONFIRMED|SCTP_ADDR_NOT_REACHABLE)) == SCTP_ADDR_UNCONFIRMED)) {
+		state_overide = 1;
+	} else {
+		state_overide = 0;
+	}
+
+	if (highest_ms && (((unsigned int)highest_ms >= hnet->RTO) || state_overide)) {
+		/* Found the one with longest delay bounds
+		 * OR it is unconfirmed and still not marked
+		 * unreachable.
+		 */
+#ifdef SCTP_DEBUG
+		if (sctp_debug_on & SCTP_DEBUG_OUTPUT4) {
+			printf("net:%p is the hb winner -",
+				hnet);
+			if (hnet)
+				sctp_print_address((struct sockaddr *)&hnet->ro._l_addr);
+			else
+				printf(" none\n");
+		}
+#endif
+		/* update the timer now */
+		hnet->last_sent_time = *now;
+		return (hnet);
+	}
+	/* Nothing to HB */
+	return (NULL);
+}
+
+int
+sctp_send_hb(struct sctp_tcb *stcb, int user_req, struct sctp_nets *u_net)
+{
+	struct sctp_tmit_chunk *chk;
+	struct sctp_nets *net;
+	struct sctp_heartbeat_chunk *hb;
+	struct timeval now;
+	struct sockaddr_in *sin;
+	struct sockaddr_in6 *sin6;
+
+	if (user_req == 0) {
+		net = sctp_select_hb_destination(stcb, &now);
+		if (net == NULL) {
+			/* All our busy none to send to, just
+			 * start the timer again.
+			 */
+			if (stcb->asoc.state == 0) {
+				return (0);
+			}
+			sctp_timer_start(SCTP_TIMER_TYPE_HEARTBEAT,
+					 stcb->sctp_ep,
+					 stcb,
+					 net);
+			return (0);
+		}
+#ifndef SCTP_USE_ALLMAN_BURST
+		else {
+			/* found one idle.. decay cwnd on this one
+			 * by 1/2 if none outstanding.
+			 */
+
+			if (net->flight_size == 0) {
+				net->cwnd /= 2;
+				if (net->addr_is_local) {
+					if (net->cwnd < (net->mtu *4)) {
+						net->cwnd = net->mtu * 4;
+					}
+				} else {
+					if (net->cwnd < (net->mtu * 2)) {
+						net->cwnd = net->mtu * 2;
+					}
+				}
+
+			}
+
+		}
+#endif
+	} else {
+		net = u_net;
+		if (net == NULL) {
+			return (0);
+		}
+		SCTP_GETTIME_TIMEVAL(&now);
+	}
+	sin = (struct sockaddr_in *)&net->ro._l_addr;
+	if (sin->sin_family != AF_INET) {
+		if (sin->sin_family != AF_INET6) {
+			/* huh */
+			return (0);
+		}
+	}
+	chk = (struct sctp_tmit_chunk *)SCTP_ZONE_GET(sctppcbinfo.ipi_zone_chunk);
+	if (chk == NULL) {
+#ifdef SCTP_DEBUG
+		if (sctp_debug_on & SCTP_DEBUG_OUTPUT4) {
+			printf("Gak, can't get a chunk for hb\n");
+		}
+#endif
+		return (0);
+	}
+	sctppcbinfo.ipi_gencnt_chunk++;
+	sctppcbinfo.ipi_count_chunk++;
+	chk->rec.chunk_id = SCTP_HEARTBEAT_REQUEST;
+	chk->asoc = &stcb->asoc;
+	chk->send_size = sizeof(struct sctp_heartbeat_chunk);
+	MGETHDR(chk->data, MB_DONTWAIT, MT_DATA);
+	if (chk->data == NULL) {
+		SCTP_ZONE_FREE(sctppcbinfo.ipi_zone_chunk, chk);
+		sctppcbinfo.ipi_count_chunk--;
+		if ((int)sctppcbinfo.ipi_count_chunk < 0) {
+			panic("Chunk count is negative");
+		}
+		sctppcbinfo.ipi_gencnt_chunk++;
+		return (0);
+	}
+	chk->data->m_data += SCTP_MIN_OVERHEAD;
+	chk->data->m_pkthdr.len = chk->data->m_len = chk->send_size;
+	chk->sent = SCTP_DATAGRAM_UNSENT;
+	chk->snd_count = 0;
+	chk->whoTo = net;
+	chk->whoTo->ref_count++;
+	/* Now we have a mbuf that we can fill in with the details */
+	hb = mtod(chk->data, struct sctp_heartbeat_chunk *);
+
+	/* fill out chunk header */
+	hb->ch.chunk_type = SCTP_HEARTBEAT_REQUEST;
+	hb->ch.chunk_flags = 0;
+	hb->ch.chunk_length = htons(chk->send_size);
+	/* Fill out hb parameter */
+	hb->heartbeat.hb_info.ph.param_type = htons(SCTP_HEARTBEAT_INFO);
+	hb->heartbeat.hb_info.ph.param_length = htons(sizeof(struct sctp_heartbeat_info_param));
+	hb->heartbeat.hb_info.time_value_1 = now.tv_sec;
+	hb->heartbeat.hb_info.time_value_2 = now.tv_usec;
+	/* Did our user request this one, put it in */
+	hb->heartbeat.hb_info.user_req = user_req;
+	hb->heartbeat.hb_info.addr_family = sin->sin_family;
+	hb->heartbeat.hb_info.addr_len = sin->sin_len;
+	if (net->dest_state & SCTP_ADDR_UNCONFIRMED) {
+		/* we only take from the entropy pool if the address is
+		 * not confirmed.
+		 */
+ 		net->heartbeat_random1 = hb->heartbeat.hb_info.random_value1 = sctp_select_initial_TSN(&stcb->sctp_ep->sctp_ep);
+ 		net->heartbeat_random2 = hb->heartbeat.hb_info.random_value2 = sctp_select_initial_TSN(&stcb->sctp_ep->sctp_ep);
+	} else {
+		net->heartbeat_random1 = hb->heartbeat.hb_info.random_value1 = 0;
+		net->heartbeat_random2 = hb->heartbeat.hb_info.random_value2 = 0;
+	}
+	if (sin->sin_family == AF_INET) {
+		memcpy(hb->heartbeat.hb_info.address, &sin->sin_addr, sizeof(sin->sin_addr));
+	} else if (sin->sin_family == AF_INET6) {
+		/* We leave the scope the way it is in our lookup table. */
+		sin6 = (struct sockaddr_in6 *)&net->ro._l_addr;
+		memcpy(hb->heartbeat.hb_info.address, &sin6->sin6_addr, sizeof(sin6->sin6_addr));
+	} else {
+		/* huh compiler bug */
+#ifdef SCTP_DEBUG
+		if (sctp_debug_on & SCTP_DEBUG_OUTPUT1) {
+			printf("Compiler bug bleeds a mbuf and a chunk\n");
+		}
+#endif
+		return (0);
+	}
+	/* ok we have a destination that needs a beat */
+	/* lets do the theshold management Qiaobing style */
+	if (user_req == 0) {
+		if (sctp_threshold_management(stcb->sctp_ep, stcb, net,
+					      stcb->asoc.max_send_times)) {
+			/* we have lost the association, in a way this
+			 * is quite bad since we really are one less time
+			 * since we really did not send yet. This is the
+			 * down side to the Q's style as defined in the RFC
+			 * and not my alternate style defined in the RFC.
+			 */
+			if (chk->data != NULL) {
+				sctp_m_freem(chk->data);
+				chk->data = NULL;
+			}
+			SCTP_ZONE_FREE(sctppcbinfo.ipi_zone_chunk, chk);
+			sctppcbinfo.ipi_count_chunk--;
+			if ((int)sctppcbinfo.ipi_count_chunk < 0) {
+				panic("Chunk count is negative");
+			}
+			sctppcbinfo.ipi_gencnt_chunk++;
+			return (-1);
+		}
+	}
+	net->hb_responded = 0;
+#ifdef SCTP_DEBUG
+	if (sctp_debug_on & SCTP_DEBUG_OUTPUT4) {
+		printf("Inserting chunk for HB\n");
+	}
+#endif
+	TAILQ_INSERT_TAIL(&stcb->asoc.control_send_queue, chk, sctp_next);
+	stcb->asoc.ctrl_queue_cnt++;
+	sctp_pegs[SCTP_HB_SENT]++;
+	/*
+	 * Call directly med level routine to put out the chunk. It will
+	 * always tumble out control chunks aka HB but it may even tumble
+	 * out data too.
+	 */
+	if (user_req == 0) {
+		/* Ok now lets start the HB timer if it is NOT a user req */
+		sctp_timer_start(SCTP_TIMER_TYPE_HEARTBEAT, stcb->sctp_ep,
+				 stcb, net);
+	}
+	return (1);
+}
+
+void
+sctp_send_ecn_echo(struct sctp_tcb *stcb, struct sctp_nets *net,
+		   uint32_t high_tsn)
+{
+	struct sctp_association *asoc;
+	struct sctp_ecne_chunk *ecne;
+	struct sctp_tmit_chunk *chk;
+	asoc = &stcb->asoc;
+	TAILQ_FOREACH(chk, &asoc->control_send_queue, sctp_next) {
+		if (chk->rec.chunk_id == SCTP_ECN_ECHO) {
+			/* found a previous ECN_ECHO update it if needed */
+			ecne = mtod(chk->data, struct sctp_ecne_chunk *);
+			ecne->tsn = htonl(high_tsn);
+			return;
+		}
+	}
+	/* nope could not find one to update so we must build one */
+	chk = (struct sctp_tmit_chunk *)SCTP_ZONE_GET(sctppcbinfo.ipi_zone_chunk);
+	if (chk == NULL) {
+		return;
+	}
+	sctp_pegs[SCTP_ECNE_SENT]++;
+	sctppcbinfo.ipi_count_chunk++;
+	sctppcbinfo.ipi_gencnt_chunk++;
+	chk->rec.chunk_id = SCTP_ECN_ECHO;
+	chk->asoc = &stcb->asoc;
+	chk->send_size = sizeof(struct sctp_ecne_chunk);
+	MGETHDR(chk->data, MB_DONTWAIT, MT_DATA);
+	if (chk->data == NULL) {
+		SCTP_ZONE_FREE(sctppcbinfo.ipi_zone_chunk, chk);
+		sctppcbinfo.ipi_count_chunk--;
+		if ((int)sctppcbinfo.ipi_count_chunk < 0) {
+			panic("Chunk count is negative");
+		}
+		sctppcbinfo.ipi_gencnt_chunk++;
+		return;
+	}
+	chk->data->m_data += SCTP_MIN_OVERHEAD;
+	chk->data->m_pkthdr.len = chk->data->m_len = chk->send_size;
+	chk->sent = SCTP_DATAGRAM_UNSENT;
+	chk->snd_count = 0;
+	chk->whoTo = net;
+	chk->whoTo->ref_count++;
+	ecne = mtod(chk->data, struct sctp_ecne_chunk *);
+	ecne->ch.chunk_type = SCTP_ECN_ECHO;
+	ecne->ch.chunk_flags = 0;
+	ecne->ch.chunk_length = htons(sizeof(struct sctp_ecne_chunk));
+	ecne->tsn = htonl(high_tsn);
+	TAILQ_INSERT_TAIL(&stcb->asoc.control_send_queue, chk, sctp_next);
+	asoc->ctrl_queue_cnt++;
+}
+
+void
+sctp_send_packet_dropped(struct sctp_tcb *stcb, struct sctp_nets *net,
+			 struct mbuf *m, int iphlen, int bad_crc)
+{
+	struct sctp_association *asoc;
+	struct sctp_pktdrop_chunk *drp;
+	struct sctp_tmit_chunk *chk;
+	uint8_t *datap;
+	int len;
+	unsigned int small_one;
+	struct ip *iph;
+
+	long spc;
+	asoc = &stcb->asoc;
+	if (asoc->peer_supports_pktdrop == 0) {
+		/* peer must declare support before I
+		 * send one.
+		 */
+		return;
+	}
+	chk = (struct sctp_tmit_chunk *)SCTP_ZONE_GET(sctppcbinfo.ipi_zone_chunk);
+	if (chk == NULL) {
+		return;
+	}
+	sctppcbinfo.ipi_count_chunk++;
+	sctppcbinfo.ipi_gencnt_chunk++;
+
+	iph = mtod(m, struct ip *);
+	if (iph == NULL) {
+		return;
+	}
+	if (iph->ip_v == IPVERSION) {
+		/* IPv4 */
+#if defined(__FreeBSD__)
+		len = chk->send_size = iph->ip_len;
+#else
+		len = chk->send_size = (iph->ip_len - iphlen);
+#endif
+	} else {
+		struct ip6_hdr *ip6h;
+		/* IPv6 */
+		ip6h = mtod(m, struct ip6_hdr *);
+		len = chk->send_size = htons(ip6h->ip6_plen);
+	}
+	if ((len+iphlen) > m->m_pkthdr.len) {
+		/* huh */
+		chk->send_size = len = m->m_pkthdr.len - iphlen;
+	}
+	chk->asoc = &stcb->asoc;
+	MGETHDR(chk->data, MB_DONTWAIT, MT_DATA);
+	if (chk->data == NULL) {
+	jump_out:
+		SCTP_ZONE_FREE(sctppcbinfo.ipi_zone_chunk, chk);
+		sctppcbinfo.ipi_count_chunk--;
+		if ((int)sctppcbinfo.ipi_count_chunk < 0) {
+			panic("Chunk count is negative");
+		}
+		sctppcbinfo.ipi_gencnt_chunk++;
+		return;
+	}
+	if ((chk->send_size+sizeof(struct sctp_pktdrop_chunk)+SCTP_MIN_OVERHEAD) > MHLEN) {
+		MCLGET(chk->data, MB_DONTWAIT);
+		if ((chk->data->m_flags & M_EXT) == 0) {
+			/* Give up */
+			sctp_m_freem(chk->data);
+			chk->data = NULL;
+			goto jump_out;
+		}
+	}
+	chk->data->m_data += SCTP_MIN_OVERHEAD;
+	drp = mtod(chk->data, struct sctp_pktdrop_chunk *);
+	if (drp == NULL) {
+		sctp_m_freem(chk->data);
+		chk->data = NULL;
+		goto jump_out;
+	}
+	small_one = asoc->smallest_mtu;
+	if (small_one > MCLBYTES) {
+		/* Only one cluster worth of data MAX */
+		small_one = MCLBYTES;
+	}
+	chk->book_size = (chk->send_size + sizeof(struct sctp_pktdrop_chunk) +
+			  sizeof(struct sctphdr) + SCTP_MED_OVERHEAD);
+	if (chk->book_size > small_one) {
+		drp->ch.chunk_flags = SCTP_PACKET_TRUNCATED;
+		drp->trunc_len = htons(chk->send_size);
+		chk->send_size = small_one - (SCTP_MED_OVERHEAD +
+					     sizeof(struct sctp_pktdrop_chunk) +
+					     sizeof(struct sctphdr));
+		len = chk->send_size;
+	} else {
+		/* no truncation needed */
+		drp->ch.chunk_flags = 0;
+		drp->trunc_len = htons(0);
+	}
+	if (bad_crc) {
+		drp->ch.chunk_flags |= SCTP_BADCRC;
+	}
+	chk->send_size += sizeof(struct sctp_pktdrop_chunk);
+	chk->data->m_pkthdr.len = chk->data->m_len = chk->send_size;
+	chk->sent = SCTP_DATAGRAM_UNSENT;
+	chk->snd_count = 0;
+	if (net) {
+		/* we should hit here */
+		chk->whoTo = net;
+	} else {
+		chk->whoTo = asoc->primary_destination;
+	}
+	chk->whoTo->ref_count++;
+	chk->rec.chunk_id = SCTP_PACKET_DROPPED;
+	drp->ch.chunk_type = SCTP_PACKET_DROPPED;
+	drp->ch.chunk_length = htons(chk->send_size);
+	spc = stcb->sctp_socket->so_rcv.sb_hiwat;
+	if (spc < 0) {
+		spc = 0;
+	}
+	drp->bottle_bw = htonl(spc);
+	drp->current_onq = htonl(asoc->size_on_delivery_queue +
+				 asoc->size_on_reasm_queue +
+				 asoc->size_on_all_streams +
+				 asoc->my_rwnd_control_len +
+		                 stcb->sctp_socket->so_rcv.sb_cc);
+	drp->reserved = 0;
+	datap = drp->data;
+        m_copydata(m, iphlen, len, datap);
+	TAILQ_INSERT_TAIL(&stcb->asoc.control_send_queue, chk, sctp_next);
+	asoc->ctrl_queue_cnt++;
+}
+
+void
+sctp_send_cwr(struct sctp_tcb *stcb, struct sctp_nets *net, uint32_t high_tsn)
+{
+	struct sctp_association *asoc;
+	struct sctp_cwr_chunk *cwr;
+	struct sctp_tmit_chunk *chk;
+
+	asoc = &stcb->asoc;
+	TAILQ_FOREACH(chk, &asoc->control_send_queue, sctp_next) {
+		if (chk->rec.chunk_id == SCTP_ECN_CWR) {
+			/* found a previous ECN_CWR update it if needed */
+			cwr = mtod(chk->data, struct sctp_cwr_chunk *);
+			if (compare_with_wrap(high_tsn, ntohl(cwr->tsn),
+					      MAX_TSN)) {
+				cwr->tsn = htonl(high_tsn);
+			}
+			return;
+		}
+	}
+	/* nope could not find one to update so we must build one */
+	chk = (struct sctp_tmit_chunk *)SCTP_ZONE_GET(sctppcbinfo.ipi_zone_chunk);
+	if (chk == NULL) {
+		return;
+	}
+	sctppcbinfo.ipi_count_chunk++;
+	sctppcbinfo.ipi_gencnt_chunk++;
+	chk->rec.chunk_id = SCTP_ECN_CWR;
+	chk->asoc = &stcb->asoc;
+	chk->send_size = sizeof(struct sctp_cwr_chunk);
+	MGETHDR(chk->data, MB_DONTWAIT, MT_DATA);
+	if (chk->data == NULL) {
+		SCTP_ZONE_FREE(sctppcbinfo.ipi_zone_chunk, chk);
+		sctppcbinfo.ipi_count_chunk--;
+		if ((int)sctppcbinfo.ipi_count_chunk < 0) {
+			panic("Chunk count is negative");
+		}
+		sctppcbinfo.ipi_gencnt_chunk++;
+		return;
+	}
+	chk->data->m_data += SCTP_MIN_OVERHEAD;
+	chk->data->m_pkthdr.len = chk->data->m_len = chk->send_size;
+	chk->sent = SCTP_DATAGRAM_UNSENT;
+	chk->snd_count = 0;
+	chk->whoTo = net;
+	chk->whoTo->ref_count++;
+	cwr = mtod(chk->data, struct sctp_cwr_chunk *);
+	cwr->ch.chunk_type = SCTP_ECN_CWR;
+	cwr->ch.chunk_flags = 0;
+	cwr->ch.chunk_length = htons(sizeof(struct sctp_cwr_chunk));
+	cwr->tsn = htonl(high_tsn);
+	TAILQ_INSERT_TAIL(&stcb->asoc.control_send_queue, chk, sctp_next);
+	asoc->ctrl_queue_cnt++;
+}
+static void
+sctp_reset_the_streams(struct sctp_tcb *stcb,
+     struct sctp_stream_reset_request *req, int number_entries, uint16_t *list)
+{
+	int i;
+
+	if (req->reset_flags & SCTP_RESET_ALL) {
+		for (i=0; i<stcb->asoc.streamoutcnt; i++) {
+			stcb->asoc.strmout[i].next_sequence_sent = 0;
+		}
+	} else if (number_entries) {
+		for (i=0; i<number_entries; i++) {
+			if (list[i] >= stcb->asoc.streamoutcnt) {
+				/* no such stream */
+				continue;
+			}
+			stcb->asoc.strmout[(list[i])].next_sequence_sent = 0;
+		}
+	}
+	sctp_ulp_notify(SCTP_NOTIFY_STR_RESET_SEND, stcb, number_entries, (void *)list);
+}
+
+void
+sctp_send_str_reset_ack(struct sctp_tcb *stcb,
+     struct sctp_stream_reset_request *req)
+{
+	struct sctp_association *asoc;
+	struct sctp_stream_reset_resp *strack;
+	struct sctp_tmit_chunk *chk;
+	uint32_t seq;
+	int number_entries, i;
+	uint8_t two_way=0, not_peer=0;
+	uint16_t *list=NULL;
+
+	asoc = &stcb->asoc;
+	if (req->reset_flags & SCTP_RESET_ALL)
+		number_entries = 0;
+	else
+		number_entries = (ntohs(req->ph.param_length) - sizeof(struct sctp_stream_reset_request)) / sizeof(uint16_t);
+
+	chk = (struct sctp_tmit_chunk *)SCTP_ZONE_GET(sctppcbinfo.ipi_zone_chunk);
+	if (chk == NULL) {
+		return;
+	}
+	sctppcbinfo.ipi_count_chunk++;
+	sctppcbinfo.ipi_gencnt_chunk++;
+	chk->rec.chunk_id = SCTP_STREAM_RESET;
+	chk->asoc = &stcb->asoc;
+	chk->send_size = sizeof(struct sctp_stream_reset_resp) + (number_entries * sizeof(uint16_t));
+	MGETHDR(chk->data, MB_DONTWAIT, MT_DATA);
+	if (chk->data == NULL) {
+	strresp_jump_out:
+		SCTP_ZONE_FREE(sctppcbinfo.ipi_zone_chunk, chk);
+		sctppcbinfo.ipi_count_chunk--;
+		if ((int)sctppcbinfo.ipi_count_chunk < 0) {
+			panic("Chunk count is negative");
+		}
+		sctppcbinfo.ipi_gencnt_chunk++;
+		return;
+	}
+	chk->data->m_data += SCTP_MIN_OVERHEAD;
+	chk->data->m_pkthdr.len = chk->data->m_len = SCTP_SIZE32(chk->send_size);
+	if (M_TRAILINGSPACE(chk->data) < (int)SCTP_SIZE32(chk->send_size)) {
+		MCLGET(chk->data, MB_DONTWAIT);
+		if ((chk->data->m_flags & M_EXT) == 0) {
+			/* Give up */
+			sctp_m_freem(chk->data);
+			chk->data = NULL;
+			goto strresp_jump_out;
+		}
+		chk->data->m_data += SCTP_MIN_OVERHEAD;
+	}
+	if (M_TRAILINGSPACE(chk->data) < (int)SCTP_SIZE32(chk->send_size)) {
+		/* can't do it, no room */
+		/* Give up */
+		sctp_m_freem(chk->data);
+		chk->data = NULL;
+		goto strresp_jump_out;
+
+	}
+	chk->sent = SCTP_DATAGRAM_UNSENT;
+	chk->snd_count = 0;
+	chk->whoTo = asoc->primary_destination;
+	chk->whoTo->ref_count++;
+	strack = mtod(chk->data, struct sctp_stream_reset_resp *);
+
+	strack->ch.chunk_type = SCTP_STREAM_RESET;
+	strack->ch.chunk_flags = 0;
+	strack->ch.chunk_length = htons(chk->send_size);
+
+	memset(strack->sr_resp.reset_pad, 0, sizeof(strack->sr_resp.reset_pad));
+
+	strack->sr_resp.ph.param_type = ntohs(SCTP_STR_RESET_RESPONSE);
+	strack->sr_resp.ph.param_length = htons((chk->send_size - sizeof(struct sctp_chunkhdr)));
+
+
+
+	if (chk->send_size % 4) {
+		/* need a padding for the end */
+		int pad;
+		uint8_t *end;
+		end = (uint8_t *)((caddr_t)strack + chk->send_size);
+		pad = chk->send_size % 4;
+		for (i = 0; i < pad; i++) {
+			end[i] = 0;
+		}
+		chk->send_size += pad;
+	}
+
+        /* actual response */
+	if (req->reset_flags & SCTP_RESET_YOUR) {
+		strack->sr_resp.reset_flags = SCTP_RESET_PERFORMED;
+	} else {
+		strack->sr_resp.reset_flags = 0;
+	}
+
+	/* copied from reset request */
+	strack->sr_resp.reset_req_seq_resp = req->reset_req_seq;
+	seq = ntohl(req->reset_req_seq);
+
+	list = req->list_of_streams;
+	/* copy the un-converted network byte order streams */
+	for (i=0; i<number_entries; i++) {
+		strack->sr_resp.list_of_streams[i] = list[i];
+	}
+	if (asoc->str_reset_seq_in == seq) {
+		/* is it the next expected? */
+		asoc->str_reset_seq_in++;
+		strack->sr_resp.reset_at_tsn = htonl(asoc->sending_seq);
+		asoc->str_reset_sending_seq = asoc->sending_seq;
+		if (number_entries) {
+			int i;
+			uint16_t temp;
+			/* convert them to host byte order */
+			for (i=0 ; i<number_entries; i++) {
+				temp = ntohs(list[i]);
+				list[i] = temp;
+			}
+		}
+		if (req->reset_flags & SCTP_RESET_YOUR) {
+			/* reset my outbound streams */
+			sctp_reset_the_streams(stcb, req , number_entries, list);
+		}
+		if (req->reset_flags & SCTP_RECIPRICAL) {
+			/* reset peer too */
+			sctp_send_str_reset_req(stcb, number_entries, list, two_way, not_peer);
+		}
+
+	} else {
+		/* no its a retran so I must just ack and do nothing */
+		strack->sr_resp.reset_at_tsn = htonl(asoc->str_reset_sending_seq);
+	}
+	strack->sr_resp.cumulative_tsn = htonl(asoc->cumulative_tsn);
+	TAILQ_INSERT_TAIL(&asoc->control_send_queue,
+			  chk,
+			  sctp_next);
+	asoc->ctrl_queue_cnt++;
+}
+
+
+void
+sctp_send_str_reset_req(struct sctp_tcb *stcb,
+     int number_entrys, uint16_t *list, uint8_t two_way, uint8_t not_peer)
+{
+	/* Send a stream reset request. The number_entrys may be 0 and list NULL
+	 * if the request is to reset all streams. If two_way is true then we
+	 * not only request a RESET of the received streams but we also
+	 * request the peer to send a reset req to us too.
+	 * Flag combinations in table:
+	 *
+	 *       two_way | not_peer  | = | Flags
+	 *       ------------------------------
+	 *         0     |    0      | = | SCTP_RESET_YOUR (just the peer)
+	 *         1     |    0      | = | SCTP_RESET_YOUR | SCTP_RECIPRICAL (both sides)
+	 *         0     |    1      | = | Not a Valid Request (not anyone)
+	 *         1     |    1      | = | SCTP_RESET_RECIPRICAL (Just local host)
+	 */
+	struct sctp_association *asoc;
+	struct sctp_stream_reset_req *strreq;
+	struct sctp_tmit_chunk *chk;
+
+
+	asoc = &stcb->asoc;
+	if (asoc->stream_reset_outstanding) {
+		/* Already one pending, must get ACK back
+		 * to clear the flag.
+		 */
+		return;
+	}
+
+	if ((two_way == 0) && (not_peer == 1)) {
+		/* not a valid request */
+		return;
+	}
+
+	chk = (struct sctp_tmit_chunk *)SCTP_ZONE_GET(sctppcbinfo.ipi_zone_chunk);
+	if (chk == NULL) {
+		return;
+	}
+	sctppcbinfo.ipi_count_chunk++;
+	sctppcbinfo.ipi_gencnt_chunk++;
+	chk->rec.chunk_id = SCTP_STREAM_RESET;
+	chk->asoc = &stcb->asoc;
+	chk->send_size = sizeof(struct sctp_stream_reset_req) + (number_entrys * sizeof(uint16_t));
+	MGETHDR(chk->data, MB_DONTWAIT, MT_DATA);
+	if (chk->data == NULL) {
+	strreq_jump_out:
+		SCTP_ZONE_FREE(sctppcbinfo.ipi_zone_chunk, chk);
+		sctppcbinfo.ipi_count_chunk--;
+		if ((int)sctppcbinfo.ipi_count_chunk < 0) {
+			panic("Chunk count is negative");
+		}
+		sctppcbinfo.ipi_gencnt_chunk++;
+		return;
+	}
+	chk->data->m_data += SCTP_MIN_OVERHEAD;
+	chk->data->m_pkthdr.len = chk->data->m_len = SCTP_SIZE32(chk->send_size);
+	if (M_TRAILINGSPACE(chk->data) < (int)SCTP_SIZE32(chk->send_size)) {
+		MCLGET(chk->data, MB_DONTWAIT);
+		if ((chk->data->m_flags & M_EXT) == 0) {
+			/* Give up */
+			sctp_m_freem(chk->data);
+			chk->data = NULL;
+			goto strreq_jump_out;
+		}
+		chk->data->m_data += SCTP_MIN_OVERHEAD;
+	}
+	if (M_TRAILINGSPACE(chk->data) < (int)SCTP_SIZE32(chk->send_size)) {
+		/* can't do it, no room */
+		/* Give up */
+		sctp_m_freem(chk->data);
+		chk->data = NULL;
+		goto strreq_jump_out;
+	}
+	chk->sent = SCTP_DATAGRAM_UNSENT;
+	chk->snd_count = 0;
+	chk->whoTo = asoc->primary_destination;
+	chk->whoTo->ref_count++;
+
+	strreq = mtod(chk->data, struct sctp_stream_reset_req *);
+	strreq->ch.chunk_type = SCTP_STREAM_RESET;
+	strreq->ch.chunk_flags = 0;
+	strreq->ch.chunk_length = htons(chk->send_size);
+
+	strreq->sr_req.ph.param_type = ntohs(SCTP_STR_RESET_REQUEST);
+	strreq->sr_req.ph.param_length = htons((chk->send_size - sizeof(struct sctp_chunkhdr)));
+
+	if (chk->send_size % 4) {
+		/* need a padding for the end */
+		int pad, i;
+		uint8_t *end;
+		end = (uint8_t *)((caddr_t)strreq + chk->send_size);
+		pad = chk->send_size % 4;
+		for (i=0; i<pad; i++) {
+			end[i] = 0;
+		}
+		chk->send_size += pad;
+	}
+
+	strreq->sr_req.reset_flags = 0;
+	if (number_entrys == 0) {
+		strreq->sr_req.reset_flags |= SCTP_RESET_ALL;
+	}
+	if (two_way == 0) {
+		strreq->sr_req.reset_flags |= SCTP_RESET_YOUR;
+	} else {
+		if (not_peer == 0) {
+			strreq->sr_req.reset_flags |= SCTP_RECIPRICAL | SCTP_RESET_YOUR;
+		} else {
+			strreq->sr_req.reset_flags |= SCTP_RECIPRICAL;
+		}
+	}
+	memset(strreq->sr_req.reset_pad, 0, sizeof(strreq->sr_req.reset_pad));
+	strreq->sr_req.reset_req_seq = htonl(asoc->str_reset_seq_out);
+	if (number_entrys) {
+		/* populate the specific entry's */
+		int i;
+		for (i=0; i < number_entrys; i++) {
+			strreq->sr_req.list_of_streams[i] = htons(list[i]);
+		}
+	}
+	TAILQ_INSERT_TAIL(&asoc->control_send_queue,
+			  chk,
+			  sctp_next);
+	asoc->ctrl_queue_cnt++;
+	sctp_timer_start(SCTP_TIMER_TYPE_STRRESET, stcb->sctp_ep, stcb, chk->whoTo);
+	asoc->stream_reset_outstanding = 1;
+}
+
+void
+sctp_send_abort(struct mbuf *m, int iphlen, struct sctphdr *sh, uint32_t vtag,
+    struct mbuf *err_cause)
+{
+	/*
+	 * Formulate the abort message, and send it back down.
+	 */
+	struct mbuf *mout;
+	struct sctp_abort_msg *abm;
+	struct ip *iph, *iph_out;
+	struct ip6_hdr *ip6, *ip6_out;
+	int iphlen_out;
+
+	/* don't respond to ABORT with ABORT */
+	if (sctp_is_there_an_abort_here(m, iphlen, &vtag)) {
+		if (err_cause)
+			sctp_m_freem(err_cause);
+		return;
+	}
+	MGETHDR(mout, MB_DONTWAIT, MT_HEADER);
+	if (mout == NULL) {
+		if (err_cause)
+			sctp_m_freem(err_cause);
+		return;
+	}
+	iph = mtod(m, struct ip *);
+	iph_out = NULL;
+	ip6_out = NULL;
+	if (iph->ip_v == IPVERSION) {
+		iph_out = mtod(mout, struct ip *);
+		mout->m_len = sizeof(*iph_out) + sizeof(*abm);
+		mout->m_next = err_cause;
+
+		/* Fill in the IP header for the ABORT */
+		iph_out->ip_v = IPVERSION;
+		iph_out->ip_hl = (sizeof(struct ip) / 4);
+		iph_out->ip_tos = (u_char)0;
+		iph_out->ip_id = 0;
+		iph_out->ip_off = 0;
+		iph_out->ip_ttl = MAXTTL;
+		iph_out->ip_p = IPPROTO_SCTP;
+		iph_out->ip_src.s_addr = iph->ip_dst.s_addr;
+		iph_out->ip_dst.s_addr = iph->ip_src.s_addr;
+		/* let IP layer calculate this */
+		iph_out->ip_sum = 0;
+
+		iphlen_out = sizeof(*iph_out);
+		abm = (struct sctp_abort_msg *)((caddr_t)iph_out + iphlen_out);
+	} else if (iph->ip_v == (IPV6_VERSION >> 4)) {
+		ip6 = (struct ip6_hdr *)iph;
+		ip6_out = mtod(mout, struct ip6_hdr *);
+		mout->m_len = sizeof(*ip6_out) + sizeof(*abm);
+		mout->m_next = err_cause;
+
+		/* Fill in the IP6 header for the ABORT */
+		ip6_out->ip6_flow = ip6->ip6_flow;
+		ip6_out->ip6_hlim = ip6_defhlim;
+		ip6_out->ip6_nxt = IPPROTO_SCTP;
+		ip6_out->ip6_src = ip6->ip6_dst;
+		ip6_out->ip6_dst = ip6->ip6_src;
+
+		iphlen_out = sizeof(*ip6_out);
+		abm = (struct sctp_abort_msg *)((caddr_t)ip6_out + iphlen_out);
+	} else {
+		/* Currently not supported */
+		return;
+	}
+
+	abm->sh.src_port = sh->dest_port;
+	abm->sh.dest_port = sh->src_port;
+	abm->sh.checksum = 0;
+	if (vtag == 0) {
+		abm->sh.v_tag = sh->v_tag;
+		abm->msg.ch.chunk_flags = SCTP_HAD_NO_TCB;
+	} else {
+		abm->sh.v_tag = htonl(vtag);
+		abm->msg.ch.chunk_flags = 0;
+	}
+	abm->msg.ch.chunk_type = SCTP_ABORT_ASSOCIATION;
+
+	if (err_cause) {
+		struct mbuf *m_tmp = err_cause;
+		int err_len = 0;
+		/* get length of the err_cause chain */
+		while (m_tmp != NULL) {
+			err_len += m_tmp->m_len;
+			m_tmp = m_tmp->m_next;
+		}
+		mout->m_pkthdr.len = mout->m_len + err_len;
+		if (err_len % 4) {
+			/* need pad at end of chunk */
+			u_int32_t cpthis=0;
+			int padlen;
+			padlen = 4 - (mout->m_pkthdr.len % 4);
+			m_copyback(mout, mout->m_pkthdr.len, padlen, (caddr_t)&cpthis);
+		}
+		abm->msg.ch.chunk_length = htons(sizeof(abm->msg.ch) + err_len);
+	} else {
+		mout->m_pkthdr.len = mout->m_len;
+		abm->msg.ch.chunk_length = htons(sizeof(abm->msg.ch));
+	}
+
+	/* add checksum */
+	if ((sctp_no_csum_on_loopback) &&
+	   (m->m_pkthdr.rcvif) &&
+	   (m->m_pkthdr.rcvif->if_type == IFT_LOOP)) {
+		abm->sh.checksum =  0;
+	} else {
+		abm->sh.checksum = sctp_calculate_sum(mout, NULL, iphlen_out);
+	}
+
+	/* zap the rcvif, it should be null */
+	mout->m_pkthdr.rcvif = 0;
+	if (iph_out != NULL) {
+		struct route ro;
+
+		/* zap the stack pointer to the route */
+		bzero(&ro, sizeof ro);
+#ifdef SCTP_DEBUG
+		if (sctp_debug_on & SCTP_DEBUG_OUTPUT2) {
+                        printf("sctp_send_abort calling ip_output:\n");
+			sctp_print_address_pkt(iph_out, &abm->sh);
+                }
+#endif
+		/* set IPv4 length */
+#if defined(__FreeBSD__)
+		iph_out->ip_len = mout->m_pkthdr.len;
+#else
+		iph_out->ip_len = htons(mout->m_pkthdr.len);
+#endif
+		/* out it goes */
+		(void)ip_output(mout, 0, &ro, IP_RAWOUTPUT, NULL
+#if defined(__OpenBSD__) || (defined(__FreeBSD__) && __FreeBSD_version >= 480000) \
+    || defined(__NetBSD__) || defined(__DragonFly__)
+		    , NULL
+#endif
+		    );
+		/* Free the route if we got one back */
+		if (ro.ro_rt)
+			RTFREE(ro.ro_rt);
+	} else if (ip6_out != NULL) {
+#ifdef NEW_STRUCT_ROUTE
+		struct route ro;
+#else
+		struct route_in6 ro;
+#endif
+
+		/* zap the stack pointer to the route */
+		bzero(&ro, sizeof(ro));
+#ifdef SCTP_DEBUG
+		if (sctp_debug_on & SCTP_DEBUG_OUTPUT2) {
+                        printf("sctp_send_abort calling ip6_output:\n");
+			sctp_print_address_pkt((struct ip *)ip6_out, &abm->sh);
+                }
+#endif
+		ip6_output(mout, NULL, &ro, 0, NULL, NULL
+#if defined(__NetBSD__)
+			, NULL
+#endif
+#if (defined(__FreeBSD__) && __FreeBSD_version >= 480000) || defined(__DragonFly__)
+		    , NULL
+#endif
+		    );
+		/* Free the route if we got one back */
+		if (ro.ro_rt)
+			RTFREE(ro.ro_rt);
+	}
+	sctp_pegs[SCTP_DATAGRAMS_SENT]++;
+}
+
+void
+sctp_send_operr_to(struct mbuf *m, int iphlen,
+		   struct mbuf *scm,
+		   uint32_t vtag)
+{
+	struct sctphdr *ihdr;
+	int retcode;
+	struct sctphdr *ohdr;
+	struct sctp_chunkhdr *ophdr;
+
+	struct ip *iph;
+#ifdef SCTP_DEBUG
+	struct sockaddr_in6 lsa6, fsa6;
+#endif
+	uint32_t val;
+	iph = mtod(m, struct ip *);
+	ihdr = (struct sctphdr *)((caddr_t)iph + iphlen);
+	if (!(scm->m_flags & M_PKTHDR)) {
+		/* must be a pkthdr */
+		printf("Huh, not a packet header in send_operr\n");
+		m_freem(scm);
+		return;
+	}
+	M_PREPEND(scm, (sizeof(struct sctphdr) + sizeof(struct sctp_chunkhdr)), MB_DONTWAIT);
+	if (scm == NULL) {
+		/* can't send because we can't add a mbuf */
+		return;
+	}
+	ohdr = mtod(scm, struct sctphdr *);
+	ohdr->src_port = ihdr->dest_port;
+	ohdr->dest_port = ihdr->src_port;
+	ohdr->v_tag = vtag;
+	ohdr->checksum = 0;
+	ophdr = (struct sctp_chunkhdr *)(ohdr + 1);
+	ophdr->chunk_type = SCTP_OPERATION_ERROR;
+	ophdr->chunk_flags = 0;
+	ophdr->chunk_length = htons(scm->m_pkthdr.len - sizeof(struct sctphdr));
+	if (scm->m_pkthdr.len % 4) {
+		/* need padding */
+		u_int32_t cpthis=0;
+		int padlen;
+		padlen = 4 - (scm->m_pkthdr.len % 4);
+		m_copyback(scm, scm->m_pkthdr.len, padlen, (caddr_t)&cpthis);
+	}
+	if ((sctp_no_csum_on_loopback) &&
+	    (m->m_pkthdr.rcvif) &&
+	    (m->m_pkthdr.rcvif->if_type == IFT_LOOP)) {
+		val = 0;
+	} else {
+		val = sctp_calculate_sum(scm, NULL, 0);
+	}
+	ohdr->checksum = val;
+	if (iph->ip_v == IPVERSION) {
+		/* V4 */
+		struct ip *out;
+		struct route ro;
+		M_PREPEND(scm, sizeof(struct ip), MB_DONTWAIT);
+		if (scm == NULL)
+			return;
+		bzero(&ro, sizeof ro);
+		out = mtod(scm, struct ip *);
+		out->ip_v = iph->ip_v;
+		out->ip_hl = (sizeof(struct ip)/4);
+		out->ip_tos = iph->ip_tos;
+		out->ip_id = iph->ip_id;
+		out->ip_off = 0;
+		out->ip_ttl = MAXTTL;
+		out->ip_p = IPPROTO_SCTP;
+		out->ip_sum = 0;
+		out->ip_src = iph->ip_dst;
+		out->ip_dst = iph->ip_src;
+#if defined(__FreeBSD__)
+		out->ip_len = scm->m_pkthdr.len;
+#else
+		out->ip_len = htons(scm->m_pkthdr.len);
+#endif
+		retcode = ip_output(scm, 0, &ro, IP_RAWOUTPUT, NULL
+#if defined(__OpenBSD__) || (defined(__FreeBSD__) && __FreeBSD_version >= 480000) \
+    || defined(__NetBSD__) || defined(__DragonFly__)
+		    , NULL
+#endif
+			);
+		sctp_pegs[SCTP_DATAGRAMS_SENT]++;
+		/* Free the route if we got one back */
+		if (ro.ro_rt)
+			RTFREE(ro.ro_rt);
+	} else {
+		/* V6 */
+#ifdef NEW_STRUCT_ROUTE
+		struct route ro;
+#else
+		struct route_in6 ro;
+#endif
+		struct ip6_hdr *out6, *in6;
+
+		M_PREPEND(scm, sizeof(struct ip6_hdr), MB_DONTWAIT);
+		if (scm == NULL)
+			return;
+		bzero(&ro, sizeof ro);
+		in6 = mtod(m, struct ip6_hdr *);
+		out6 = mtod(scm, struct ip6_hdr *);
+		out6->ip6_flow = in6->ip6_flow;
+		out6->ip6_hlim = ip6_defhlim;
+		out6->ip6_nxt = IPPROTO_SCTP;
+		out6->ip6_src = in6->ip6_dst;
+		out6->ip6_dst = in6->ip6_src;
+
+#ifdef SCTP_DEBUG
+		bzero(&lsa6, sizeof(lsa6));
+		lsa6.sin6_len = sizeof(lsa6);
+		lsa6.sin6_family = AF_INET6;
+		lsa6.sin6_addr = out6->ip6_src;
+		bzero(&fsa6, sizeof(fsa6));
+		fsa6.sin6_len = sizeof(fsa6);
+		fsa6.sin6_family = AF_INET6;
+		fsa6.sin6_addr = out6->ip6_dst;
+		if (sctp_debug_on & SCTP_DEBUG_OUTPUT2) {
+			printf("sctp_operr_to calling ipv6 output:\n");
+			printf("src: ");
+			sctp_print_address((struct sockaddr *)&lsa6);
+			printf("dst ");
+			sctp_print_address((struct sockaddr *)&fsa6);
+		}
+#endif /* SCTP_DEBUG */
+		ip6_output(scm, NULL, &ro, 0, NULL, NULL
+#if defined(__NetBSD__)
+	    , NULL
+#endif
+#if (defined(__FreeBSD__) && __FreeBSD_version >= 480000) || defined(__DragonFly__)
+	    , NULL
+#endif
+		);
+		sctp_pegs[SCTP_DATAGRAMS_SENT]++;
+		/* Free the route if we got one back */
+		if (ro.ro_rt)
+			RTFREE(ro.ro_rt);
+	}
+}
+
+static int
+sctp_copy_one(struct mbuf *m, struct uio *uio, int cpsz, int resv_upfront, int *mbcnt)
+{
+	int left, cancpy, willcpy, error;
+	left = cpsz;
+
+	if (m == NULL) {
+		/* TSNH */
+		*mbcnt = 0;
+		return (ENOMEM);
+	}
+	m->m_len = 0;
+	if ((left+resv_upfront) > (int)MHLEN) {
+		MCLGET(m, MB_WAIT);
+		if (m == NULL) {
+			*mbcnt = 0;
+			return (ENOMEM);
+		}
+		if ((m->m_flags & M_EXT) == 0) {
+			*mbcnt = 0;
+			return (ENOMEM);
+		}
+		*mbcnt += m->m_ext.ext_size;
+	}
+	*mbcnt += MSIZE;
+	cancpy = M_TRAILINGSPACE(m);
+	willcpy = min(cancpy, left);
+	if ((willcpy + resv_upfront) > cancpy) {
+		willcpy -= resv_upfront;
+	}
+	while (left > 0) {
+		/* Align data to the end */
+		if ((m->m_flags & M_EXT) == 0) {
+			if (m->m_flags & M_PKTHDR) {
+				MH_ALIGN(m, willcpy);
+			} else {
+				M_ALIGN(m, willcpy);
+			}
+		} else {
+			MC_ALIGN(m, willcpy);
+		}
+		error = uiomove(mtod(m, caddr_t), willcpy, uio);
+		if (error) {
+			return (error);
+		}
+		m->m_len = willcpy;
+		m->m_nextpkt = 0;
+		left -= willcpy;
+		if (left > 0) {
+			MGET(m->m_next, MB_WAIT, MT_DATA);
+			if (m->m_next == NULL) {
+				*mbcnt = 0;
+				return (ENOMEM);
+			}
+			m = m->m_next;
+			m->m_len = 0;
+			*mbcnt += MSIZE;
+			if (left > (int)MHLEN) {
+				MCLGET(m, MB_WAIT);
+				if (m == NULL) {
+					*mbcnt = 0;
+					return (ENOMEM);
+				}
+				if ((m->m_flags & M_EXT) == 0) {
+					*mbcnt = 0;
+					return (ENOMEM);
+				}
+				*mbcnt += m->m_ext.ext_size;
+			}
+			cancpy = M_TRAILINGSPACE(m);
+			willcpy = min(cancpy, left);
+		}
+	}
+	return (0);
+}
+
+static int
+sctp_copy_it_in(struct sctp_inpcb *inp,
+		struct sctp_tcb *stcb,
+		struct sctp_association *asoc,
+		struct sctp_nets *net,
+		struct sctp_sndrcvinfo *srcv,
+		struct uio *uio,
+		int flags)
+{
+	/* This routine must be very careful in
+	 * its work. Protocol processing is
+	 * up and running so care must be taken to
+	 * spl...() when you need to do something
+	 * that may effect the stcb/asoc. The sb is
+	 * locked however. When data is copied the
+	 * protocol processing should be enabled since
+	 * this is a slower operation...
+	 */
+	struct socket *so;
+	int error = 0;
+	int frag_size, mbcnt = 0, mbcnt_e = 0;
+	unsigned int sndlen;
+	unsigned int tot_demand;
+	int tot_out, dataout;
+	struct sctp_tmit_chunk *chk;
+	struct mbuf *mm;
+	struct sctp_stream_out *strq;
+	uint32_t my_vtag;
+	int resv_in_first;
+
+	crit_enter();
+	so = stcb->sctp_socket;
+	chk = NULL;
+	mm = NULL;
+
+	sndlen = uio->uio_resid;
+	/* lock the socket buf */
+	SOCKBUF_LOCK(&so->so_snd);
+	error = sblock(&so->so_snd, SBLOCKWAIT(flags));
+	if (error)
+		goto out_locked;
+
+	/* will it ever fit ? */
+	if (sndlen > so->so_snd.sb_hiwat) {
+		/* It will NEVER fit */
+		error = EMSGSIZE;
+		crit_exit();
+		goto release;
+	}
+	/* Do I need to block? */
+	if ((so->so_snd.sb_hiwat <
+	    (sndlen + asoc->total_output_queue_size)) ||
+	    (asoc->chunks_on_out_queue > sctp_max_chunks_on_queue) ||
+	    (asoc->total_output_mbuf_queue_size >
+	    so->so_snd.sb_mbmax)
+	) {
+		/* prune any prsctp bufs out */
+		if (asoc->peer_supports_prsctp) {
+			sctp_prune_prsctp(stcb, asoc, srcv, sndlen);
+		}
+		/*
+		 * We store off a pointer to the endpoint.
+		 * Since on return from this we must check to
+		 * see if an so_error is set. If so we may have
+		 * been reset and our stcb destroyed. Returning
+		 * an error will flow back to the user...
+		 */
+		while ((so->so_snd.sb_hiwat <
+		    (sndlen + asoc->total_output_queue_size)) ||
+		    (asoc->chunks_on_out_queue >
+		    sctp_max_chunks_on_queue) ||
+		    (asoc->total_output_mbuf_queue_size >
+		    so->so_snd.sb_mbmax)
+		) {
+			if ((so->so_state & SS_NBIO)
+#if defined(__FreeBSD__) && __FreeBSD_version >= 500000
+			    || (flags & MSG_NBIO)
+#endif
+				) {
+				/* Non-blocking io in place */
+				error = EWOULDBLOCK;
+				goto release;
+			}
+			inp->sctp_tcb_at_block = (void *)stcb;
+			inp->error_on_block = 0;
+#ifdef SCTP_BLK_LOGGING
+			sctp_log_block(SCTP_BLOCK_LOG_INTO_BLK,
+			    so, asoc);
+#endif
+			sbunlock(&so->so_snd);
+			SCTP_TCB_UNLOCK(stcb);
+			error = sbwait(&so->so_snd);
+			SCTP_INP_RLOCK(inp);
+			if ((inp->sctp_flags & SCTP_PCB_FLAGS_SOCKET_GONE) ||
+			    (inp->sctp_flags & SCTP_PCB_FLAGS_SOCKET_GONE)) {
+				/* Should I really unlock ? */
+				SCTP_INP_RUNLOCK(inp);
+				error = EFAULT;
+				goto out_locked;
+			}
+			SCTP_TCB_LOCK(stcb);
+			SCTP_INP_RUNLOCK(inp);
+
+			inp->sctp_tcb_at_block = 0;
+#ifdef SCTP_BLK_LOGGING
+			sctp_log_block(SCTP_BLOCK_LOG_OUTOF_BLK,
+			    so, asoc);
+#endif
+			if (inp->error_on_block) {
+				/*
+				 * if our asoc was killed, the free code
+				 * (in sctp_pcb.c) will save a error in
+				 * here for us
+				 */
+ 				error = inp->error_on_block;
+				crit_exit();
+				goto out_locked;
+			}
+			if (error) {
+				crit_exit();
+				goto out_locked;
+			}
+			/* did we encounter a socket error? */
+			if (so->so_error) {
+				error = so->so_error;
+				crit_exit();
+				goto out_locked;
+			}
+			error = sblock(&so->so_snd, M_WAITOK);
+			if (error) {
+				/* Can't aquire the lock */
+				crit_exit();
+				goto out_locked;
+			}
+#if defined(__FreeBSD__) && __FreeBSD_version >= 502115
+			if (so->so_rcv.sb_state & SBS_CANTSENDMORE) {
+#else
+			if (so->so_state & SS_CANTSENDMORE) {
+#endif
+				/* The socket is now set not to sendmore.. its gone */
+				error = EPIPE;
+				crit_exit();
+				goto release;
+			}
+			if (so->so_error) {
+				error = so->so_error;
+				crit_exit();
+				goto release;
+			}
+			if (asoc->peer_supports_prsctp) {
+				sctp_prune_prsctp(stcb, asoc, srcv, sndlen);
+			}
+		}
+	}
+	dataout = tot_out = uio->uio_resid;
+ 	if (inp->sctp_flags & SCTP_PCB_FLAGS_BOUND_V6) {
+		resv_in_first = SCTP_MED_OVERHEAD;
+	} else {
+		resv_in_first = SCTP_MED_V4_OVERHEAD;
+	}
+
+	/* Are we aborting? */
+	if (srcv->sinfo_flags & MSG_ABORT) {
+		if ((SCTP_GET_STATE(asoc) != SCTP_STATE_COOKIE_WAIT) &&
+		    (SCTP_GET_STATE(asoc) != SCTP_STATE_COOKIE_ECHOED)) {
+			/* It has to be up before we abort */
+			/* how big is the user initiated abort? */
+
+			/* I wonder about doing a MGET without a splnet set.
+			 * it is done that way in the sosend code so I guess
+			 * it is ok :-0
+			 */
+ 			MGETHDR(mm, MB_WAIT, MT_DATA);
+			if (mm) {
+				struct sctp_paramhdr *ph;
+
+				tot_demand = (tot_out + sizeof(struct sctp_paramhdr));
+				if (tot_demand > MHLEN) {
+					if (tot_demand > MCLBYTES) {
+						/* truncate user data */
+						tot_demand = MCLBYTES;
+						tot_out = tot_demand - sizeof(struct sctp_paramhdr);
+					}
+					MCLGET(mm, MB_WAIT);
+					if ((mm->m_flags & M_EXT) == 0) {
+						/* truncate further */
+						tot_demand = MHLEN;
+						tot_out = tot_demand - sizeof(struct sctp_paramhdr);
+					}
+				}
+				/* now move forward the data pointer */
+				ph = mtod(mm, struct sctp_paramhdr *);
+				ph->param_type = htons(SCTP_CAUSE_USER_INITIATED_ABT);
+				ph->param_length = htons((sizeof(struct sctp_paramhdr) + tot_out));
+				ph++;
+				mm->m_pkthdr.len = tot_out + sizeof(struct sctp_paramhdr);
+				mm->m_len = mm->m_pkthdr.len;
+				error = uiomove((caddr_t)ph, (int)tot_out, uio);
+				if (error) {
+					/*
+					 * Here if we can't get his data we
+					 * still abort we just don't get to
+					 * send the users note :-0
+					 */
+					sctp_m_freem(mm);
+					mm = NULL;
+				}
+			}
+			sbunlock(&so->so_snd);
+			SOCKBUF_UNLOCK(&so->so_snd);
+			sctp_abort_an_association(stcb->sctp_ep, stcb,
+						  SCTP_RESPONSE_TO_USER_REQ,
+						  mm);
+			mm = NULL;
+			crit_exit();
+			goto out_notlocked;
+		}
+		crit_exit();
+		goto release;
+	}
+
+	/* Now can we send this? */
+	if ((SCTP_GET_STATE(asoc) == SCTP_STATE_SHUTDOWN_SENT) ||
+	    (SCTP_GET_STATE(asoc) == SCTP_STATE_SHUTDOWN_ACK_SENT) ||
+	    (SCTP_GET_STATE(asoc) == SCTP_STATE_SHUTDOWN_RECEIVED) ||
+	    (asoc->state & SCTP_STATE_SHUTDOWN_PENDING)) {
+		/* got data while shutting down */
+		error = ECONNRESET;
+		crit_exit();
+		goto release;
+ 	}
+ 	/* Is the stream no. valid? */
+	if (srcv->sinfo_stream >= asoc->streamoutcnt) {
+ 		/* Invalid stream number */
+		error = EINVAL;
+		crit_exit();
+		goto release;
+ 	}
+	if (asoc->strmout == NULL) {
+		/* huh? software error */
+#ifdef SCTP_DEBUG
+ 		if (sctp_debug_on & SCTP_DEBUG_OUTPUT1) {
+ 			printf("software error in sctp_copy_it_in\n");
+ 		}
+#endif
+		error = EFAULT;
+		crit_exit();
+		goto release;
+	}
+	if ((srcv->sinfo_flags & MSG_EOF) &&
+	    (stcb->sctp_ep->sctp_flags & SCTP_PCB_FLAGS_UDPTYPE) &&
+	    (tot_out == 0)) {
+		crit_exit();
+		goto zap_by_it_now;
+	}
+ 	if (tot_out == 0) {
+ 		/* not allowed */
+ 		error = EMSGSIZE;
+		crit_exit();
+		goto release;
+ 	}
+	/* save off the tag */
+	my_vtag = asoc->my_vtag;
+	strq = &asoc->strmout[srcv->sinfo_stream];
+	/* First lets figure out the "chunking" point */
+	frag_size = sctp_get_frag_point(stcb, asoc);
+
+	/* two choices here, it all fits in one chunk or
+	 * we need multiple chunks.
+	 */
+	crit_exit();
+	SOCKBUF_UNLOCK(&so->so_snd);
+	if (tot_out <= frag_size) {
+		/* no need to setup a template */
+		chk = (struct sctp_tmit_chunk *)SCTP_ZONE_GET(sctppcbinfo.ipi_zone_chunk);
+		if (chk == NULL) {
+			error = ENOMEM;
+			SOCKBUF_LOCK(&so->so_snd);
+			goto release;
+		}
+		sctppcbinfo.ipi_count_chunk++;
+		sctppcbinfo.ipi_gencnt_chunk++;
+		asoc->chunks_on_out_queue++;
+		MGETHDR(mm, MB_WAIT, MT_DATA);
+		if (mm == NULL) {
+			error = ENOMEM;
+			goto clean_up;
+		}
+		error = sctp_copy_one(mm, uio, tot_out, resv_in_first, &mbcnt_e);
+		if (error)
+			goto clean_up;
+		sctp_prepare_chunk(chk, stcb, srcv, strq, net);
+		chk->mbcnt = mbcnt_e;
+		mbcnt += mbcnt_e;
+		mbcnt_e = 0;
+		mm->m_pkthdr.len = tot_out;
+		chk->data = mm;
+		mm = NULL;
+
+		/* the actual chunk flags */
+		chk->rec.data.rcv_flags |= SCTP_DATA_NOT_FRAG;
+		chk->whoTo->ref_count++;
+
+		/* fix up the send_size if it is not present */
+		chk->send_size = tot_out;
+		chk->book_size = chk->send_size;
+		/* ok, we are commited */
+		if ((srcv->sinfo_flags & MSG_UNORDERED) == 0) {
+			/* bump the ssn if we are unordered. */
+			strq->next_sequence_sent++;
+		}
+		if (chk->flags & SCTP_PR_SCTP_BUFFER) {
+			asoc->sent_queue_cnt_removeable++;
+		}
+		crit_enter();
+		if ((asoc->state == 0) ||
+		    (my_vtag != asoc->my_vtag) ||
+		    (so != inp->sctp_socket) ||
+		    (inp->sctp_socket == 0)) {
+			/* connection was aborted */
+			crit_exit();
+			error = ECONNRESET;
+			goto clean_up;
+		}
+		asoc->stream_queue_cnt++;
+		TAILQ_INSERT_TAIL(&strq->outqueue, chk, sctp_next);
+		/* now check if this stream is on the wheel */
+		if ((strq->next_spoke.tqe_next == NULL) &&
+		    (strq->next_spoke.tqe_prev == NULL)) {
+			/* Insert it on the wheel since it is not
+			 * on it currently
+			 */
+			sctp_insert_on_wheel(asoc, strq);
+		}
+		crit_exit();
+clean_up:
+		if (error) {
+			SCTP_ZONE_FREE(sctppcbinfo.ipi_zone_chunk, chk);
+			sctppcbinfo.ipi_count_chunk--;
+			if ((int)sctppcbinfo.ipi_count_chunk < 0) {
+				panic("Chunk count is negative");
+			}
+			SOCKBUF_LOCK(&so->so_snd);
+			goto release;
+		}
+	} else {
+		/* we need to setup a template */
+		struct sctp_tmit_chunk template;
+		struct sctpchunk_listhead tmp;
+
+		/* setup the template */
+		sctp_prepare_chunk(&template, stcb, srcv, strq, net);
+
+		/* Prepare the temp list */
+		TAILQ_INIT(&tmp);
+
+		/* Template is complete, now time for the work */
+		while (tot_out > 0) {
+			/* Get a chunk */
+ 			chk = (struct sctp_tmit_chunk *)SCTP_ZONE_GET(sctppcbinfo.ipi_zone_chunk);
+			if (chk == NULL) {
+				/*
+				 * ok we must spin through and dump anything
+				 * we have allocated and then jump to the
+				 * no_membad
+				 */
+				error = ENOMEM;
+			}
+			sctppcbinfo.ipi_count_chunk++;
+			asoc->chunks_on_out_queue++;
+
+			sctppcbinfo.ipi_gencnt_chunk++;
+			*chk = template;
+			chk->whoTo->ref_count++;
+			MGETHDR(chk->data, MB_WAIT, MT_DATA);
+			if (chk->data == NULL) {
+				error = ENOMEM;
+				goto temp_clean_up;
+			}
+			tot_demand = min(tot_out, frag_size);
+			error = sctp_copy_one(chk->data, uio, tot_demand , resv_in_first, &mbcnt_e);
+			if (error)
+				goto temp_clean_up;
+			/* now fix the chk->send_size */
+			chk->mbcnt = mbcnt_e;
+			mbcnt += mbcnt_e;
+			mbcnt_e = 0;
+			chk->send_size = tot_demand;
+			chk->data->m_pkthdr.len = tot_demand;
+			chk->book_size = chk->send_size;
+			if (chk->flags & SCTP_PR_SCTP_BUFFER) {
+				asoc->sent_queue_cnt_removeable++;
+			}
+			TAILQ_INSERT_TAIL(&tmp, chk, sctp_next);
+			tot_out -= tot_demand;
+		}
+		/* Now the tmp list holds all chunks and data */
+		if ((srcv->sinfo_flags & MSG_UNORDERED) == 0) {
+			/* bump the ssn if we are unordered. */
+			strq->next_sequence_sent++;
+		}
+		/* Mark the first/last flags. This will
+		 * result int a 3 for a single item on the list
+		 */
+		chk = TAILQ_FIRST(&tmp);
+		chk->rec.data.rcv_flags |= SCTP_DATA_FIRST_FRAG;
+		chk = TAILQ_LAST(&tmp, sctpchunk_listhead);
+		chk->rec.data.rcv_flags |= SCTP_DATA_LAST_FRAG;
+
+		/* now move it to the streams actual queue */
+		/* first stop protocol processing */
+ 		crit_enter();
+		if ((asoc->state == 0) ||
+		    (my_vtag != asoc->my_vtag) ||
+		    (so != inp->sctp_socket) ||
+		    (inp->sctp_socket == 0)) {
+			/* connection was aborted */
+			crit_exit();
+			error = ECONNRESET;
+			goto temp_clean_up;
+		}
+		chk = TAILQ_FIRST(&tmp);
+		while (chk) {
+			chk->data->m_nextpkt = 0;
+			TAILQ_REMOVE(&tmp, chk, sctp_next);
+			asoc->stream_queue_cnt++;
+			TAILQ_INSERT_TAIL(&strq->outqueue, chk, sctp_next);
+			chk = TAILQ_FIRST(&tmp);
+		}
+		/* now check if this stream is on the wheel */
+		if ((strq->next_spoke.tqe_next == NULL) &&
+		    (strq->next_spoke.tqe_prev == NULL)) {
+			/* Insert it on the wheel since it is not
+			 * on it currently
+			 */
+			sctp_insert_on_wheel(asoc, strq);
+		}
+		/* Ok now we can allow pping */
+		crit_exit();
+temp_clean_up:
+		if (error) {
+			SOCKBUF_LOCK(&so->so_snd);
+			chk = TAILQ_FIRST(&tmp);
+			while (chk) {
+				if (chk->data) {
+					sctp_m_freem(chk->data);
+					chk->data = NULL;
+				}
+				TAILQ_REMOVE(&tmp, chk, sctp_next);
+				SCTP_ZONE_FREE(sctppcbinfo.ipi_zone_chunk, chk);
+				sctppcbinfo.ipi_count_chunk--;
+				asoc->chunks_on_out_queue--;
+				if ((int)sctppcbinfo.ipi_count_chunk < 0) {
+					panic("Chunk count is negative");
+				}
+				sctppcbinfo.ipi_gencnt_chunk++;
+				chk = TAILQ_FIRST(&tmp);
+			}
+			goto release;
+		}
+	}
+zap_by_it_now:
+#ifdef SCTP_MBCNT_LOGGING
+	sctp_log_mbcnt(SCTP_LOG_MBCNT_INCREASE,
+		       asoc->total_output_queue_size,
+		       dataout,
+		       asoc->total_output_mbuf_queue_size,
+		       mbcnt);
+#endif
+	crit_enter();
+	SOCKBUF_LOCK(&so->so_snd);
+	asoc->total_output_queue_size += dataout;
+	asoc->total_output_mbuf_queue_size += mbcnt;
+	if ((stcb->sctp_ep->sctp_flags & SCTP_PCB_FLAGS_TCPTYPE) ||
+	    (stcb->sctp_ep->sctp_flags & SCTP_PCB_FLAGS_IN_TCPPOOL)) {
+		so->so_snd.sb_cc += dataout;
+		so->so_snd.sb_mbcnt += mbcnt;
+	}
+	if ((srcv->sinfo_flags & MSG_EOF) &&
+	    (stcb->sctp_ep->sctp_flags & SCTP_PCB_FLAGS_UDPTYPE)
+		) {
+		int some_on_streamwheel = 0;
+		error = 0;
+		if (!TAILQ_EMPTY(&asoc->out_wheel)) {
+			/* Check to see if some data queued */
+			struct sctp_stream_out *outs;
+			TAILQ_FOREACH(outs, &asoc->out_wheel, next_spoke) {
+				if (!TAILQ_EMPTY(&outs->outqueue)) {
+					some_on_streamwheel = 1;
+					break;
+				}
+			}
+		}
+		if (TAILQ_EMPTY(&asoc->send_queue) &&
+		    TAILQ_EMPTY(&asoc->sent_queue) &&
+		    (some_on_streamwheel == 0)) {
+			/* there is nothing queued to send, so I'm done... */
+			if ((SCTP_GET_STATE(asoc) != SCTP_STATE_SHUTDOWN_SENT) &&
+			    (SCTP_GET_STATE(asoc) != SCTP_STATE_SHUTDOWN_ACK_SENT)) {
+				/* only send SHUTDOWN the first time through */
+#ifdef SCTP_DEBUG
+				if (sctp_debug_on & SCTP_DEBUG_OUTPUT4) {
+					printf("%s:%d sends a shutdown\n",
+					       __FILE__,
+					       __LINE__
+						);
+				}
+#endif
+				sctp_send_shutdown(stcb, stcb->asoc.primary_destination);
+				asoc->state = SCTP_STATE_SHUTDOWN_SENT;
+				sctp_timer_start(SCTP_TIMER_TYPE_SHUTDOWN, stcb->sctp_ep, stcb,
+						 asoc->primary_destination);
+				sctp_timer_start(SCTP_TIMER_TYPE_SHUTDOWNGUARD, stcb->sctp_ep, stcb,
+						 asoc->primary_destination);
+			}
+		} else {
+			/*
+			 * we still got (or just got) data to send, so set
+			 * SHUTDOWN_PENDING
+			 */
+			/*
+			 * XXX sockets draft says that MSG_EOF should be sent
+			 * with no data.  currently, we will allow user data
+			 * to be sent first and move to SHUTDOWN-PENDING
+			 */
+			asoc->state |= SCTP_STATE_SHUTDOWN_PENDING;
+		}
+	}
+	crit_exit();
+#ifdef SCTP_DEBUG
+	if (sctp_debug_on & SCTP_DEBUG_OUTPUT2) {
+		printf("++total out:%d total_mbuf_out:%d\n",
+		       (int)asoc->total_output_queue_size,
+		       (int)asoc->total_output_mbuf_queue_size);
+	}
+#endif
+
+release:
+	sbunlock(&so->so_snd);
+out_locked:
+	SOCKBUF_UNLOCK(&so->so_snd);
+out_notlocked:
+	if (mm)
+		sctp_m_freem(mm);
+	return (error);
+}
+
+
+int
+sctp_sosend(struct socket *so,
+#ifdef __NetBSD__
+	    struct mbuf *addr_mbuf,
+#else
+	    struct sockaddr *addr,
+#endif
+	    struct uio *uio,
+	    struct mbuf *top,
+	    struct mbuf *control,
+#if defined(__NetBSD__) || defined(__APPLE__)
+	    int flags
+#else
+	    int flags,
+#if (defined(__FreeBSD__) && __FreeBSD_version >= 500000) || defined(__DragonFly__)
+	    struct thread *p
+#else
+	    struct proc *p
+#endif
+#endif
+)
+{
+ 	unsigned int sndlen;
+	int error, use_rcvinfo;
+	int queue_only = 0, queue_only_for_init=0;
+	int un_sent = 0;
+	int now_filled=0;
+	struct sctp_inpcb *inp;
+ 	struct sctp_tcb *stcb=NULL;
+	struct sctp_sndrcvinfo srcv;
+	struct timeval now;
+	struct sctp_nets *net;
+	struct sctp_association *asoc;
+	struct sctp_inpcb *t_inp;
+	int create_lock_applied = 0;
+#if defined(__APPLE__)
+	struct proc *p = current_proc();
+#elif defined(__NetBSD__)
+	struct proc *p = curproc; /* XXX */
+	struct sockaddr *addr = NULL;
+	if (addr_mbuf)
+		addr = mtod(addr_mbuf, struct sockaddr *);
+#endif
+
+	error = use_rcvinfo = 0;
+	net = NULL;
+	stcb = NULL;
+	asoc = NULL;
+	t_inp = inp = (struct sctp_inpcb *)so->so_pcb;
+	if (uio)
+		sndlen = uio->uio_resid;
+	else
+		sndlen = top->m_pkthdr.len;
+
+
+	crit_enter();
+
+	if ((inp->sctp_flags & SCTP_PCB_FLAGS_TCPTYPE) &&
+	    (inp->sctp_flags & SCTP_PCB_FLAGS_ACCEPTING)) {
+		/* The listner can NOT send */
+		error = EFAULT;
+		crit_exit();
+		goto out;
+	}
+	if (addr) {
+		SCTP_ASOC_CREATE_LOCK(inp);
+		if ((inp->sctp_flags & SCTP_PCB_FLAGS_SOCKET_GONE) ||
+		    (inp->sctp_flags & SCTP_PCB_FLAGS_SOCKET_GONE)) {
+			/* Should I really unlock ? */
+			error = EFAULT;
+			crit_exit();
+			goto out;
+
+		}
+		create_lock_applied = 1;
+		if (((inp->sctp_flags & SCTP_PCB_FLAGS_BOUND_V6) == 0) &&
+		    (addr->sa_family == AF_INET6)) {
+			error = EINVAL;
+			crit_exit();
+			goto out;
+		}
+	}
+	/* now we must find the assoc */
+	if (inp->sctp_flags & SCTP_PCB_FLAGS_CONNECTED) {
+		SCTP_INP_RLOCK(inp);
+		stcb = LIST_FIRST(&inp->sctp_asoc_list);
+		if (stcb == NULL) {
+			SCTP_INP_RUNLOCK(inp);
+			error = ENOTCONN;
+			crit_exit();
+			goto out;
+		}
+		SCTP_TCB_LOCK(stcb);
+		SCTP_INP_RUNLOCK(inp);
+		net = stcb->asoc.primary_destination;
+	}
+	/* get control */
+	if (control) {
+		/* process cmsg snd/rcv info (maybe a assoc-id) */
+		if (sctp_find_cmsg(SCTP_SNDRCV, (void *)&srcv, control,
+				   sizeof(srcv))) {
+			/* got one */
+			if (srcv.sinfo_flags & MSG_SENDALL) {
+				/* its a sendall */
+				sctppcbinfo.mbuf_track--;
+				sctp_m_freem(control);
+
+				if (create_lock_applied) {
+					SCTP_ASOC_CREATE_UNLOCK(inp);
+					create_lock_applied = 0;
+				}
+				return (sctp_sendall(inp, uio, top, &srcv));
+			}
+			use_rcvinfo = 1;
+		}
+	}
+	if (stcb == NULL) {
+		/* Need to do a lookup */
+		if (use_rcvinfo && srcv.sinfo_assoc_id) {
+			stcb = sctp_findassociation_ep_asocid(inp, srcv.sinfo_assoc_id);
+			/*
+			 * Question: Should I error here if the assoc_id is
+			 * no longer valid? i.e. I can't find it?
+			 */
+			if ((stcb) &&
+			    (addr != NULL)) {
+				/* Must locate the net structure */
+				net = sctp_findnet(stcb, addr);
+			}
+		}
+		if (stcb == NULL) {
+			if (addr != NULL) {
+				/* Since we did not use findep we must
+				 * increment it, and if we don't find a
+				 * tcb decrement it.
+				 */
+				SCTP_INP_WLOCK(inp);
+				SCTP_INP_INCR_REF(inp);
+				SCTP_INP_WUNLOCK(inp);
+				stcb = sctp_findassociation_ep_addr(&t_inp, addr, &net, NULL, NULL);
+				if (stcb == NULL) {
+					SCTP_INP_WLOCK(inp);
+					SCTP_INP_DECR_REF(inp);
+					SCTP_INP_WUNLOCK(inp);
+				}
+			}
+		}
+	}
+	if ((stcb == NULL) &&
+	    (inp->sctp_flags & SCTP_PCB_FLAGS_TCPTYPE)) {
+		error = ENOTCONN;
+		crit_exit();
+		goto out;
+	} else if ((stcb == NULL) && (addr == NULL)) {
+		error = ENOENT;
+		crit_exit();
+		goto out;
+	} else if (stcb == NULL) {
+		/* UDP style, we must go ahead and start the INIT process */
+		if ((use_rcvinfo) &&
+		    (srcv.sinfo_flags & MSG_ABORT)) {
+			/* User asks to abort a non-existant asoc */
+			error = ENOENT;
+			crit_exit();
+			goto out;
+		}
+		/* get an asoc/stcb struct */
+		stcb = sctp_aloc_assoc(inp, addr, 1, &error, 0);
+		if (stcb == NULL) {
+			/* Error is setup for us in the call */
+			crit_exit();
+			goto out;
+		}
+		if (create_lock_applied) {
+			SCTP_ASOC_CREATE_UNLOCK(inp);
+			create_lock_applied = 0;
+		} else {
+			printf("Huh-3? create lock should have been on??\n");
+		}
+		/* Turn on queue only flag to prevent data from being sent */
+ 		queue_only = 1;
+		asoc = &stcb->asoc;
+		asoc->state = SCTP_STATE_COOKIE_WAIT;
+		SCTP_GETTIME_TIMEVAL(&asoc->time_entered);
+		if (control) {
+			/* see if a init structure exists in cmsg headers */
+			struct sctp_initmsg initm;
+			int i;
+			if (sctp_find_cmsg(SCTP_INIT, (void *)&initm, control, sizeof(initm))) {
+				/* we have an INIT override of the default */
+				if (initm.sinit_max_attempts)
+					asoc->max_init_times = initm.sinit_max_attempts;
+				if (initm.sinit_num_ostreams)
+					asoc->pre_open_streams = initm.sinit_num_ostreams;
+				if (initm.sinit_max_instreams)
+					asoc->max_inbound_streams = initm.sinit_max_instreams;
+				if (initm.sinit_max_init_timeo)
+					asoc->initial_init_rto_max = initm.sinit_max_init_timeo;
+				if (asoc->streamoutcnt < asoc->pre_open_streams) {
+					/* Default is NOT correct */
+#ifdef SCTP_DEBUG
+					if (sctp_debug_on & SCTP_DEBUG_OUTPUT1) {
+						printf("Ok, defout:%d pre_open:%d\n",
+						       asoc->streamoutcnt, asoc->pre_open_streams);
+					}
+#endif
+					FREE(asoc->strmout, M_PCB);
+					asoc->strmout = NULL;
+					asoc->streamoutcnt = asoc->pre_open_streams;
+
+					/* What happesn if this fails? .. we panic ...*/
+					MALLOC(asoc->strmout,
+					       struct sctp_stream_out *,
+					       asoc->streamoutcnt *
+					       sizeof(struct sctp_stream_out),
+					       M_PCB, MB_WAIT);
+					for (i = 0; i < asoc->streamoutcnt; i++) {
+						/*
+						 * inbound side must be set to 0xffff,
+						 * also NOTE when we get the INIT-ACK
+						 * back (for INIT sender) we MUST
+						 * reduce the count (streamoutcnt) but
+						 * first check if we sent to any of the
+						 * upper streams that were dropped (if
+						 * some were). Those that were dropped
+						 * must be notified to the upper layer
+						 * as failed to send.
+						 */
+						asoc->strmout[i].next_sequence_sent = 0x0;
+						TAILQ_INIT(&asoc->strmout[i].outqueue);
+						asoc->strmout[i].stream_no = i;
+						asoc->strmout[i].next_spoke.tqe_next = 0;
+						asoc->strmout[i].next_spoke.tqe_prev = 0;
+					}
+				}
+			}
+
+		}
+		/* out with the INIT */
+		queue_only_for_init = 1;
+		sctp_send_initiate(inp, stcb);
+		/*
+		 * we may want to dig in after this call and adjust the MTU
+		 * value. It defaulted to 1500 (constant) but the ro structure
+		 * may now have an update and thus we may need to change it
+		 * BEFORE we append the message.
+		 */
+		net = stcb->asoc.primary_destination;
+		asoc = &stcb->asoc;
+	} else {
+		asoc = &stcb->asoc;
+	}
+	if (create_lock_applied) {
+		SCTP_ASOC_CREATE_UNLOCK(inp);
+		create_lock_applied = 0;
+	}
+	if ((SCTP_GET_STATE(asoc) == SCTP_STATE_COOKIE_WAIT) ||
+	    (SCTP_GET_STATE(asoc) == SCTP_STATE_COOKIE_ECHOED)) {
+		queue_only = 1;
+	}
+	if (use_rcvinfo == 0) {
+		/* Grab the default stuff from the asoc */
+		srcv = stcb->asoc.def_send;
+	}
+	/* we are now done with all control */
+	if (control) {
+		sctp_m_freem(control);
+		control = NULL;
+	}
+
+	if ((SCTP_GET_STATE(asoc) == SCTP_STATE_SHUTDOWN_SENT) ||
+	    (SCTP_GET_STATE(asoc) == SCTP_STATE_SHUTDOWN_RECEIVED) ||
+	    (SCTP_GET_STATE(asoc) == SCTP_STATE_SHUTDOWN_ACK_SENT) ||
+	    (asoc->state & SCTP_STATE_SHUTDOWN_PENDING)) {
+		if ((use_rcvinfo) &&
+		    (srcv.sinfo_flags & MSG_ABORT)) {
+			;
+		} else {
+			error = ECONNRESET;
+			crit_exit();
+			goto out;
+		}
+	}
+	/* Ok, we will attempt a msgsnd :> */
+	if (p)
+#if (defined(__FreeBSD__) && __FreeBSD_version >= 500000) || defined(__DragonFly__)
+		p->td_proc->p_stats->p_ru.ru_msgsnd++;
+#else
+	p->p_stats->p_ru.ru_msgsnd++;
+#endif
+
+	if (stcb) {
+		if (net && ((srcv.sinfo_flags & MSG_ADDR_OVER))) {
+			/* we take the override or the unconfirmed */
+			;
+		} else {
+			net = stcb->asoc.primary_destination;
+		}
+	}
+
+	if (top == NULL) {
+		/* Must copy it all in from user land. The
+		 * socket buf is locked but we don't suspend
+		 * protocol processing until we are ready to
+		 * send/queue it.
+		 */
+ 		crit_exit();
+		error = sctp_copy_it_in(inp, stcb, asoc, net, &srcv, uio, flags);
+		if (error)
+			goto out;
+	} else {
+		/* Here we must either pull in the user data to chunk
+		 * buffers, or use top to do a msg_append.
+		 */
+ 		error = sctp_msg_append(stcb, net, top, &srcv, flags);
+ 		crit_exit();
+		if (error)
+			goto out;
+		/* zap the top since it is now being used */
+		top = 0;
+	}
+
+	if (net->flight_size > net->cwnd) {
+		sctp_pegs[SCTP_SENDTO_FULL_CWND]++;
+		queue_only = 1;
+
+	} else if (asoc->ifp_had_enobuf) {
+		sctp_pegs[SCTP_QUEONLY_BURSTLMT]++;
+		queue_only = 1;
+ 	} else {
+		un_sent = ((stcb->asoc.total_output_queue_size - stcb->asoc.total_flight) +
+			   ((stcb->asoc.chunks_on_out_queue - stcb->asoc.total_flight_count) * sizeof(struct sctp_data_chunk)) +
+			   SCTP_MED_OVERHEAD);
+
+		if (((inp->sctp_flags & SCTP_PCB_FLAGS_NODELAY) == 0) &&
+		    (stcb->asoc.total_flight > 0) &&
+		    (un_sent < (int)stcb->asoc.smallest_mtu)) {
+
+			/* Ok, Nagle is set on and we have data outstanding. Don't
+			 * send anything and let SACKs drive out the data unless we
+			 * have a "full" segment to send.
+			 */
+			sctp_pegs[SCTP_NAGLE_NOQ]++;
+			queue_only = 1;
+		} else {
+			sctp_pegs[SCTP_NAGLE_OFF]++;
+		}
+	}
+	if (queue_only_for_init) {
+		/* It is possible to have a turn around of the
+		 * INIT/INIT-ACK/COOKIE before I have a chance to
+		 * copy in the data. In such a case I DO want to
+		 * send it out by reversing the queue only flag.
+		 */
+		if ((SCTP_GET_STATE(asoc) != SCTP_STATE_COOKIE_WAIT) ||
+		    (SCTP_GET_STATE(asoc) != SCTP_STATE_COOKIE_ECHOED)) {
+			/* yep, reverse it */
+			queue_only = 0;
+		}
+ 	}
+
+	if ((queue_only == 0) && (stcb->asoc.peers_rwnd  && un_sent)) {
+		/* we can attempt to send too.*/
+#ifdef SCTP_DEBUG
+		if (sctp_debug_on & SCTP_DEBUG_OUTPUT1) {
+			printf("USR Send calls sctp_chunk_output\n");
+		}
+#endif
+		crit_enter();
+		sctp_pegs[SCTP_OUTPUT_FRM_SND]++;
+		sctp_chunk_output(inp, stcb, 0);
+		crit_exit();
+	} else if ((queue_only == 0) &&
+		   (stcb->asoc.peers_rwnd == 0) &&
+		   (stcb->asoc.total_flight == 0)) {
+		/* We get to have a probe outstanding */
+		crit_enter();
+		sctp_from_user_send = 1;
+		sctp_chunk_output(inp, stcb, 0);
+		sctp_from_user_send = 0;
+		crit_exit();
+
+	} else if (!TAILQ_EMPTY(&stcb->asoc.control_send_queue)) {
+		int num_out, reason, cwnd_full;
+		/* Here we do control only */
+		crit_enter();
+		sctp_med_chunk_output(inp, stcb, &stcb->asoc, &num_out,
+				      &reason, 1, &cwnd_full, 1, &now, &now_filled);
+		crit_exit();
+	}
+#ifdef SCTP_DEBUG
+	if (sctp_debug_on & SCTP_DEBUG_OUTPUT1) {
+		printf("USR Send complete qo:%d prw:%d unsent:%d tf:%d cooq:%d toqs:%d \n",
+		       queue_only, stcb->asoc.peers_rwnd, un_sent,
+		       stcb->asoc.total_flight, stcb->asoc.chunks_on_out_queue,
+		       stcb->asoc.total_output_queue_size);
+	}
+#endif
+ out:
+	if (create_lock_applied) {
+		SCTP_ASOC_CREATE_UNLOCK(inp);
+		create_lock_applied = 0;
+	}
+	if (stcb)
+		SCTP_TCB_UNLOCK(stcb);
+	if (top)
+		sctp_m_freem(top);
+	if (control)
+		sctp_m_freem(control);
+	return (error);
+}
Index: sys/netinet/sctp_output.h
===================================================================
RCS file: sys/netinet/sctp_output.h
diff -N sys/netinet/sctp_output.h
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ sys/netinet/sctp_output.h	18 Jun 2005 16:05:50 -0000
@@ -0,0 +1,152 @@
+/*	$KAME: sctp_output.h,v 1.13 2004/08/17 04:06:18 itojun Exp $	*/
+
+#ifndef __sctp_output_h__
+#define __sctp_output_h__
+
+/*
+ * Copyright (C) 2002, 2003, 2004 Cisco Systems Inc,
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the project nor the names of its contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+
+
+#include <netinet/sctp_header.h>
+#if defined(_KERNEL) || (defined(__APPLE__) && defined(KERNEL))
+void sctp_send_initiate(struct sctp_inpcb *, struct sctp_tcb *);
+
+void sctp_send_initiate_ack(struct sctp_inpcb *, struct sctp_tcb *,
+    struct mbuf *, int, int, struct sctphdr *, struct sctp_init_chunk *);
+
+struct mbuf *sctp_arethere_unrecognized_parameters(struct mbuf *, int, int *,
+    struct sctp_chunkhdr *);
+void sctp_queue_op_err(struct sctp_tcb *, struct mbuf *);
+
+int sctp_send_cookie_echo(struct mbuf *, int, struct sctp_tcb *,
+    struct sctp_nets *);
+int sctp_send_cookie_ack(struct sctp_tcb *);
+
+void sctp_send_heartbeat_ack(struct sctp_tcb *, struct mbuf *, int, int,
+    struct sctp_nets *);
+
+int sctp_is_addr_restricted(struct sctp_tcb *, struct sockaddr *);
+
+struct in_addr sctp_ipv4_source_address_selection(struct sctp_inpcb *,
+    struct sctp_tcb *, struct route *, struct sctp_nets *, int);
+
+
+struct in6_addr sctp_ipv6_source_address_selection(struct sctp_inpcb *,
+	struct sctp_tcb *, struct route *, struct sctp_nets *, int);
+
+
+int sctp_send_shutdown(struct sctp_tcb *, struct sctp_nets *);
+
+int sctp_send_shutdown_ack(struct sctp_tcb *, struct sctp_nets *);
+
+int sctp_send_shutdown_complete(struct sctp_tcb *, struct sctp_nets *);
+
+int sctp_send_shutdown_complete2(struct mbuf *, int, struct sctphdr *);
+
+int sctp_send_asconf(struct sctp_tcb *, struct sctp_nets *);
+
+int sctp_send_asconf_ack(struct sctp_tcb *, uint32_t);
+
+int sctp_get_frag_point(struct sctp_tcb *, struct sctp_association *);
+
+void sctp_toss_old_cookies(struct sctp_association *);
+
+void sctp_toss_old_asconf(struct sctp_tcb *);
+
+void sctp_fix_ecn_echo(struct sctp_association *);
+
+#if (defined(__FreeBSD__) && __FreeBSD_version >= 500000) || defined(__DragonFly__)
+int sctp_output(struct sctp_inpcb *, struct mbuf *, struct sockaddr *,
+    struct mbuf *, struct thread *, int);
+#else
+int sctp_output(struct sctp_inpcb *, struct mbuf *, struct sockaddr *,
+    struct mbuf *, struct proc *, int);
+#endif
+
+int sctp_chunk_output(struct sctp_inpcb *, struct sctp_tcb *, int);
+void sctp_send_abort_tcb(struct sctp_tcb *, struct mbuf *);
+
+void send_forward_tsn(struct sctp_tcb *, struct sctp_association *);
+
+void sctp_send_sack(struct sctp_tcb *);
+
+int sctp_send_hb(struct sctp_tcb *, int, struct sctp_nets *);
+
+void sctp_send_ecn_echo(struct sctp_tcb *, struct sctp_nets *, uint32_t);
+
+
+void
+sctp_send_packet_dropped(struct sctp_tcb *, struct sctp_nets *, struct mbuf *,
+    int, int);
+
+
+
+void sctp_send_cwr(struct sctp_tcb *, struct sctp_nets *, uint32_t);
+
+
+void
+sctp_send_str_reset_ack(struct sctp_tcb *stcb,
+    struct sctp_stream_reset_request *req);
+
+
+void
+sctp_send_str_reset_req(struct sctp_tcb *stcb,
+    int number_entrys, uint16_t *list, uint8_t two_way, uint8_t not_peer);
+
+
+void sctp_send_abort(struct mbuf *, int, struct sctphdr *, uint32_t,
+    struct mbuf *);
+
+void sctp_send_operr_to(struct mbuf *, int, struct mbuf *, uint32_t);
+
+int
+sctp_sosend(struct socket *so,
+#ifdef __NetBSD__
+    struct mbuf *addr_mbuf,
+#else
+    struct sockaddr *addr,
+#endif
+    struct uio *uio,
+    struct mbuf *top,
+    struct mbuf *control,
+#if defined(__NetBSD__) || defined(__APPLE__)
+    int flags
+#else
+    int flags,
+#if (defined(__FreeBSD__) && __FreeBSD_version >= 500000) || defined(__DragonFly__)
+    struct thread *p
+#else
+    struct proc *p
+#endif
+#endif
+	);
+
+#endif
+#endif
Index: sys/netinet/sctp_pcb.c
===================================================================
RCS file: sys/netinet/sctp_pcb.c
diff -N sys/netinet/sctp_pcb.c
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ sys/netinet/sctp_pcb.c	18 Jun 2005 16:05:50 -0000
@@ -0,0 +1,5185 @@
+/*	$KAME: sctp_pcb.c,v 1.37 2004/08/17 06:28:02 t-momose Exp $	*/
+
+/*
+ * Copyright (c) 2001, 2002, 2003, 2004 Cisco Systems, Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *      This product includes software developed by Cisco Systems, Inc.
+ * 4. Neither the name of the project nor the names of its contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY CISCO SYSTEMS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL CISCO SYSTEMS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+#if !(defined(__OpenBSD__) || defined(__APPLE__))
+#include "opt_ipsec.h"
+#endif
+#if defined(__FreeBSD__) || defined(__DragonFly__)
+#include "opt_compat.h"
+#include "opt_inet6.h"
+#include "opt_inet.h"
+#endif
+#if defined(__NetBSD__)
+#include "opt_inet.h"
+#endif
+#ifdef __APPLE__
+#include <sctp.h>
+#elif !defined(__OpenBSD__)
+#include "opt_sctp.h"
+#endif
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/malloc.h>
+#include <sys/mbuf.h>
+#include <sys/domain.h>
+#include <sys/protosw.h>
+#include <sys/socket.h>
+#include <sys/socketvar.h>
+#include <sys/proc.h>
+#include <sys/kernel.h>
+#include <sys/sysctl.h>
+#include <sys/thread2.h>
+#if defined(__FreeBSD__) || defined(__APPLE__) || defined(__DragonFly__)
+#include <sys/random.h>
+#endif
+#if defined(__NetBSD__)
+#include <sys/rnd.h>
+#endif
+#if defined(__OpenBSD__)
+#include <dev/rndvar.h>
+#endif
+
+#if defined(__APPLE__)
+#include <netinet/sctp_callout.h>
+#elif defined(__OpenBSD__)
+#include <sys/timeout.h>
+#else
+#include <sys/callout.h>
+#endif
+
+#if (defined(__FreeBSD__) && __FreeBSD_version >= 500000)
+#include <sys/limits.h>
+#else
+#include <machine/limits.h>
+#endif
+#include <machine/cpu.h>
+
+#include <net/if.h>
+#include <net/if_types.h>
+#include <net/route.h>
+#include <netinet/in.h>
+#include <netinet/in_systm.h>
+#include <netinet/ip.h>
+#include <netinet/in_pcb.h>
+#include <netinet/in_var.h>
+#include <netinet/ip_var.h>
+
+#ifdef INET6
+#include <netinet/ip6.h>
+#include <netinet6/ip6_var.h>
+#include <netinet6/scope6_var.h>
+#if defined(__FreeBSD__) || (__NetBSD__) || defined(__DragonFly__)
+#include <netinet6/in6_pcb.h>
+#elif defined(__OpenBSD__)
+#include <netinet/in_pcb.h>
+#endif
+#endif /* INET6 */
+
+#ifdef IPSEC
+#ifndef __OpenBSD__
+#include <netinet6/ipsec.h>
+#include <netkey/key.h>
+#else
+#undef IPSEC
+#endif
+#endif /* IPSEC */
+
+#include <netinet/sctp_var.h>
+#include <netinet/sctp_pcb.h>
+#include <netinet/sctputil.h>
+#include <netinet/sctp.h>
+#include <netinet/sctp_header.h>
+#include <netinet/sctp_asconf.h>
+#include <netinet/sctp_output.h>
+#include <netinet/sctp_timer.h>
+
+#ifndef SCTP_PCBHASHSIZE
+/* default number of association hash buckets in each endpoint */
+#define SCTP_PCBHASHSIZE 256
+#endif
+
+#ifdef SCTP_DEBUG
+u_int32_t sctp_debug_on = 0;
+#endif /* SCTP_DEBUG */
+
+u_int32_t sctp_pegs[SCTP_NUMBER_OF_PEGS];
+
+int sctp_pcbtblsize = SCTP_PCBHASHSIZE;
+
+struct sctp_epinfo sctppcbinfo;
+
+/* FIX: we don't handle multiple link local scopes */
+/* "scopeless" replacement IN6_ARE_ADDR_EQUAL */
+int
+SCTP6_ARE_ADDR_EQUAL(struct in6_addr *a, struct in6_addr *b)
+{
+	struct in6_addr tmp_a, tmp_b;
+	/* use a copy of a and b */
+	tmp_a = *a;
+	tmp_b = *b;
+	in6_clearscope(&tmp_a);
+	in6_clearscope(&tmp_b);
+	return (IN6_ARE_ADDR_EQUAL(&tmp_a, &tmp_b));
+}
+
+#ifdef __OpenBSD__
+extern int ipport_firstauto;
+extern int ipport_lastauto;
+extern int ipport_hifirstauto;
+extern int ipport_hilastauto;
+#endif
+
+#if defined(__FreeBSD__) && __FreeBSD_version > 500000
+
+#ifndef xyzzy
+void sctp_validate_no_locks(void);
+
+void
+SCTP_INP_RLOCK(struct sctp_inpcb *inp)
+{
+	struct sctp_tcb *stcb;
+	LIST_FOREACH(stcb, &inp->sctp_asoc_list, sctp_tcblist) {
+		if (mtx_owned(&(stcb)->tcb_mtx))
+			panic("I own TCB lock?");
+	}
+        if (mtx_owned(&(inp)->inp_mtx))
+		panic("INP Recursive Lock-R");
+        mtx_lock(&(inp)->inp_mtx);
+}
+
+void
+SCTP_INP_WLOCK(struct sctp_inpcb *inp)
+{
+	SCTP_INP_RLOCK(inp);
+}
+
+void
+SCTP_INP_INFO_RLOCK()
+{
+	struct sctp_inpcb *inp;
+	struct sctp_tcb *stcb;
+	LIST_FOREACH(inp, &sctppcbinfo.listhead, sctp_list) {
+		if (mtx_owned(&(inp)->inp_mtx))
+			panic("info-lock and own inp lock?");
+		LIST_FOREACH(stcb, &inp->sctp_asoc_list, sctp_tcblist) {
+			if (mtx_owned(&(stcb)->tcb_mtx))
+				panic("Info lock and own a tcb lock?");
+		}
+	}
+	if (mtx_owned(&sctppcbinfo.ipi_ep_mtx))
+		panic("INP INFO Recursive Lock-R");
+	mtx_lock(&sctppcbinfo.ipi_ep_mtx);
+}
+
+void
+SCTP_INP_INFO_WLOCK()
+{
+	SCTP_INP_INFO_RLOCK();
+}
+
+
+void sctp_validate_no_locks()
+{
+	struct sctp_inpcb *inp;
+	struct sctp_tcb *stcb;
+
+	if (mtx_owned(&sctppcbinfo.ipi_ep_mtx))
+		panic("INP INFO lock is owned?");
+
+	LIST_FOREACH(inp, &sctppcbinfo.listhead, sctp_list) {
+		if (mtx_owned(&(inp)->inp_mtx))
+			panic("You own an INP lock?");
+		LIST_FOREACH(stcb, &inp->sctp_asoc_list, sctp_tcblist) {
+			if (mtx_owned(&(stcb)->tcb_mtx))
+				panic("You own a TCB lock?");
+		}
+	}
+}
+
+#endif
+#endif
+
+void
+sctp_fill_pcbinfo(struct sctp_pcbinfo *spcb)
+{
+	/* We really don't need
+	 * to lock this, but I will
+	 * just because it does not hurt.
+	 */
+	SCTP_INP_INFO_RLOCK();
+	spcb->ep_count = sctppcbinfo.ipi_count_ep;
+	spcb->asoc_count = sctppcbinfo.ipi_count_asoc;
+	spcb->laddr_count = sctppcbinfo.ipi_count_laddr;
+	spcb->raddr_count = sctppcbinfo.ipi_count_raddr;
+	spcb->chk_count = sctppcbinfo.ipi_count_chunk;
+	spcb->sockq_count = sctppcbinfo.ipi_count_sockq;
+	spcb->mbuf_track = sctppcbinfo.mbuf_track;
+	SCTP_INP_INFO_RUNLOCK();
+}
+
+
+/*
+ * Notes on locks for FreeBSD 5 and up. All association
+ * lookups that have a definte ep, the INP structure is
+ * assumed to be locked for reading. If we need to go
+ * find the INP (ususally when a **inp is passed) then
+ * we must lock the INFO structure first and if needed
+ * lock the INP too. Note that if we lock it we must
+ *
+ */
+
+
+/*
+ * Given a endpoint, look and find in its association list any association
+ * with the "to" address given. This can be a "from" address, too, for
+ * inbound packets. For outbound packets it is a true "to" address.
+ */
+static struct sctp_tcb *
+sctp_tcb_special_locate(struct sctp_inpcb **inp_p, struct sockaddr *from,
+			struct sockaddr *to, struct sctp_nets **netp)
+{
+	/**** ASSUMSES THE CALLER holds the INP_INFO_RLOCK */
+
+	/*
+	 * Note for this module care must be taken when observing what to is
+	 * for. In most of the rest of the code the TO field represents my
+	 * peer and the FROM field represents my address. For this module it
+	 * is reversed of that.
+	 */
+	/*
+	 * If we support the TCP model, then we must now dig through to
+	 * see if we can find our endpoint in the list of tcp ep's.
+	 */
+	uint16_t lport, rport;
+	struct sctppcbhead *ephead;
+	struct sctp_inpcb *inp;
+	struct sctp_laddr *laddr;
+	struct sctp_tcb *stcb;
+	struct sctp_nets *net;
+
+	if ((to == NULL) || (from == NULL)) {
+		return (NULL);
+	}
+
+	if (to->sa_family == AF_INET && from->sa_family == AF_INET) {
+		lport = ((struct sockaddr_in *)to)->sin_port;
+		rport = ((struct sockaddr_in *)from)->sin_port;
+	} else if (to->sa_family == AF_INET6 && from->sa_family == AF_INET6) {
+		lport = ((struct sockaddr_in6 *)to)->sin6_port;
+		rport = ((struct sockaddr_in6 *)from)->sin6_port;
+	} else {
+		return NULL;
+	}
+	ephead = &sctppcbinfo.sctp_tcpephash[SCTP_PCBHASH_ALLADDR(
+						     (lport + rport), sctppcbinfo.hashtcpmark)];
+	/*
+	 * Ok now for each of the guys in this bucket we must look
+	 * and see:
+	 *  - Does the remote port match.
+	 *  - Does there single association's addresses match this
+	 *    address (to).
+	 * If so we update p_ep to point to this ep and return the
+	 * tcb from it.
+	 */
+	LIST_FOREACH(inp, ephead, sctp_hash) {
+		if (lport != inp->sctp_lport) {
+			continue;
+		}
+		SCTP_INP_RLOCK(inp);
+		/* check to see if the ep has one of the addresses */
+		if ((inp->sctp_flags & SCTP_PCB_FLAGS_BOUNDALL) == 0) {
+			/* We are NOT bound all, so look further */
+			int match = 0;
+
+			LIST_FOREACH(laddr, &inp->sctp_addr_list, sctp_nxt_addr) {
+				if (laddr->ifa == NULL) {
+#ifdef SCTP_DEBUG
+					if (sctp_debug_on & SCTP_DEBUG_PCB1) {
+						printf("An ounce of prevention is worth a pound of cure\n");
+					}
+#endif
+					continue;
+				}
+				if (laddr->ifa->ifa_addr == NULL) {
+#ifdef SCTP_DEBUG
+					if (sctp_debug_on & SCTP_DEBUG_PCB1) {
+						printf("ifa with a NULL address\n");
+					}
+#endif
+					continue;
+				}
+				if (laddr->ifa->ifa_addr->sa_family ==
+				    to->sa_family) {
+					/* see if it matches */
+					struct sockaddr_in *intf_addr, *sin;
+					intf_addr = (struct sockaddr_in *)
+						laddr->ifa->ifa_addr;
+					sin = (struct sockaddr_in *)to;
+					if (from->sa_family == AF_INET) {
+						if (sin->sin_addr.s_addr ==
+						    intf_addr->sin_addr.s_addr) {
+							match = 1;
+							SCTP_INP_RUNLOCK(inp);
+							break;
+						}
+					} else {
+						struct sockaddr_in6 *intf_addr6;
+						struct sockaddr_in6 *sin6;
+						sin6 = (struct sockaddr_in6 *)
+							to;
+						intf_addr6 = (struct sockaddr_in6 *)
+							laddr->ifa->ifa_addr;
+
+						if (SCTP6_ARE_ADDR_EQUAL(&sin6->sin6_addr,
+									 &intf_addr6->sin6_addr)) {
+							match = 1;
+							SCTP_INP_RUNLOCK(inp);
+							break;
+						}
+					}
+				}
+			}
+			if (match == 0) {
+				/* This endpoint does not have this address */
+				SCTP_INP_RUNLOCK(inp);
+				continue;
+			}
+		}
+		/*
+		 * Ok if we hit here the ep has the address, does it hold the
+		 * tcb?
+		 */
+
+		stcb = LIST_FIRST(&inp->sctp_asoc_list);
+		if (stcb == NULL) {
+			SCTP_INP_RUNLOCK(inp);
+			continue;
+		}
+		SCTP_TCB_LOCK(stcb);
+		if (stcb->rport != rport) {
+			/* remote port does not match. */
+			SCTP_TCB_UNLOCK(stcb);
+			SCTP_INP_RUNLOCK(inp);
+			continue;
+		}
+		/* Does this TCB have a matching address? */
+		TAILQ_FOREACH(net, &stcb->asoc.nets, sctp_next) {
+			if (net->ro._l_addr.sa.sa_family != from->sa_family) {
+				/* not the same family, can't be a match */
+				continue;
+			}
+			if (from->sa_family == AF_INET) {
+				struct sockaddr_in *sin, *rsin;
+				sin = (struct sockaddr_in *)&net->ro._l_addr;
+				rsin = (struct sockaddr_in *)from;
+				if (sin->sin_addr.s_addr ==
+				    rsin->sin_addr.s_addr) {
+					/* found it */
+					if (netp != NULL) {
+						*netp = net;
+					}
+					/* Update the endpoint pointer */
+					*inp_p = inp;
+					SCTP_INP_RUNLOCK(inp);
+					return (stcb);
+				}
+			} else {
+				struct sockaddr_in6 *sin6, *rsin6;
+				sin6 = (struct sockaddr_in6 *)&net->ro._l_addr;
+				rsin6 = (struct sockaddr_in6 *)from;
+				if (SCTP6_ARE_ADDR_EQUAL(&sin6->sin6_addr,
+							 &rsin6->sin6_addr)) {
+					/* found it */
+					if (netp != NULL) {
+						*netp = net;
+					}
+					/* Update the endpoint pointer */
+					*inp_p = inp;
+					SCTP_INP_RUNLOCK(inp);
+					return (stcb);
+				}
+			}
+		}
+		SCTP_TCB_UNLOCK(stcb);
+
+		SCTP_INP_RUNLOCK(inp);
+	}
+	return (NULL);
+}
+
+struct sctp_tcb *
+sctp_findassociation_ep_asconf(struct mbuf *m, int iphlen, int offset,
+    struct sctphdr *sh, struct sctp_inpcb **inp_p, struct sctp_nets **netp)
+{
+	struct sctp_tcb *stcb;
+	struct sockaddr_in *sin;
+	struct sockaddr_in6 *sin6;
+	struct sockaddr_storage local_store, remote_store;
+	struct ip *iph;
+	struct sctp_paramhdr parm_buf, *phdr;
+	int ptype;
+
+	memset(&local_store, 0, sizeof(local_store));
+	memset(&remote_store, 0, sizeof(remote_store));
+
+	/* First get the destination address setup too. */
+	iph = mtod(m, struct ip *);
+	if (iph->ip_v == IPVERSION) {
+		/* its IPv4 */
+		sin = (struct sockaddr_in *)&local_store;
+		sin->sin_family = AF_INET;
+		sin->sin_len = sizeof(*sin);
+		sin->sin_port = sh->dest_port;
+		sin->sin_addr.s_addr = iph->ip_dst.s_addr ;
+	} else if (iph->ip_v == (IPV6_VERSION >> 4)) {
+		/* its IPv6 */
+		struct ip6_hdr *ip6;
+		ip6 = mtod(m, struct ip6_hdr *);
+		sin6 = (struct sockaddr_in6 *)&local_store;
+		sin6->sin6_family = AF_INET6;
+		sin6->sin6_len = sizeof(*sin6);
+		sin6->sin6_port = sh->dest_port;
+		sin6->sin6_addr = ip6->ip6_dst;
+	} else {
+		return NULL;
+	}
+
+	phdr = sctp_get_next_param(m, offset + sizeof(struct sctp_asconf_chunk),
+	    &parm_buf, sizeof(struct sctp_paramhdr));
+	if (phdr == NULL) {
+#ifdef SCTP_DEBUG
+		if (sctp_debug_on & SCTP_DEBUG_INPUT3) {
+			printf("sctp_process_control: failed to get asconf lookup addr\n");
+		}
+#endif /* SCTP_DEBUG */
+		return NULL;
+	}
+	ptype = (int)((u_int)ntohs(phdr->param_type));
+	/* get the correlation address */
+	if (ptype == SCTP_IPV6_ADDRESS) {
+		/* ipv6 address param */
+		struct sctp_ipv6addr_param *p6, p6_buf;
+		if (ntohs(phdr->param_length) != sizeof(struct sctp_ipv6addr_param)) {
+			return NULL;
+		}
+
+		p6 = (struct sctp_ipv6addr_param *)sctp_get_next_param(m,
+		    offset + sizeof(struct sctp_asconf_chunk),
+		    &p6_buf.ph, sizeof(*p6));
+		if (p6 == NULL) {
+#ifdef SCTP_DEBUG
+			if (sctp_debug_on & SCTP_DEBUG_INPUT3) {
+				printf("sctp_process_control: failed to get asconf v6 lookup addr\n");
+			}
+#endif /* SCTP_DEBUG */
+			return (NULL);
+		}
+		sin6 = (struct sockaddr_in6 *)&remote_store;
+		sin6->sin6_family = AF_INET6;
+		sin6->sin6_len = sizeof(*sin6);
+		sin6->sin6_port = sh->src_port;
+		memcpy(&sin6->sin6_addr, &p6->addr, sizeof(struct in6_addr));
+	} else if (ptype == SCTP_IPV4_ADDRESS) {
+		/* ipv4 address param */
+		struct sctp_ipv4addr_param *p4, p4_buf;
+		if (ntohs(phdr->param_length) != sizeof(struct sctp_ipv4addr_param)) {
+			return NULL;
+		}
+
+		p4 = (struct sctp_ipv4addr_param *)sctp_get_next_param(m,
+		    offset + sizeof(struct sctp_asconf_chunk),
+		    &p4_buf.ph, sizeof(*p4));
+		if (p4 == NULL) {
+#ifdef SCTP_DEBUG
+			if (sctp_debug_on & SCTP_DEBUG_INPUT3) {
+				printf("sctp_process_control: failed to get asconf v4 lookup addr\n");
+			}
+#endif /* SCTP_DEBUG */
+			return (NULL);
+		}
+		sin = (struct sockaddr_in *)&remote_store;
+		sin->sin_family = AF_INET;
+		sin->sin_len = sizeof(*sin);
+		sin->sin_port = sh->src_port;
+		memcpy(&sin->sin_addr, &p4->addr, sizeof(struct in_addr));
+	} else {
+		/* invalid address param type */
+		return NULL;
+	}
+
+	stcb = sctp_findassociation_ep_addr(inp_p,
+	    (struct sockaddr *)&remote_store, netp,
+	    (struct sockaddr *)&local_store, NULL);
+	return (stcb);
+}
+
+struct sctp_tcb *
+sctp_findassociation_ep_addr(struct sctp_inpcb **inp_p, struct sockaddr *remote,
+    struct sctp_nets **netp, struct sockaddr *local, struct sctp_tcb *locked_tcb)
+{
+	struct sctpasochead *head;
+	struct sctp_inpcb *inp;
+	struct sctp_tcb *stcb;
+	struct sctp_nets *net;
+	uint16_t rport;
+
+	inp = *inp_p;
+	if (remote->sa_family == AF_INET) {
+		rport = (((struct sockaddr_in *)remote)->sin_port);
+	} else if (remote->sa_family == AF_INET6) {
+		rport = (((struct sockaddr_in6 *)remote)->sin6_port);
+	} else {
+		return (NULL);
+	}
+	if (locked_tcb) {
+		/* UN-lock so we can do proper locking here
+		 * this occurs when called from load_addresses_from_init.
+		 */
+		SCTP_TCB_UNLOCK(locked_tcb);
+	}
+	SCTP_INP_INFO_RLOCK();
+	if (inp->sctp_flags & SCTP_PCB_FLAGS_TCPTYPE) {
+		/*
+		 * Now either this guy is our listner or it's the connector.
+		 * If it is the one that issued the connect, then it's only
+		 * chance is to be the first TCB in the list. If it is the
+		 * acceptor, then do the special_lookup to hash and find the
+		 * real inp.
+		 */
+		if (inp->sctp_flags & SCTP_PCB_FLAGS_ACCEPTING) {
+			/* to is peer addr, from is my addr */
+			stcb = sctp_tcb_special_locate(inp_p, remote, local,
+						       netp);
+			if ((stcb != NULL) && (locked_tcb == NULL)){
+				/* we have a locked tcb, lower refcount */
+				SCTP_INP_WLOCK(inp);
+				SCTP_INP_DECR_REF(inp);
+				SCTP_INP_WUNLOCK(inp);
+			}
+			if (locked_tcb != NULL) {
+				SCTP_INP_RLOCK(locked_tcb->sctp_ep);
+				SCTP_TCB_LOCK(locked_tcb);
+				SCTP_INP_RUNLOCK(locked_tcb->sctp_ep);
+				if (stcb != NULL)
+					SCTP_TCB_UNLOCK(stcb);
+			}
+			SCTP_INP_INFO_RUNLOCK();
+			return (stcb);
+		} else {
+			SCTP_INP_WLOCK(inp);
+			stcb = LIST_FIRST(&inp->sctp_asoc_list);
+			if (stcb == NULL) {
+				goto null_return;
+			}
+			SCTP_TCB_LOCK(stcb);
+			if (stcb->rport != rport) {
+				/* remote port does not match. */
+				SCTP_TCB_UNLOCK(stcb);
+				goto null_return;
+			}
+			/* now look at the list of remote addresses */
+			TAILQ_FOREACH(net, &stcb->asoc.nets, sctp_next) {
+				if (net->ro._l_addr.sa.sa_family !=
+				    remote->sa_family) {
+					/* not the same family */
+					continue;
+				}
+				if (remote->sa_family == AF_INET) {
+					struct sockaddr_in *sin, *rsin;
+					sin = (struct sockaddr_in *)
+						&net->ro._l_addr;
+					rsin = (struct sockaddr_in *)remote;
+					if (sin->sin_addr.s_addr ==
+					    rsin->sin_addr.s_addr) {
+						/* found it */
+						if (netp != NULL) {
+							*netp = net;
+						}
+						if (locked_tcb == NULL) {
+							SCTP_INP_DECR_REF(inp);
+						}
+						SCTP_INP_WUNLOCK(inp);
+						SCTP_INP_INFO_RUNLOCK();
+						return (stcb);
+					}
+				} else if (remote->sa_family == AF_INET6) {
+					struct sockaddr_in6 *sin6, *rsin6;
+					sin6 = (struct sockaddr_in6 *)&net->ro._l_addr;
+					rsin6 = (struct sockaddr_in6 *)remote;
+					if (SCTP6_ARE_ADDR_EQUAL(&sin6->sin6_addr,
+								 &rsin6->sin6_addr)) {
+						/* found it */
+						if (netp != NULL) {
+							*netp = net;
+						}
+						if (locked_tcb == NULL) {
+							SCTP_INP_DECR_REF(inp);
+						}
+						SCTP_INP_WUNLOCK(inp);
+						SCTP_INP_INFO_RUNLOCK();
+						return (stcb);
+					}
+				}
+			}
+			SCTP_TCB_UNLOCK(stcb);
+		}
+	} else {
+		SCTP_INP_WLOCK(inp);
+		head = &inp->sctp_tcbhash[SCTP_PCBHASH_ALLADDR(rport,
+							       inp->sctp_hashmark)];
+		if (head == NULL) {
+			goto null_return;
+		}
+		LIST_FOREACH(stcb, head, sctp_tcbhash) {
+			if (stcb->rport != rport) {
+				/* remote port does not match */
+				continue;
+			}
+			/* now look at the list of remote addresses */
+			SCTP_TCB_LOCK(stcb);
+			TAILQ_FOREACH(net, &stcb->asoc.nets, sctp_next) {
+				if (net->ro._l_addr.sa.sa_family !=
+				    remote->sa_family) {
+					/* not the same family */
+					continue;
+				}
+				if (remote->sa_family == AF_INET) {
+					struct sockaddr_in *sin, *rsin;
+					sin = (struct sockaddr_in *)
+						&net->ro._l_addr;
+					rsin = (struct sockaddr_in *)remote;
+					if (sin->sin_addr.s_addr ==
+					    rsin->sin_addr.s_addr) {
+						/* found it */
+						if (netp != NULL) {
+							*netp = net;
+						}
+						if (locked_tcb == NULL) {
+							SCTP_INP_DECR_REF(inp);
+						}
+						SCTP_INP_WUNLOCK(inp);
+						SCTP_INP_INFO_RUNLOCK();
+						return (stcb);
+					}
+				} else if (remote->sa_family == AF_INET6) {
+					struct sockaddr_in6 *sin6, *rsin6;
+					sin6 = (struct sockaddr_in6 *)
+						&net->ro._l_addr;
+					rsin6 = (struct sockaddr_in6 *)remote;
+					if (SCTP6_ARE_ADDR_EQUAL(&sin6->sin6_addr,
+								 &rsin6->sin6_addr)) {
+						/* found it */
+						if (netp != NULL) {
+							*netp = net;
+						}
+						if (locked_tcb == NULL) {
+							SCTP_INP_DECR_REF(inp);
+						}
+						SCTP_INP_WUNLOCK(inp);
+						SCTP_INP_INFO_RUNLOCK();
+						return (stcb);
+					}
+				}
+			}
+			SCTP_TCB_UNLOCK(stcb);
+		}
+	}
+ null_return:
+	/* clean up for returning null */
+	if (locked_tcb){
+		if (locked_tcb->sctp_ep != inp) {
+			SCTP_INP_RLOCK(locked_tcb->sctp_ep);
+			SCTP_TCB_LOCK(locked_tcb);
+			SCTP_INP_RUNLOCK(locked_tcb->sctp_ep);
+		} else
+			SCTP_TCB_LOCK(locked_tcb);
+	}
+	SCTP_INP_WUNLOCK(inp);
+	SCTP_INP_INFO_RUNLOCK();
+	/* not found */
+	return (NULL);
+}
+
+/*
+ * Find an association for a specific endpoint using the association id
+ * given out in the COMM_UP notification
+ */
+struct sctp_tcb *
+sctp_findassociation_ep_asocid(struct sctp_inpcb *inp, caddr_t asoc_id)
+{
+	/*
+	 * Use my the assoc_id to find a endpoint
+	 */
+	struct sctpasochead *head;
+	struct sctp_tcb *stcb;
+	u_int32_t vtag;
+
+	if (asoc_id == 0 || inp == NULL) {
+		return (NULL);
+	}
+	SCTP_INP_INFO_RLOCK();
+	vtag = (u_int32_t)asoc_id;
+	head = &sctppcbinfo.sctp_asochash[SCTP_PCBHASH_ASOC(vtag,
+	    sctppcbinfo.hashasocmark)];
+	if (head == NULL) {
+		/* invalid vtag */
+		SCTP_INP_INFO_RUNLOCK();
+		return (NULL);
+	}
+	LIST_FOREACH(stcb, head, sctp_asocs) {
+		SCTP_INP_RLOCK(stcb->sctp_ep);
+		SCTP_TCB_LOCK(stcb);
+		SCTP_INP_RUNLOCK(stcb->sctp_ep);
+		if (stcb->asoc.my_vtag == vtag) {
+			/* candidate */
+			if (inp != stcb->sctp_ep) {
+				/* some other guy has the
+				 * same vtag active (vtag collision).
+				 */
+				sctp_pegs[SCTP_VTAG_BOGUS]++;
+				SCTP_TCB_UNLOCK(stcb);
+				continue;
+			}
+			sctp_pegs[SCTP_VTAG_EXPR]++;
+			SCTP_INP_INFO_RUNLOCK();
+			return (stcb);
+		}
+		SCTP_TCB_UNLOCK(stcb);
+	}
+	SCTP_INP_INFO_RUNLOCK();
+	return (NULL);
+}
+
+static struct sctp_inpcb *
+sctp_endpoint_probe(struct sockaddr *nam, struct sctppcbhead *head,
+		    uint16_t lport)
+{
+	struct sctp_inpcb *inp;
+	struct sockaddr_in *sin;
+	struct sockaddr_in6 *sin6;
+	struct sctp_laddr *laddr;
+
+	/* Endpoing probe expects
+	 * that the INP_INFO is locked.
+	 */
+	if (nam->sa_family == AF_INET) {
+		sin = (struct sockaddr_in *)nam;
+		sin6 = NULL;
+	} else if (nam->sa_family == AF_INET6) {
+		sin6 = (struct sockaddr_in6 *)nam;
+		sin = NULL;
+	} else {
+		/* unsupported family */
+		return (NULL);
+	}
+	if (head == NULL)
+		return (NULL);
+
+	LIST_FOREACH(inp, head, sctp_hash) {
+		SCTP_INP_RLOCK(inp);
+
+		if ((inp->sctp_flags & SCTP_PCB_FLAGS_BOUNDALL) &&
+		    (inp->sctp_lport == lport)) {
+			/* got it */
+			if ((nam->sa_family == AF_INET) &&
+			    (inp->sctp_flags & SCTP_PCB_FLAGS_BOUND_V6) &&
+#if defined(__FreeBSD__) || defined(__APPLE__) || defined(__DragonFly__)
+			    (((struct inpcb *)inp)->inp_flags & IN6P_IPV6_V6ONLY)
+#else
+#if defined(__OpenBSD__)
+			    (0)	/* For open bsd we do dual bind only */
+#else
+			    (((struct in6pcb *)inp)->in6p_flags & IN6P_IPV6_V6ONLY)
+#endif
+#endif
+				) {
+				/* IPv4 on a IPv6 socket with ONLY IPv6 set */
+				SCTP_INP_RUNLOCK(inp);
+				continue;
+			}
+			/* A V6 address and the endpoint is NOT bound V6 */
+			if (nam->sa_family == AF_INET6 &&
+			   (inp->sctp_flags & SCTP_PCB_FLAGS_BOUND_V6) == 0) {
+				SCTP_INP_RUNLOCK(inp);
+				continue;
+			}
+			SCTP_INP_RUNLOCK(inp);
+			return (inp);
+		}
+		SCTP_INP_RUNLOCK(inp);
+	}
+
+	if ((nam->sa_family == AF_INET) &&
+	    (sin->sin_addr.s_addr == INADDR_ANY)) {
+		/* Can't hunt for one that has no address specified */
+		return (NULL);
+	} else if ((nam->sa_family == AF_INET6) &&
+		   (IN6_IS_ADDR_UNSPECIFIED(&sin6->sin6_addr))) {
+		/* Can't hunt for one that has no address specified */
+		return (NULL);
+	}
+	/*
+	 * ok, not bound to all so see if we can find a EP bound to this
+	 * address.
+	 */
+#ifdef SCTP_DEBUG
+	if (sctp_debug_on & SCTP_DEBUG_PCB1) {
+		printf("Ok, there is NO bound-all available for port:%x\n", ntohs(lport));
+	}
+#endif
+	LIST_FOREACH(inp, head, sctp_hash) {
+		SCTP_INP_RLOCK(inp);
+		if ((inp->sctp_flags & SCTP_PCB_FLAGS_BOUNDALL)) {
+			SCTP_INP_RUNLOCK(inp);
+			continue;
+		}
+		/*
+		 * Ok this could be a likely candidate, look at all of
+		 * its addresses
+		 */
+		if (inp->sctp_lport != lport) {
+			SCTP_INP_RUNLOCK(inp);
+			continue;
+		}
+#ifdef SCTP_DEBUG
+		if (sctp_debug_on & SCTP_DEBUG_PCB1) {
+			printf("Ok, found maching local port\n");
+		}
+#endif
+		LIST_FOREACH(laddr, &inp->sctp_addr_list, sctp_nxt_addr) {
+			if (laddr->ifa == NULL) {
+#ifdef SCTP_DEBUG
+				if (sctp_debug_on & SCTP_DEBUG_PCB1) {
+					printf("An ounce of prevention is worth a pound of cure\n");
+				}
+#endif
+				continue;
+			}
+#ifdef SCTP_DEBUG
+			if (sctp_debug_on & SCTP_DEBUG_PCB1) {
+				printf("Ok laddr->ifa:%p is possible, ",
+				    laddr->ifa);
+			}
+#endif
+			if (laddr->ifa->ifa_addr == NULL) {
+#ifdef SCTP_DEBUG
+				if (sctp_debug_on & SCTP_DEBUG_PCB1) {
+					printf("Huh IFA as an ifa_addr=NULL, ");
+				}
+#endif
+				continue;
+			}
+#ifdef SCTP_DEBUG
+			if (sctp_debug_on & SCTP_DEBUG_PCB1) {
+				printf("Ok laddr->ifa:%p is possible, ",
+				    laddr->ifa->ifa_addr);
+				sctp_print_address(laddr->ifa->ifa_addr);
+				printf("looking for ");
+				sctp_print_address(nam);
+			}
+#endif
+			if (laddr->ifa->ifa_addr->sa_family == nam->sa_family) {
+				/* possible, see if it matches */
+				struct sockaddr_in *intf_addr;
+				intf_addr = (struct sockaddr_in *)
+				    laddr->ifa->ifa_addr;
+				if (nam->sa_family == AF_INET) {
+					if (sin->sin_addr.s_addr ==
+					    intf_addr->sin_addr.s_addr) {
+#ifdef SCTP_DEBUG
+						if (sctp_debug_on & SCTP_DEBUG_PCB1) {
+							printf("YES, return ep:%p\n", inp);
+						}
+#endif
+						SCTP_INP_RUNLOCK(inp);
+						return (inp);
+					}
+				} else if (nam->sa_family == AF_INET6) {
+					struct sockaddr_in6 *intf_addr6;
+					intf_addr6 = (struct sockaddr_in6 *)
+					    laddr->ifa->ifa_addr;
+					if (SCTP6_ARE_ADDR_EQUAL(&sin6->sin6_addr,
+				 	    &intf_addr6->sin6_addr)) {
+#ifdef SCTP_DEBUG
+						if (sctp_debug_on & SCTP_DEBUG_PCB1) {
+							printf("YES, return ep:%p\n", inp);
+						}
+#endif
+						SCTP_INP_RUNLOCK(inp);
+						return (inp);
+					}
+				}
+			}
+			SCTP_INP_RUNLOCK(inp);
+		}
+	}
+#ifdef SCTP_DEBUG
+	if (sctp_debug_on & SCTP_DEBUG_PCB1) {
+		printf("NO, Falls out to NULL\n");
+	}
+#endif
+	return (NULL);
+}
+
+
+struct sctp_inpcb *
+sctp_pcb_findep(struct sockaddr *nam, int find_tcp_pool, int have_lock)
+{
+	/*
+	 * First we check the hash table to see if someone has this port
+	 * bound with just the port.
+	 */
+	struct sctp_inpcb *inp;
+	struct sctppcbhead *head;
+	struct sockaddr_in *sin;
+	struct sockaddr_in6 *sin6;
+	int lport;
+#ifdef SCTP_DEBUG
+	if (sctp_debug_on & SCTP_DEBUG_PCB1) {
+		printf("Looking for endpoint %d :",
+		       ntohs(((struct sockaddr_in *)nam)->sin_port));
+		sctp_print_address(nam);
+	}
+#endif
+	if (nam->sa_family == AF_INET) {
+		sin = (struct sockaddr_in *)nam;
+		lport = ((struct sockaddr_in *)nam)->sin_port;
+	} else if (nam->sa_family == AF_INET6) {
+		sin6 = (struct sockaddr_in6 *)nam;
+		lport = ((struct sockaddr_in6 *)nam)->sin6_port;
+	} else {
+		/* unsupported family */
+		return (NULL);
+	}
+	/*
+	 * I could cheat here and just cast to one of the types but we will
+	 * do it right. It also provides the check against an Unsupported
+	 * type too.
+	 */
+	/* Find the head of the ALLADDR chain */
+	if (have_lock == 0)
+		SCTP_INP_INFO_RLOCK();
+	head = &sctppcbinfo.sctp_ephash[SCTP_PCBHASH_ALLADDR(lport,
+							     sctppcbinfo.hashmark)];
+#ifdef SCTP_DEBUG
+	if (sctp_debug_on & SCTP_DEBUG_PCB1) {
+		printf("Main hash to lookup at head:%p\n", head);
+	}
+#endif
+ 	inp = sctp_endpoint_probe(nam, head, lport);
+
+	/*
+	 * If the TCP model exists it could be that the main listening
+	 * endpoint is gone but there exists a connected socket for this
+	 * guy yet. If so we can return the first one that we find. This
+	 * may NOT be the correct one but the sctp_findassociation_ep_addr
+	 * has further code to look at all TCP models.
+	 */
+	if (inp == NULL && find_tcp_pool) {
+		unsigned int i;
+#ifdef SCTP_DEBUG
+		if (sctp_debug_on & SCTP_DEBUG_PCB1) {
+			printf("EP was NULL and TCP model is supported\n");
+		}
+#endif
+		for (i = 0; i < sctppcbinfo.hashtblsize; i++) {
+			/*
+			 * This is real gross, but we do NOT have a remote
+			 * port at this point depending on who is calling. We
+			 * must therefore look for ANY one that matches our
+			 * local port :/
+			 */
+			head = &sctppcbinfo.sctp_tcpephash[i];
+			if (LIST_FIRST(head)) {
+				inp = sctp_endpoint_probe(nam, head, lport);
+				if (inp) {
+					/* Found one */
+					break;
+				}
+			}
+		}
+	}
+#ifdef SCTP_DEBUG
+	if (sctp_debug_on & SCTP_DEBUG_PCB1) {
+		printf("EP to return is %p\n", inp);
+	}
+#endif
+	if (have_lock == 0) {
+		if (inp) {
+			SCTP_INP_WLOCK(inp);
+			SCTP_INP_INCR_REF(inp);
+			SCTP_INP_WUNLOCK(inp);
+		}
+		SCTP_INP_INFO_RUNLOCK();
+	} else {
+		if (inp) {
+			SCTP_INP_WLOCK(inp);
+			SCTP_INP_INCR_REF(inp);
+			SCTP_INP_WUNLOCK(inp);
+		}
+	}
+	return (inp);
+}
+
+/*
+ * Find an association for an endpoint with the pointer to whom you want
+ * to send to and the endpoint pointer. The address can be IPv4 or IPv6.
+ * We may need to change the *to to some other struct like a mbuf...
+ */
+struct sctp_tcb *
+sctp_findassociation_addr_sa(struct sockaddr *to, struct sockaddr *from,
+    struct sctp_inpcb **inp_p, struct sctp_nets **netp, int find_tcp_pool)
+{
+	struct sctp_inpcb *inp;
+	struct sctp_tcb *retval;
+
+	SCTP_INP_INFO_RLOCK();
+	if (find_tcp_pool) {
+		if (inp_p != NULL) {
+			retval = sctp_tcb_special_locate(inp_p, from, to, netp);
+		} else {
+			retval = sctp_tcb_special_locate(&inp, from, to, netp);
+		}
+		if (retval != NULL) {
+			SCTP_INP_INFO_RUNLOCK();
+			return (retval);
+		}
+	}
+	inp = sctp_pcb_findep(to, 0, 1);
+	if (inp_p != NULL) {
+		*inp_p = inp;
+	}
+	SCTP_INP_INFO_RUNLOCK();
+
+	if (inp == NULL) {
+		return (NULL);
+	}
+
+	/*
+	 * ok, we have an endpoint, now lets find the assoc for it (if any)
+	 * we now place the source address or from in the to of the find
+	 * endpoint call. Since in reality this chain is used from the
+	 * inbound packet side.
+	 */
+	if (inp_p != NULL) {
+		return (sctp_findassociation_ep_addr(inp_p, from, netp, to, NULL));
+	} else {
+		return (sctp_findassociation_ep_addr(&inp, from, netp, to, NULL));
+	}
+}
+
+
+/*
+ * This routine will grub through the mbuf that is a INIT or INIT-ACK and
+ * find all addresses that the sender has specified in any address list.
+ * Each address will be used to lookup the TCB and see if one exits.
+ */
+static struct sctp_tcb *
+sctp_findassociation_special_addr(struct mbuf *m, int iphlen, int offset,
+    struct sctphdr *sh, struct sctp_inpcb **inp_p, struct sctp_nets **netp,
+    struct sockaddr *dest)
+{
+	struct sockaddr_in sin4;
+	struct sockaddr_in6 sin6;
+	struct sctp_paramhdr *phdr, parm_buf;
+	struct sctp_tcb *retval;
+	u_int32_t ptype, plen;
+
+	memset(&sin4, 0, sizeof(sin4));
+	memset(&sin6, 0, sizeof(sin6));
+	sin4.sin_len = sizeof(sin4);
+	sin4.sin_family = AF_INET;
+	sin4.sin_port = sh->src_port;
+	sin6.sin6_len = sizeof(sin6);
+	sin6.sin6_family = AF_INET6;
+	sin6.sin6_port = sh->src_port;
+
+	retval = NULL;
+	offset += sizeof(struct sctp_init_chunk);
+
+	phdr = sctp_get_next_param(m, offset, &parm_buf, sizeof(parm_buf));
+	while (phdr != NULL) {
+		/* now we must see if we want the parameter */
+		ptype = ntohs(phdr->param_type);
+		plen = ntohs(phdr->param_length);
+		if (plen == 0) {
+#ifdef SCTP_DEBUG
+			if (sctp_debug_on & SCTP_DEBUG_PCB1) {
+				printf("sctp_findassociation_special_addr: Impossible length in parameter\n");
+			}
+#endif /* SCTP_DEBUG */
+			break;
+		}
+		if (ptype == SCTP_IPV4_ADDRESS &&
+		    plen == sizeof(struct sctp_ipv4addr_param)) {
+			/* Get the rest of the address */
+			struct sctp_ipv4addr_param ip4_parm, *p4;
+
+			phdr = sctp_get_next_param(m, offset,
+			    (struct sctp_paramhdr *)&ip4_parm, plen);
+			if (phdr == NULL) {
+				return (NULL);
+			}
+			p4 = (struct sctp_ipv4addr_param *)phdr;
+			memcpy(&sin4.sin_addr, &p4->addr, sizeof(p4->addr));
+			/* look it up */
+			retval = sctp_findassociation_ep_addr(inp_p,
+			    (struct sockaddr *)&sin4, netp, dest, NULL);
+			if (retval != NULL) {
+				return (retval);
+			}
+		} else if (ptype == SCTP_IPV6_ADDRESS &&
+		    plen == sizeof(struct sctp_ipv6addr_param)) {
+			/* Get the rest of the address */
+			struct sctp_ipv6addr_param ip6_parm, *p6;
+
+			phdr = sctp_get_next_param(m, offset,
+			    (struct sctp_paramhdr *)&ip6_parm, plen);
+			if (phdr == NULL) {
+				return (NULL);
+			}
+			p6 = (struct sctp_ipv6addr_param *)phdr;
+			memcpy(&sin6.sin6_addr, &p6->addr, sizeof(p6->addr));
+			/* look it up */
+			retval = sctp_findassociation_ep_addr(inp_p,
+			    (struct sockaddr *)&sin6, netp, dest, NULL);
+			if (retval != NULL) {
+				return (retval);
+			}
+		}
+		offset += SCTP_SIZE32(plen);
+		phdr = sctp_get_next_param(m, offset, &parm_buf,
+		    sizeof(parm_buf));
+	}
+	return (NULL);
+}
+
+static struct sctp_tcb *
+sctp_findassoc_by_vtag(struct sockaddr *from, uint32_t vtag,
+    struct sctp_inpcb **inp_p, struct sctp_nets **netp, uint16_t rport,
+    uint16_t lport)
+{
+	/*
+	 * Use my vtag to hash. If we find it we then verify the source addr
+	 * is in the assoc. If all goes well we save a bit on rec of a packet.
+	 */
+	struct sctpasochead *head;
+	struct sctp_nets *net;
+	struct sctp_tcb *stcb;
+
+	SCTP_INP_INFO_RLOCK();
+	head = &sctppcbinfo.sctp_asochash[SCTP_PCBHASH_ASOC(vtag,
+	    sctppcbinfo.hashasocmark)];
+	if (head == NULL) {
+		/* invalid vtag */
+		SCTP_INP_INFO_RUNLOCK();
+		return (NULL);
+	}
+	LIST_FOREACH(stcb, head, sctp_asocs) {
+		SCTP_INP_RLOCK(stcb->sctp_ep);
+		SCTP_TCB_LOCK(stcb);
+		SCTP_INP_RUNLOCK(stcb->sctp_ep);
+		if (stcb->asoc.my_vtag == vtag) {
+			/* candidate */
+			if (stcb->rport != rport) {
+				/*
+				 * we could remove this if vtags are unique
+				 * across the system.
+				 */
+				SCTP_TCB_UNLOCK(stcb);
+				continue;
+			}
+			if (stcb->sctp_ep->sctp_lport != lport) {
+				/*
+				 * we could remove this if vtags are unique
+				 * across the system.
+				 */
+				SCTP_TCB_UNLOCK(stcb);
+				continue;
+			}
+			net = sctp_findnet(stcb, from);
+			if (net) {
+				/* yep its him. */
+				*netp = net;
+				sctp_pegs[SCTP_VTAG_EXPR]++;
+				*inp_p = stcb->sctp_ep;
+				SCTP_INP_INFO_RUNLOCK();
+				return (stcb);
+			} else {
+ 				/* not him, this should only
+ 				 * happen in rare cases so
+ 				 * I peg it.
+  				 */
+ 				sctp_pegs[SCTP_VTAG_BOGUS]++;
+			}
+		}
+		SCTP_TCB_UNLOCK(stcb);
+	}
+	SCTP_INP_INFO_RUNLOCK();
+	return (NULL);
+}
+
+/*
+ * Find an association with the pointer to the inbound IP packet. This
+ * can be a IPv4 or IPv6 packet.
+ */
+struct sctp_tcb *
+sctp_findassociation_addr(struct mbuf *m, int iphlen, int offset,
+    struct sctphdr *sh, struct sctp_chunkhdr *ch,
+    struct sctp_inpcb **inp_p, struct sctp_nets **netp)
+{
+	int find_tcp_pool;
+	struct ip *iph;
+	struct sctp_tcb *retval;
+	struct sockaddr_storage to_store, from_store;
+	struct sockaddr *to = (struct sockaddr *)&to_store;
+	struct sockaddr *from = (struct sockaddr *)&from_store;
+	struct sctp_inpcb *inp;
+
+
+	iph = mtod(m, struct ip *);
+	if (iph->ip_v == IPVERSION) {
+		/* its IPv4 */
+		struct sockaddr_in *to4, *from4;
+
+		to4 = (struct sockaddr_in *)&to_store;
+		from4 = (struct sockaddr_in *)&from_store;
+		bzero(to4, sizeof(*to4));
+		bzero(from4, sizeof(*from4));
+		from4->sin_family = to4->sin_family = AF_INET;
+		from4->sin_len = to4->sin_len = sizeof(struct sockaddr_in);
+		from4->sin_addr.s_addr  = iph->ip_src.s_addr;
+		to4->sin_addr.s_addr = iph->ip_dst.s_addr ;
+		from4->sin_port = sh->src_port;
+		to4->sin_port = sh->dest_port;
+	} else if (iph->ip_v == (IPV6_VERSION >> 4)) {
+		/* its IPv6 */
+		struct ip6_hdr *ip6;
+		struct sockaddr_in6 *to6, *from6;
+
+		ip6 = mtod(m, struct ip6_hdr *);
+		to6 = (struct sockaddr_in6 *)&to_store;
+		from6 = (struct sockaddr_in6 *)&from_store;
+		bzero(to6, sizeof(*to6));
+		bzero(from6, sizeof(*from6));
+		from6->sin6_family = to6->sin6_family = AF_INET6;
+		from6->sin6_len = to6->sin6_len = sizeof(struct sockaddr_in6);
+		to6->sin6_addr = ip6->ip6_dst;
+		from6->sin6_addr = ip6->ip6_src;
+		from6->sin6_port = sh->src_port;
+		to6->sin6_port = sh->dest_port;
+		/* Get the scopes in properly to the sin6 addr's */
+		(void)in6_recoverscope(to6, &to6->sin6_addr, NULL);
+#if defined(SCTP_BASE_FREEBSD) || defined(__APPLE__) || defined(__DragonFly__)
+		(void)in6_embedscope(&to6->sin6_addr, to6, NULL, NULL);
+#else
+		(void)in6_embedscope(&to6->sin6_addr, to6);
+#endif
+
+		(void)in6_recoverscope(from6, &from6->sin6_addr, NULL);
+#if defined(SCTP_BASE_FREEBSD) || defined(__APPLE__) || defined(__DragonFly__)
+		(void)in6_embedscope(&from6->sin6_addr, from6, NULL, NULL);
+#else
+		(void)in6_embedscope(&from6->sin6_addr, from6);
+#endif
+	} else {
+		/* Currently not supported. */
+		return (NULL);
+	}
+#ifdef SCTP_DEBUG
+	if (sctp_debug_on & SCTP_DEBUG_PCB1) {
+		printf("Looking for port %d address :",
+		       ntohs(((struct sockaddr_in *)to)->sin_port));
+		sctp_print_address(to);
+		printf("From for port %d address :",
+		       ntohs(((struct sockaddr_in *)from)->sin_port));
+		sctp_print_address(from);
+	}
+#endif
+
+	if (sh->v_tag) {
+		/* we only go down this path if vtag is non-zero */
+		retval = sctp_findassoc_by_vtag(from, ntohl(sh->v_tag),
+		    inp_p, netp, sh->src_port, sh->dest_port);
+		if (retval) {
+			return (retval);
+		}
+	}
+	find_tcp_pool = 0;
+	if ((ch->chunk_type != SCTP_INITIATION) &&
+	    (ch->chunk_type != SCTP_INITIATION_ACK) &&
+	    (ch->chunk_type != SCTP_COOKIE_ACK) &&
+	    (ch->chunk_type != SCTP_COOKIE_ECHO)) {
+		/* Other chunk types go to the tcp pool. */
+		find_tcp_pool = 1;
+	}
+	if (inp_p) {
+		retval = sctp_findassociation_addr_sa(to, from, inp_p, netp,
+		    find_tcp_pool);
+		inp = *inp_p;
+	} else {
+		retval = sctp_findassociation_addr_sa(to, from, &inp, netp,
+		    find_tcp_pool);
+	}
+#ifdef SCTP_DEBUG
+	if (sctp_debug_on & SCTP_DEBUG_PCB1) {
+		printf("retval:%p inp:%p\n", retval, inp);
+	}
+#endif
+	if (retval == NULL && inp) {
+		/* Found a EP but not this address */
+#ifdef SCTP_DEBUG
+		if (sctp_debug_on & SCTP_DEBUG_PCB1) {
+			printf("Found endpoint %p but no asoc - ep state:%x\n",
+			    inp, inp->sctp_flags);
+		}
+#endif
+		if ((ch->chunk_type == SCTP_INITIATION) ||
+		    (ch->chunk_type == SCTP_INITIATION_ACK)) {
+			/*
+			 * special hook, we do NOT return linp or an
+			 * association that is linked to an existing
+			 * association that is under the TCP pool (i.e. no
+			 * listener exists). The endpoint finding routine
+			 * will always find a listner before examining the
+			 * TCP pool.
+			 */
+			if (inp->sctp_flags & SCTP_PCB_FLAGS_IN_TCPPOOL) {
+#ifdef SCTP_DEBUG
+				if (sctp_debug_on & SCTP_DEBUG_PCB1) {
+					printf("Gak, its in the TCP pool... return NULL");
+				}
+#endif
+				if (inp_p) {
+					*inp_p = NULL;
+				}
+				return (NULL);
+			}
+#ifdef SCTP_DEBUG
+			if (sctp_debug_on & SCTP_DEBUG_PCB1) {
+				printf("Now doing SPECIAL find\n");
+			}
+#endif
+			retval = sctp_findassociation_special_addr(m, iphlen,
+			    offset, sh, inp_p, netp, to);
+		}
+	}
+#ifdef SCTP_DEBUG
+	if (sctp_debug_on & SCTP_DEBUG_PCB1) {
+	    printf("retval is %p\n", retval);
+	}
+#endif
+	return (retval);
+}
+
+extern int sctp_max_burst_default;
+
+extern unsigned int sctp_delayed_sack_time_default;
+extern unsigned int sctp_heartbeat_interval_default;
+extern unsigned int sctp_pmtu_raise_time_default;
+extern unsigned int sctp_shutdown_guard_time_default;
+extern unsigned int sctp_secret_lifetime_default;
+
+extern unsigned int sctp_rto_max_default;
+extern unsigned int sctp_rto_min_default;
+extern unsigned int sctp_rto_initial_default;
+extern unsigned int sctp_init_rto_max_default;
+extern unsigned int sctp_valid_cookie_life_default;
+extern unsigned int sctp_init_rtx_max_default;
+extern unsigned int sctp_assoc_rtx_max_default;
+extern unsigned int sctp_path_rtx_max_default;
+extern unsigned int sctp_nr_outgoing_streams_default;
+
+/*
+ * allocate a sctp_inpcb and setup a temporary binding to a port/all
+ * addresses. This way if we don't get a bind we by default pick a ephemeral
+ * port with all addresses bound.
+ */
+int
+sctp_inpcb_alloc(struct socket *so)
+{
+	/*
+	 * we get called when a new endpoint starts up. We need to allocate
+	 * the sctp_inpcb structure from the zone and init it. Mark it as
+	 * unbound and find a port that we can use as an ephemeral with
+	 * INADDR_ANY. If the user binds later no problem we can then add
+	 * in the specific addresses. And setup the default parameters for
+	 * the EP.
+	 */
+	int i, error;
+	struct sctp_inpcb *inp, *n_inp;
+	struct sctp_pcb *m;
+	struct timeval time;
+
+	error = 0;
+
+        /* Hack alert:
+	 *
+	 * This code audits the entire INP list to see if
+	 * any ep's that are in the GONE state are now
+	 * all free. This should not happen really since when
+	 * the last association if freed we should end up deleting
+	 * the inpcb. This code including the locks should
+	 * be taken out ... since the last set of fixes I
+	 * have not seen the "Found a GONE on list" has not
+	 * came out. But i am paranoid and we will leave this
+	 * in at the cost of efficency on allocation of PCB's.
+	 * Probably we should move this to the invariant
+	 * compile options
+	 */
+/* #ifdef INVARIANTS*/
+	SCTP_INP_INFO_RLOCK();
+	inp = LIST_FIRST(&sctppcbinfo.listhead);
+	while (inp) {
+		n_inp = LIST_NEXT(inp, sctp_list);
+		if (inp->sctp_flags & SCTP_PCB_FLAGS_SOCKET_GONE) {
+			if (LIST_FIRST(&inp->sctp_asoc_list) == NULL) {
+				/* finish the job now */
+				printf("Found a GONE on list\n");
+				SCTP_INP_INFO_RUNLOCK();
+				sctp_inpcb_free(inp, 1);
+				SCTP_INP_INFO_RLOCK();
+			}
+		}
+		inp = n_inp;
+	}
+	SCTP_INP_INFO_RUNLOCK();
+/* #endif INVARIANTS*/
+
+	SCTP_INP_INFO_WLOCK();
+	inp = (struct sctp_inpcb *)SCTP_ZONE_GET(sctppcbinfo.ipi_zone_ep);
+	if (inp == NULL) {
+		printf("Out of SCTP-INPCB structures - no resources\n");
+		SCTP_INP_INFO_WUNLOCK();
+		return (ENOBUFS);
+	}
+
+	/* zap it */
+	bzero(inp, sizeof(*inp));
+
+	/* bump generations */
+	inp->ip_inp.inp.inp_socket = so;
+
+	/* setup socket pointers */
+	inp->sctp_socket = so;
+
+	/* setup inpcb socket too */
+	inp->ip_inp.inp.inp_socket = so;
+	inp->sctp_frag_point = SCTP_DEFAULT_MAXSEGMENT;
+#ifdef IPSEC
+#if !(defined(__OpenBSD__) || defined(__APPLE__))
+	{
+		struct inpcbpolicy *pcb_sp = NULL;
+		error = ipsec_init_pcbpolicy(so, &pcb_sp);
+		/* Arrange to share the policy */
+		inp->ip_inp.inp.inp_sp = pcb_sp;
+		((struct in6pcb *)(&inp->ip_inp.inp))->in6p_sp = pcb_sp;
+	}
+#else
+	/* not sure what to do for openbsd here */
+	error = 0;
+#endif
+	if (error != 0) {
+		SCTP_ZONE_FREE(sctppcbinfo.ipi_zone_ep, inp);
+		SCTP_INP_INFO_WUNLOCK();
+		return error;
+	}
+#endif /* IPSEC */
+	sctppcbinfo.ipi_count_ep++;
+#if defined(__FreeBSD__) || defined(__APPLE__) || defined(__DragonFly__)
+	inp->ip_inp.inp.inp_gencnt = ++sctppcbinfo.ipi_gencnt_ep;
+	inp->ip_inp.inp.inp_ip_ttl = ip_defttl;
+#else
+	inp->inp_ip_ttl = ip_defttl;
+	inp->inp_ip_tos = 0;
+#endif
+
+	so->so_pcb = (caddr_t)inp;
+
+	if ((so->so_type == SOCK_DGRAM) ||
+	    (so->so_type == SOCK_SEQPACKET)) {
+		/* UDP style socket */
+		inp->sctp_flags = (SCTP_PCB_FLAGS_UDPTYPE |
+		    SCTP_PCB_FLAGS_UNBOUND);
+		inp->sctp_flags |= (SCTP_PCB_FLAGS_RECVDATAIOEVNT);
+		/* Be sure it is NON-BLOCKING IO for UDP */
+		/*so->so_state |= SS_NBIO;*/
+	} else if (so->so_type == SOCK_STREAM) {
+		/* TCP style socket */
+		inp->sctp_flags = (SCTP_PCB_FLAGS_TCPTYPE |
+		    SCTP_PCB_FLAGS_UNBOUND);
+		inp->sctp_flags |= (SCTP_PCB_FLAGS_RECVDATAIOEVNT);
+		/* Be sure we have blocking IO bu default */
+		so->so_state &= ~SS_NBIO;
+	} else {
+		/*
+		 * unsupported socket type (RAW, etc)- in case we missed
+		 * it in protosw
+		 */
+		SCTP_ZONE_FREE(sctppcbinfo.ipi_zone_ep, inp);
+		SCTP_INP_INFO_WUNLOCK();
+		return (EOPNOTSUPP);
+	}
+	inp->sctp_tcbhash = hashinit(sctp_pcbtblsize,
+#ifdef __NetBSD__
+	    HASH_LIST,
+#endif
+	    M_PCB,
+#if defined(__NetBSD__) || defined(__OpenBSD__)
+	    M_WAITOK,
+#endif
+	    &inp->sctp_hashmark);
+	if (inp->sctp_tcbhash == NULL) {
+		printf("Out of SCTP-INPCB->hashinit - no resources\n");
+		SCTP_ZONE_FREE(sctppcbinfo.ipi_zone_ep, inp);
+		SCTP_INP_INFO_WUNLOCK();
+		return (ENOBUFS);
+	}
+        /* LOCK init's */
+	SCTP_INP_LOCK_INIT(inp);
+	SCTP_ASOC_CREATE_LOCK_INIT(inp);
+	/* lock the new ep */
+	SCTP_INP_WLOCK(inp);
+
+	/* add it to the info area */
+	LIST_INSERT_HEAD(&sctppcbinfo.listhead, inp, sctp_list);
+	SCTP_INP_INFO_WUNLOCK();
+
+	LIST_INIT(&inp->sctp_addr_list);
+	LIST_INIT(&inp->sctp_asoc_list);
+	TAILQ_INIT(&inp->sctp_queue_list);
+	/* Init the timer structure for signature change */
+#if defined (__FreeBSD__) && __FreeBSD_version >= 500000
+	callout_init(&inp->sctp_ep.signature_change.timer, 0);
+#else
+	callout_init(&inp->sctp_ep.signature_change.timer);
+#endif
+	inp->sctp_ep.signature_change.type = SCTP_TIMER_TYPE_NEWCOOKIE;
+
+	/* now init the actual endpoint default data */
+	m = &inp->sctp_ep;
+
+	/* setup the base timeout information */
+	m->sctp_timeoutticks[SCTP_TIMER_SEND] = SEC_TO_TICKS(SCTP_SEND_SEC); /* needed ? */
+	m->sctp_timeoutticks[SCTP_TIMER_INIT] = SEC_TO_TICKS(SCTP_INIT_SEC); /* needed ? */
+	m->sctp_timeoutticks[SCTP_TIMER_RECV] = MSEC_TO_TICKS(sctp_delayed_sack_time_default);
+	m->sctp_timeoutticks[SCTP_TIMER_HEARTBEAT] = sctp_heartbeat_interval_default; /* this is in MSEC */
+	m->sctp_timeoutticks[SCTP_TIMER_PMTU] = SEC_TO_TICKS(sctp_pmtu_raise_time_default);
+	m->sctp_timeoutticks[SCTP_TIMER_MAXSHUTDOWN] = SEC_TO_TICKS(sctp_shutdown_guard_time_default);
+	m->sctp_timeoutticks[SCTP_TIMER_SIGNATURE] = SEC_TO_TICKS(sctp_secret_lifetime_default);
+	/* all max/min max are in ms */
+	m->sctp_maxrto = sctp_rto_max_default;
+	m->sctp_minrto = sctp_rto_min_default;
+	m->initial_rto = sctp_rto_initial_default;
+	m->initial_init_rto_max = sctp_init_rto_max_default;
+
+	m->max_open_streams_intome = MAX_SCTP_STREAMS;
+
+	m->max_init_times = sctp_init_rtx_max_default;
+	m->max_send_times = sctp_assoc_rtx_max_default;
+	m->def_net_failure = sctp_path_rtx_max_default;
+	m->sctp_sws_sender = SCTP_SWS_SENDER_DEF;
+	m->sctp_sws_receiver = SCTP_SWS_RECEIVER_DEF;
+	m->max_burst = sctp_max_burst_default;
+	/* number of streams to pre-open on a association */
+	m->pre_open_stream_count = sctp_nr_outgoing_streams_default;
+
+	/* Add adaption cookie */
+	m->adaption_layer_indicator = 0x504C5253;
+
+	/* seed random number generator */
+	m->random_counter = 1;
+	m->store_at = SCTP_SIGNATURE_SIZE;
+#if (defined(__FreeBSD__) && (__FreeBSD_version < 500000)) || defined(__DragonFly__)
+	read_random_unlimited(m->random_numbers, sizeof(m->random_numbers));
+#elif defined(__APPLE__) || (__FreeBSD_version > 500000)
+	read_random(m->random_numbers, sizeof(m->random_numbers));
+#elif defined(__OpenBSD__)
+	get_random_bytes(m->random_numbers, sizeof(m->random_numbers));
+#elif defined(__NetBSD__) && NRND > 0
+	rnd_extract_data(m->random_numbers, sizeof(m->random_numbers),
+			 RND_EXTRACT_ANY);
+#else
+	{
+		u_int32_t *ranm, *ranp;
+		ranp = (u_int32_t *)&m->random_numbers;
+		ranm = ranp + (SCTP_SIGNATURE_ALOC_SIZE/sizeof(u_int32_t));
+		if ((u_long)ranp % 4) {
+			/* not a even boundary? */
+			ranp = (u_int32_t *)SCTP_SIZE32((u_long)ranp);
+		}
+		while (ranp < ranm) {
+			*ranp = random();
+			ranp++;
+		}
+	}
+#endif
+	sctp_fill_random_store(m);
+
+	/* Minimum cookie size */
+	m->size_of_a_cookie = (sizeof(struct sctp_init_msg) * 2) +
+		sizeof(struct sctp_state_cookie);
+	m->size_of_a_cookie += SCTP_SIGNATURE_SIZE;
+
+	/* Setup the initial secret */
+	SCTP_GETTIME_TIMEVAL(&time);
+	m->time_of_secret_change = time.tv_sec;
+
+	for (i = 0; i < SCTP_NUMBER_OF_SECRETS; i++) {
+		m->secret_key[0][i] = sctp_select_initial_TSN(m);
+	}
+	sctp_timer_start(SCTP_TIMER_TYPE_NEWCOOKIE, inp, NULL, NULL);
+
+	/* How long is a cookie good for ? */
+	m->def_cookie_life = sctp_valid_cookie_life_default;
+	SCTP_INP_WUNLOCK(inp);
+	return (error);
+}
+
+
+void
+sctp_move_pcb_and_assoc(struct sctp_inpcb *old_inp, struct sctp_inpcb *new_inp,
+    struct sctp_tcb *stcb)
+{
+	uint16_t lport, rport;
+	struct sctppcbhead *head;
+	struct sctp_laddr *laddr, *oladdr;
+
+	SCTP_TCB_UNLOCK(stcb);
+	SCTP_INP_INFO_WLOCK();
+	SCTP_INP_WLOCK(old_inp);
+	SCTP_INP_WLOCK(new_inp);
+	SCTP_TCB_LOCK(stcb);
+
+	new_inp->sctp_ep.time_of_secret_change =
+	    old_inp->sctp_ep.time_of_secret_change;
+	memcpy(new_inp->sctp_ep.secret_key, old_inp->sctp_ep.secret_key,
+	    sizeof(old_inp->sctp_ep.secret_key));
+	new_inp->sctp_ep.current_secret_number =
+	    old_inp->sctp_ep.current_secret_number;
+	new_inp->sctp_ep.last_secret_number =
+	    old_inp->sctp_ep.last_secret_number;
+	new_inp->sctp_ep.size_of_a_cookie = old_inp->sctp_ep.size_of_a_cookie;
+
+	/* Copy the port across */
+	lport = new_inp->sctp_lport = old_inp->sctp_lport;
+	rport = stcb->rport;
+	/* Pull the tcb from the old association */
+	LIST_REMOVE(stcb, sctp_tcbhash);
+	LIST_REMOVE(stcb, sctp_tcblist);
+
+	/* Now insert the new_inp into the TCP connected hash */
+	head = &sctppcbinfo.sctp_tcpephash[SCTP_PCBHASH_ALLADDR((lport + rport),
+	    sctppcbinfo.hashtcpmark)];
+
+	LIST_INSERT_HEAD(head, new_inp, sctp_hash);
+
+	/* Now move the tcb into the endpoint list */
+	LIST_INSERT_HEAD(&new_inp->sctp_asoc_list, stcb, sctp_tcblist);
+	/*
+	 * Question, do we even need to worry about the ep-hash since
+	 * we only have one connection? Probably not :> so lets
+	 * get rid of it and not suck up any kernel memory in that.
+	 */
+	SCTP_INP_INFO_WUNLOCK();
+	stcb->sctp_socket = new_inp->sctp_socket;
+	stcb->sctp_ep = new_inp;
+	if (new_inp->sctp_tcbhash != NULL) {
+		FREE(new_inp->sctp_tcbhash, M_PCB);
+		new_inp->sctp_tcbhash = NULL;
+	}
+	if ((new_inp->sctp_flags & SCTP_PCB_FLAGS_BOUNDALL) == 0) {
+		/* Subset bound, so copy in the laddr list from the old_inp */
+		LIST_FOREACH(oladdr, &old_inp->sctp_addr_list, sctp_nxt_addr) {
+			laddr = (struct sctp_laddr *)SCTP_ZONE_GET(
+			    sctppcbinfo.ipi_zone_laddr);
+			if (laddr == NULL) {
+				/*
+				 * Gak, what can we do? This assoc is really
+				 * HOSED. We probably should send an abort
+				 * here.
+				 */
+#ifdef SCTP_DEBUG
+				if (sctp_debug_on & SCTP_DEBUG_PCB1) {
+					printf("Association hosed in TCP model, out of laddr memory\n");
+				}
+#endif /* SCTP_DEBUG */
+				continue;
+			}
+			sctppcbinfo.ipi_count_laddr++;
+			sctppcbinfo.ipi_gencnt_laddr++;
+			bzero(laddr, sizeof(*laddr));
+			laddr->ifa = oladdr->ifa;
+			LIST_INSERT_HEAD(&new_inp->sctp_addr_list, laddr,
+			    sctp_nxt_addr);
+			new_inp->laddr_count++;
+		}
+	}
+	SCTP_INP_WUNLOCK(new_inp);
+	SCTP_INP_WUNLOCK(old_inp);
+}
+
+static int
+sctp_isport_inuse(struct sctp_inpcb *inp, uint16_t lport)
+{
+	struct sctppcbhead *head;
+	struct sctp_inpcb *t_inp;
+
+	head = &sctppcbinfo.sctp_ephash[SCTP_PCBHASH_ALLADDR(lport,
+	    sctppcbinfo.hashmark)];
+	LIST_FOREACH(t_inp, head, sctp_hash) {
+		if (t_inp->sctp_lport != lport) {
+			continue;
+		}
+		/* This one is in use. */
+		/* check the v6/v4 binding issue */
+		if ((t_inp->sctp_flags & SCTP_PCB_FLAGS_BOUND_V6) &&
+#if defined(__FreeBSD__)
+		    (((struct inpcb *)t_inp)->inp_flags & IN6P_IPV6_V6ONLY)
+#else
+#if defined(__OpenBSD__)
+		    (0)	/* For open bsd we do dual bind only */
+#else
+		    (((struct in6pcb *)t_inp)->in6p_flags & IN6P_IPV6_V6ONLY)
+#endif
+#endif
+			) {
+			if (inp->sctp_flags & SCTP_PCB_FLAGS_BOUND_V6) {
+				/* collision in V6 space */
+				return (1);
+			} else {
+				/* inp is BOUND_V4 no conflict */
+				continue;
+			}
+		} else if (t_inp->sctp_flags & SCTP_PCB_FLAGS_BOUND_V6) {
+			/* t_inp is bound v4 and v6, conflict always */
+			return (1);
+		} else {
+			/* t_inp is bound only V4 */
+			if ((inp->sctp_flags & SCTP_PCB_FLAGS_BOUND_V6) &&
+#if defined(__FreeBSD__)
+			    (((struct inpcb *)inp)->inp_flags & IN6P_IPV6_V6ONLY)
+#else
+#if defined(__OpenBSD__)
+			    (0)	/* For open bsd we do dual bind only */
+#else
+			    (((struct in6pcb *)inp)->in6p_flags & IN6P_IPV6_V6ONLY)
+#endif
+#endif
+				) {
+				/* no conflict */
+				continue;
+			}
+			/* else fall through to conflict */
+		}
+		return (1);
+	}
+	return (0);
+}
+
+#if !(defined(__FreeBSD__) || defined(__APPLE__) || defined(__DragonFly__))
+/*
+ * Don't know why, but without this there is an unknown reference when
+ * compiling NetBSD... hmm
+ */
+extern void in6_sin6_2_sin (struct sockaddr_in *, struct sockaddr_in6 *sin6);
+#endif
+
+
+int
+#if (defined(__FreeBSD__) && __FreeBSD_version >= 500000) || defined(__DragonFly__)
+sctp_inpcb_bind(struct socket *so, struct sockaddr *addr, struct thread *p)
+#else
+sctp_inpcb_bind(struct socket *so, struct sockaddr *addr, struct proc *p)
+#endif
+{
+	/* bind a ep to a socket address */
+	struct sctppcbhead *head;
+	struct sctp_inpcb *inp, *inp_tmp;
+	struct inpcb *ip_inp;
+	int bindall;
+	uint16_t lport;
+	int error;
+
+	lport = 0;
+	error = 0;
+	bindall = 1;
+	inp = (struct sctp_inpcb *)so->so_pcb;
+	ip_inp = (struct inpcb *)so->so_pcb;
+#ifdef SCTP_DEBUG
+	if (sctp_debug_on & SCTP_DEBUG_PCB1) {
+		if (addr) {
+			printf("Bind called port:%d\n",
+			       ntohs(((struct sockaddr_in *)addr)->sin_port));
+			printf("Addr :");
+			sctp_print_address(addr);
+		}
+	}
+#endif /* SCTP_DEBUG */
+	if ((inp->sctp_flags & SCTP_PCB_FLAGS_UNBOUND) == 0) {
+		/* already did a bind, subsequent binds NOT allowed ! */
+		return (EINVAL);
+	}
+
+	if (addr != NULL) {
+		if (addr->sa_family == AF_INET) {
+			struct sockaddr_in *sin;
+
+			/* IPV6_V6ONLY socket? */
+			if (
+#if defined(__FreeBSD__) || defined(__APPLE__) || defined(__DragonFly__)
+				(ip_inp->inp_flags & IN6P_IPV6_V6ONLY)
+#else
+#if defined(__OpenBSD__)
+				(0)	/* For openbsd we do dual bind only */
+#else
+				(((struct in6pcb *)inp)->in6p_flags & IN6P_IPV6_V6ONLY)
+#endif
+#endif
+				) {
+				return (EINVAL);
+			}
+
+			if (addr->sa_len != sizeof(*sin))
+				return (EINVAL);
+
+			sin = (struct sockaddr_in *)addr;
+			lport = sin->sin_port;
+
+			if (sin->sin_addr.s_addr != INADDR_ANY) {
+				bindall = 0;
+			}
+		} else if (addr->sa_family == AF_INET6) {
+			/* Only for pure IPv6 Address. (No IPv4 Mapped!) */
+			struct sockaddr_in6 *sin6;
+
+			sin6 = (struct sockaddr_in6 *)addr;
+
+			if (addr->sa_len != sizeof(*sin6))
+				return (EINVAL);
+
+			lport = sin6->sin6_port;
+			if (!IN6_IS_ADDR_UNSPECIFIED(&sin6->sin6_addr)) {
+				bindall = 0;
+				/* KAME hack: embed scopeid */
+#if defined(SCTP_BASE_FREEBSD) || defined(__APPLE__) || defined(__DragonFly__)
+				if (in6_embedscope(&sin6->sin6_addr, sin6,
+				    ip_inp, NULL) != 0)
+					return (EINVAL);
+#elif defined(__FreeBSD__)
+				error = scope6_check_id(sin6, ip6_use_defzone);
+				if (error != 0)
+					return (error);
+#else
+				if (in6_embedscope(&sin6->sin6_addr, sin6) != 0) {
+					return (EINVAL);
+				}
+#endif
+			}
+#ifndef SCOPEDROUTING
+			/* this must be cleared for ifa_ifwithaddr() */
+			sin6->sin6_scope_id = 0;
+#endif /* SCOPEDROUTING */
+		} else {
+			return (EAFNOSUPPORT);
+		}
+	}
+	SCTP_INP_INFO_WLOCK();
+	SCTP_INP_WLOCK(inp);
+	/* increase our count due to the unlock we do */
+	SCTP_INP_INCR_REF(inp);
+	if (lport) {
+		/*
+		 * Did the caller specify a port? if so we must see if a
+		 * ep already has this one bound.
+		 */
+		/* got to be root to get at low ports */
+		if (ntohs(lport) < IPPORT_RESERVED) {
+			if (p && (error =
+#if defined(__FreeBSD__)
+#if __FreeBSD_version >= 500000
+			    suser_cred(p->td_ucred, 0)
+#else
+			    suser(p)
+#endif
+#elif defined(__NetBSD__) || defined(__APPLE__)
+			    suser(p->p_ucred, &p->p_acflag)
+#elif defined(__DragonFly__)
+			    suser(p)
+#else
+			    suser(p, 0)
+#endif
+			    )) {
+				SCTP_INP_DECR_REF(inp);
+				SCTP_INP_WUNLOCK(inp);
+				SCTP_INP_INFO_WUNLOCK();
+				return (error);
+			}
+		}
+		if (p == NULL) {
+			SCTP_INP_DECR_REF(inp);
+			SCTP_INP_WUNLOCK(inp);
+			SCTP_INP_INFO_WUNLOCK();
+			return (error);
+		}
+		SCTP_INP_WUNLOCK(inp);
+		inp_tmp = sctp_pcb_findep(addr, 0, 1);
+		if (inp_tmp != NULL) {
+			/* lock guy returned and lower count
+			 * note that we are not bound so inp_tmp
+			 * should NEVER be inp. And it is this
+			 * inp (inp_tmp) that gets the reference
+			 * bump, so we must lower it.
+			 */
+			SCTP_INP_WLOCK(inp_tmp);
+			SCTP_INP_DECR_REF(inp_tmp);
+			SCTP_INP_WUNLOCK(inp_tmp);
+
+			/* unlock info */
+			SCTP_INP_INFO_WUNLOCK();
+			return (EADDRNOTAVAIL);
+		}
+		SCTP_INP_WLOCK(inp);
+		if (bindall) {
+			/* verify that no lport is not used by a singleton */
+			if (sctp_isport_inuse(inp, lport)) {
+				/* Sorry someone already has this one bound */
+				SCTP_INP_DECR_REF(inp);
+				SCTP_INP_WUNLOCK(inp);
+				SCTP_INP_INFO_WUNLOCK();
+				return (EADDRNOTAVAIL);
+			}
+		}
+	} else {
+		/*
+		 * get any port but lets make sure no one has any address
+		 * with this port bound
+		 */
+
+		/*
+		 * setup the inp to the top (I could use the union but this
+		 * is just as easy
+		 */
+		uint32_t port_guess;
+		uint16_t port_attempt;
+		int not_done=1;
+
+		while (not_done) {
+			port_guess = sctp_select_initial_TSN(&inp->sctp_ep);
+			port_attempt = (port_guess &  0x0000ffff);
+			if (port_attempt == 0) {
+				goto next_half;
+			}
+			if (port_attempt < IPPORT_RESERVED) {
+				port_attempt += IPPORT_RESERVED;
+			}
+
+			if (sctp_isport_inuse(inp, htons(port_attempt)) == 0) {
+				/* got a port we can use */
+				not_done = 0;
+				continue;
+			}
+			/* try upper half */
+		next_half:
+			port_attempt = ((port_guess >> 16) &  0x0000ffff);
+			if (port_attempt == 0) {
+				goto last_try;
+			}
+			if (port_attempt < IPPORT_RESERVED) {
+				port_attempt += IPPORT_RESERVED;
+			}
+			if (sctp_isport_inuse(inp, htons(port_attempt)) == 0) {
+				/* got a port we can use */
+				not_done = 0;
+				continue;
+			}
+			/* try two half's added together */
+		last_try:
+			port_attempt = (((port_guess >> 16) &  0x0000ffff) + (port_guess & 0x0000ffff));
+			if (port_attempt == 0) {
+				/* get a new random number */
+				continue;
+			}
+			if (port_attempt < IPPORT_RESERVED) {
+				port_attempt += IPPORT_RESERVED;
+			}
+			if (sctp_isport_inuse(inp, htons(port_attempt)) == 0) {
+				/* got a port we can use */
+				not_done = 0;
+				continue;
+			}
+		}
+		/* we don't get out of the loop until we have a port */
+		lport = htons(port_attempt);
+	}
+	SCTP_INP_DECR_REF(inp);
+	if (inp->sctp_flags & (SCTP_PCB_FLAGS_SOCKET_GONE|SCTP_PCB_FLAGS_SOCKET_ALLGONE)) {
+		/* this really should not happen. The guy
+		 * did a non-blocking bind and then did a close
+		 * at the same time.
+		 */
+		SCTP_INP_WUNLOCK(inp);
+		SCTP_INP_INFO_WUNLOCK();
+		return (EINVAL);
+	}
+	/* ok we look clear to give out this port, so lets setup the binding */
+	if (bindall) {
+		/* binding to all addresses, so just set in the proper flags */
+		inp->sctp_flags |= (SCTP_PCB_FLAGS_BOUNDALL |
+		    SCTP_PCB_FLAGS_DO_ASCONF);
+		/* set the automatic addr changes from kernel flag */
+		if (sctp_auto_asconf == 0) {
+			inp->sctp_flags &= ~SCTP_PCB_FLAGS_AUTO_ASCONF;
+		} else {
+			inp->sctp_flags |= SCTP_PCB_FLAGS_AUTO_ASCONF;
+		}
+	} else {
+		/*
+		 * bind specific, make sure flags is off and add a new address
+		 * structure to the sctp_addr_list inside the ep structure.
+		 *
+		 * We will need to allocate one and insert it at the head.
+		 * The socketopt call can just insert new addresses in there
+		 * as well. It will also have to do the embed scope kame hack
+		 * too (before adding).
+		 */
+		struct ifaddr *ifa;
+		struct sockaddr_storage store_sa;
+
+		memset(&store_sa, 0, sizeof(store_sa));
+		if (addr->sa_family == AF_INET) {
+			struct sockaddr_in *sin;
+
+			sin = (struct sockaddr_in *)&store_sa;
+			memcpy(sin, addr, sizeof(struct sockaddr_in));
+			sin->sin_port = 0;
+		} else if (addr->sa_family == AF_INET6) {
+			struct sockaddr_in6 *sin6;
+
+			sin6 = (struct sockaddr_in6 *)&store_sa;
+			memcpy(sin6, addr, sizeof(struct sockaddr_in6));
+			sin6->sin6_port = 0;
+		}
+		/*
+		 * first find the interface with the bound address
+		 * need to zero out the port to find the address! yuck!
+		 * can't do this earlier since need port for sctp_pcb_findep()
+		 */
+		ifa = sctp_find_ifa_by_addr((struct sockaddr *)&store_sa);
+		if (ifa == NULL) {
+			/* Can't find an interface with that address */
+			SCTP_INP_WUNLOCK(inp);
+			SCTP_INP_INFO_WUNLOCK();
+			return (EADDRNOTAVAIL);
+		}
+		if (addr->sa_family == AF_INET6) {
+			struct in6_ifaddr *ifa6;
+			ifa6 = (struct in6_ifaddr *)ifa;
+			/*
+			 * allow binding of deprecated addresses as per
+			 * RFC 2462 and ipng discussion
+			 */
+			if (ifa6->ia6_flags & (IN6_IFF_DETACHED |
+			    IN6_IFF_ANYCAST |
+			    IN6_IFF_NOTREADY)) {
+				/* Can't bind a non-existent addr. */
+				SCTP_INP_WUNLOCK(inp);
+				SCTP_INP_INFO_WUNLOCK();
+				return (EINVAL);
+			}
+		}
+		/* we're not bound all */
+		inp->sctp_flags &= ~SCTP_PCB_FLAGS_BOUNDALL;
+#if 0 /* use sysctl now */
+		/* don't allow automatic addr changes from kernel */
+		inp->sctp_flags &= ~SCTP_PCB_FLAGS_AUTO_ASCONF;
+#endif
+		/* set the automatic addr changes from kernel flag */
+		if (sctp_auto_asconf == 0) {
+			inp->sctp_flags &= ~SCTP_PCB_FLAGS_AUTO_ASCONF;
+		} else {
+			inp->sctp_flags |= SCTP_PCB_FLAGS_AUTO_ASCONF;
+		}
+		/* allow bindx() to send ASCONF's for binding changes */
+		inp->sctp_flags |= SCTP_PCB_FLAGS_DO_ASCONF;
+		/* add this address to the endpoint list */
+		error = sctp_insert_laddr(&inp->sctp_addr_list, ifa);
+		if (error != 0) {
+			SCTP_INP_WUNLOCK(inp);
+			SCTP_INP_INFO_WUNLOCK();
+			return (error);
+		}
+		inp->laddr_count++;
+	}
+	/* find the bucket */
+	head = &sctppcbinfo.sctp_ephash[SCTP_PCBHASH_ALLADDR(lport,
+	    sctppcbinfo.hashmark)];
+	/* put it in the bucket */
+	LIST_INSERT_HEAD(head, inp, sctp_hash);
+#ifdef SCTP_DEBUG
+	if (sctp_debug_on & SCTP_DEBUG_PCB1) {
+		printf("Main hash to bind at head:%p, bound port:%d\n", head, ntohs(lport));
+	}
+#endif
+	/* set in the port */
+	inp->sctp_lport = lport;
+
+	/* turn off just the unbound flag */
+	inp->sctp_flags &= ~SCTP_PCB_FLAGS_UNBOUND;
+	SCTP_INP_WUNLOCK(inp);
+	SCTP_INP_INFO_WUNLOCK();
+	return (0);
+}
+
+
+static void
+sctp_iterator_inp_being_freed(struct sctp_inpcb *inp, struct sctp_inpcb *inp_next)
+{
+	struct sctp_iterator *it;
+	/* We enter with the only the ITERATOR_LOCK in place and
+	 * A write lock on the inp_info stuff.
+	 */
+
+	/* Go through all iterators, we must do this since
+	 * it is possible that some iterator does NOT have
+	 * the lock, but is waiting for it. And the one that
+	 * had the lock has either moved in the last iteration
+	 * or we just cleared it above. We need to find all
+	 * of those guys. The list of iterators should never
+	 * be very big though.
+	 */
+ 	LIST_FOREACH(it, &sctppcbinfo.iteratorhead, sctp_nxt_itr) {
+		if (it == inp->inp_starting_point_for_iterator)
+			/* skip this guy, he's special */
+			continue;
+ 		if (it->inp == inp) {
+			/* This is tricky and we DON'T lock the iterator.
+			 * Reason is he's running but waiting for me since
+			 * inp->inp_starting_point_for_iterator has the lock
+			 * on me (the guy above we skipped). This tells us
+			 * its is not running but waiting for inp->inp_starting_point_for_iterator
+			 * to be released by the guy that does have our INP in a lock.
+			 */
+			if (it->iterator_flags & SCTP_ITERATOR_DO_SINGLE_INP) {
+				it->inp = NULL;
+				it->stcb = NULL;
+			} else {
+				/* set him up to do the next guy not me */
+				it->inp = inp_next;
+				it->stcb = NULL;
+			}
+		}
+	}
+	it = inp->inp_starting_point_for_iterator;
+	if (it) {
+		if (it->iterator_flags & SCTP_ITERATOR_DO_SINGLE_INP) {
+			it->inp = NULL;
+		} else {
+			it->inp = inp_next;
+		}
+		it->stcb = NULL;
+	}
+}
+
+/* release sctp_inpcb unbind the port */
+void
+sctp_inpcb_free(struct sctp_inpcb *inp, int immediate)
+{
+	/*
+	 * Here we free a endpoint. We must find it (if it is in the Hash
+	 * table) and remove it from there. Then we must also find it in
+	 * the overall list and remove it from there. After all removals are
+	 * complete then any timer has to be stopped. Then start the actual
+	 * freeing.
+	 * a) Any local lists.
+	 * b) Any associations.
+	 * c) The hash of all associations.
+	 * d) finally the ep itself.
+	 */
+	struct sctp_pcb *m;
+	struct sctp_inpcb *inp_save;
+	struct sctp_tcb *asoc, *nasoc;
+	struct sctp_laddr *laddr, *nladdr;
+	struct inpcb *ip_pcb;
+	struct socket *so;
+	struct sctp_socket_q_list *sq;
+#if !defined(__FreeBSD__) || __FreeBSD_version < 500000
+ 	struct rtentry *rt;
+#endif
+	int cnt;
+
+	crit_enter();
+	SCTP_ASOC_CREATE_LOCK(inp);
+	SCTP_INP_WLOCK(inp);
+
+	if (inp->sctp_flags & SCTP_PCB_FLAGS_SOCKET_ALLGONE) {
+		/* been here before */
+		crit_exit();
+		printf("Endpoint was all gone (dup free)?\n");
+		SCTP_INP_WUNLOCK(inp);
+		SCTP_ASOC_CREATE_UNLOCK(inp);
+		return;
+	}
+	sctp_timer_stop(SCTP_TIMER_TYPE_NEWCOOKIE, inp, NULL, NULL);
+
+	if (inp->control) {
+		sctp_m_freem(inp->control);
+		inp->control = NULL;
+	}
+	if (inp->pkt) {
+		sctp_m_freem(inp->pkt);
+		inp->pkt = NULL;
+	}
+	so  = inp->sctp_socket;
+	m = &inp->sctp_ep;
+	ip_pcb = &inp->ip_inp.inp; /* we could just cast the main
+				   * pointer here but I will
+				   * be nice :> (i.e. ip_pcb = ep;)
+				   */
+
+	if (immediate == 0) {
+		int cnt_in_sd;
+		cnt_in_sd = 0;
+		for ((asoc = LIST_FIRST(&inp->sctp_asoc_list)); asoc != NULL;
+		     asoc = nasoc) {
+			nasoc = LIST_NEXT(asoc, sctp_tcblist);
+			if ((SCTP_GET_STATE(&asoc->asoc) == SCTP_STATE_COOKIE_WAIT) ||
+			    (SCTP_GET_STATE(&asoc->asoc) == SCTP_STATE_COOKIE_ECHOED)) {
+				/* Just abandon things in the front states */
+				SCTP_TCB_LOCK(asoc);
+				SCTP_INP_WUNLOCK(inp);
+				sctp_free_assoc(inp, asoc);
+				SCTP_INP_WLOCK(inp);
+				continue;
+			} else {
+				asoc->asoc.state |= SCTP_STATE_CLOSED_SOCKET;
+			}
+			if ((asoc->asoc.size_on_delivery_queue  > 0) ||
+			    (asoc->asoc.size_on_reasm_queue > 0) ||
+			    (asoc->asoc.size_on_all_streams > 0) ||
+			    (so && (so->so_rcv.sb_cc > 0))
+				) {
+				/* Left with Data unread */
+				struct mbuf *op_err;
+				MGET(op_err, MB_DONTWAIT, MT_DATA);
+				if (op_err) {
+					/* Fill in the user initiated abort */
+					struct sctp_paramhdr *ph;
+					op_err->m_len =
+					    sizeof(struct sctp_paramhdr);
+					ph = mtod(op_err,
+					    struct sctp_paramhdr *);
+					ph->param_type = htons(
+					    SCTP_CAUSE_USER_INITIATED_ABT);
+					ph->param_length = htons(op_err->m_len);
+				}
+				SCTP_TCB_LOCK(asoc);
+				sctp_send_abort_tcb(asoc, op_err);
+
+				SCTP_INP_WUNLOCK(inp);
+				sctp_free_assoc(inp, asoc);
+				SCTP_INP_WLOCK(inp);
+				continue;
+			} else if (TAILQ_EMPTY(&asoc->asoc.send_queue) &&
+			    TAILQ_EMPTY(&asoc->asoc.sent_queue)) {
+				if ((SCTP_GET_STATE(&asoc->asoc) != SCTP_STATE_SHUTDOWN_SENT) &&
+				    (SCTP_GET_STATE(&asoc->asoc) != SCTP_STATE_SHUTDOWN_ACK_SENT)) {
+					/* there is nothing queued to send, so I send shutdown */
+					SCTP_TCB_LOCK(asoc);
+					sctp_send_shutdown(asoc, asoc->asoc.primary_destination);
+					asoc->asoc.state = SCTP_STATE_SHUTDOWN_SENT;
+					sctp_timer_start(SCTP_TIMER_TYPE_SHUTDOWN, asoc->sctp_ep, asoc,
+							 asoc->asoc.primary_destination);
+					sctp_timer_start(SCTP_TIMER_TYPE_SHUTDOWNGUARD, asoc->sctp_ep, asoc,
+							 asoc->asoc.primary_destination);
+					sctp_chunk_output(inp, asoc, 1);
+					SCTP_TCB_UNLOCK(asoc);
+				}
+			} else {
+				/* mark into shutdown pending */
+				asoc->asoc.state |= SCTP_STATE_SHUTDOWN_PENDING;
+			}
+			cnt_in_sd++;
+		}
+		/* now is there some left in our SHUTDOWN state? */
+		if (cnt_in_sd) {
+			inp->sctp_flags |= SCTP_PCB_FLAGS_SOCKET_GONE;
+			crit_exit();
+			SCTP_INP_WUNLOCK(inp);
+			SCTP_ASOC_CREATE_UNLOCK(inp);
+			return;
+		}
+	}
+#if defined(__FreeBSD__) && __FreeBSD_version >= 503000
+	if (inp->refcount) {
+		sctp_timer_start(SCTP_TIMER_TYPE_INPKILL, inp, NULL, NULL);
+		SCTP_INP_WUNLOCK(inp);
+		SCTP_ASOC_CREATE_UNLOCK(inp);
+		return;
+	}
+#endif
+	inp->sctp_flags |= SCTP_PCB_FLAGS_SOCKET_ALLGONE;
+#if !defined(__FreeBSD__) || __FreeBSD_version < 500000
+ 	rt = ip_pcb->inp_route.ro_rt;
+#endif
+
+	sctp_timer_stop(SCTP_TIMER_TYPE_INPKILL, inp, NULL, NULL);
+
+	if (so) {
+	/* First take care of socket level things */
+#ifdef IPSEC
+#ifdef __OpenBSD__
+	/* XXX IPsec cleanup here */
+		crit_enter();
+		if (ip_pcb->inp_tdb_in)
+		    TAILQ_REMOVE(&ip_pcb->inp_tdb_in->tdb_inp_in,
+				 ip_pcb, inp_tdb_in_next);
+		if (ip_pcb->inp_tdb_out)
+		    TAILQ_REMOVE(&ip_pcb->inp_tdb_out->tdb_inp_out, ip_pcb,
+				 inp_tdb_out_next);
+		if (ip_pcb->inp_ipsec_localid)
+		    ipsp_reffree(ip_pcb->inp_ipsec_localid);
+		if (ip_pcb->inp_ipsec_remoteid)
+		    ipsp_reffree(ip_pcb->inp_ipsec_remoteid);
+		if (ip_pcb->inp_ipsec_localcred)
+		    ipsp_reffree(ip_pcb->inp_ipsec_localcred);
+		if (ip_pcb->inp_ipsec_remotecred)
+		    ipsp_reffree(ip_pcb->inp_ipsec_remotecred);
+		if (ip_pcb->inp_ipsec_localauth)
+		    ipsp_reffree(ip_pcb->inp_ipsec_localauth);
+		if (ip_pcb->inp_ipsec_remoteauth)
+		    ipsp_reffree(ip_pcb->inp_ipsec_remoteauth);
+		crit_exit();
+#else
+		ipsec4_delete_pcbpolicy(ip_pcb);
+#endif
+#endif /*IPSEC*/
+#if defined(__FreeBSD__) && __FreeBSD_version > 500000
+		ACCEPT_LOCK();
+		SOCK_LOCK(so);
+#endif
+		so->so_pcb = 0;
+#if defined(__FreeBSD__) && __FreeBSD_version > 500000
+		sotryfree(so);
+#else
+		sofree(so);
+#endif
+	}
+
+	if (ip_pcb->inp_options) {
+		(void)m_free(ip_pcb->inp_options);
+		ip_pcb->inp_options = 0;
+	}
+#if !defined(__FreeBSD__) || __FreeBSD_version < 500000
+ 	if (rt) {
+ 		RTFREE(rt);
+ 		ip_pcb->inp_route.ro_rt = 0;
+ 	}
+#endif
+	if (ip_pcb->inp_moptions) {
+		ip_freemoptions(ip_pcb->inp_moptions);
+		ip_pcb->inp_moptions = 0;
+	}
+#if !(defined(__FreeBSD__) || defined(__APPLE__) || defined(__DragonFly__))
+	inp->inp_vflag = 0;
+#else
+	ip_pcb->inp_vflag = 0;
+#endif
+
+	/* Now the sctp_pcb things */
+
+	/*
+	 * free each asoc if it is not already closed/free. we can't use
+	 * the macro here since le_next will get freed as part of the
+	 * sctp_free_assoc() call.
+	 */
+	cnt = 0;
+	for ((asoc = LIST_FIRST(&inp->sctp_asoc_list)); asoc != NULL;
+	     asoc = nasoc) {
+		nasoc = LIST_NEXT(asoc, sctp_tcblist);
+		SCTP_TCB_LOCK(asoc);
+		if (SCTP_GET_STATE(&asoc->asoc) != SCTP_STATE_COOKIE_WAIT) {
+			struct mbuf *op_err;
+			MGET(op_err, MB_DONTWAIT, MT_DATA);
+			if (op_err) {
+				/* Fill in the user initiated abort */
+				struct sctp_paramhdr *ph;
+				op_err->m_len = sizeof(struct sctp_paramhdr);
+				ph = mtod(op_err, struct sctp_paramhdr *);
+				ph->param_type = htons(
+				    SCTP_CAUSE_USER_INITIATED_ABT);
+				ph->param_length = htons(op_err->m_len);
+			}
+			sctp_send_abort_tcb(asoc, op_err);
+		}
+		cnt++;
+		/*
+		 * sctp_free_assoc() will call sctp_inpcb_free(),
+		 * if SCTP_PCB_FLAGS_SOCKET_GONE set.
+		 * So, we clear it before sctp_free_assoc() making sure
+		 * no double sctp_inpcb_free().
+		 */
+		inp->sctp_flags &= ~SCTP_PCB_FLAGS_SOCKET_GONE;
+		SCTP_INP_WUNLOCK(inp);
+		sctp_free_assoc(inp, asoc);
+		SCTP_INP_WLOCK(inp);
+	}
+	while ((sq = TAILQ_FIRST(&inp->sctp_queue_list)) != NULL) {
+		TAILQ_REMOVE(&inp->sctp_queue_list, sq, next_sq);
+		SCTP_ZONE_FREE(sctppcbinfo.ipi_zone_sockq, sq);
+		sctppcbinfo.ipi_count_sockq--;
+		sctppcbinfo.ipi_gencnt_sockq++;
+	}
+	inp->sctp_socket = 0;
+	/* Now first we remove ourselves from the overall list of all EP's */
+
+	/* Unlock inp first, need correct order */
+	SCTP_INP_WUNLOCK(inp);
+	/* now iterator lock */
+	SCTP_ITERATOR_LOCK();
+	/* now info lock */
+	SCTP_INP_INFO_WLOCK();
+	/* now reget the inp lock */
+	SCTP_INP_WLOCK(inp);
+
+	inp_save = LIST_NEXT(inp, sctp_list);
+	LIST_REMOVE(inp, sctp_list);
+	/*
+	 * Now the question comes as to if this EP was ever bound at all.
+	 * If it was, then we must pull it out of the EP hash list.
+	 */
+	if ((inp->sctp_flags & SCTP_PCB_FLAGS_UNBOUND) !=
+	    SCTP_PCB_FLAGS_UNBOUND) {
+		/*
+		 * ok, this guy has been bound. It's port is somewhere
+		 * in the sctppcbinfo hash table. Remove it!
+		 */
+		LIST_REMOVE(inp, sctp_hash);
+	}
+        /* fix any iterators only after out of the list */
+	sctp_iterator_inp_being_freed(inp, inp_save);
+	SCTP_ITERATOR_UNLOCK();
+	/*
+	 * if we have an address list the following will free the list of
+	 * ifaddr's that are set into this ep. Again macro limitations here,
+	 * since the LIST_FOREACH could be a bad idea.
+	 */
+	for ((laddr = LIST_FIRST(&inp->sctp_addr_list)); laddr != NULL;
+	     laddr = nladdr) {
+		nladdr = LIST_NEXT(laddr, sctp_nxt_addr);
+		LIST_REMOVE(laddr, sctp_nxt_addr);
+		SCTP_ZONE_FREE(sctppcbinfo.ipi_zone_laddr, laddr);
+		sctppcbinfo.ipi_gencnt_laddr++;
+		sctppcbinfo.ipi_count_laddr--;
+	}
+	/* Now lets see about freeing the EP hash table. */
+	if (inp->sctp_tcbhash != NULL) {
+		FREE(inp->sctp_tcbhash, M_PCB);
+		inp->sctp_tcbhash = 0;
+	}
+	SCTP_INP_WUNLOCK(inp);
+	SCTP_ASOC_CREATE_UNLOCK(inp);
+	SCTP_INP_LOCK_DESTROY(inp);
+	SCTP_ASOC_CREATE_LOCK_DESTROY(inp);
+
+	/* Now we must put the ep memory back into the zone pool */
+	SCTP_ZONE_FREE(sctppcbinfo.ipi_zone_ep, inp);
+	sctppcbinfo.ipi_count_ep--;
+
+	SCTP_INP_INFO_WUNLOCK();
+	crit_exit();
+}
+
+
+struct sctp_nets *
+sctp_findnet(struct sctp_tcb *stcb, struct sockaddr *addr)
+{
+	struct sctp_nets *net;
+	struct sockaddr_in *sin;
+	struct sockaddr_in6 *sin6;
+	/* use the peer's/remote port for lookup if unspecified */
+	sin = (struct sockaddr_in *)addr;
+	sin6 = (struct sockaddr_in6 *)addr;
+#if 0 /* why do we need to check the port for a nets list on an assoc? */
+	if (stcb->rport != sin->sin_port) {
+		/* we cheat and just a sin for this test */
+		return (NULL);
+	}
+#endif
+	/* locate the address */
+	TAILQ_FOREACH(net, &stcb->asoc.nets, sctp_next) {
+		if (sctp_cmpaddr(addr, (struct sockaddr *)&net->ro._l_addr))
+			return (net);
+	}
+	return (NULL);
+}
+
+
+/*
+ * add's a remote endpoint address, done with the INIT/INIT-ACK
+ * as well as when a ASCONF arrives that adds it. It will also
+ * initialize all the cwnd stats of stuff.
+ */
+int
+sctp_is_address_on_local_host(struct sockaddr *addr)
+{
+	struct ifnet *ifn;
+	struct ifaddr *ifa;
+	TAILQ_FOREACH(ifn, &ifnet, if_list) {
+		TAILQ_FOREACH(ifa, &ifn->if_addrlist, ifa_list) {
+			if (addr->sa_family == ifa->ifa_addr->sa_family) {
+				/* same family */
+				if (addr->sa_family == AF_INET) {
+					struct sockaddr_in *sin, *sin_c;
+					sin = (struct sockaddr_in *)addr;
+					sin_c = (struct sockaddr_in *)
+					    ifa->ifa_addr;
+					if (sin->sin_addr.s_addr ==
+					    sin_c->sin_addr.s_addr) {
+						/* we are on the same machine */
+						return (1);
+					}
+				} else if (addr->sa_family == AF_INET6) {
+					struct sockaddr_in6 *sin6, *sin_c6;
+					sin6 = (struct sockaddr_in6 *)addr;
+					sin_c6 = (struct sockaddr_in6 *)
+					    ifa->ifa_addr;
+					if (SCTP6_ARE_ADDR_EQUAL(&sin6->sin6_addr,
+					    &sin_c6->sin6_addr)) {
+						/* we are on the same machine */
+						return (1);
+					}
+				}
+			}
+		}
+	}
+	return (0);
+}
+
+int
+sctp_add_remote_addr(struct sctp_tcb *stcb, struct sockaddr *newaddr,
+    int set_scope, int from)
+{
+	/*
+	 * The following is redundant to the same lines in the
+	 * sctp_aloc_assoc() but is needed since other's call the add
+	 * address function
+	 */
+	struct sctp_nets *net, *netfirst;
+	int addr_inscope;
+
+#ifdef SCTP_DEBUG
+	if (sctp_debug_on & SCTP_DEBUG_PCB1) {
+		printf("Adding an address (from:%d) to the peer: ", from);
+		sctp_print_address(newaddr);
+	}
+#endif
+	netfirst = sctp_findnet(stcb, newaddr);
+	if (netfirst) {
+		/*
+		 * Lie and return ok, we don't want to make the association
+		 * go away for this behavior. It will happen in the TCP model
+		 * in a connected socket. It does not reach the hash table
+		 * until after the association is built so it can't be found.
+		 * Mark as reachable, since the initial creation will have
+		 * been cleared and the NOT_IN_ASSOC flag will have been
+		 * added... and we don't want to end up removing it back out.
+		 */
+		if (netfirst->dest_state & SCTP_ADDR_UNCONFIRMED) {
+			netfirst->dest_state = (SCTP_ADDR_REACHABLE|
+			    SCTP_ADDR_UNCONFIRMED);
+		} else {
+			netfirst->dest_state = SCTP_ADDR_REACHABLE;
+		}
+
+		return (0);
+	}
+	addr_inscope = 1;
+	if (newaddr->sa_family == AF_INET) {
+		struct sockaddr_in *sin;
+		sin = (struct sockaddr_in *)newaddr;
+		if (sin->sin_addr.s_addr == 0) {
+			/* Invalid address */
+			return (-1);
+		}
+		/* zero out the bzero area */
+		memset(&sin->sin_zero, 0, sizeof(sin->sin_zero));
+
+		/* assure len is set */
+		sin->sin_len = sizeof(struct sockaddr_in);
+		if (set_scope) {
+#ifdef SCTP_DONT_DO_PRIVADDR_SCOPE
+			stcb->ipv4_local_scope = 1;
+#else
+			if (IN4_ISPRIVATE_ADDRESS(&sin->sin_addr)) {
+				stcb->asoc.ipv4_local_scope = 1;
+			}
+#endif /* SCTP_DONT_DO_PRIVADDR_SCOPE */
+
+			if (sctp_is_address_on_local_host(newaddr)) {
+				stcb->asoc.loopback_scope = 1;
+				stcb->asoc.ipv4_local_scope = 1;
+				stcb->asoc.local_scope = 1;
+				stcb->asoc.site_scope = 1;
+			}
+		} else {
+			if (from == 8) {
+				/* From connectx */
+				if (sctp_is_address_on_local_host(newaddr)) {
+					stcb->asoc.loopback_scope = 1;
+					stcb->asoc.ipv4_local_scope = 1;
+					stcb->asoc.local_scope = 1;
+					stcb->asoc.site_scope = 1;
+				}
+			}
+			/* Validate the address is in scope */
+			if ((IN4_ISPRIVATE_ADDRESS(&sin->sin_addr)) &&
+			    (stcb->asoc.ipv4_local_scope == 0)) {
+				addr_inscope = 0;
+			}
+		}
+	} else if (newaddr->sa_family == AF_INET6) {
+		struct sockaddr_in6 *sin6;
+		sin6 = (struct sockaddr_in6 *)newaddr;
+		if (IN6_IS_ADDR_UNSPECIFIED(&sin6->sin6_addr)) {
+			/* Invalid address */
+			return (-1);
+		}
+		/* assure len is set */
+		sin6->sin6_len = sizeof(struct sockaddr_in6);
+		if (set_scope) {
+			if (sctp_is_address_on_local_host(newaddr)) {
+				stcb->asoc.loopback_scope = 1;
+				stcb->asoc.local_scope = 1;
+				stcb->asoc.ipv4_local_scope = 1;
+				stcb->asoc.site_scope = 1;
+			} else if (IN6_IS_ADDR_LINKLOCAL(&sin6->sin6_addr)) {
+				/*
+				 * If the new destination is a LINK_LOCAL
+				 * we must have common site scope. Don't set
+				 * the local scope since we may not share all
+				 * links, only loopback can do this.
+ 				 * Links on the local network would also
+ 				 * be on our private network for v4 too.
+				 */
+ 				stcb->asoc.ipv4_local_scope = 1;
+				stcb->asoc.site_scope = 1;
+			} else if (IN6_IS_ADDR_SITELOCAL(&sin6->sin6_addr)) {
+				/*
+				 * If the new destination is SITE_LOCAL
+				 * then we must have site scope in common.
+				 */
+				stcb->asoc.site_scope = 1;
+			}
+		} else {
+			if (from == 8) {
+				/* From connectx */
+				if (sctp_is_address_on_local_host(newaddr)) {
+					stcb->asoc.loopback_scope = 1;
+					stcb->asoc.ipv4_local_scope = 1;
+					stcb->asoc.local_scope = 1;
+					stcb->asoc.site_scope = 1;
+				}
+			}
+			/* Validate the address is in scope */
+			if (IN6_IS_ADDR_LOOPBACK(&sin6->sin6_addr) &&
+			    (stcb->asoc.loopback_scope == 0)) {
+				addr_inscope = 0;
+			} else if (IN6_IS_ADDR_LINKLOCAL(&sin6->sin6_addr) &&
+				   (stcb->asoc.local_scope == 0)) {
+				addr_inscope = 0;
+			} else if (IN6_IS_ADDR_SITELOCAL(&sin6->sin6_addr) &&
+				   (stcb->asoc.site_scope == 0)) {
+				addr_inscope = 0;
+			}
+		}
+	} else {
+		/* not supported family type */
+		return (-1);
+	}
+	net = (struct sctp_nets *)SCTP_ZONE_GET(sctppcbinfo.ipi_zone_net);
+	if (net == NULL) {
+		return (-1);
+	}
+	sctppcbinfo.ipi_count_raddr++;
+	sctppcbinfo.ipi_gencnt_raddr++;
+	bzero(net, sizeof(*net));
+	memcpy(&net->ro._l_addr, newaddr, newaddr->sa_len);
+	if (newaddr->sa_family == AF_INET) {
+		((struct sockaddr_in *)&net->ro._l_addr)->sin_port = stcb->rport;
+	} else if (newaddr->sa_family == AF_INET6) {
+		((struct sockaddr_in6 *)&net->ro._l_addr)->sin6_port = stcb->rport;
+	}
+	net->addr_is_local = sctp_is_address_on_local_host(newaddr);
+	net->failure_threshold = stcb->asoc.def_net_failure;
+	if (addr_inscope == 0) {
+#ifdef SCTP_DEBUG
+		if (sctp_debug_on & SCTP_DEBUG_PCB1) {
+			printf("Adding an address which is OUT OF SCOPE\n");
+		}
+#endif /* SCTP_DEBUG */
+		net->dest_state = (SCTP_ADDR_REACHABLE |
+		    SCTP_ADDR_OUT_OF_SCOPE);
+	} else {
+		if (from == 8)
+			/* 8 is passed by connect_x */
+			net->dest_state = SCTP_ADDR_REACHABLE;
+		else
+			net->dest_state = SCTP_ADDR_REACHABLE |
+			    SCTP_ADDR_UNCONFIRMED;
+	}
+	net->RTO = stcb->asoc.initial_rto;
+	stcb->asoc.numnets++;
+	net->ref_count = 1;
+
+	/* Init the timer structure */
+#if defined(__FreeBSD__) && __FreeBSD_version >= 500000
+	callout_init(&net->rxt_timer.timer, 0);
+	callout_init(&net->pmtu_timer.timer, 0);
+#else
+	callout_init(&net->rxt_timer.timer);
+	callout_init(&net->pmtu_timer.timer);
+#endif
+
+	/* Now generate a route for this guy */
+	/* KAME hack: embed scopeid */
+	if (newaddr->sa_family == AF_INET6) {
+		struct sockaddr_in6 *sin6;
+		sin6 = (struct sockaddr_in6 *)&net->ro._l_addr;
+#if defined(SCTP_BASE_FREEBSD) || defined(__APPLE__) || defined(__DragonFly__)
+		(void)in6_embedscope(&sin6->sin6_addr, sin6,
+		    &stcb->sctp_ep->ip_inp.inp, NULL);
+#else
+		(void)in6_embedscope(&sin6->sin6_addr, sin6);
+#endif
+#ifndef SCOPEDROUTING
+		sin6->sin6_scope_id = 0;
+#endif
+	}
+#if defined(__FreeBSD__) || defined(__APPLE__) || defined(__DragonFly__)
+	rtalloc_ign((struct route *)&net->ro, 0UL);
+#else
+	rtalloc((struct route *)&net->ro);
+#endif
+	if (newaddr->sa_family == AF_INET6) {
+		struct sockaddr_in6 *sin6;
+		sin6 = (struct sockaddr_in6 *)&net->ro._l_addr;
+		(void)in6_recoverscope(sin6, &sin6->sin6_addr, NULL);
+	}
+	if ((net->ro.ro_rt) &&
+	    (net->ro.ro_rt->rt_ifp)) {
+		net->mtu = net->ro.ro_rt->rt_ifp->if_mtu;
+		if (from == 1) {
+			stcb->asoc.smallest_mtu = net->mtu;
+		}
+		/* start things off to match mtu of interface please. */
+		net->ro.ro_rt->rt_rmx.rmx_mtu = net->ro.ro_rt->rt_ifp->if_mtu;
+	} else {
+		net->mtu = stcb->asoc.smallest_mtu;
+	}
+	if (stcb->asoc.smallest_mtu > net->mtu) {
+		stcb->asoc.smallest_mtu = net->mtu;
+	}
+	/* We take the max of the burst limit times a MTU or the INITIAL_CWND.
+	 * We then limit this to 4 MTU's of sending.
+	 */
+ 	net->cwnd = min((net->mtu * 4), max((stcb->asoc.max_burst * net->mtu), SCTP_INITIAL_CWND));
+
+	/* we always get at LEAST 2 MTU's */
+	if (net->cwnd < (2 * net->mtu)) {
+		net->cwnd = 2 * net->mtu;
+	}
+
+	net->ssthresh = stcb->asoc.peers_rwnd;
+
+	net->src_addr_selected = 0;
+	netfirst = TAILQ_FIRST(&stcb->asoc.nets);
+	if (net->ro.ro_rt == NULL) {
+		/* Since we have no route put it at the back */
+		TAILQ_INSERT_TAIL(&stcb->asoc.nets, net, sctp_next);
+	} else if (netfirst == NULL) {
+		/* We are the first one in the pool. */
+		TAILQ_INSERT_HEAD(&stcb->asoc.nets, net, sctp_next);
+	} else if (netfirst->ro.ro_rt == NULL) {
+		/*
+		 * First one has NO route. Place this one ahead of the
+		 * first one.
+		 */
+		TAILQ_INSERT_HEAD(&stcb->asoc.nets, net, sctp_next);
+	} else if (net->ro.ro_rt->rt_ifp != netfirst->ro.ro_rt->rt_ifp) {
+		/*
+		 * This one has a different interface than the one at the
+		 * top of the list. Place it ahead.
+		 */
+		TAILQ_INSERT_HEAD(&stcb->asoc.nets, net, sctp_next);
+	} else {
+		/*
+		 * Ok we have the same interface as the first one. Move
+		 * forward until we find either
+		 *   a) one with a NULL route... insert ahead of that
+		 *   b) one with a different ifp.. insert after that.
+		 *   c) end of the list.. insert at the tail.
+		 */
+		struct sctp_nets *netlook;
+		do {
+			netlook = TAILQ_NEXT(netfirst, sctp_next);
+			if (netlook == NULL) {
+				/* End of the list */
+				TAILQ_INSERT_TAIL(&stcb->asoc.nets, net,
+				    sctp_next);
+				break;
+			} else if (netlook->ro.ro_rt == NULL) {
+				/* next one has NO route */
+				TAILQ_INSERT_BEFORE(netfirst, net, sctp_next);
+				break;
+			} else if (netlook->ro.ro_rt->rt_ifp !=
+				   net->ro.ro_rt->rt_ifp) {
+				TAILQ_INSERT_AFTER(&stcb->asoc.nets, netlook,
+				    net, sctp_next);
+				break;
+			}
+			/* Shift forward */
+			netfirst = netlook;
+		} while (netlook != NULL);
+	}
+	/* got to have a primary set */
+	if (stcb->asoc.primary_destination == 0) {
+		stcb->asoc.primary_destination = net;
+	} else if ((stcb->asoc.primary_destination->ro.ro_rt == NULL) &&
+		   (net->ro.ro_rt)) {
+		/* No route to current primary adopt new primary */
+		stcb->asoc.primary_destination = net;
+	}
+	sctp_timer_start(SCTP_TIMER_TYPE_PATHMTURAISE, stcb->sctp_ep, stcb,
+	    net);
+
+	return (0);
+}
+
+
+/*
+ * allocate an association and add it to the endpoint. The caller must
+ * be careful to add all additional addresses once they are know right
+ * away or else the assoc will be may experience a blackout scenario.
+ */
+struct sctp_tcb *
+sctp_aloc_assoc(struct sctp_inpcb *inp, struct sockaddr *firstaddr,
+    int for_a_init, int *error,  uint32_t override_tag)
+{
+	struct sctp_tcb *stcb;
+	struct sctp_association *asoc;
+	struct sctpasochead *head;
+	uint16_t rport;
+	int err;
+
+	/*
+	 * Assumption made here:
+	 *  Caller has done a sctp_findassociation_ep_addr(ep, addr's);
+	 *  to make sure the address does not exist already.
+	 */
+	if (sctppcbinfo.ipi_count_asoc >= SCTP_MAX_NUM_OF_ASOC) {
+		/* Hit max assoc, sorry no more */
+		*error = ENOBUFS;
+		return (NULL);
+	}
+	SCTP_INP_RLOCK(inp);
+	if (inp->sctp_flags & SCTP_PCB_FLAGS_IN_TCPPOOL) {
+		/*
+		 * If its in the TCP pool, its NOT allowed to create an
+		 * association. The parent listener needs to call
+		 * sctp_aloc_assoc.. or the one-2-many socket. If a
+		 * peeled off, or connected one does this.. its an error.
+		 */
+		SCTP_INP_RUNLOCK(inp);
+		*error = EINVAL;
+		return (NULL);
+ 	}
+
+#ifdef SCTP_DEBUG
+	if (sctp_debug_on & SCTP_DEBUG_PCB3) {
+		printf("Allocate an association for peer:");
+		if (firstaddr)
+			sctp_print_address(firstaddr);
+		else
+			printf("None\n");
+		printf("Port:%d\n",
+		       ntohs(((struct sockaddr_in *)firstaddr)->sin_port));
+	}
+#endif /* SCTP_DEBUG */
+	if (firstaddr->sa_family == AF_INET) {
+		struct sockaddr_in *sin;
+		sin = (struct sockaddr_in *)firstaddr;
+		if ((sin->sin_port == 0) || (sin->sin_addr.s_addr == 0)) {
+			/* Invalid address */
+#ifdef SCTP_DEBUG
+			if (sctp_debug_on & SCTP_DEBUG_PCB3) {
+				printf("peer address invalid\n");
+			}
+#endif
+			SCTP_INP_RUNLOCK(inp);
+			*error = EINVAL;
+			return (NULL);
+		}
+		rport = sin->sin_port;
+	} else if (firstaddr->sa_family == AF_INET6) {
+		struct sockaddr_in6 *sin6;
+		sin6 = (struct sockaddr_in6 *)firstaddr;
+		if ((sin6->sin6_port == 0) ||
+		    (IN6_IS_ADDR_UNSPECIFIED(&sin6->sin6_addr))) {
+			/* Invalid address */
+#ifdef SCTP_DEBUG
+			if (sctp_debug_on & SCTP_DEBUG_PCB3) {
+				printf("peer address invalid\n");
+			}
+#endif
+			SCTP_INP_RUNLOCK(inp);
+			*error = EINVAL;
+			return (NULL);
+		}
+		rport = sin6->sin6_port;
+	} else {
+		/* not supported family type */
+#ifdef SCTP_DEBUG
+		if (sctp_debug_on & SCTP_DEBUG_PCB3) {
+			printf("BAD family %d\n", firstaddr->sa_family);
+		}
+#endif
+		SCTP_INP_RUNLOCK(inp);
+		*error = EINVAL;
+		return (NULL);
+	}
+	SCTP_INP_RUNLOCK(inp);
+	if (inp->sctp_flags & SCTP_PCB_FLAGS_UNBOUND) {
+		/*
+		 * If you have not performed a bind, then we need to do
+		 * the ephemerial bind for you.
+		 */
+#ifdef SCTP_DEBUG
+		if (sctp_debug_on & SCTP_DEBUG_PCB3) {
+			printf("Doing implicit BIND\n");
+		}
+#endif
+
+		if ((err = sctp_inpcb_bind(inp->sctp_socket,
+		    (struct sockaddr *)NULL,
+#if (defined(__FreeBSD__) && __FreeBSD_version >= 500000) || defined(__DragonFly__)
+					   (struct thread *)NULL
+#else
+					   (struct proc *)NULL
+#endif
+			     ))){
+			/* bind error, probably perm */
+#ifdef SCTP_DEBUG
+			if (sctp_debug_on & SCTP_DEBUG_PCB3) {
+				printf("BIND FAILS ret:%d\n", err);
+			}
+#endif
+
+			*error = err;
+			return (NULL);
+		}
+	}
+	stcb = (struct sctp_tcb *)SCTP_ZONE_GET(sctppcbinfo.ipi_zone_asoc);
+	if (stcb == NULL) {
+		/* out of memory? */
+#ifdef SCTP_DEBUG
+		if (sctp_debug_on & SCTP_DEBUG_PCB3) {
+			printf("aloc_assoc: no assoc mem left, stcb=NULL\n");
+		}
+#endif
+		*error = ENOMEM;
+		return (NULL);
+	}
+	sctppcbinfo.ipi_count_asoc++;
+	sctppcbinfo.ipi_gencnt_asoc++;
+
+	bzero(stcb, sizeof(*stcb));
+	asoc = &stcb->asoc;
+	SCTP_TCB_LOCK_INIT(stcb);
+	/* setup back pointer's */
+	stcb->sctp_ep = inp;
+	stcb->sctp_socket = inp->sctp_socket;
+	if ((err = sctp_init_asoc(inp, asoc, for_a_init, override_tag))) {
+		/* failed */
+		SCTP_TCB_LOCK_DESTROY (stcb);
+		SCTP_ZONE_FREE(sctppcbinfo.ipi_zone_asoc, stcb);
+		sctppcbinfo.ipi_count_asoc--;
+#ifdef SCTP_DEBUG
+		if (sctp_debug_on & SCTP_DEBUG_PCB3) {
+			printf("aloc_assoc: couldn't init asoc, out of mem?!\n");
+		}
+#endif
+		*error = err;
+		return (NULL);
+	}
+	/* and the port */
+	stcb->rport = rport;
+	SCTP_INP_INFO_WLOCK();
+	SCTP_INP_WLOCK(inp);
+	if (inp->sctp_flags & (SCTP_PCB_FLAGS_SOCKET_GONE|SCTP_PCB_FLAGS_SOCKET_ALLGONE)) {
+		/* inpcb freed while alloc going on */
+		SCTP_TCB_LOCK_DESTROY (stcb);
+		SCTP_ZONE_FREE(sctppcbinfo.ipi_zone_asoc, stcb);
+		SCTP_INP_WUNLOCK(inp);
+		SCTP_INP_INFO_WUNLOCK();
+		sctppcbinfo.ipi_count_asoc--;
+#ifdef SCTP_DEBUG
+		if (sctp_debug_on & SCTP_DEBUG_PCB3) {
+			printf("aloc_assoc: couldn't init asoc, out of mem?!\n");
+		}
+#endif
+		*error = EINVAL;
+		return (NULL);
+	}
+	SCTP_TCB_LOCK(stcb);
+
+	/* now that my_vtag is set, add it to the  hash */
+	head = &sctppcbinfo.sctp_asochash[SCTP_PCBHASH_ASOC(stcb->asoc.my_vtag,
+	     sctppcbinfo.hashasocmark)];
+	/* put it in the bucket in the vtag hash of assoc's for the system */
+	LIST_INSERT_HEAD(head, stcb, sctp_asocs);
+	SCTP_INP_INFO_WUNLOCK();
+
+
+	if ((err = sctp_add_remote_addr(stcb, firstaddr, 1, 1))) {
+		/* failure.. memory error? */
+		if (asoc->strmout)
+			FREE(asoc->strmout, M_PCB);
+		if (asoc->mapping_array)
+			FREE(asoc->mapping_array, M_PCB);
+
+		SCTP_ZONE_FREE(sctppcbinfo.ipi_zone_asoc, stcb);
+		sctppcbinfo.ipi_count_asoc--;
+#ifdef SCTP_DEBUG
+		if (sctp_debug_on & SCTP_DEBUG_PCB3) {
+			printf("aloc_assoc: couldn't add remote addr!\n");
+		}
+#endif
+		SCTP_TCB_LOCK_DESTROY (stcb);
+		*error = ENOBUFS;
+		return (NULL);
+	}
+	/* Init all the timers */
+#if defined(__FreeBSD__) && __FreeBSD_version >= 500000
+	callout_init(&asoc->hb_timer.timer, 0);
+	callout_init(&asoc->dack_timer.timer, 0);
+	callout_init(&asoc->asconf_timer.timer, 0);
+	callout_init(&asoc->shut_guard_timer.timer, 0);
+	callout_init(&asoc->autoclose_timer.timer, 0);
+	callout_init(&asoc->delayed_event_timer.timer, 0);
+#else
+	callout_init(&asoc->hb_timer.timer);
+	callout_init(&asoc->dack_timer.timer);
+	callout_init(&asoc->asconf_timer.timer);
+	callout_init(&asoc->shut_guard_timer.timer);
+	callout_init(&asoc->autoclose_timer.timer);
+	callout_init(&asoc->delayed_event_timer.timer);
+#endif
+	LIST_INSERT_HEAD(&inp->sctp_asoc_list, stcb, sctp_tcblist);
+	/* now file the port under the hash as well */
+	if (inp->sctp_tcbhash != NULL) {
+		head = &inp->sctp_tcbhash[SCTP_PCBHASH_ALLADDR(stcb->rport,
+		   inp->sctp_hashmark)];
+		LIST_INSERT_HEAD(head, stcb, sctp_tcbhash);
+	}
+	SCTP_INP_WUNLOCK(inp);
+#ifdef SCTP_DEBUG
+	if (sctp_debug_on & SCTP_DEBUG_PCB1) {
+		printf("Association %p now allocated\n", stcb);
+	}
+#endif
+	return (stcb);
+}
+
+void
+sctp_free_remote_addr(struct sctp_nets *net)
+{
+	if (net == NULL)
+		return;
+	net->ref_count--;
+	if (net->ref_count <= 0) {
+		/* stop timer if running */
+		callout_stop(&net->rxt_timer.timer);
+		callout_stop(&net->pmtu_timer.timer);
+		net->dest_state = SCTP_ADDR_NOT_REACHABLE;
+		SCTP_ZONE_FREE(sctppcbinfo.ipi_zone_net, net);
+		sctppcbinfo.ipi_count_raddr--;
+	}
+}
+
+/*
+ * remove a remote endpoint address from an association, it
+ * will fail if the address does not exist.
+ */
+int
+sctp_del_remote_addr(struct sctp_tcb *stcb, struct sockaddr *remaddr)
+{
+	/*
+	 * Here we need to remove a remote address. This is quite simple, we
+	 * first find it in the list of address for the association
+	 * (tasoc->asoc.nets) and then if it is there, we do a LIST_REMOVE on
+	 * that item.
+	 * Note we do not allow it to be removed if there are no other
+	 * addresses.
+	 */
+	struct sctp_association *asoc;
+	struct sctp_nets *net, *net_tmp;
+	asoc = &stcb->asoc;
+	if (asoc->numnets < 2) {
+		/* Must have at LEAST two remote addresses */
+		return (-1);
+	}
+	/* locate the address */
+	for (net = TAILQ_FIRST(&asoc->nets); net != NULL; net = net_tmp) {
+		net_tmp = TAILQ_NEXT(net, sctp_next);
+		if (net->ro._l_addr.sa.sa_family != remaddr->sa_family) {
+			continue;
+		}
+		if (sctp_cmpaddr((struct sockaddr *)&net->ro._l_addr,
+		    remaddr)) {
+			/* we found the guy */
+			asoc->numnets--;
+			TAILQ_REMOVE(&asoc->nets, net, sctp_next);
+			sctp_free_remote_addr(net);
+			if (net == asoc->primary_destination) {
+				/* Reset primary */
+				struct sctp_nets *lnet;
+				lnet = TAILQ_FIRST(&asoc->nets);
+				/* Try to find a confirmed primary */
+				asoc->primary_destination =
+				    sctp_find_alternate_net(stcb, lnet);
+			}
+			if (net == asoc->last_data_chunk_from) {
+				/* Reset primary */
+				asoc->last_data_chunk_from =
+				    TAILQ_FIRST(&asoc->nets);
+			}
+			if (net == asoc->last_control_chunk_from) {
+				/* Reset primary */
+				asoc->last_control_chunk_from =
+				    TAILQ_FIRST(&asoc->nets);
+			}
+			if (net == asoc->asconf_last_sent_to) {
+				/* Reset primary */
+				asoc->asconf_last_sent_to =
+				    TAILQ_FIRST(&asoc->nets);
+			}
+			return (0);
+		}
+	}
+	/* not found. */
+	return (-2);
+}
+
+
+static void
+sctp_add_vtag_to_timewait(struct sctp_inpcb *inp, u_int32_t tag)
+{
+	struct sctpvtaghead *chain;
+	struct sctp_tagblock *twait_block;
+	struct timeval now;
+	int set, i;
+	SCTP_GETTIME_TIMEVAL(&now);
+	chain = &sctppcbinfo.vtag_timewait[(tag % SCTP_STACK_VTAG_HASH_SIZE)];
+	set = 0;
+	if (!LIST_EMPTY(chain)) {
+		/* Block(s) present, lets find space, and expire on the fly */
+		LIST_FOREACH(twait_block, chain, sctp_nxt_tagblock) {
+			for (i = 0; i < SCTP_NUMBER_IN_VTAG_BLOCK; i++) {
+				if ((twait_block->vtag_block[i].v_tag == 0) &&
+				    !set) {
+					twait_block->vtag_block[0].tv_sec_at_expire =
+					    now.tv_sec + SCTP_TIME_WAIT;
+					twait_block->vtag_block[0].v_tag = tag;
+					set = 1;
+				} else if ((twait_block->vtag_block[i].v_tag) &&
+				    ((long)twait_block->vtag_block[i].tv_sec_at_expire >
+				    now.tv_sec)) {
+					/* Audit expires this guy */
+					twait_block->vtag_block[i].tv_sec_at_expire = 0;
+					twait_block->vtag_block[i].v_tag = 0;
+					if (set == 0) {
+						/* Reuse it for my new tag */
+						twait_block->vtag_block[0].tv_sec_at_expire = now.tv_sec + SCTP_TIME_WAIT;
+						twait_block->vtag_block[0].v_tag = tag;
+						set = 1;
+					}
+				}
+			}
+			if (set) {
+				/*
+				 * We only do up to the block where we can
+				 * place our tag for audits
+				 */
+				break;
+			}
+		}
+	}
+	/* Need to add a new block to chain */
+	if (!set) {
+		MALLOC(twait_block, struct sctp_tagblock *,
+		       sizeof(struct sctp_tagblock), M_PCB, M_NOWAIT);
+		if (twait_block == NULL) {
+			return;
+		}
+		memset(twait_block, 0, sizeof(struct sctp_timewait));
+		LIST_INSERT_HEAD(chain, twait_block, sctp_nxt_tagblock);
+		twait_block->vtag_block[0].tv_sec_at_expire = now.tv_sec +
+		    SCTP_TIME_WAIT;
+		twait_block->vtag_block[0].v_tag = tag;
+	}
+}
+
+
+static void
+sctp_iterator_asoc_being_freed(struct sctp_inpcb *inp, struct sctp_tcb *stcb)
+{
+	struct sctp_iterator *it;
+
+
+
+	/* Unlock the tcb lock we do this so
+	 * we avoid a dead lock scenario where
+	 * the iterator is waiting on the TCB lock
+	 * and the TCB lock is waiting on the iterator
+	 * lock.
+	 */
+	SCTP_ITERATOR_LOCK();
+	SCTP_INP_INFO_WLOCK();
+	SCTP_INP_WLOCK(inp);
+	SCTP_TCB_LOCK(stcb);
+
+	it = stcb->asoc.stcb_starting_point_for_iterator;
+	if (it == NULL) {
+		return;
+	}
+	if (it->inp != stcb->sctp_ep) {
+		/* hm, focused on the wrong one? */
+		return;
+	}
+	if (it->stcb != stcb) {
+		return;
+	}
+	it->stcb = LIST_NEXT(stcb, sctp_tcblist);
+	if (it->stcb == NULL) {
+		/* done with all asoc's in this assoc */
+		if (it->iterator_flags & SCTP_ITERATOR_DO_SINGLE_INP) {
+			it->inp = NULL;
+		} else {
+
+			it->inp = LIST_NEXT(inp, sctp_list);
+		}
+	}
+}
+
+/*
+ * Free the association after un-hashing the remote port.
+ */
+void
+sctp_free_assoc(struct sctp_inpcb *inp, struct sctp_tcb *stcb)
+{
+	struct sctp_association *asoc;
+	struct sctp_nets *net, *prev;
+	struct sctp_laddr *laddr;
+	struct sctp_tmit_chunk *chk;
+	struct sctp_asconf_addr *aparam;
+	struct sctp_socket_q_list *sq;
+
+	/* first, lets purge the entry from the hash table. */
+	crit_enter();
+	if (stcb->asoc.state == 0) {
+		printf("Freeing already free association:%p - huh??\n",
+		    stcb);
+		crit_exit();
+		return;
+	}
+	asoc = &stcb->asoc;
+	asoc->state = 0;
+	/* now clean up any other timers */
+	callout_stop(&asoc->hb_timer.timer);
+	callout_stop(&asoc->dack_timer.timer);
+	callout_stop(&asoc->asconf_timer.timer);
+	callout_stop(&asoc->shut_guard_timer.timer);
+	callout_stop(&asoc->autoclose_timer.timer);
+	callout_stop(&asoc->delayed_event_timer.timer);
+	TAILQ_FOREACH(net, &asoc->nets, sctp_next) {
+		callout_stop(&net->rxt_timer.timer);
+		callout_stop(&net->pmtu_timer.timer);
+	}
+
+	/* Iterator asoc being freed we send an
+	 * unlocked TCB. It returns with INP_INFO
+	 * and INP write locked and the TCB locked
+	 * too and of course the iterator lock
+	 * in place as well..
+	 */
+	SCTP_TCB_UNLOCK(stcb);
+	sctp_iterator_asoc_being_freed(inp, stcb);
+
+	/* Null all of my entry's on the socket q */
+	TAILQ_FOREACH(sq, &inp->sctp_queue_list, next_sq) {
+		if (sq->tcb == stcb) {
+			sq->tcb = NULL;
+		}
+	}
+
+	if (inp->sctp_tcb_at_block == (void *)stcb) {
+		inp->error_on_block = ECONNRESET;
+	}
+
+	if (inp->sctp_tcbhash) {
+		LIST_REMOVE(stcb, sctp_tcbhash);
+	}
+	/* Now lets remove it from the list of ALL associations in the EP */
+	LIST_REMOVE(stcb, sctp_tcblist);
+	SCTP_INP_WUNLOCK(inp);
+	SCTP_ITERATOR_UNLOCK();
+
+
+	/* pull from vtag hash */
+	LIST_REMOVE(stcb, sctp_asocs);
+
+	/*
+	 * Now before we can free the assoc, we must  remove all of the
+	 * networks and any other allocated space.. i.e. add removes here
+	 * before the SCTP_ZONE_FREE() of the tasoc entry.
+	 */
+
+	sctp_add_vtag_to_timewait(inp, asoc->my_vtag);
+	SCTP_INP_INFO_WUNLOCK();
+	prev = NULL;
+	while (!TAILQ_EMPTY(&asoc->nets)) {
+		net = TAILQ_FIRST(&asoc->nets);
+		/* pull from list */
+		if ((sctppcbinfo.ipi_count_raddr == 0) || (prev == net)) {
+			break;
+		}
+		prev = net;
+		TAILQ_REMOVE(&asoc->nets, net, sctp_next);
+		/* free it */
+		net->ref_count = 0;
+		SCTP_ZONE_FREE(sctppcbinfo.ipi_zone_net, net);
+		sctppcbinfo.ipi_count_raddr--;
+	}
+	/*
+	 * The chunk lists and such SHOULD be empty but we check them
+	 * just in case.
+	 */
+	/* anything on the wheel needs to be removed */
+	while (!TAILQ_EMPTY(&asoc->out_wheel)) {
+		struct sctp_stream_out *outs;
+		outs = TAILQ_FIRST(&asoc->out_wheel);
+		TAILQ_REMOVE(&asoc->out_wheel, outs, next_spoke);
+		/* now clean up any chunks here */
+		chk = TAILQ_FIRST(&outs->outqueue);
+		while (chk) {
+			TAILQ_REMOVE(&outs->outqueue, chk, sctp_next);
+			if (chk->data) {
+				sctp_m_freem(chk->data);
+				chk->data = NULL;
+			}
+			chk->whoTo = NULL;
+			chk->asoc = NULL;
+			/* Free the chunk */
+			SCTP_ZONE_FREE(sctppcbinfo.ipi_zone_chunk, chk);
+			sctppcbinfo.ipi_count_chunk--;
+			sctppcbinfo.ipi_gencnt_chunk++;
+			if ((int)sctppcbinfo.ipi_count_chunk < 0) {
+				panic("Chunk count is negative");
+			}
+			chk = TAILQ_FIRST(&outs->outqueue);
+		}
+		outs = TAILQ_FIRST(&asoc->out_wheel);
+	}
+
+	if (asoc->pending_reply) {
+		FREE(asoc->pending_reply, M_PCB);
+		asoc->pending_reply = NULL;
+	}
+	chk = TAILQ_FIRST(&asoc->pending_reply_queue);
+	while (chk) {
+		TAILQ_REMOVE(&asoc->pending_reply_queue, chk, sctp_next);
+		if (chk->data) {
+			sctp_m_freem(chk->data);
+			chk->data = NULL;
+		}
+		chk->whoTo = NULL;
+		chk->asoc = NULL;
+		/* Free the chunk */
+		SCTP_ZONE_FREE(sctppcbinfo.ipi_zone_chunk, chk);
+		sctppcbinfo.ipi_count_chunk--;
+		sctppcbinfo.ipi_gencnt_chunk++;
+		if ((int)sctppcbinfo.ipi_count_chunk < 0) {
+			panic("Chunk count is negative");
+		}
+		chk = TAILQ_FIRST(&asoc->pending_reply_queue);
+	}
+	/* pending send queue SHOULD be empty */
+	if (!TAILQ_EMPTY(&asoc->send_queue)) {
+		chk = TAILQ_FIRST(&asoc->send_queue);
+		while (chk) {
+			TAILQ_REMOVE(&asoc->send_queue, chk, sctp_next);
+			if (chk->data) {
+				sctp_m_freem(chk->data);
+				chk->data = NULL;
+			}
+			SCTP_ZONE_FREE(sctppcbinfo.ipi_zone_chunk, chk);
+			sctppcbinfo.ipi_count_chunk--;
+			if ((int)sctppcbinfo.ipi_count_chunk < 0) {
+				panic("Chunk count is negative");
+			}
+			sctppcbinfo.ipi_gencnt_chunk++;
+			chk = TAILQ_FIRST(&asoc->send_queue);
+		}
+	}
+	/* sent queue SHOULD be empty */
+	if (!TAILQ_EMPTY(&asoc->sent_queue)) {
+		chk = TAILQ_FIRST(&asoc->sent_queue);
+		while (chk) {
+			TAILQ_REMOVE(&asoc->sent_queue, chk, sctp_next);
+			if (chk->data) {
+				sctp_m_freem(chk->data);
+				chk->data = NULL;
+			}
+			SCTP_ZONE_FREE(sctppcbinfo.ipi_zone_chunk, chk);
+			sctppcbinfo.ipi_count_chunk--;
+			if ((int)sctppcbinfo.ipi_count_chunk < 0) {
+				panic("Chunk count is negative");
+			}
+			sctppcbinfo.ipi_gencnt_chunk++;
+			chk = TAILQ_FIRST(&asoc->sent_queue);
+		}
+	}
+	/* control queue MAY not be empty */
+	if (!TAILQ_EMPTY(&asoc->control_send_queue)) {
+		chk = TAILQ_FIRST(&asoc->control_send_queue);
+		while (chk) {
+			TAILQ_REMOVE(&asoc->control_send_queue, chk, sctp_next);
+			if (chk->data) {
+				sctp_m_freem(chk->data);
+				chk->data = NULL;
+			}
+			SCTP_ZONE_FREE(sctppcbinfo.ipi_zone_chunk, chk);
+			sctppcbinfo.ipi_count_chunk--;
+			if ((int)sctppcbinfo.ipi_count_chunk < 0) {
+				panic("Chunk count is negative");
+			}
+			sctppcbinfo.ipi_gencnt_chunk++;
+			chk = TAILQ_FIRST(&asoc->control_send_queue);
+		}
+	}
+	if (!TAILQ_EMPTY(&asoc->reasmqueue)) {
+		chk = TAILQ_FIRST(&asoc->reasmqueue);
+		while (chk) {
+			TAILQ_REMOVE(&asoc->reasmqueue, chk, sctp_next);
+			if (chk->data) {
+				sctp_m_freem(chk->data);
+				chk->data = NULL;
+			}
+			SCTP_ZONE_FREE(sctppcbinfo.ipi_zone_chunk, chk);
+			sctppcbinfo.ipi_count_chunk--;
+			if ((int)sctppcbinfo.ipi_count_chunk < 0) {
+				panic("Chunk count is negative");
+			}
+			sctppcbinfo.ipi_gencnt_chunk++;
+			chk = TAILQ_FIRST(&asoc->reasmqueue);
+		}
+	}
+	if (!TAILQ_EMPTY(&asoc->delivery_queue)) {
+		chk = TAILQ_FIRST(&asoc->delivery_queue);
+		while (chk) {
+			TAILQ_REMOVE(&asoc->delivery_queue, chk, sctp_next);
+			if (chk->data) {
+				sctp_m_freem(chk->data);
+				chk->data = NULL;
+			}
+			SCTP_ZONE_FREE(sctppcbinfo.ipi_zone_chunk, chk);
+			sctppcbinfo.ipi_count_chunk--;
+			if ((int)sctppcbinfo.ipi_count_chunk < 0) {
+				panic("Chunk count is negative");
+			}
+			sctppcbinfo.ipi_gencnt_chunk++;
+			chk = TAILQ_FIRST(&asoc->delivery_queue);
+		}
+	}
+	if (asoc->mapping_array) {
+		FREE(asoc->mapping_array, M_PCB);
+		asoc->mapping_array = NULL;
+	}
+
+	/* the stream outs */
+	if (asoc->strmout) {
+		FREE(asoc->strmout, M_PCB);
+		asoc->strmout = NULL;
+	}
+	asoc->streamoutcnt = 0;
+	if (asoc->strmin) {
+		int i;
+		for (i = 0; i < asoc->streamincnt; i++) {
+			if (!TAILQ_EMPTY(&asoc->strmin[i].inqueue)) {
+				/* We have somethings on the streamin queue */
+				chk = TAILQ_FIRST(&asoc->strmin[i].inqueue);
+				while (chk) {
+					TAILQ_REMOVE(&asoc->strmin[i].inqueue,
+					    chk, sctp_next);
+					if (chk->data) {
+						sctp_m_freem(chk->data);
+						chk->data = NULL;
+					}
+					SCTP_ZONE_FREE(sctppcbinfo.ipi_zone_chunk,
+					    chk);
+					sctppcbinfo.ipi_count_chunk--;
+					if ((int)sctppcbinfo.ipi_count_chunk < 0) {
+						panic("Chunk count is negative");
+					}
+					sctppcbinfo.ipi_gencnt_chunk++;
+					chk = TAILQ_FIRST(&asoc->strmin[i].inqueue);
+				}
+			}
+		}
+		FREE(asoc->strmin, M_PCB);
+		asoc->strmin = NULL;
+	}
+	asoc->streamincnt = 0;
+	/* local addresses, if any */
+	while (!LIST_EMPTY(&asoc->sctp_local_addr_list)) {
+		laddr = LIST_FIRST(&asoc->sctp_local_addr_list);
+		LIST_REMOVE(laddr, sctp_nxt_addr);
+		SCTP_ZONE_FREE(sctppcbinfo.ipi_zone_laddr, laddr);
+		sctppcbinfo.ipi_count_laddr--;
+	}
+	/* pending asconf (address) parameters */
+	while (!TAILQ_EMPTY(&asoc->asconf_queue)) {
+		aparam = TAILQ_FIRST(&asoc->asconf_queue);
+		TAILQ_REMOVE(&asoc->asconf_queue, aparam, next);
+		FREE(aparam, M_PCB);
+	}
+	if (asoc->last_asconf_ack_sent != NULL) {
+		sctp_m_freem(asoc->last_asconf_ack_sent);
+		asoc->last_asconf_ack_sent = NULL;
+	}
+	/* Insert new items here :> */
+
+	/* Get rid of LOCK */
+	SCTP_TCB_LOCK_DESTROY(stcb);
+
+	/* now clean up the tasoc itself */
+	SCTP_ZONE_FREE(sctppcbinfo.ipi_zone_asoc, stcb);
+	sctppcbinfo.ipi_count_asoc--;
+	if ((inp->sctp_socket->so_snd.sb_cc) ||
+	    (inp->sctp_socket->so_snd.sb_mbcnt)) {
+		/* This will happen when a abort is done */
+		inp->sctp_socket->so_snd.sb_cc = 0;
+		inp->sctp_socket->so_snd.sb_mbcnt = 0;
+	}
+	if (inp->sctp_flags & SCTP_PCB_FLAGS_TCPTYPE) {
+		if ((inp->sctp_flags & SCTP_PCB_FLAGS_IN_TCPPOOL) == 0) {
+			if (inp->sctp_flags & SCTP_PCB_FLAGS_CONNECTED) {
+				/*
+				 * For the base fd, that is NOT in TCP pool we
+				 * turn off the connected flag. This allows
+				 * non-listening endpoints to connect/shutdown/
+				 * connect.
+				 */
+				inp->sctp_flags &= ~SCTP_PCB_FLAGS_CONNECTED;
+				soisdisconnected(inp->sctp_socket);
+			}
+			/*
+			 * For those that are in the TCP pool we just leave
+			 * so it cannot be used. When they close the fd we
+			 * will free it all.
+			 */
+		}
+	}
+	if (inp->sctp_flags & SCTP_PCB_FLAGS_SOCKET_GONE) {
+		sctp_inpcb_free(inp, 0);
+	}
+	crit_exit();
+}
+
+
+/*
+ * determine if a destination is "reachable" based upon the addresses
+ * bound to the current endpoint (e.g. only v4 or v6 currently bound)
+ */
+/*
+ * FIX: if we allow assoc-level bindx(), then this needs to be fixed
+ * to use assoc level v4/v6 flags, as the assoc *may* not have the
+ * same address types bound as its endpoint
+ */
+int
+sctp_destination_is_reachable(struct sctp_tcb *stcb, struct sockaddr *destaddr)
+{
+	struct sctp_inpcb *inp;
+	int answer;
+
+	/* No locks here, the TCB, in all cases is already
+	 * locked and an assoc is up. There is either a
+	 * INP lock by the caller applied (in asconf case when
+	 * deleting an address) or NOT in the HB case, however
+	 * if HB then the INP increment is up and the INP
+	 * will not be removed (on top of the fact that
+	 * we have a TCB lock). So we only want to
+	 * read the sctp_flags, which is either bound-all
+	 * or not.. no protection needed since once an
+	 * assoc is up you can't be changing your binding.
+	 */
+	inp = stcb->sctp_ep;
+	if (inp->sctp_flags & SCTP_PCB_FLAGS_BOUNDALL) {
+		/* if bound all, destination is not restricted */
+		/* RRS: Question during lock work: Is this
+		 * correct? If you are bound-all you still
+		 * might need to obey the V4--V6 flags???
+		 * IMO this bound-all stuff needs to be removed!
+		 */
+		return (1);
+	}
+	/* NOTE: all "scope" checks are done when local addresses are added */
+	if (destaddr->sa_family == AF_INET6) {
+#if !(defined(__FreeBSD__) || defined(__APPLE__) || defined(__DragonFly__))
+		answer = inp->inp_vflag & INP_IPV6;
+#else
+		answer = inp->ip_inp.inp.inp_vflag & INP_IPV6;
+#endif
+	} else if (destaddr->sa_family == AF_INET) {
+#if !(defined(__FreeBSD__) || defined(__APPLE__) || defined(__DragonFly__))
+		answer = inp->inp_vflag & INP_IPV4;
+#else
+		answer = inp->ip_inp.inp.inp_vflag & INP_IPV4;
+#endif
+	} else {
+		/* invalid family, so it's unreachable */
+		answer = 0;
+	}
+	return (answer);
+}
+
+/*
+ * update the inp_vflags on an endpoint
+ */
+static void
+sctp_update_ep_vflag(struct sctp_inpcb *inp) {
+	struct sctp_laddr *laddr;
+
+	/* first clear the flag */
+#if !(defined(__FreeBSD__) || defined(__APPLE__) || defined(__DragonFly__))
+	inp->inp_vflag = 0;
+#else
+	inp->ip_inp.inp.inp_vflag = 0;
+#endif
+	/* set the flag based on addresses on the ep list */
+	LIST_FOREACH(laddr, &inp->sctp_addr_list, sctp_nxt_addr) {
+		if (laddr->ifa == NULL) {
+#ifdef SCTP_DEBUG
+			if (sctp_debug_on & SCTP_DEBUG_PCB1) {
+				printf("An ounce of prevention is worth a pound of cure\n");
+			}
+#endif /* SCTP_DEBUG */
+			continue;
+		}
+		if (laddr->ifa->ifa_addr) {
+			continue;
+		}
+		if (laddr->ifa->ifa_addr->sa_family == AF_INET6) {
+#if !(defined(__FreeBSD__) || defined(__APPLE__) || defined(__DragonFly__))
+			inp->inp_vflag |= INP_IPV6;
+#else
+			inp->ip_inp.inp.inp_vflag |= INP_IPV6;
+#endif
+		} else if (laddr->ifa->ifa_addr->sa_family == AF_INET) {
+#if !(defined(__FreeBSD__) || defined(__APPLE__) || defined(__DragonFly__))
+			inp->inp_vflag |= INP_IPV4;
+#else
+			inp->ip_inp.inp.inp_vflag |= INP_IPV4;
+#endif
+		}
+	}
+}
+
+/*
+ * Add the address to the endpoint local address list
+ * There is nothing to be done if we are bound to all addresses
+ */
+int
+sctp_add_local_addr_ep(struct sctp_inpcb *inp, struct ifaddr *ifa)
+{
+	struct sctp_laddr *laddr;
+	int fnd, error;
+	fnd = 0;
+
+	if (inp->sctp_flags & SCTP_PCB_FLAGS_BOUNDALL) {
+		/* You are already bound to all. You have it already */
+		return (0);
+	}
+	if (ifa->ifa_addr->sa_family == AF_INET6) {
+		struct in6_ifaddr *ifa6;
+		ifa6 = (struct in6_ifaddr *)ifa;
+		if (ifa6->ia6_flags & (IN6_IFF_DETACHED |
+		    IN6_IFF_DEPRECATED | IN6_IFF_ANYCAST | IN6_IFF_NOTREADY))
+			/* Can't bind a non-existent addr. */
+			return (-1);
+	}
+	/* first, is it already present? */
+	LIST_FOREACH(laddr, &inp->sctp_addr_list, sctp_nxt_addr) {
+		if (laddr->ifa == ifa) {
+			fnd = 1;
+			break;
+		}
+	}
+
+	if (((inp->sctp_flags & SCTP_PCB_FLAGS_BOUNDALL) == 0) && (fnd == 0)) {
+		/* Not bound to all */
+		error = sctp_insert_laddr(&inp->sctp_addr_list, ifa);
+		if (error != 0)
+			return (error);
+		inp->laddr_count++;
+		/* update inp_vflag flags */
+		if (ifa->ifa_addr->sa_family == AF_INET6) {
+#if !(defined(__FreeBSD__) || defined(__APPLE__) || defined(__DragonFly__))
+			inp->inp_vflag |= INP_IPV6;
+#else
+			inp->ip_inp.inp.inp_vflag |= INP_IPV6;
+#endif
+		} else if (ifa->ifa_addr->sa_family == AF_INET) {
+#if !(defined(__FreeBSD__) || defined(__APPLE__) || defined(__DragonFly__))
+			inp->inp_vflag |= INP_IPV4;
+#else
+			inp->ip_inp.inp.inp_vflag |= INP_IPV4;
+#endif
+		}
+	}
+	return (0);
+}
+
+
+/*
+ * select a new (hopefully reachable) destination net
+ * (should only be used when we deleted an ep addr that is the
+ * only usable source address to reach the destination net)
+ */
+static void
+sctp_select_primary_destination(struct sctp_tcb *stcb)
+{
+	struct sctp_nets *net;
+
+	TAILQ_FOREACH(net, &stcb->asoc.nets, sctp_next) {
+		/* for now, we'll just pick the first reachable one we find */
+		if (net->dest_state & SCTP_ADDR_UNCONFIRMED)
+			continue;
+		if (sctp_destination_is_reachable(stcb,
+		    (struct sockaddr *)&net->ro._l_addr)) {
+			/* found a reachable destination */
+			stcb->asoc.primary_destination = net;
+		}
+	}
+	/* I can't there from here! ...we're gonna die shortly... */
+}
+
+
+/*
+ * Delete the address from the endpoint local address list
+ * There is nothing to be done if we are bound to all addresses
+ */
+int
+sctp_del_local_addr_ep(struct sctp_inpcb *inp, struct ifaddr *ifa)
+{
+	struct sctp_laddr *laddr;
+	int fnd;
+	fnd = 0;
+	if (inp->sctp_flags & SCTP_PCB_FLAGS_BOUNDALL) {
+		/* You are already bound to all. You have it already */
+		return (EINVAL);
+	}
+
+	LIST_FOREACH(laddr, &inp->sctp_addr_list, sctp_nxt_addr) {
+		if (laddr->ifa == ifa) {
+			fnd = 1;
+			break;
+		}
+	}
+	if (fnd && (inp->laddr_count < 2)) {
+		/* can't delete unless there are at LEAST 2 addresses */
+		return (-1);
+	}
+	if (((inp->sctp_flags & SCTP_PCB_FLAGS_BOUNDALL) == 0) && (fnd)) {
+		/*
+		 * clean up any use of this address
+		 * go through our associations and clear any
+		 *  last_used_address that match this one
+		 * for each assoc, see if a new primary_destination is needed
+		 */
+		struct sctp_tcb *stcb;
+
+		/* clean up "next_addr_touse" */
+		if (inp->next_addr_touse == laddr)
+			/* delete this address */
+			inp->next_addr_touse = NULL;
+
+		/* clean up "last_used_address" */
+		LIST_FOREACH(stcb, &inp->sctp_asoc_list, sctp_tcblist) {
+			if (stcb->asoc.last_used_address == laddr)
+				/* delete this address */
+				stcb->asoc.last_used_address = NULL;
+		} /* for each tcb */
+
+		/* remove it from the ep list */
+		sctp_remove_laddr(laddr);
+		inp->laddr_count--;
+		/* update inp_vflag flags */
+		sctp_update_ep_vflag(inp);
+		/* select a new primary destination if needed */
+		LIST_FOREACH(stcb, &inp->sctp_asoc_list, sctp_tcblist) {
+			/* presume caller (sctp_asconf.c) already owns INP lock */
+			SCTP_TCB_LOCK(stcb);
+			if (sctp_destination_is_reachable(stcb,
+			    (struct sockaddr *)&stcb->asoc.primary_destination->ro._l_addr) == 0) {
+				sctp_select_primary_destination(stcb);
+			}
+			SCTP_TCB_UNLOCK(stcb);
+		} /* for each tcb */
+	}
+	return (0);
+}
+
+/*
+ * Add the addr to the TCB local address list
+ * For the BOUNDALL or dynamic case, this is a "pending" address list
+ * (eg. addresses waiting for an ASCONF-ACK response)
+ * For the subset binding, static case, this is a "valid" address list
+ */
+int
+sctp_add_local_addr_assoc(struct sctp_tcb *stcb, struct ifaddr *ifa)
+{
+	struct sctp_inpcb *inp;
+	struct sctp_laddr *laddr;
+	int error;
+
+	/* Assumes TCP is locked.. and possiblye
+	 * the INP. May need to confirm/fix that if
+	 * we need it and is not the case.
+	 */
+	inp = stcb->sctp_ep;
+	if (ifa->ifa_addr->sa_family == AF_INET6) {
+		struct in6_ifaddr *ifa6;
+		ifa6 = (struct in6_ifaddr *)ifa;
+		if (ifa6->ia6_flags & (IN6_IFF_DETACHED |
+		    /* IN6_IFF_DEPRECATED | */
+		    IN6_IFF_ANYCAST |
+		    IN6_IFF_NOTREADY))
+			/* Can't bind a non-existent addr. */
+			return (-1);
+	}
+	/* does the address already exist? */
+	LIST_FOREACH(laddr, &stcb->asoc.sctp_local_addr_list, sctp_nxt_addr) {
+		if (laddr->ifa == ifa) {
+			return (-1);
+		}
+	}
+
+	/* add to the list */
+	error = sctp_insert_laddr(&stcb->asoc.sctp_local_addr_list, ifa);
+	if (error != 0)
+		return (error);
+	return (0);
+}
+
+/*
+ * insert an laddr entry with the given ifa for the desired list
+ */
+int
+sctp_insert_laddr(struct sctpladdr *list, struct ifaddr *ifa) {
+	struct sctp_laddr *laddr;
+
+	crit_enter();
+	laddr = (struct sctp_laddr *)SCTP_ZONE_GET(sctppcbinfo.ipi_zone_laddr);
+	if (laddr == NULL) {
+		/* out of memory? */
+		crit_exit();
+		return (EINVAL);
+	}
+	sctppcbinfo.ipi_count_laddr++;
+	sctppcbinfo.ipi_gencnt_laddr++;
+	bzero(laddr, sizeof(*laddr));
+	laddr->ifa = ifa;
+	/* insert it */
+	LIST_INSERT_HEAD(list, laddr, sctp_nxt_addr);
+
+	crit_exit();
+	return (0);
+}
+
+/*
+ * Remove an laddr entry from the local address list (on an assoc)
+ */
+void
+sctp_remove_laddr(struct sctp_laddr *laddr)
+{
+	crit_enter();
+	/* remove from the list */
+	LIST_REMOVE(laddr, sctp_nxt_addr);
+	SCTP_ZONE_FREE(sctppcbinfo.ipi_zone_laddr, laddr);
+	sctppcbinfo.ipi_count_laddr--;
+	sctppcbinfo.ipi_gencnt_laddr++;
+	crit_exit();
+}
+
+/*
+ * Remove an address from the TCB local address list
+ */
+int
+sctp_del_local_addr_assoc(struct sctp_tcb *stcb, struct ifaddr *ifa)
+{
+	struct sctp_inpcb *inp;
+	struct sctp_laddr *laddr;
+
+	/* This is called by asconf work. It is assumed that
+	 * a) The TCB is locked
+	 * and
+	 * b) The INP is locked.
+	 * This is true in as much as I can trace through
+	 * the entry asconf code where I did these locks.
+	 * Again, the ASCONF code is a bit different in
+	 * that it does lock the INP during its work often
+	 * times. This must be since we don't want other
+	 * proc's looking up things while what they are
+	 * looking up is changing :-D
+	 */
+
+	inp = stcb->sctp_ep;
+	/* if subset bound and don't allow ASCONF's, can't delete last */
+	if (((inp->sctp_flags & SCTP_PCB_FLAGS_BOUNDALL) == 0) &&
+	    ((inp->sctp_flags & SCTP_PCB_FLAGS_DO_ASCONF) == 0)) {
+		if (stcb->asoc.numnets < 2) {
+			/* can't delete last address */
+			return (-1);
+		}
+	}
+
+	LIST_FOREACH(laddr, &stcb->asoc.sctp_local_addr_list, sctp_nxt_addr) {
+		/* remove the address if it exists */
+		if (laddr->ifa == NULL)
+			continue;
+		if (laddr->ifa == ifa) {
+			sctp_remove_laddr(laddr);
+			return (0);
+		}
+	}
+
+	/* address not found! */
+	return (-1);
+}
+
+/*
+ * Remove an address from the TCB local address list
+ * lookup using a sockaddr addr
+ */
+int
+sctp_del_local_addr_assoc_sa(struct sctp_tcb *stcb, struct sockaddr *sa)
+{
+	struct sctp_inpcb *inp;
+	struct sctp_laddr *laddr;
+	struct sockaddr *l_sa;
+
+        /*
+         * This function I find does not seem to have a caller.
+	 * As such we NEED TO DELETE this code. If we do
+	 * find a caller, the caller MUST have locked the TCB
+	 * at the least and probably the INP as well.
+         */
+	inp = stcb->sctp_ep;
+	/* if subset bound and don't allow ASCONF's, can't delete last */
+	if (((inp->sctp_flags & SCTP_PCB_FLAGS_BOUNDALL) == 0) &&
+	    ((inp->sctp_flags & SCTP_PCB_FLAGS_DO_ASCONF) == 0)) {
+		if (stcb->asoc.numnets < 2) {
+			/* can't delete last address */
+			return (-1);
+		}
+	}
+
+	LIST_FOREACH(laddr, &stcb->asoc.sctp_local_addr_list, sctp_nxt_addr) {
+		/* make sure the address exists */
+		if (laddr->ifa == NULL)
+			continue;
+		if (laddr->ifa->ifa_addr == NULL)
+			continue;
+
+		l_sa = laddr->ifa->ifa_addr;
+		if (l_sa->sa_family == AF_INET6) {
+			/* IPv6 address */
+			struct sockaddr_in6 *sin1, *sin2;
+			sin1 = (struct sockaddr_in6 *)l_sa;
+			sin2 = (struct sockaddr_in6 *)sa;
+			if (memcmp(&sin1->sin6_addr, &sin2->sin6_addr,
+			    sizeof(struct in6_addr)) == 0) {
+				/* matched */
+				sctp_remove_laddr(laddr);
+				return (0);
+			}
+		} else if (l_sa->sa_family == AF_INET) {
+			/* IPv4 address */
+			struct sockaddr_in *sin1, *sin2;
+			sin1 = (struct sockaddr_in *)l_sa;
+			sin2 = (struct sockaddr_in *)sa;
+			if (sin1->sin_addr.s_addr == sin2->sin_addr.s_addr) {
+				/* matched */
+				sctp_remove_laddr(laddr);
+				return (0);
+			}
+		} else {
+			/* invalid family */
+			return (-1);
+		}
+	} /* end foreach */
+	/* address not found! */
+	return (-1);
+}
+
+static char sctp_pcb_initialized = 0;
+
+#if defined(__FreeBSD__) || defined(__APPLE__) || defined(__DragonFly__)
+/* sysctl */
+static int sctp_max_number_of_assoc = SCTP_MAX_NUM_OF_ASOC;
+static int sctp_scale_up_for_address = SCTP_SCALE_FOR_ADDR;
+
+#endif /* FreeBSD || APPLE || DragonFly */
+
+#ifndef SCTP_TCBHASHSIZE
+#define SCTP_TCBHASHSIZE 1024
+#endif
+
+#ifndef SCTP_CHUNKQUEUE_SCALE
+#define SCTP_CHUNKQUEUE_SCALE 10
+#endif
+
+void
+sctp_pcb_init()
+{
+	/*
+	 * SCTP initialization for the PCB structures
+	 * should be called by the sctp_init() funciton.
+	 */
+	int i;
+	int hashtblsize = SCTP_TCBHASHSIZE;
+
+#if defined(__FreeBSD__) || defined(__APPLE__) || defined(__DragonFly__)
+	int sctp_chunkscale = SCTP_CHUNKQUEUE_SCALE;
+#endif
+
+	if (sctp_pcb_initialized != 0) {
+		/* error I was called twice */
+		return;
+	}
+	sctp_pcb_initialized = 1;
+
+	/* Init all peg counts */
+	for (i = 0; i < SCTP_NUMBER_OF_PEGS; i++) {
+		sctp_pegs[i] = 0;
+	}
+
+	/* init the empty list of (All) Endpoints */
+	LIST_INIT(&sctppcbinfo.listhead);
+
+	/* init the iterator head */
+	LIST_INIT(&sctppcbinfo.iteratorhead);
+
+	/* init the hash table of endpoints */
+#if defined(__FreeBSD__)
+#if defined(__FreeBSD_cc_version) && __FreeBSD_cc_version >= 440000
+	TUNABLE_INT_FETCH("net.inet.sctp.tcbhashsize", &hashtblsize);
+	TUNABLE_INT_FETCH("net.inet.sctp.pcbhashsize", &sctp_pcbtblsize);
+	TUNABLE_INT_FETCH("net.inet.sctp.chunkscale", &sctp_chunkscale);
+#else
+	TUNABLE_INT_FETCH("net.inet.sctp.tcbhashsize", SCTP_TCBHASHSIZE,
+	    hashtblsize);
+	TUNABLE_INT_FETCH("net.inet.sctp.pcbhashsize", SCTP_PCBHASHSIZE,
+	    sctp_pcbtblsize);
+	TUNABLE_INT_FETCH("net.inet.sctp.chunkscale", SCTP_CHUNKQUEUE_SCALE,
+	    sctp_chunkscale);
+#endif
+#endif
+
+	sctppcbinfo.sctp_asochash = hashinit((hashtblsize * 31),
+#ifdef __NetBSD__
+	    HASH_LIST,
+#endif
+	    M_PCB,
+#if defined(__NetBSD__) || defined(__OpenBSD__)
+	    M_WAITOK,
+#endif
+	    &sctppcbinfo.hashasocmark);
+
+	sctppcbinfo.sctp_ephash = hashinit(hashtblsize,
+#ifdef __NetBSD__
+	    HASH_LIST,
+#endif
+	    M_PCB,
+#if defined(__NetBSD__) || defined(__OpenBSD__)
+	    M_WAITOK,
+#endif
+	    &sctppcbinfo.hashmark);
+
+	sctppcbinfo.sctp_tcpephash = hashinit(hashtblsize,
+#ifdef __NetBSD__
+	    HASH_LIST,
+#endif
+	    M_PCB,
+#if defined(__NetBSD__) || defined(__OpenBSD__)
+	    M_WAITOK,
+#endif
+	    &sctppcbinfo.hashtcpmark);
+
+	sctppcbinfo.hashtblsize = hashtblsize;
+
+	/* init the zones */
+	/*
+	 * FIX ME: Should check for NULL returns, but if it does fail we
+	 * are doomed to panic anyways... add later maybe.
+	 */
+	SCTP_ZONE_INIT(sctppcbinfo.ipi_zone_ep, "sctp_ep",
+	    sizeof(struct sctp_inpcb), maxsockets);
+
+	SCTP_ZONE_INIT(sctppcbinfo.ipi_zone_asoc, "sctp_asoc",
+	    sizeof(struct sctp_tcb), sctp_max_number_of_assoc);
+
+	SCTP_ZONE_INIT(sctppcbinfo.ipi_zone_laddr, "sctp_laddr",
+	    sizeof(struct sctp_laddr),
+	    (sctp_max_number_of_assoc * sctp_scale_up_for_address));
+
+	SCTP_ZONE_INIT(sctppcbinfo.ipi_zone_net, "sctp_raddr",
+	    sizeof(struct sctp_nets),
+	    (sctp_max_number_of_assoc * sctp_scale_up_for_address));
+
+	SCTP_ZONE_INIT(sctppcbinfo.ipi_zone_chunk, "sctp_chunk",
+	    sizeof(struct sctp_tmit_chunk),
+	    (sctp_max_number_of_assoc * sctp_scale_up_for_address *
+	    sctp_chunkscale));
+
+	SCTP_ZONE_INIT(sctppcbinfo.ipi_zone_sockq, "sctp_sockq",
+	    sizeof(struct sctp_socket_q_list),
+	    (sctp_max_number_of_assoc * sctp_scale_up_for_address *
+	    sctp_chunkscale));
+
+        /* Master Lock INIT for info structure */
+	SCTP_INP_INFO_LOCK_INIT();
+	SCTP_ITERATOR_LOCK_INIT();
+	/* not sure if we need all the counts */
+	sctppcbinfo.ipi_count_ep = 0;
+	sctppcbinfo.ipi_gencnt_ep = 0;
+	/* assoc/tcb zone info */
+	sctppcbinfo.ipi_count_asoc = 0;
+	sctppcbinfo.ipi_gencnt_asoc = 0;
+	/* local addrlist zone info */
+	sctppcbinfo.ipi_count_laddr = 0;
+	sctppcbinfo.ipi_gencnt_laddr = 0;
+	/* remote addrlist zone info */
+	sctppcbinfo.ipi_count_raddr = 0;
+	sctppcbinfo.ipi_gencnt_raddr = 0;
+	/* chunk info */
+	sctppcbinfo.ipi_count_chunk = 0;
+	sctppcbinfo.ipi_gencnt_chunk = 0;
+
+	/* socket queue zone info */
+	sctppcbinfo.ipi_count_sockq = 0;
+	sctppcbinfo.ipi_gencnt_sockq = 0;
+
+	/* mbuf tracker */
+	sctppcbinfo.mbuf_track = 0;
+	/* port stuff */
+#if defined(__FreeBSD__) || defined(__OpenBSD__) || defined(__APPLE__) \
+    || defined(__DragonFly__)
+	sctppcbinfo.lastlow = ipport_firstauto;
+#else
+	sctppcbinfo.lastlow = anonportmin;
+#endif
+	/* Init the TIMEWAIT list */
+	for (i = 0; i < SCTP_STACK_VTAG_HASH_SIZE; i++) {
+		LIST_INIT(&sctppcbinfo.vtag_timewait[i]);
+	}
+
+#if defined(_SCTP_NEEDS_CALLOUT_) && !defined(__APPLE__)
+	TAILQ_INIT(&sctppcbinfo.callqueue);
+#endif
+
+}
+
+int
+sctp_load_addresses_from_init(struct sctp_tcb *stcb, struct mbuf *m,
+    int iphlen, int offset, int limit, struct sctphdr *sh,
+    struct sockaddr *altsa)
+{
+	/*
+	 * grub through the INIT pulling addresses and
+	 * loading them to the nets structure in the asoc.
+	 * The from address in the mbuf should also be loaded
+	 * (if it is not already). This routine can be called
+	 * with either INIT or INIT-ACK's as long as the
+	 * m points to the IP packet and the offset points
+	 * to the beginning of the parameters.
+	 */
+	struct sctp_inpcb *inp, *l_inp;
+	struct sctp_nets *net, *net_tmp;
+	struct ip *iph;
+	struct sctp_paramhdr *phdr, parm_buf;
+	struct sctp_tcb *stcb_tmp;
+	u_int16_t ptype, plen;
+	struct sockaddr *sa;
+	struct sockaddr_storage dest_store;
+	struct sockaddr *local_sa = (struct sockaddr *)&dest_store;
+	struct sockaddr_in sin;
+	struct sockaddr_in6 sin6;
+
+	/* First get the destination address setup too. */
+	memset(&sin, 0, sizeof(sin));
+	memset(&sin6, 0, sizeof(sin6));
+
+	sin.sin_family = AF_INET;
+	sin.sin_len = sizeof(sin);
+	sin.sin_port = stcb->rport;
+
+	sin6.sin6_family = AF_INET6;
+	sin6.sin6_len = sizeof(struct sockaddr_in6);
+	sin6.sin6_port = stcb->rport;
+	if (altsa == NULL) {
+		iph = mtod(m, struct ip *);
+		if (iph->ip_v == IPVERSION) {
+			/* its IPv4 */
+			struct sockaddr_in *sin_2;
+			sin_2 = (struct sockaddr_in *)(local_sa);
+			memset(sin_2, 0, sizeof(sin));
+			sin_2->sin_family = AF_INET;
+			sin_2->sin_len = sizeof(sin);
+			sin_2->sin_port = sh->dest_port;
+			sin_2->sin_addr.s_addr = iph->ip_dst.s_addr ;
+			sin.sin_addr = iph->ip_src;
+			sa = (struct sockaddr *)&sin;
+		} else if (iph->ip_v == (IPV6_VERSION >> 4)) {
+			/* its IPv6 */
+			struct ip6_hdr *ip6;
+			struct sockaddr_in6 *sin6_2;
+
+			ip6 = mtod(m, struct ip6_hdr *);
+			sin6_2 = (struct sockaddr_in6 *)(local_sa);
+			memset(sin6_2, 0, sizeof(sin6));
+			sin6_2->sin6_family = AF_INET6;
+			sin6_2->sin6_len = sizeof(struct sockaddr_in6);
+			sin6_2->sin6_port = sh->dest_port;
+			sin6.sin6_addr = ip6->ip6_src;
+			sa = (struct sockaddr *)&sin6;
+		} else {
+			sa = NULL;
+		}
+	} else {
+		/*
+		 * For cookies we use the src address NOT from the packet
+		 * but from the original INIT
+		 */
+		sa = altsa;
+	}
+	/* Turn off ECN until we get through all params */
+	stcb->asoc.ecn_allowed = 0;
+
+	TAILQ_FOREACH(net, &stcb->asoc.nets, sctp_next) {
+		/* mark all addresses that we have currently on the list */
+		net->dest_state |= SCTP_ADDR_NOT_IN_ASSOC;
+	}
+	/* does the source address already exist? if so skip it */
+	l_inp = inp = stcb->sctp_ep;
+	stcb_tmp = sctp_findassociation_ep_addr(&inp, sa, &net_tmp, local_sa, stcb);
+	if ((stcb_tmp == NULL && inp == stcb->sctp_ep) || inp == NULL) {
+		/* we must add the source address */
+		/* no scope set here since we have a tcb already. */
+		if ((sa->sa_family == AF_INET) &&
+		    (stcb->asoc.ipv4_addr_legal)) {
+			if (sctp_add_remote_addr(stcb, sa, 0, 2)) {
+				return (-1);
+			}
+		} else if ((sa->sa_family == AF_INET6) &&
+		    (stcb->asoc.ipv6_addr_legal)) {
+			if (sctp_add_remote_addr(stcb, sa, 0, 3)) {
+				return (-1);
+			}
+		}
+	} else {
+		if (net_tmp != NULL && stcb_tmp == stcb) {
+			net_tmp->dest_state &= ~SCTP_ADDR_NOT_IN_ASSOC;
+		} else if (stcb_tmp != stcb) {
+			/* It belongs to another association? */
+			return (-1);
+		}
+	}
+	/* since a unlock occured we must check the
+	 * TCB's state and the pcb's gone flags.
+	 */
+	if (l_inp->sctp_flags & (SCTP_PCB_FLAGS_SOCKET_GONE|SCTP_PCB_FLAGS_SOCKET_ALLGONE)) {
+		/* the user freed the ep */
+		return (-1);
+	}
+	if (stcb->asoc.state == 0) {
+		/* the assoc was freed? */
+		return (-1);
+	}
+
+	/* now we must go through each of the params. */
+	phdr = sctp_get_next_param(m, offset, &parm_buf, sizeof(parm_buf));
+	while (phdr) {
+		ptype = ntohs(phdr->param_type);
+		plen = ntohs(phdr->param_length);
+		/*printf("ptype => %d, plen => %d\n", ptype, plen);*/
+		if (offset + plen > limit) {
+			break;
+		}
+		if (plen == 0) {
+			break;
+		}
+		if ((ptype == SCTP_IPV4_ADDRESS) &&
+		    (stcb->asoc.ipv4_addr_legal)) {
+			struct sctp_ipv4addr_param *p4, p4_buf;
+			/* ok get the v4 address and check/add */
+			phdr = sctp_get_next_param(m, offset,
+			    (struct sctp_paramhdr *)&p4_buf, sizeof(p4_buf));
+			if (plen != sizeof(struct sctp_ipv4addr_param) ||
+			    phdr == NULL) {
+				return (-1);
+			}
+			p4 = (struct sctp_ipv4addr_param *)phdr;
+			sin.sin_addr.s_addr = p4->addr;
+			sa = (struct sockaddr *)&sin;
+			inp = stcb->sctp_ep;
+			stcb_tmp = sctp_findassociation_ep_addr(&inp, sa, &net,
+			    local_sa, stcb);
+
+			if ((stcb_tmp== NULL && inp == stcb->sctp_ep) ||
+			    inp == NULL) {
+				/* we must add the source address */
+				/* no scope set since we have a tcb already */
+
+				/* we must validate the state again here */
+				if (l_inp->sctp_flags & (SCTP_PCB_FLAGS_SOCKET_GONE|SCTP_PCB_FLAGS_SOCKET_ALLGONE)) {
+					/* the user freed the ep */
+					return (-1);
+				}
+				if (stcb->asoc.state == 0) {
+					/* the assoc was freed? */
+					return (-1);
+				}
+				if (sctp_add_remote_addr(stcb, sa, 0, 4)) {
+					return (-1);
+				}
+			} else if (stcb_tmp == stcb) {
+				if (l_inp->sctp_flags & (SCTP_PCB_FLAGS_SOCKET_GONE|SCTP_PCB_FLAGS_SOCKET_ALLGONE)) {
+					/* the user freed the ep */
+					return (-1);
+				}
+				if (stcb->asoc.state == 0) {
+					/* the assoc was freed? */
+					return (-1);
+				}
+				if (net != NULL) {
+					/* clear flag */
+					net->dest_state &=
+					    ~SCTP_ADDR_NOT_IN_ASSOC;
+				}
+			} else {
+				/* strange, address is in another assoc?
+				 * straighten out locks.
+				 */
+				SCTP_TCB_UNLOCK(stcb_tmp);
+				SCTP_INP_RLOCK(inp);
+				if (l_inp->sctp_flags & (SCTP_PCB_FLAGS_SOCKET_GONE|SCTP_PCB_FLAGS_SOCKET_ALLGONE)) {
+					/* the user freed the ep */
+					SCTP_INP_RUNLOCK(l_inp);
+					return (-1);
+				}
+				if (stcb->asoc.state == 0) {
+					/* the assoc was freed? */
+					SCTP_INP_RUNLOCK(l_inp);
+					return (-1);
+				}
+				SCTP_TCB_LOCK(stcb);
+				SCTP_INP_RUNLOCK(stcb->sctp_ep);
+				return (-1);
+			}
+		} else if ((ptype == SCTP_IPV6_ADDRESS) &&
+		    (stcb->asoc.ipv6_addr_legal)) {
+			/* ok get the v6 address and check/add */
+			struct sctp_ipv6addr_param *p6, p6_buf;
+			phdr = sctp_get_next_param(m, offset,
+			    (struct sctp_paramhdr *)&p6_buf, sizeof(p6_buf));
+			if (plen != sizeof(struct sctp_ipv6addr_param) ||
+			    phdr == NULL) {
+				return (-1);
+			}
+			p6 = (struct sctp_ipv6addr_param *)phdr;
+			memcpy((caddr_t)&sin6.sin6_addr, p6->addr,
+			    sizeof(p6->addr));
+			sa = (struct sockaddr *)&sin6;
+			inp = stcb->sctp_ep;
+			stcb_tmp= sctp_findassociation_ep_addr(&inp, sa, &net,
+			    local_sa, stcb);
+			if (stcb_tmp == NULL && (inp == stcb->sctp_ep ||
+			    inp == NULL)) {
+				/* we must validate the state again here */
+				if (l_inp->sctp_flags & (SCTP_PCB_FLAGS_SOCKET_GONE|SCTP_PCB_FLAGS_SOCKET_ALLGONE)) {
+					/* the user freed the ep */
+					return (-1);
+				}
+				if (stcb->asoc.state == 0) {
+					/* the assoc was freed? */
+					return (-1);
+				}
+				/* we must add the address, no scope set */
+				if (sctp_add_remote_addr(stcb, sa, 0, 5)) {
+					return (-1);
+				}
+			} else if (stcb_tmp == stcb) {
+				/* we must validate the state again here */
+				if (l_inp->sctp_flags & (SCTP_PCB_FLAGS_SOCKET_GONE|SCTP_PCB_FLAGS_SOCKET_ALLGONE)) {
+					/* the user freed the ep */
+					return (-1);
+				}
+				if (stcb->asoc.state == 0) {
+					/* the assoc was freed? */
+					return (-1);
+				}
+				if (net != NULL) {
+					/* clear flag */
+					net->dest_state &=
+					    ~SCTP_ADDR_NOT_IN_ASSOC;
+				}
+			} else {
+				/* strange, address is in another assoc?
+				 * straighten out locks.
+				 */
+				SCTP_TCB_UNLOCK(stcb_tmp);
+				SCTP_INP_RLOCK(l_inp);
+				/* we must validate the state again here */
+				if (l_inp->sctp_flags & (SCTP_PCB_FLAGS_SOCKET_GONE|SCTP_PCB_FLAGS_SOCKET_ALLGONE)) {
+					/* the user freed the ep */
+					SCTP_INP_RUNLOCK(l_inp);
+					return (-1);
+				}
+				if (stcb->asoc.state == 0) {
+					/* the assoc was freed? */
+					SCTP_INP_RUNLOCK(l_inp);
+					return (-1);
+				}
+				SCTP_TCB_LOCK(stcb);
+				SCTP_INP_RUNLOCK(l_inp);
+				return (-1);
+			}
+		} else if (ptype == SCTP_ECN_CAPABLE) {
+			stcb->asoc.ecn_allowed = 1;
+		} else if (ptype == SCTP_ULP_ADAPTION) {
+			if (stcb->asoc.state != SCTP_STATE_OPEN) {
+				struct sctp_adaption_layer_indication ai, *aip;
+
+				phdr = sctp_get_next_param(m, offset,
+							   (struct sctp_paramhdr *)&ai, sizeof(ai));
+				aip = (struct sctp_adaption_layer_indication *)phdr;
+				sctp_ulp_notify(SCTP_NOTIFY_ADAPTION_INDICATION,
+						stcb, ntohl(aip->indication), NULL);
+			}
+		} else if (ptype == SCTP_SET_PRIM_ADDR) {
+			struct sctp_asconf_addr_param lstore, *fee;
+			struct sctp_asconf_addrv4_param *fii;
+			int lptype;
+			struct sockaddr *lsa = NULL;
+
+			stcb->asoc.peer_supports_asconf = 1;
+			stcb->asoc.peer_supports_asconf_setprim = 1;
+			if (plen > sizeof(lstore)) {
+				return (-1);
+			}
+			phdr = sctp_get_next_param(m, offset,
+    			    (struct sctp_paramhdr *)&lstore, plen);
+			if (phdr == NULL) {
+				return (-1);
+			}
+
+			fee  = (struct sctp_asconf_addr_param *)phdr;
+			lptype = ntohs(fee->addrp.ph.param_type);
+			if (lptype == SCTP_IPV4_ADDRESS) {
+				if (plen !=
+				    sizeof(struct sctp_asconf_addrv4_param)) {
+					printf("Sizeof setprim in init/init ack not %d but %d - ignored\n",
+				           (int)sizeof(struct sctp_asconf_addrv4_param),
+				           plen);
+				} else {
+					fii = (struct sctp_asconf_addrv4_param *)fee;
+					sin.sin_addr.s_addr = fii->addrp.addr;
+					lsa = (struct sockaddr *)&sin;
+				}
+			} else if (lptype == SCTP_IPV6_ADDRESS) {
+				if (plen !=
+				    sizeof(struct sctp_asconf_addr_param)) {
+					printf("Sizeof setprim (v6) in init/init ack not %d but %d - ignored\n",
+				           (int)sizeof(struct sctp_asconf_addr_param),
+				           plen);
+				} else {
+					memcpy(sin6.sin6_addr.s6_addr,
+					    fee->addrp.addr,
+					    sizeof(fee->addrp.addr));
+					lsa = (struct sockaddr *)&sin6;
+				}
+			}
+			if (lsa) {
+				sctp_set_primary_addr(stcb, sa, NULL);
+			}
+
+		} else if (ptype == SCTP_PRSCTP_SUPPORTED) {
+			/* Peer supports pr-sctp */
+			stcb->asoc.peer_supports_prsctp = 1;
+		} else if (ptype == SCTP_SUPPORTED_CHUNK_EXT) {
+			/* A supported extension chunk */
+			struct sctp_supported_chunk_types_param *pr_supported;
+			uint8_t local_store[128];
+			int num_ent, i;
+
+			phdr = sctp_get_next_param(m, offset,
+    			    (struct sctp_paramhdr *)&local_store, plen);
+			if (phdr == NULL) {
+				return (-1);
+			}
+			stcb->asoc.peer_supports_asconf = 0;
+			stcb->asoc.peer_supports_asconf_setprim = 0;
+			stcb->asoc.peer_supports_prsctp = 0;
+			stcb->asoc.peer_supports_pktdrop = 0;
+			stcb->asoc.peer_supports_strreset = 0;
+			pr_supported = (struct sctp_supported_chunk_types_param *)phdr;
+			num_ent = plen - sizeof(struct sctp_paramhdr);
+			for (i=0; i<num_ent; i++) {
+				switch (pr_supported->chunk_types[i]) {
+				case SCTP_ASCONF:
+					stcb->asoc.peer_supports_asconf = 1;
+					stcb->asoc.peer_supports_asconf_setprim = 1;
+					break;
+				case SCTP_ASCONF_ACK:
+					stcb->asoc.peer_supports_asconf = 1;
+					stcb->asoc.peer_supports_asconf_setprim = 1;
+					break;
+				case SCTP_FORWARD_CUM_TSN:
+					stcb->asoc.peer_supports_prsctp = 1;
+					break;
+				case SCTP_PACKET_DROPPED:
+					stcb->asoc.peer_supports_pktdrop = 1;
+					break;
+				case SCTP_STREAM_RESET:
+					stcb->asoc.peer_supports_strreset = 1;
+					break;
+				default:
+					/* one I have not learned yet */
+					break;
+
+				}
+			}
+		} else if (ptype == SCTP_ECN_NONCE_SUPPORTED) {
+			/* Peer supports ECN-nonce */
+			stcb->asoc.peer_supports_ecn_nonce = 1;
+			stcb->asoc.ecn_nonce_allowed = 1;
+		} else if ((ptype == SCTP_HEARTBEAT_INFO) ||
+			   (ptype == SCTP_STATE_COOKIE) ||
+			   (ptype == SCTP_UNRECOG_PARAM) ||
+			   (ptype == SCTP_COOKIE_PRESERVE) ||
+			   (ptype == SCTP_SUPPORTED_ADDRTYPE) ||
+			   (ptype == SCTP_ADD_IP_ADDRESS) ||
+			   (ptype == SCTP_DEL_IP_ADDRESS) ||
+			   (ptype == SCTP_ERROR_CAUSE_IND) ||
+			   (ptype == SCTP_SUCCESS_REPORT)) {
+			/* don't care */;
+		} else {
+			if ((ptype & 0x8000) == 0x0000) {
+				/* must stop processing the rest of
+				 * the param's. Any report bits were
+				 * handled with the call to sctp_arethere_unrecognized_parameters()
+				 * when the INIT or INIT-ACK was first seen.
+				 */
+				break;
+			}
+		}
+		offset += SCTP_SIZE32(plen);
+		if (offset >= limit) {
+			break;
+		}
+		phdr = sctp_get_next_param(m, offset, &parm_buf,
+		    sizeof(parm_buf));
+	}
+	/* Now check to see if we need to purge any addresses */
+	for (net = TAILQ_FIRST(&stcb->asoc.nets); net != NULL; net = net_tmp) {
+		net_tmp = TAILQ_NEXT(net, sctp_next);
+		if ((net->dest_state & SCTP_ADDR_NOT_IN_ASSOC) ==
+		    SCTP_ADDR_NOT_IN_ASSOC) {
+			/* This address has been removed from the asoc */
+			/* remove and free it */
+			stcb->asoc.numnets--;
+			TAILQ_REMOVE(&stcb->asoc.nets, net, sctp_next);
+			sctp_free_remote_addr(net);
+			if (net == stcb->asoc.primary_destination) {
+				stcb->asoc.primary_destination = NULL;
+				sctp_select_primary_destination(stcb);
+			}
+		}
+	}
+	return (0);
+}
+
+int
+sctp_set_primary_addr(struct sctp_tcb *stcb, struct sockaddr *sa,
+    struct sctp_nets *net)
+{
+	/* make sure the requested primary address exists in the assoc */
+	if (net == NULL && sa)
+		net = sctp_findnet(stcb, sa);
+
+	if (net == NULL) {
+		/* didn't find the requested primary address! */
+		return (-1);
+	} else {
+		/* set the primary address */
+		if (net->dest_state & SCTP_ADDR_UNCONFIRMED) {
+			/* Must be confirmed */
+			return (-1);
+		}
+		stcb->asoc.primary_destination = net;
+		net->dest_state &= ~SCTP_ADDR_WAS_PRIMARY;
+		return (0);
+	}
+}
+
+
+int
+sctp_is_vtag_good(struct sctp_inpcb *inp, u_int32_t tag, struct timeval *now)
+{
+	/*
+	 * This function serves two purposes. It will see if a TAG can be
+	 * re-used and return 1 for yes it is ok and 0 for don't use that
+	 * tag.
+	 * A secondary function it will do is purge out old tags that can
+	 * be removed.
+	 */
+	struct sctpasochead *head;
+	struct sctpvtaghead *chain;
+	struct sctp_tagblock *twait_block;
+	struct sctp_tcb *stcb;
+
+	int i;
+	SCTP_INP_INFO_WLOCK();
+	chain = &sctppcbinfo.vtag_timewait[(tag % SCTP_STACK_VTAG_HASH_SIZE)];
+	/* First is the vtag in use ? */
+
+	head = &sctppcbinfo.sctp_asochash[SCTP_PCBHASH_ASOC(tag,
+	    sctppcbinfo.hashasocmark)];
+	if (head == NULL) {
+		SCTP_INP_INFO_WUNLOCK();
+		return (0);
+	}
+	LIST_FOREACH(stcb, head, sctp_asocs) {
+		if (stcb->asoc.my_vtag == tag) {
+			/* We should remove this if and
+			 * return 0 always if we want vtags
+			 * unique across all endpoints. For
+			 * now within a endpoint is ok.
+			 */
+ 			if (inp == stcb->sctp_ep) {
+				/* bad tag, in use */
+				SCTP_INP_INFO_WUNLOCK();
+				return (0);
+			}
+		}
+	}
+	if (!LIST_EMPTY(chain)) {
+		/*
+		 * Block(s) are present, lets see if we have this tag in
+		 * the list
+		 */
+		LIST_FOREACH(twait_block, chain, sctp_nxt_tagblock) {
+			for (i = 0; i < SCTP_NUMBER_IN_VTAG_BLOCK; i++) {
+				if (twait_block->vtag_block[i].v_tag == 0) {
+					/* not used */
+					continue;
+				} else if ((long)twait_block->vtag_block[i].tv_sec_at_expire >
+				    now->tv_sec) {
+					/* Audit expires this guy */
+					twait_block->vtag_block[i].tv_sec_at_expire = 0;
+					twait_block->vtag_block[i].v_tag = 0;
+				} else if (twait_block->vtag_block[i].v_tag ==
+				    tag) {
+					/* Bad tag, sorry :< */
+					SCTP_INP_INFO_WUNLOCK();
+					return (0);
+				}
+			}
+		}
+	}
+	/* Not found, ok to use the tag */
+	SCTP_INP_INFO_WUNLOCK();
+	return (1);
+}
+
+
+/*
+ * Delete the address from the endpoint local address list
+ * Lookup using a sockaddr address (ie. not an ifaddr)
+ */
+int
+sctp_del_local_addr_ep_sa(struct sctp_inpcb *inp, struct sockaddr *sa)
+{
+	struct sctp_laddr *laddr;
+	struct sockaddr *l_sa;
+	int found = 0;
+	/* Here is another function I cannot find a
+	 * caller for. As such we SHOULD delete it
+	 * if we have no users. If we find a user that
+	 * user MUST have the INP locked.
+	 *
+	 */
+
+	if (inp->sctp_flags & SCTP_PCB_FLAGS_BOUNDALL) {
+		/* You are already bound to all. You have it already */
+		return (EINVAL);
+	}
+
+	LIST_FOREACH(laddr, &inp->sctp_addr_list, sctp_nxt_addr) {
+		/* make sure the address exists */
+		if (laddr->ifa == NULL)
+			continue;
+		if (laddr->ifa->ifa_addr == NULL)
+			continue;
+
+		l_sa = laddr->ifa->ifa_addr;
+		if (l_sa->sa_family == AF_INET6) {
+			/* IPv6 address */
+			struct sockaddr_in6 *sin1, *sin2;
+			sin1 = (struct sockaddr_in6 *)l_sa;
+			sin2 = (struct sockaddr_in6 *)sa;
+			if (memcmp(&sin1->sin6_addr, &sin2->sin6_addr,
+			    sizeof(struct in6_addr)) == 0) {
+				/* matched */
+				found = 1;
+				break;
+			}
+		} else if (l_sa->sa_family == AF_INET) {
+			/* IPv4 address */
+			struct sockaddr_in *sin1, *sin2;
+			sin1 = (struct sockaddr_in *)l_sa;
+			sin2 = (struct sockaddr_in *)sa;
+			if (sin1->sin_addr.s_addr == sin2->sin_addr.s_addr) {
+				/* matched */
+				found = 1;
+				break;
+			}
+		} else {
+			/* invalid family */
+			return (-1);
+		}
+	}
+
+	if (found && inp->laddr_count < 2) {
+		/* can't delete unless there are at LEAST 2 addresses */
+		return (-1);
+	}
+
+	if (found && (inp->sctp_flags & SCTP_PCB_FLAGS_BOUNDALL) == 0) {
+		/*
+		 * remove it from the ep list, this should NOT be
+		 * done until its really gone from the interface list and
+		 * we won't be receiving more of these. Probably right
+		 * away. If we do allow a removal of an address from
+		 * an association (sub-set bind) than this should NOT
+		 * be called until the all ASCONF come back from this
+		 * association.
+		 */
+		sctp_remove_laddr(laddr);
+		return (0);
+	} else {
+		return (-1);
+	}
+}
+
+static void
+sctp_drain_mbufs(struct sctp_inpcb *inp, struct sctp_tcb *stcb)
+{
+	/*
+	 * We must hunt this association for MBUF's past the cumack
+	 * (i.e. out of order data that we can renege on).
+	 */
+	struct sctp_association *asoc;
+	struct sctp_tmit_chunk *chk, *nchk;
+	u_int32_t cumulative_tsn_p1, tsn;
+	int cnt, strmat, gap;
+	/* We look for anything larger than the cum-ack + 1 */
+
+	asoc = &stcb->asoc;
+	cumulative_tsn_p1 = asoc->cumulative_tsn + 1;
+	cnt = 0;
+	/* First look in the re-assembly queue */
+	chk = TAILQ_FIRST(&asoc->reasmqueue);
+	while (chk) {
+		/* Get the next one */
+		nchk = TAILQ_NEXT(chk, sctp_next);
+		if (compare_with_wrap(chk->rec.data.TSN_seq,
+		    cumulative_tsn_p1, MAX_TSN)) {
+			/* Yep it is above cum-ack */
+			cnt++;
+			tsn = chk->rec.data.TSN_seq;
+			if (tsn >= asoc->mapping_array_base_tsn) {
+				gap  = tsn - asoc->mapping_array_base_tsn;
+			} else {
+				gap = (MAX_TSN - asoc->mapping_array_base_tsn) +
+				    tsn + 1;
+			}
+			asoc->size_on_reasm_queue -= chk->send_size;
+			asoc->cnt_on_reasm_queue--;
+			SCTP_UNSET_TSN_PRESENT(asoc->mapping_array, gap);
+			TAILQ_REMOVE(&asoc->reasmqueue, chk, sctp_next);
+			if (chk->data) {
+				sctp_m_freem(chk->data);
+				chk->data = NULL;
+			}
+			SCTP_ZONE_FREE(sctppcbinfo.ipi_zone_chunk, chk);
+			sctppcbinfo.ipi_count_chunk--;
+			if ((int)sctppcbinfo.ipi_count_chunk < 0) {
+				panic("Chunk count is negative");
+			}
+			sctppcbinfo.ipi_gencnt_chunk++;
+		}
+		chk = nchk;
+	}
+	/* Ok that was fun, now we will drain all the inbound streams? */
+	for (strmat = 0; strmat < asoc->streamincnt; strmat++) {
+		chk = TAILQ_FIRST(&asoc->strmin[strmat].inqueue);
+		while (chk) {
+			nchk = TAILQ_NEXT(chk, sctp_next);
+			if (compare_with_wrap(chk->rec.data.TSN_seq,
+			    cumulative_tsn_p1, MAX_TSN)) {
+				/* Yep it is above cum-ack */
+				cnt++;
+				tsn = chk->rec.data.TSN_seq;
+				if (tsn >= asoc->mapping_array_base_tsn) {
+					gap = tsn -
+					    asoc->mapping_array_base_tsn;
+				} else {
+					gap = (MAX_TSN -
+					    asoc->mapping_array_base_tsn) +
+					    tsn + 1;
+				}
+				asoc->size_on_all_streams -= chk->send_size;
+				asoc->cnt_on_all_streams--;
+
+				SCTP_UNSET_TSN_PRESENT(asoc->mapping_array,
+				    gap);
+				TAILQ_REMOVE(&asoc->strmin[strmat].inqueue,
+				    chk, sctp_next);
+				if (chk->data) {
+					sctp_m_freem(chk->data);
+					chk->data = NULL;
+				}
+				SCTP_ZONE_FREE(sctppcbinfo.ipi_zone_chunk, chk);
+				sctppcbinfo.ipi_count_chunk--;
+				if ((int)sctppcbinfo.ipi_count_chunk < 0) {
+					panic("Chunk count is negative");
+				}
+				sctppcbinfo.ipi_gencnt_chunk++;
+			}
+			chk = nchk;
+		}
+	}
+	/*
+	 * Question, should we go through the delivery queue?
+	 * The only reason things are on here is the app not reading OR a
+	 * p-d-api up. An attacker COULD send enough in to initiate the
+	 * PD-API and then send a bunch of stuff to other streams... these
+	 * would wind up on the delivery queue.. and then we would not get
+	 * to them. But in order to do this I then have to back-track and
+	 * un-deliver sequence numbers in streams.. el-yucko. I think for
+	 * now we will NOT look at the delivery queue and leave it to be
+	 * something to consider later. An alternative would be to abort
+	 * the P-D-API with a notification and then deliver the data....
+	 * Or another method might be to keep track of how many times the
+	 * situation occurs and if we see a possible attack underway just
+	 * abort the association.
+	 */
+#ifdef SCTP_DEBUG
+	if (sctp_debug_on & SCTP_DEBUG_PCB1) {
+		if (cnt) {
+			printf("Freed %d chunks from reneg harvest\n", cnt);
+		}
+	}
+#endif /* SCTP_DEBUG */
+
+	/*
+	 * Another issue, in un-setting the TSN's in the mapping array we
+	 * DID NOT adjust the higest_tsn marker.  This will cause one of
+	 * two things to occur. It may cause us to do extra work in checking
+	 * for our mapping array movement. More importantly it may cause us
+	 * to SACK every datagram. This may not be a bad thing though since
+	 * we will recover once we get our cum-ack above and all this stuff
+	 * we dumped recovered.
+	 */
+}
+
+void
+sctp_drain()
+{
+	/*
+	 * We must walk the PCB lists for ALL associations here. The system
+	 * is LOW on MBUF's and needs help. This is where reneging will
+	 * occur. We really hope this does NOT happen!
+	 */
+	struct sctp_inpcb *inp;
+	struct sctp_tcb *stcb;
+
+	SCTP_INP_INFO_RLOCK();
+	LIST_FOREACH(inp, &sctppcbinfo.listhead, sctp_list) {
+		/* For each endpoint */
+		SCTP_INP_RLOCK(inp);
+		LIST_FOREACH(stcb, &inp->sctp_asoc_list, sctp_tcblist) {
+			/* For each association */
+			SCTP_TCB_LOCK(stcb);
+			sctp_drain_mbufs(inp, stcb);
+			SCTP_TCB_UNLOCK(stcb);
+		}
+		SCTP_INP_RUNLOCK(inp);
+	}
+	SCTP_INP_INFO_RUNLOCK();
+}
+
+int
+sctp_add_to_socket_q(struct sctp_inpcb *inp, struct sctp_tcb *stcb)
+{
+	struct sctp_socket_q_list *sq;
+
+	/* write lock on INP assumed */
+	if ((inp == NULL) || (stcb == NULL)) {
+		/* I am paranoid */
+		return (0);
+	}
+	sq = (struct sctp_socket_q_list *)SCTP_ZONE_GET(
+	    sctppcbinfo.ipi_zone_sockq);
+	if (sq == NULL) {
+		/* out of sq structs */
+		return (0);
+	}
+	sctppcbinfo.ipi_count_sockq++;
+	sctppcbinfo.ipi_gencnt_sockq++;
+	if (stcb)
+		stcb->asoc.cnt_msg_on_sb++;
+	sq->tcb = stcb;
+	TAILQ_INSERT_TAIL(&inp->sctp_queue_list, sq, next_sq);
+	return (1);
+}
+
+
+struct sctp_tcb *
+sctp_remove_from_socket_q(struct sctp_inpcb *inp)
+{
+	struct sctp_tcb *stcb = NULL;
+	struct sctp_socket_q_list *sq;
+
+	/* W-Lock on INP assumed held */
+	sq = TAILQ_FIRST(&inp->sctp_queue_list);
+	if (sq == NULL)
+		return (NULL);
+
+	stcb = sq->tcb;
+	TAILQ_REMOVE(&inp->sctp_queue_list, sq, next_sq);
+	SCTP_ZONE_FREE(sctppcbinfo.ipi_zone_sockq, sq);
+	sctppcbinfo.ipi_count_sockq--;
+	sctppcbinfo.ipi_gencnt_sockq++;
+	if (stcb) {
+		stcb->asoc.cnt_msg_on_sb--;
+	}
+	return (stcb);
+}
+
+int
+sctp_initiate_iterator(asoc_func af, uint32_t pcb_state, uint32_t asoc_state,
+		       void *argp, uint32_t argi, end_func ef,
+		       struct sctp_inpcb *s_inp)
+{
+	struct sctp_iterator *it=NULL;
+
+	if (af == NULL) {
+		return (-1);
+	}
+	MALLOC(it, struct sctp_iterator *, sizeof(struct sctp_iterator), M_PCB,
+	       M_WAITOK);
+	if (it == NULL) {
+		return (ENOMEM);
+	}
+	memset(it, 0, sizeof(*it));
+	it->function_toapply = af;
+	it->function_atend = ef;
+	it->pointer = argp;
+	it->val = argi;
+	it->pcb_flags = pcb_state;
+	it->asoc_state = asoc_state;
+	if (s_inp) {
+		it->inp = s_inp;
+		it->iterator_flags = SCTP_ITERATOR_DO_SINGLE_INP;
+	} else {
+		SCTP_INP_INFO_RLOCK();
+		it->inp = LIST_FIRST(&sctppcbinfo.listhead);
+		SCTP_INP_INFO_RUNLOCK();
+		it->iterator_flags = SCTP_ITERATOR_DO_ALL_INP;
+
+	}
+	/* Init the timer */
+#if defined(__FreeBSD__) && __FreeBSD_version >= 500000
+	callout_init(&it->tmr.timer, 0);
+#else
+	callout_init(&it->tmr.timer);
+#endif
+	/* add to the list of all iterators */
+	SCTP_INP_INFO_WLOCK();
+	LIST_INSERT_HEAD(&sctppcbinfo.iteratorhead, it, sctp_nxt_itr);
+	SCTP_INP_INFO_WUNLOCK();
+	crit_enter();
+	sctp_iterator_timer(it);
+	crit_exit();
+	return (0);
+}
+
+
+/*
+ * Callout/Timer routines for OS that doesn't have them
+ */
+#ifdef _SCTP_NEEDS_CALLOUT_
+#ifndef __APPLE__
+extern int ticks;
+#endif
+
+void
+callout_init(struct callout *c)
+{
+	bzero(c, sizeof(*c));
+}
+
+void
+callout_reset(struct callout *c, int to_ticks, void (*ftn)(void *), void *arg)
+{
+	crit_enter();
+	if (c->c_flags & CALLOUT_PENDING)
+		callout_stop(c);
+
+	/*
+	 * We could spl down here and back up at the TAILQ_INSERT_TAIL,
+	 * but there's no point since doing this setup doesn't take much
+	 * time.
+	 */
+	if (to_ticks <= 0)
+		to_ticks = 1;
+
+	c->c_arg = arg;
+	c->c_flags = (CALLOUT_ACTIVE | CALLOUT_PENDING);
+	c->c_func = ftn;
+#ifdef __APPLE__
+	c->c_time = to_ticks;	/* just store the requested timeout */
+	timeout(ftn, arg, to_ticks);
+#else
+	c->c_time = ticks + to_ticks;
+	TAILQ_INSERT_TAIL(&sctppcbinfo.callqueue, c, tqe);
+#endif
+	crit_exit();
+}
+
+int
+callout_stop(struct callout *c)
+{
+	crit_enter();
+	/*
+	 * Don't attempt to delete a callout that's not on the queue.
+	 */
+	if (!(c->c_flags & CALLOUT_PENDING)) {
+		c->c_flags &= ~CALLOUT_ACTIVE;
+		crit_exit();
+		return (0);
+	}
+	c->c_flags &= ~(CALLOUT_ACTIVE | CALLOUT_PENDING| CALLOUT_FIRED);
+#ifdef __APPLE__
+/*	thread_call_cancel(c->c_call); */
+	untimeout(c->c_func, c->c_arg);
+#else
+	TAILQ_REMOVE(&sctppcbinfo.callqueue, c, tqe);
+	c->c_func = NULL;
+#endif
+	crit_exit();
+	return (1);
+}
+
+#if !defined(__APPLE__)
+void
+sctp_fasttim(void)
+{
+	struct callout *c, *n;
+	struct calloutlist locallist;
+	int inited = 0;
+
+	crit_enter();
+	/* run through and subtract and mark all callouts */
+	c = TAILQ_FIRST(&sctppcbinfo.callqueue);
+	while (c) {
+		n = TAILQ_NEXT(c, tqe);
+		if (c->c_time <= ticks) {
+			c->c_flags |= CALLOUT_FIRED;
+			c->c_time = 0;
+			TAILQ_REMOVE(&sctppcbinfo.callqueue, c, tqe);
+			if (inited == 0) {
+				TAILQ_INIT(&locallist);
+				inited = 1;
+			}
+			/* move off of main list */
+			TAILQ_INSERT_TAIL(&locallist, c, tqe);
+		}
+		c = n;
+	}
+	/* Now all the ones on the locallist must be called */
+	if (inited) {
+		c = TAILQ_FIRST(&locallist);
+		while (c) {
+			/* remove it */
+			TAILQ_REMOVE(&locallist, c, tqe);
+			/* now validate that it did not get canceled */
+			if (c->c_flags & CALLOUT_FIRED) {
+				c->c_flags &= ~CALLOUT_PENDING;
+				crit_exit();
+				(*c->c_func)(c->c_arg);
+				crit_enter();
+			}
+			c = TAILQ_FIRST(&locallist);
+		}
+	}
+	crit_exit();
+}
+#endif
+#endif /* _SCTP_NEEDS_CALLOUT_ */
Index: sys/netinet/sctp_pcb.h
===================================================================
RCS file: sys/netinet/sctp_pcb.h
diff -N sys/netinet/sctp_pcb.h
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ sys/netinet/sctp_pcb.h	18 Jun 2005 16:05:50 -0000
@@ -0,0 +1,706 @@
+/*	$KAME: sctp_pcb.h,v 1.19 2004/08/17 06:28:02 t-momose Exp $	*/
+
+#ifndef __sctp_pcb_h__
+#define __sctp_pcb_h__
+
+/*
+ * Copyright (c) 2001, 2002, 2003, 2004 Cisco Systems, Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *      This product includes software developed by Cisco Systems, Inc.
+ * 4. Neither the name of the project nor the names of its contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY CISCO SYSTEMS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL CISCO SYSTEMS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+/*
+ * We must have V6 so the size of the proto can be calculated. Otherwise
+ * we would not allocate enough for Net/Open BSD :-<
+ */
+#if defined(__FreeBSD__) && __FreeBSD_version > 500000
+#include <net/pfil.h>
+#endif
+#include <net/if.h>
+#ifdef __FreeBSD__
+#include <net/if_var.h>
+#endif
+#include <netinet/ip6.h>
+#include <netinet6/ip6_var.h>
+#include <netinet6/ip6protosw.h>
+#include <netinet6/in6_var.h>
+#if defined(__OpenBSD__)
+#include <netinet/in_pcb.h>
+#else
+#include <netinet6/in6_pcb.h>
+#endif
+
+#if defined(__OpenBSD__) || defined(__FreeBSD__)
+#ifndef in6pcb
+#define in6pcb		inpcb
+#endif
+#endif
+
+#include <netinet/sctp.h>
+#include <netinet/sctp_constants.h>
+
+LIST_HEAD(sctppcbhead, sctp_inpcb);
+LIST_HEAD(sctpasochead, sctp_tcb);
+TAILQ_HEAD(sctpsocketq, sctp_socket_q_list);
+LIST_HEAD(sctpladdr, sctp_laddr);
+LIST_HEAD(sctpvtaghead, sctp_tagblock);
+
+#include <netinet/sctp_structs.h>
+#include <netinet/sctp_uio.h>
+
+/*
+ * PCB flags
+ */
+#define SCTP_PCB_FLAGS_UDPTYPE		0x00000001
+#define SCTP_PCB_FLAGS_TCPTYPE		0x00000002
+#define SCTP_PCB_FLAGS_BOUNDALL		0x00000004
+#define SCTP_PCB_FLAGS_ACCEPTING	0x00000008
+#define SCTP_PCB_FLAGS_UNBOUND		0x00000010
+#define SCTP_PCB_FLAGS_DO_ASCONF	0x00000020
+#define SCTP_PCB_FLAGS_AUTO_ASCONF	0x00000040
+/* socket options */
+#define SCTP_PCB_FLAGS_NODELAY		0x00000100
+#define SCTP_PCB_FLAGS_AUTOCLOSE	0x00000200
+#define SCTP_PCB_FLAGS_RECVDATAIOEVNT	0x00000400
+#define SCTP_PCB_FLAGS_RECVASSOCEVNT	0x00000800
+#define SCTP_PCB_FLAGS_RECVPADDREVNT	0x00001000
+#define SCTP_PCB_FLAGS_RECVPEERERR	0x00002000
+#define SCTP_PCB_FLAGS_RECVSENDFAILEVNT	0x00004000
+#define SCTP_PCB_FLAGS_RECVSHUTDOWNEVNT	0x00008000
+#define SCTP_PCB_FLAGS_ADAPTIONEVNT	0x00010000
+#define SCTP_PCB_FLAGS_PDAPIEVNT	0x00020000
+#define SCTP_PCB_FLAGS_STREAM_RESETEVNT 0x00040000
+#define SCTP_PCB_FLAGS_NO_FRAGMENT	0x00080000
+/* TCP model support */
+#define SCTP_PCB_FLAGS_CONNECTED	0x00100000
+#define SCTP_PCB_FLAGS_IN_TCPPOOL	0x00200000
+#define SCTP_PCB_FLAGS_DONT_WAKE	0x00400000
+#define SCTP_PCB_FLAGS_WAKEOUTPUT	0x00800000
+#define SCTP_PCB_FLAGS_WAKEINPUT	0x01000000
+#define SCTP_PCB_FLAGS_BOUND_V6		0x02000000
+#define SCTP_PCB_FLAGS_NEEDS_MAPPED_V4	0x04000000
+#define SCTP_PCB_FLAGS_BLOCKING_IO	0x08000000
+#define SCTP_PCB_FLAGS_SOCKET_GONE	0x10000000
+#define SCTP_PCB_FLAGS_SOCKET_ALLGONE	0x20000000
+
+/* flags to copy to new PCB */
+#define SCTP_PCB_COPY_FLAGS		0x0707ff64
+
+#define SCTP_PCBHASH_ALLADDR(port, mask) (port & mask)
+#define SCTP_PCBHASH_ASOC(tag, mask) (tag & mask)
+
+struct sctp_laddr {
+	LIST_ENTRY(sctp_laddr) sctp_nxt_addr;	/* next in list */
+	struct ifaddr *ifa;
+};
+
+struct sctp_timewait {
+	uint32_t tv_sec_at_expire;	/* the seconds from boot to expire */
+	uint32_t v_tag;		/* the vtag that can not be reused */
+};
+
+struct sctp_tagblock {
+        LIST_ENTRY(sctp_tagblock) sctp_nxt_tagblock;
+	struct sctp_timewait vtag_block[SCTP_NUMBER_IN_VTAG_BLOCK];
+};
+
+struct sctp_epinfo {
+	struct sctpasochead *sctp_asochash;
+	u_long hashasocmark;
+
+	struct sctppcbhead *sctp_ephash;
+	u_long hashmark;
+
+	/*
+	 * The TCP model represents a substantial overhead in that we get
+	 * an additional hash table to keep explicit connections in. The
+	 * listening TCP endpoint will exist in the usual ephash above and
+	 * accept only INIT's. It will be incapable of sending off an INIT.
+	 * When a dg arrives we must look in the normal ephash. If we find
+	 * a TCP endpoint that will tell us to go to the specific endpoint
+	 * hash and re-hash to find the right assoc/socket. If we find a
+	 * UDP model socket we then must complete the lookup. If this fails,
+	 * i.e. no association can be found then we must continue to see if
+	 * a sctp_peeloff()'d socket is in the tcpephash (a spun off socket
+	 * acts like a TCP model connected socket).
+	 */
+	struct sctppcbhead *sctp_tcpephash;
+	u_long hashtcpmark;
+	uint32_t hashtblsize;
+
+	struct sctppcbhead listhead;
+
+	struct sctpiterators iteratorhead;
+
+	/* ep zone info */
+#if defined(__FreeBSD__) || defined(__APPLE__) || defined(__DragonFly__)
+#if __FreeBSD_version >= 500000
+	struct uma_zone *ipi_zone_ep;
+	struct uma_zone *ipi_zone_asoc;
+	struct uma_zone *ipi_zone_laddr;
+	struct uma_zone *ipi_zone_net;
+	struct uma_zone *ipi_zone_chunk;
+	struct uma_zone *ipi_zone_sockq;
+#else
+	struct vm_zone *ipi_zone_ep;
+	struct vm_zone *ipi_zone_asoc;
+	struct vm_zone *ipi_zone_laddr;
+	struct vm_zone *ipi_zone_net;
+	struct vm_zone *ipi_zone_chunk;
+	struct vm_zone *ipi_zone_sockq;
+#endif
+#endif
+#if defined(__NetBSD__) || defined(__OpenBSD__)
+	struct pool ipi_zone_ep;
+	struct pool ipi_zone_asoc;
+	struct pool ipi_zone_laddr;
+	struct pool ipi_zone_net;
+	struct pool ipi_zone_chunk;
+	struct pool ipi_zone_sockq;
+#endif
+
+#if defined(__FreeBSD__) && __FreeBSD_version >= 503000
+	struct mtx ipi_ep_mtx;
+	struct mtx it_mtx;
+#endif
+	u_int ipi_count_ep;
+	u_quad_t ipi_gencnt_ep;
+
+	/* assoc/tcb zone info */
+	u_int ipi_count_asoc;
+	u_quad_t ipi_gencnt_asoc;
+
+	/* local addrlist zone info */
+	u_int ipi_count_laddr;
+	u_quad_t ipi_gencnt_laddr;
+
+	/* remote addrlist zone info */
+	u_int ipi_count_raddr;
+	u_quad_t ipi_gencnt_raddr;
+
+	/* chunk structure list for output */
+	u_int ipi_count_chunk;
+	u_quad_t ipi_gencnt_chunk;
+
+	/* socket queue zone info */
+	u_int ipi_count_sockq;
+	u_quad_t ipi_gencnt_sockq;
+
+	struct sctpvtaghead vtag_timewait[SCTP_STACK_VTAG_HASH_SIZE];
+
+#ifdef _SCTP_NEEDS_CALLOUT_
+	struct calloutlist callqueue;
+#endif /* _SCTP_NEEDS_CALLOUT_ */
+
+	uint32_t mbuf_track;
+
+	/* for port allocations */
+	uint16_t lastport;
+	uint16_t lastlow;
+	uint16_t lasthi;
+
+};
+
+extern uint32_t sctp_pegs[SCTP_NUMBER_OF_PEGS];
+/*
+ * Here we have all the relevant information for each SCTP entity created.
+ * We will need to modify this as approprate. We also need to figure out
+ * how to access /dev/random.
+ */
+struct sctp_pcb {
+	unsigned int time_of_secret_change; /* number of seconds from timeval.tv_sec */
+	uint32_t secret_key[SCTP_HOW_MANY_SECRETS][SCTP_NUMBER_OF_SECRETS];
+	unsigned int size_of_a_cookie;
+
+	unsigned int sctp_timeoutticks[SCTP_NUM_TMRS];
+	unsigned int sctp_minrto;
+	unsigned int sctp_maxrto;
+	unsigned int initial_rto;
+
+	int initial_init_rto_max;
+
+	uint32_t sctp_sws_sender;
+	uint32_t sctp_sws_receiver;
+
+	/* various thresholds */
+	/* Max times I will init at a guy */
+	uint16_t max_init_times;
+
+	/* Max times I will send before we consider someone dead */
+	uint16_t max_send_times;
+
+	uint16_t def_net_failure;
+
+	/* number of streams to pre-open on a association */
+	uint16_t pre_open_stream_count;
+	uint16_t max_open_streams_intome;
+
+	/* random number generator */
+	uint32_t random_counter;
+	uint8_t random_numbers[SCTP_SIGNATURE_ALOC_SIZE];
+	uint8_t random_store[SCTP_SIGNATURE_ALOC_SIZE];
+
+	/*
+	 * This timer is kept running per endpoint.  When it fires it
+	 * will change the secret key.  The default is once a hour
+	 */
+	struct sctp_timer signature_change;
+	int def_cookie_life;
+	/* defaults to 0 */
+	int auto_close_time;
+	uint32_t initial_sequence_debug;
+	uint32_t adaption_layer_indicator;
+	char store_at;
+	uint8_t max_burst;
+	char current_secret_number;
+	char last_secret_number;
+};
+
+#ifndef SCTP_ALIGNMENT
+#define SCTP_ALIGNMENT 32
+#endif
+
+#ifndef SCTP_ALIGNM1
+#define SCTP_ALIGNM1 (SCTP_ALIGNMENT-1)
+#endif
+
+#define sctp_lport ip_inp.inp.inp_lport
+
+struct sctp_socket_q_list {
+	struct sctp_tcb *tcb;
+	TAILQ_ENTRY(sctp_socket_q_list) next_sq;
+};
+
+struct sctp_inpcb {
+	/*
+	 * put an inpcb in front of it all, kind of a waste but we need
+	 * to for compatability with all the other stuff.
+	 */
+	union {
+		struct inpcb inp;
+		char align[(sizeof(struct in6pcb) + SCTP_ALIGNM1) &
+			  ~SCTP_ALIGNM1];
+	} ip_inp;
+	LIST_ENTRY(sctp_inpcb) sctp_list;	/* lists all endpoints */
+	/* hash of all endpoints for model */
+	LIST_ENTRY(sctp_inpcb) sctp_hash;
+
+	/* count of local addresses bound, 0 if bound all */
+	int laddr_count;
+	/* list of addrs in use by the EP */
+	struct sctpladdr sctp_addr_list;
+	/* used for source address selection rotation */
+	struct sctp_laddr *next_addr_touse;
+	struct ifnet *next_ifn_touse;
+	/* back pointer to our socket */
+	struct socket *sctp_socket;
+	uint32_t sctp_flags;			/* flag set */
+	struct sctp_pcb sctp_ep;		/* SCTP ep data */
+	/* head of the hash of all associations */
+	struct sctpasochead *sctp_tcbhash;
+	u_long sctp_hashmark;
+	/* head of the list of all associations */
+	struct sctpasochead sctp_asoc_list;
+	/* queue of TCB's waiting to stuff data up the socket */
+	struct sctpsocketq sctp_queue_list;
+	void *sctp_tcb_at_block;
+	struct sctp_iterator *inp_starting_point_for_iterator;
+	int  error_on_block;
+	uint32_t sctp_frag_point;
+	uint32_t sctp_vtag_first;
+	struct mbuf *pkt, *pkt_last, *sb_last_mpkt;
+	struct mbuf *control;
+#if !(defined(__FreeBSD__) || defined(__APPLE__) || defined(__DragonFly__))
+#ifndef INP_IPV6
+#define INP_IPV6	0x1
+#endif
+#ifndef INP_IPV4
+#define INP_IPV4	0x2
+#endif
+	u_char inp_vflag;
+	u_char inp_ip_ttl;
+	u_char inp_ip_tos;
+	u_char inp_ip_resv;
+#endif
+#if defined(__FreeBSD__) && __FreeBSD_version >= 503000
+	struct mtx inp_mtx;
+	struct mtx inp_create_mtx;
+	u_int32_t refcount;
+#endif
+};
+
+struct sctp_tcb {
+	struct socket *sctp_socket;		/* back pointer to socket */
+	struct sctp_inpcb *sctp_ep;		/* back pointer to ep */
+	LIST_ENTRY(sctp_tcb) sctp_tcbhash;	/* next link in hash table */
+	LIST_ENTRY(sctp_tcb) sctp_tcblist;	/* list of all of the TCB's */
+	LIST_ENTRY(sctp_tcb) sctp_asocs;
+	struct sctp_association asoc;
+	uint16_t rport;			/* remote port in network format */
+	uint16_t resv;
+#if defined(__FreeBSD__) && __FreeBSD_version >= 503000
+	struct mtx tcb_mtx;
+#endif
+};
+
+#if defined(__FreeBSD__) && __FreeBSD_version >= 503000
+
+/* General locking concepts:
+ * The goal of our locking is to of course provide
+ * consistency and yet minimize overhead. We will
+ * attempt to use non-recursive locks which are supposed
+ * to be quite inexpensive. Now in order to do this the goal
+ * is that most functions are not aware of locking. Once we
+ * have a TCB we lock it and unlock when we are through. This
+ * means that the TCB lock is kind-of a "global" lock when
+ * working on an association. Caution must be used when
+ * asserting a TCB_LOCK since if we recurse we deadlock.
+ *
+ * Most other locks (INP and INFO) attempt to localize
+ * the locking i.e. we try to contain the lock and
+ * unlock within the function that needs to lock it. This
+ * sometimes mean we do extra locks and unlocks and loose
+ * a bit of efficency, but if the performance statements about
+ * non-recursive locks are true this should not be a problem.
+ * One issue that arises with this only lock when needed
+ * is that if an implicit association setup is done we
+ * have a problem. If at the time I lookup an association
+ * I have NULL in the tcb return, by the time I call to
+ * create the association some other processor could
+ * have created it. This is what the CREATE lock on
+ * the endpoint. Places where we will be implicitly
+ * creating the association OR just creating an association
+ * (the connect call) will assert the CREATE_INP lock. This
+ * will assure us that during all the lookup of INP and INFO
+ * if another creator is also locking/looking up we can
+ * gate the two to synchronize. So the CREATE_INP lock is
+ * also another one we must use extreme caution in locking
+ * to make sure we don't hit a re-entrancy issue.
+ *
+ * For non FreeBSD 5.x and above we provide a bunch
+ * of EMPTY lock macro's so we can blatantly put locks
+ * everywhere and they reduce to nothing on NetBSD/OpenBSD
+ * and FreeBSD 4.x
+ *
+ */
+
+
+/* When working with the global SCTP lists we lock and unlock
+ * the INP_INFO lock. So when we go to lookup an association
+ * we will want to do a SCTP_INP_INFO_RLOCK() and then when
+ * we want to add a new association to the sctppcbinfo list's
+ * we will do a SCTP_INP_INFO_WLOCK().
+ */
+
+/*
+ * FIX ME, all locks right now have a
+ * recursive check/panic to validate that I
+ * don't have any lock recursion going on.
+ */
+
+#define SCTP_INP_INFO_LOCK_INIT() \
+        mtx_init(&sctppcbinfo.ipi_ep_mtx, "sctp", "inp_info", MTX_DEF)
+
+#ifdef xyzzy
+#define SCTP_INP_INFO_RLOCK()	do { 					\
+             if (mtx_owned(&sctppcbinfo.ipi_ep_mtx))                     \
+		panic("INP INFO Recursive Lock-R");                     \
+             mtx_lock(&sctppcbinfo.ipi_ep_mtx);                         \
+} while (0)
+
+#define SCTP_INP_INFO_WLOCK()	do { 					\
+             if (mtx_owned(&sctppcbinfo.ipi_ep_mtx))                     \
+		panic("INP INFO Recursive Lock-W");                     \
+             mtx_lock(&sctppcbinfo.ipi_ep_mtx);                         \
+} while (0)
+
+#else
+
+void SCTP_INP_INFO_RLOCK(void);
+void SCTP_INP_INFO_WLOCK(void);
+
+#endif
+
+#define SCTP_INP_INFO_RUNLOCK()		mtx_unlock(&sctppcbinfo.ipi_ep_mtx)
+#define SCTP_INP_INFO_WUNLOCK()		mtx_unlock(&sctppcbinfo.ipi_ep_mtx)
+
+/* The INP locks we will use for locking an SCTP endpoint, so for
+ * example if we want to change something at the endpoint level for
+ * example random_store or cookie secrets we lock the INP level.
+ */
+#define SCTP_INP_LOCK_INIT(_inp) \
+	mtx_init(&(_inp)->inp_mtx, "sctp", "inp", MTX_DEF | MTX_DUPOK)
+
+#define SCTP_ASOC_CREATE_LOCK_INIT(_inp) \
+	mtx_init(&(_inp)->inp_create_mtx, "sctp", "inp_create", \
+		 MTX_DEF | MTX_DUPOK)
+
+#define SCTP_INP_LOCK_DESTROY(_inp)	mtx_destroy(&(_inp)->inp_mtx)
+#define SCTP_ASOC_CREATE_LOCK_DESTROY(_inp)	mtx_destroy(&(_inp)->inp_create_mtx)
+
+#ifdef xyzzy
+#define SCTP_INP_RLOCK(_inp)	do { 					\
+        struct sctp_tcb *xx_stcb;					\
+        xx_stcb = LIST_FIRST(&_inp->sctp_asoc_list);                    \
+        if (xx_stcb)                                                     \
+              if (mtx_owned(&(xx_stcb)->tcb_mtx))                        \
+                     panic("I own TCB lock?");                          \
+        if (mtx_owned(&(_inp)->inp_mtx))                                 \
+		panic("INP Recursive Lock-R");                          \
+        mtx_lock(&(_inp)->inp_mtx);                                     \
+} while (0)
+
+#define SCTP_INP_WLOCK(_inp)	do { 					\
+        struct sctp_tcb *xx_stcb;					\
+        xx_stcb = LIST_FIRST(&_inp->sctp_asoc_list);                    \
+        if (xx_stcb)                                                     \
+              if (mtx_owned(&(xx_stcb)->tcb_mtx))                        \
+                     panic("I own TCB lock?");                          \
+        if (mtx_owned(&(_inp)->inp_mtx))                                 \
+		panic("INP Recursive Lock-W");                          \
+        mtx_lock(&(_inp)->inp_mtx);                                     \
+} while (0)
+
+#else
+void SCTP_INP_RLOCK(struct sctp_inpcb *);
+void SCTP_INP_WLOCK(struct sctp_inpcb *);
+
+#endif
+
+
+#define SCTP_INP_INCR_REF(_inp)        _inp->refcount++
+
+#define SCTP_INP_DECR_REF(_inp)         do {                                 \
+                                             if (_inp->refcount > 0)          \
+                                                  _inp->refcount--;          \
+                                             else                            \
+                                                  panic("bad inp refcount"); \
+}while (0)
+
+#define SCTP_ASOC_CREATE_LOCK(_inp)  do {				\
+        if (mtx_owned(&(_inp)->inp_create_mtx))                          \
+		panic("INP Recursive CREATE");                          \
+        mtx_lock(&(_inp)->inp_create_mtx);                              \
+} while (0)
+
+#define SCTP_INP_RUNLOCK(_inp)		mtx_unlock(&(_inp)->inp_mtx)
+#define SCTP_INP_WUNLOCK(_inp)		mtx_unlock(&(_inp)->inp_mtx)
+#define SCTP_ASOC_CREATE_UNLOCK(_inp)	mtx_unlock(&(_inp)->inp_create_mtx)
+
+/* For the majority of things (once we have found the association) we
+ * will lock the actual association mutex. This will protect all
+ * the assoiciation level queues and streams and such. We will
+ * need to lock the socket layer when we stuff data up into
+ * the receiving sb_mb. I.e. we will need to do an extra
+ * SOCKBUF_LOCK(&so->so_rcv) even though the association is
+ * locked.
+ */
+
+#define SCTP_TCB_LOCK_INIT(_tcb) \
+	mtx_init(&(_tcb)->tcb_mtx, "sctp", "tcb", MTX_DEF | MTX_DUPOK)
+#define SCTP_TCB_LOCK_DESTROY(_tcb)	mtx_destroy(&(_tcb)->tcb_mtx)
+#define SCTP_TCB_LOCK(_tcb)  do {					\
+        if (!mtx_owned(&(_tcb->sctp_ep->inp_mtx)))                       \
+		panic("TCB locking and no INP lock");                   \
+        if (mtx_owned(&(_tcb)->tcb_mtx))                                 \
+		panic("TCB Lock-recursive");                            \
+	mtx_lock(&(_tcb)->tcb_mtx);                                     \
+} while (0)
+#define SCTP_TCB_UNLOCK(_tcb)		mtx_unlock(&(_tcb)->tcb_mtx)
+
+#define SCTP_ITERATOR_LOCK_INIT() \
+        mtx_init(&sctppcbinfo.it_mtx, "sctp", "iterator", MTX_DEF)
+#define SCTP_ITERATOR_LOCK()  do {					\
+        if (mtx_owned(&sctppcbinfo.it_mtx))                              \
+		panic("Iterator Lock");                                 \
+	mtx_lock(&sctppcbinfo.it_mtx);                                  \
+} while (0)
+
+#define SCTP_ITERATOR_UNLOCK()	        mtx_unlock(&sctppcbinfo.it_mtx)
+#define SCTP_ITERATOR_LOCK_DESTROY()	mtx_destroy(&sctppcbinfo.it_mtx)
+#else
+
+/* Empty Lock declarations for all other
+ * platforms pre-process away to nothing.
+ */
+
+/* Lock for INFO stuff */
+#define SCTP_INP_INFO_LOCK_INIT()
+#define SCTP_INP_INFO_RLOCK()
+#define SCTP_INP_INFO_RLOCK()
+#define SCTP_INP_INFO_WLOCK()
+
+#define SCTP_INP_INFO_RUNLOCK()
+#define SCTP_INP_INFO_WUNLOCK()
+/* Lock for INP */
+#define SCTP_INP_LOCK_INIT(_inp)
+#define SCTP_INP_LOCK_DESTROY(_inp)
+#define SCTP_INP_RLOCK(_inp)
+#define SCTP_INP_RUNLOCK(_inp)
+#define SCTP_INP_WLOCK(_inp)
+#define SCTP_INP_INCR_REF(_inp)
+#define SCTP_INP_DECR_REF(_inp)
+#define SCTP_INP_WUNLOCK(_inp)
+#define SCTP_ASOC_CREATE_LOCK_INIT(_inp)
+#define SCTP_ASOC_CREATE_LOCK_DESTROY(_inp)
+#define SCTP_ASOC_CREATE_LOCK(_inp)
+#define SCTP_ASOC_CREATE_UNLOCK(_inp)
+/* Lock for TCB */
+#define SCTP_TCB_LOCK_INIT(_tcb)
+#define SCTP_TCB_LOCK_DESTROY(_tcb)
+#define SCTP_TCB_LOCK(_tcb)
+#define SCTP_TCB_UNLOCK(_tcb)
+/* socket locks that are not here in other than 5.3 > FreeBSD*/
+#define SOCK_LOCK(_so)
+#define SOCK_UNLOCK(_so)
+#define SOCKBUF_LOCK(_so_buf)
+#define SOCKBUF_UNLOCK(_so_buf)
+/* iterator locks */
+#define SCTP_ITERATOR_LOCK_INIT()
+#define SCTP_ITERATOR_LOCK()
+#define SCTP_ITERATOR_UNLOCK()
+#define SCTP_ITERATOR_LOCK_DESTROY()
+#endif
+
+#if defined(_KERNEL) || (defined(__APPLE__) && defined(KERNEL))
+
+extern struct sctp_epinfo sctppcbinfo;
+extern int sctp_auto_asconf;
+
+int SCTP6_ARE_ADDR_EQUAL(struct in6_addr *a, struct in6_addr *b);
+
+void sctp_fill_pcbinfo(struct sctp_pcbinfo *);
+
+struct sctp_nets *sctp_findnet(struct sctp_tcb *, struct sockaddr *);
+
+struct sctp_inpcb *sctp_pcb_findep(struct sockaddr *, int, int);
+
+#if (defined(__FreeBSD__) && __FreeBSD_version >= 500000) || defined(__DragonFly__)
+int sctp_inpcb_bind(struct socket *, struct sockaddr *, struct thread *);
+#else
+int sctp_inpcb_bind(struct socket *, struct sockaddr *, struct proc *);
+#endif
+
+struct sctp_tcb *sctp_findassociation_addr(struct mbuf *, int, int,
+    struct sctphdr *, struct sctp_chunkhdr *, struct sctp_inpcb **,
+    struct sctp_nets **);
+
+struct sctp_tcb *sctp_findassociation_addr_sa(struct sockaddr *,
+	struct sockaddr *, struct sctp_inpcb **, struct sctp_nets **, int);
+
+void sctp_move_pcb_and_assoc(struct sctp_inpcb *, struct sctp_inpcb *,
+	struct sctp_tcb *);
+
+/*
+ * For this call ep_addr, the to is the destination endpoint address
+ * of the peer (relative to outbound). The from field is only used if
+ * the TCP model is enabled and helps distingush amongst the subset
+ * bound (non-boundall). The TCP model MAY change the actual ep field,
+ * this is why it is passed.
+ */
+struct sctp_tcb *sctp_findassociation_ep_addr(struct sctp_inpcb **,
+	struct sockaddr *, struct sctp_nets **, struct sockaddr *, struct sctp_tcb *);
+
+struct sctp_tcb *sctp_findassociation_ep_asocid(struct sctp_inpcb *, caddr_t);
+
+struct sctp_tcb *sctp_findassociation_ep_asconf(struct mbuf *, int, int,
+    struct sctphdr *, struct sctp_inpcb **, struct sctp_nets **);
+
+int sctp_inpcb_alloc(struct socket *);
+
+
+int sctp_is_address_on_local_host(struct sockaddr *addr);
+
+void sctp_inpcb_free(struct sctp_inpcb *, int);
+
+struct sctp_tcb *sctp_aloc_assoc(struct sctp_inpcb *, struct sockaddr *,
+	int, int *, uint32_t);
+
+void sctp_free_assoc(struct sctp_inpcb *, struct sctp_tcb *);
+
+int sctp_add_local_addr_ep(struct sctp_inpcb *, struct ifaddr *);
+
+int sctp_insert_laddr(struct sctpladdr *, struct ifaddr *);
+
+void sctp_remove_laddr(struct sctp_laddr *);
+
+int sctp_del_local_addr_ep(struct sctp_inpcb *, struct ifaddr *);
+
+int sctp_del_local_addr_ep_sa(struct sctp_inpcb *, struct sockaddr *);
+
+int sctp_add_remote_addr(struct sctp_tcb *, struct sockaddr *, int, int);
+
+int sctp_del_remote_addr(struct sctp_tcb *, struct sockaddr *);
+
+void sctp_pcb_init(void);
+
+void sctp_free_remote_addr(struct sctp_nets *);
+
+int sctp_add_local_addr_assoc(struct sctp_tcb *, struct ifaddr *);
+
+int sctp_del_local_addr_assoc(struct sctp_tcb *, struct ifaddr *);
+
+int sctp_del_local_addr_assoc_sa(struct sctp_tcb *, struct sockaddr *);
+
+int sctp_load_addresses_from_init(struct sctp_tcb *, struct mbuf *, int, int,
+    int, struct sctphdr *, struct sockaddr *);
+
+int sctp_set_primary_addr(struct sctp_tcb *, struct sockaddr *, struct sctp_nets *);
+
+int sctp_is_vtag_good(struct sctp_inpcb *, uint32_t, struct timeval *);
+
+/*void sctp_drain(void);*/
+
+int sctp_destination_is_reachable(struct sctp_tcb *, struct sockaddr *);
+
+int sctp_add_to_socket_q(struct sctp_inpcb *, struct sctp_tcb *);
+
+struct sctp_tcb *sctp_remove_from_socket_q(struct sctp_inpcb *);
+
+
+/* Null in last arg inpcb indicate run on ALL ep's. Specific
+ * inp in last arg indicates run on ONLY assoc's of the
+ * specified endpoint.
+ */
+int
+sctp_initiate_iterator(asoc_func af, uint32_t, uint32_t, void *, uint32_t,
+		       end_func ef, struct sctp_inpcb *);
+
+#if defined(__APPLE__)
+void	sctp_callout_alloc(struct sctp_timer *);
+void	sctp_callout_free(struct callout *);
+#endif
+
+#ifdef __NetBSD__
+extern void in6_sin6_2_sin (struct sockaddr_in *,
+                            struct sockaddr_in6 *sin6);
+#endif
+
+#endif /* _KERNEL */
+#endif /* !__sctp_pcb_h__ */
Index: sys/netinet/sctp_peeloff.c
===================================================================
RCS file: sys/netinet/sctp_peeloff.c
diff -N sys/netinet/sctp_peeloff.c
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ sys/netinet/sctp_peeloff.c	18 Jun 2005 16:05:50 -0000
@@ -0,0 +1,222 @@
+/*	$KAME: sctp_peeloff.c,v 1.12 2004/08/17 04:06:19 itojun Exp $	*/
+
+/*
+ * Copyright (C) 2002, 2003 Cisco Systems Inc,
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the project nor the names of its contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+#if !(defined(__OpenBSD__) || defined(__APPLE__))
+#include "opt_ipsec.h"
+#endif
+#if defined(__FreeBSD__) || defined(__DragonFly__)
+#include "opt_inet6.h"
+#include "opt_inet.h"
+#endif
+#if defined(__NetBSD__)
+#include "opt_inet.h"
+#endif
+
+#ifdef __APPLE__
+#include <sctp.h>
+#elif !defined(__OpenBSD__)
+#include "opt_sctp.h"
+#endif
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/kernel.h>
+#include <sys/malloc.h>
+#include <sys/mbuf.h>
+#include <sys/domain.h>
+#include <sys/proc.h>
+#include <sys/protosw.h>
+#include <sys/socket.h>
+#include <sys/socketvar.h>
+#include <sys/sysctl.h>
+#include <sys/syslog.h>
+#include <net/if.h>
+#include <net/route.h>
+#include <netinet/in.h>
+#include <netinet/in_systm.h>
+#include <netinet/ip.h>
+#ifdef INET6
+#include <netinet/ip6.h>
+#endif
+#include <netinet/in_pcb.h>
+#include <netinet/in_var.h>
+#include <netinet/ip_var.h>
+#ifdef INET6
+#include <netinet6/ip6_var.h>
+#endif
+#include <netinet/ip_icmp.h>
+#include <netinet/icmp_var.h>
+#include <netinet/sctp_pcb.h>
+#include <netinet/sctp.h>
+#include <netinet/sctp_uio.h>
+#include <netinet/sctp_var.h>
+#include <netinet/sctp_peeloff.h>
+#include <netinet/sctputil.h>
+
+#ifdef IPSEC
+#ifndef __OpenBSD__
+#include <netinet6/ipsec.h>
+#include <netkey/key.h>
+#else
+#undef IPSEC
+#endif
+#endif /*IPSEC*/
+
+#ifdef SCTP_DEBUG
+extern u_int32_t sctp_debug_on;
+#endif /* SCTP_DEBUG */
+
+
+int
+sctp_can_peel_off(struct socket *head, caddr_t assoc_id)
+{
+	struct sctp_inpcb *inp;
+	struct sctp_tcb *stcb;
+	inp = (struct sctp_inpcb *)head->so_pcb;
+	if (inp == NULL) {
+		return (EFAULT);
+	}
+	stcb = sctp_findassociation_ep_asocid(inp, assoc_id);
+	if (stcb == NULL) {
+		return (ENOTCONN);
+	}
+	/* We are clear to peel this one off */
+	return (0);
+}
+
+int
+sctp_do_peeloff(struct socket *head, struct socket *so, caddr_t assoc_id)
+{
+	struct sctp_inpcb *inp, *n_inp;
+	struct sctp_tcb *stcb;
+
+	inp = (struct sctp_inpcb *)head->so_pcb;
+	if (inp == NULL)
+		return (EFAULT);
+	stcb = sctp_findassociation_ep_asocid(inp, assoc_id);
+	if (stcb == NULL)
+		return (ENOTCONN);
+
+	n_inp = (struct sctp_inpcb *)so->so_pcb;
+	n_inp->sctp_flags = (SCTP_PCB_FLAGS_UDPTYPE |
+	    SCTP_PCB_FLAGS_CONNECTED |
+	    SCTP_PCB_FLAGS_IN_TCPPOOL | /* Turn on Blocking IO */
+	    (SCTP_PCB_COPY_FLAGS & inp->sctp_flags));
+	n_inp->sctp_socket = so;
+
+	/*
+	 * Now we must move it from one hash table to another and get
+	 * the stcb in the right place.
+	 */
+	sctp_move_pcb_and_assoc(inp, n_inp, stcb);
+	/*
+	 * And now the final hack. We move data in the
+	 * pending side i.e. head to the new socket
+	 * buffer. Let the GRUBBING begin :-0
+	 */
+	sctp_grub_through_socket_buffer(inp, head, so, stcb);
+	return (0);
+}
+
+struct socket *
+sctp_get_peeloff(struct socket *head, caddr_t assoc_id, int *error)
+{
+	struct socket *newso;
+	struct sctp_inpcb *inp, *n_inp;
+	struct sctp_tcb *stcb;
+
+#ifdef SCTP_DEBUG
+	if (sctp_debug_on & SCTP_DEBUG_PEEL1) {
+		printf("SCTP peel-off called\n");
+	}
+#endif /* SCTP_DEBUG */
+
+	inp = (struct sctp_inpcb *)head->so_pcb;
+	if (inp == NULL) {
+		*error = EFAULT;
+		return (NULL);
+	}
+	stcb = sctp_findassociation_ep_asocid(inp, assoc_id);
+	if (stcb == NULL) {
+		*error = ENOTCONN;
+		return (NULL);
+	}
+	newso = sonewconn(head, SS_ISCONNECTED);
+	if (newso == NULL) {
+#ifdef SCTP_DEBUG
+		if (sctp_debug_on & SCTP_DEBUG_PEEL1) {
+			printf("sctp_peeloff:sonewconn failed err\n");
+		}
+#endif /* SCTP_DEBUG */
+		*error = ENOMEM;
+		SCTP_TCB_UNLOCK(stcb);
+		return (NULL);
+	}
+	n_inp = (struct sctp_inpcb *)newso->so_pcb;
+	SCTP_INP_WLOCK(n_inp);
+	n_inp->sctp_flags = (SCTP_PCB_FLAGS_UDPTYPE |
+	    SCTP_PCB_FLAGS_CONNECTED |
+	    SCTP_PCB_FLAGS_IN_TCPPOOL | /* Turn on Blocking IO */
+	    (SCTP_PCB_COPY_FLAGS & inp->sctp_flags));
+	n_inp->sctp_socket = newso;
+	/* Turn off any non-blocking symantic. */
+	newso->so_state &= ~SS_NBIO;
+	newso->so_state |= SS_ISCONNECTED;
+	/* We remove it right away */
+#if defined(__FreeBSD__) || defined(__APPLE__) || defined(__DragonFly__)
+	SOCK_LOCK(head);
+	TAILQ_REMOVE(&head->so_comp, newso, so_list);
+	head->so_qlen--;
+	SOCK_UNLOCK(head);
+#else
+
+#if defined(__NetBSD__) || defined(__OpenBSD__)
+	newso = TAILQ_FIRST(&head->so_q);
+#else
+	newso = head->so_q;
+#endif
+	if (soqremque(newso, 1) == 0)
+		panic("sctp_peeloff");
+#endif /* __FreeBSD__ */
+	/*
+	 * Now we must move it from one hash table to another and get
+	 * the stcb in the right place.
+	 */
+	SCTP_INP_WUNLOCK(n_inp);
+	sctp_move_pcb_and_assoc(inp, n_inp, stcb);
+	/*
+	 * And now the final hack. We move data in the
+	 * pending side i.e. head to the new socket
+	 * buffer. Let the GRUBBING begin :-0
+	 */
+	sctp_grub_through_socket_buffer(inp, head, newso, stcb);
+	SCTP_TCB_UNLOCK(stcb);
+	return (newso);
+}
Index: sys/netinet/sctp_peeloff.h
===================================================================
RCS file: sys/netinet/sctp_peeloff.h
diff -N sys/netinet/sctp_peeloff.h
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ sys/netinet/sctp_peeloff.h	18 Jun 2005 16:05:50 -0000
@@ -0,0 +1,58 @@
+/*	$KAME: sctp_peeloff.h,v 1.5 2004/08/17 04:06:19 itojun Exp $	*/
+
+#ifndef __sctp_peeloff_h__
+#define __sctp_peeloff_h__
+
+/*
+ * Copyright (C) 2002, 2004 Cisco Systems Inc,
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the project nor the names of its contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/types.h>
+#if !defined(__OpenBSD__)
+#include <sys/socketvar.h>
+#endif
+#include <sys/socket.h>
+
+#if defined(_KERNEL) || (defined(__APPLE__) && defined(KERNEL))
+
+int sctp_can_peel_off(struct socket *, caddr_t);
+
+int sctp_do_peeloff(struct socket *, struct socket *, caddr_t);
+
+struct socket *sctp_get_peeloff(struct socket *, caddr_t, int *);
+
+#ifdef __APPLE__
+struct sctp_peeloff_args {
+	int	sd;
+	caddr_t	name;
+};
+#endif
+
+#endif /* _KERNEL */
+
+#endif
Index: sys/netinet/sctp_sha1.c
===================================================================
RCS file: sys/netinet/sctp_sha1.c
diff -N sys/netinet/sctp_sha1.c
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ sys/netinet/sctp_sha1.c	18 Jun 2005 16:05:50 -0000
@@ -0,0 +1,259 @@
+/*	$KAME: sctp_sha1.c,v 1.8 2004/02/24 21:52:27 itojun Exp $	*/
+
+/*
+ * Copyright (c) 2001, 2002, 2003, 2004 Cisco Systems, Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *      This product includes software developed by Cisco Systems, Inc.
+ * 4. Neither the name of the project nor the names of its contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY CISCO SYSTEMS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL CISCO SYSTEMS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+#include <netinet/sctp_sha1.h>
+#include <sys/param.h>
+#include <sys/systm.h>
+void
+SHA1_Init(struct sha1_context *ctx)
+{
+	/* Init the SHA-1 context structure */
+	ctx->A = 0;
+	ctx->B = 0;
+	ctx->C = 0;
+	ctx->D = 0;
+	ctx->E = 0;
+	ctx->H0 = H0INIT;
+	ctx->H1 = H1INIT;
+	ctx->H2 = H2INIT;
+	ctx->H3 = H3INIT;
+	ctx->H4 = H4INIT;
+	ctx->TEMP = 0;
+	memset(ctx->words, 0, sizeof(ctx->words));
+	ctx->how_many_in_block = 0;
+	ctx->running_total = 0;
+}
+
+static void
+sha1_process_a_block(struct sha1_context *ctx, unsigned int *block)
+{
+	int i;
+	/* init the W0-W15 to the block of words being hashed. */
+	/* step a) */
+	for (i = 0; i < 16; i++) {
+		ctx->words[i] = ntohl(block[i]);
+	}
+	/* now init the rest based on the SHA-1 formula, step b) */
+	for (i = 16; i < 80; i++) {
+		ctx->words[i] = CSHIFT(1, ((ctx->words[(i-3)]) ^
+					   (ctx->words[(i-8)]) ^
+					   (ctx->words[(i-14)]) ^
+					   (ctx->words[(i-16)])));
+	}
+	/* step c) */
+	ctx->A = ctx->H0;
+	ctx->B = ctx->H1;
+	ctx->C = ctx->H2;
+	ctx->D = ctx->H3;
+	ctx->E = ctx->H4;
+
+	/* step d) */
+	for (i = 0; i < 80; i++) {
+		if (i < 20) {
+			ctx->TEMP = ((CSHIFT(5, ctx->A)) +
+				     (F1(ctx->B, ctx->C, ctx->D)) +
+				     (ctx->E) +
+				     ctx->words[i] +
+				     K1);
+		} else if (i < 40) {
+			ctx->TEMP = ((CSHIFT(5, ctx->A)) +
+				     (F2(ctx->B, ctx->C, ctx->D)) +
+				     (ctx->E) +
+				     (ctx->words[i]) +
+				     K2);
+		} else if (i < 60) {
+			ctx->TEMP = ((CSHIFT(5, ctx->A)) +
+				     (F3(ctx->B, ctx->C, ctx->D)) +
+				     (ctx->E) +
+				     (ctx->words[i]) +
+				     K3);
+		} else {
+			ctx->TEMP = ((CSHIFT(5, ctx->A)) +
+				     (F4(ctx->B, ctx->C, ctx->D)) +
+				     (ctx->E) +
+				     (ctx->words[i]) +
+				     K4);
+		}
+		ctx->E = ctx->D;
+		ctx->D = ctx->C;
+		ctx->C = CSHIFT(30, ctx->B);
+		ctx->B = ctx->A;
+		ctx->A = ctx->TEMP;
+	}
+	/* step e) */
+	ctx->H0 = (ctx->H0) + (ctx->A);
+	ctx->H1 = (ctx->H1) + (ctx->B);
+	ctx->H2 = (ctx->H2) + (ctx->C);
+	ctx->H3 = (ctx->H3) + (ctx->D);
+	ctx->H4 = (ctx->H4) + (ctx->E);
+}
+
+
+void
+SHA1_Process(struct sha1_context *ctx, unsigned char *ptr, int siz)
+{
+	int number_left, left_to_fill;
+	number_left = siz;
+	while (number_left > 0) {
+		left_to_fill = sizeof(ctx->sha_block) - ctx->how_many_in_block;
+		if (left_to_fill > number_left) {
+			/* can only partially fill up this one */
+			memcpy(&ctx->sha_block[ctx->how_many_in_block],
+			       ptr, number_left);
+			ctx->how_many_in_block += number_left;
+			ctx->running_total += number_left;
+			number_left = 0;
+			break;
+		} else {
+			/* block is now full, process it */
+			memcpy(&ctx->sha_block[ctx->how_many_in_block],
+			       ptr, left_to_fill);
+			sha1_process_a_block(ctx,
+					     (unsigned int *)ctx->sha_block);
+			number_left -= left_to_fill;
+			ctx->running_total += left_to_fill;
+			ctx->how_many_in_block = 0;
+			ptr = (unsigned char *)((caddr_t)ptr + left_to_fill);
+		}
+	}
+}
+
+void
+SHA1_Final(struct sha1_context *ctx, unsigned char *digest)
+{
+	/*
+	 * if any left in block fill with padding and process. Then
+	 * transfer the digest to the pointer. At the last block some
+	 * special rules need to apply. We must add a 1 bit following
+	 * the message, then we pad with 0's. The total size is encoded
+	 * as a 64 bit number at the end. Now if the last buffer has
+	 * more than 55 octets in it we cannot fit the 64 bit number +
+	 * 10000000 pad on the end and must add the 10000000 pad, pad
+	 * the rest of the message with 0's and then create an all 0
+	 * message with just the 64 bit size at the end and run this
+	 * block through by itself.  Also the 64 bit int must be in
+	 * network byte order.
+	 */
+	int left_to_fill;
+	unsigned int i, *ptr;
+	if (ctx->how_many_in_block > 55) {
+		/*
+		 * special case, we need to process two blocks here.
+		 * One for the current stuff plus possibly the pad.
+		 * The other for the size.
+		 */
+		left_to_fill = sizeof(ctx->sha_block) - ctx->how_many_in_block;
+		if (left_to_fill == 0) {
+			/* Should not really happen but I am paranoid */
+			sha1_process_a_block(ctx,
+					     (unsigned int *)ctx->sha_block);
+			/* init last block, a bit different than the rest */
+			ctx->sha_block[0] = 0x80;
+			for (i = 1; i < sizeof(ctx->sha_block); i++) {
+				ctx->sha_block[i] = 0x0;
+			}
+		} else if (left_to_fill == 1) {
+			ctx->sha_block[ctx->how_many_in_block] = 0x80;
+			sha1_process_a_block(ctx,
+					     (unsigned int *)ctx->sha_block);
+			/* init last block */
+			memset(ctx->sha_block, 0, sizeof(ctx->sha_block));
+		} else {
+			ctx->sha_block[ctx->how_many_in_block] = 0x80;
+			for (i =( ctx->how_many_in_block + 1);
+			     i < sizeof(ctx->sha_block);
+			     i++) {
+				ctx->sha_block[i] = 0x0;
+			}
+			sha1_process_a_block(ctx,
+					     (unsigned int *)ctx->sha_block);
+			/* init last block */
+			memset(ctx->sha_block, 0, sizeof(ctx->sha_block));
+		}
+		/* This is in bits so multiply by 8 */
+		ctx->running_total *= 8;
+		ptr = (unsigned int *)&ctx->sha_block[60];
+		*ptr = htonl(ctx->running_total);
+		sha1_process_a_block(ctx, (unsigned int *)ctx->sha_block);
+	} else {
+		/*
+		 * easy case, we just pad this message to size - end with 0
+		 * add the magic 0x80 to the next word and then put the
+		 * network byte order size in the last spot and process
+		 * the block.
+		 */
+		ctx->sha_block[ctx->how_many_in_block] = 0x80;
+		for (i = (ctx->how_many_in_block + 1);
+		     i < sizeof(ctx->sha_block);
+		     i++) {
+			ctx->sha_block[i] = 0x0;
+		}
+		/* get last int spot */
+		ctx->running_total *= 8;
+		ptr = (unsigned int *)&ctx->sha_block[60];
+		*ptr = htonl(ctx->running_total);
+		sha1_process_a_block(ctx, (unsigned int *)ctx->sha_block);
+	}
+	/* transfer the digest back to the user */
+	digest[3] = (ctx->H0 & 0xff);
+	digest[2] = ((ctx->H0 >> 8) & 0xff);
+	digest[1] = ((ctx->H0 >> 16) & 0xff);
+	digest[0] = ((ctx->H0 >> 24) & 0xff);
+
+	digest[7] = (ctx->H1 & 0xff);
+	digest[6] = ((ctx->H1 >> 8) & 0xff);
+	digest[5] = ((ctx->H1 >> 16) & 0xff);
+	digest[4] = ((ctx->H1 >> 24) & 0xff);
+
+	digest[11] = (ctx->H2 & 0xff);
+	digest[10] = ((ctx->H2 >> 8) & 0xff);
+	digest[9] = ((ctx->H2 >> 16) & 0xff);
+	digest[8] = ((ctx->H2 >> 24) & 0xff);
+
+	digest[15] = (ctx->H3 & 0xff);
+	digest[14] = ((ctx->H3 >> 8) & 0xff);
+	digest[13] = ((ctx->H3 >> 16) & 0xff);
+	digest[12] = ((ctx->H3 >> 24) & 0xff);
+
+	digest[19] = (ctx->H4 & 0xff);
+	digest[18] = ((ctx->H4 >> 8) & 0xff);
+	digest[17] = ((ctx->H4 >> 16) & 0xff);
+	digest[16] = ((ctx->H4 >> 24) & 0xff);
+}
+
+
+
+
+
+
+
Index: sys/netinet/sctp_sha1.h
===================================================================
RCS file: sys/netinet/sctp_sha1.h
diff -N sys/netinet/sctp_sha1.h
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ sys/netinet/sctp_sha1.h	18 Jun 2005 16:05:50 -0000
@@ -0,0 +1,85 @@
+/*	$KAME: sctp_sha1.h,v 1.4 2002/10/09 18:01:22 itojun Exp $	*/
+
+#ifndef __SCTP_SLA1_h__
+#define __SCTP_SLA1_h__
+/*
+ * Copyright (c) 2001, 2002, 2004 Cisco Systems, Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *      This product includes software developed by Cisco Systems, Inc.
+ * 4. Neither the name of the project nor the names of its contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY CISCO SYSTEMS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL CISCO SYSTEMS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+#include <sys/types.h>
+
+struct sha1_context {
+	unsigned int A;
+	unsigned int B;
+	unsigned int C;
+	unsigned int D;
+	unsigned int E;
+	unsigned int H0;
+	unsigned int H1;
+	unsigned int H2;
+	unsigned int H3;
+	unsigned int H4;
+	unsigned int words[80];
+	unsigned int TEMP;
+	/* block I am collecting to process */
+	char sha_block[64];
+	/* collected so far */
+	int how_many_in_block;
+	unsigned int running_total;
+};
+
+
+#define F1(B,C,D) (((B & C) | ((~B) & D)))	/*  0  <= t <= 19 */
+#define F2(B,C,D) (B ^ C ^ D)			/*  20 <= t <= 39 */
+#define F3(B,C,D) ((B & C) | (B & D) | (C & D))	/*  40 <= t <= 59 */
+#define F4(B,C,D) (B ^ C ^ D)			/* 600 <= t <= 79 */
+
+/* circular shift */
+#define CSHIFT(A,B) ((B << A) | (B >> (32-A)))
+
+#define K1 0x5a827999       /* 0  <= t <= 19 */
+#define K2 0x6ed9eba1       /* 20 <= t <= 39 */
+#define K3 0x8f1bbcdc       /* 40 <= t <= 59 */
+#define K4 0xca62c1d6       /* 60 <= t <= 79 */
+
+#define H0INIT 0x67452301
+#define H1INIT 0xefcdab89
+#define H2INIT 0x98badcfe
+#define H3INIT 0x10325476
+#define H4INIT 0xc3d2e1f0
+
+#if defined(_KERNEL) || (defined(__APPLE__) && defined(KERNEL))
+
+void SHA1_Init(struct sha1_context *);
+void SHA1_Process(struct sha1_context *, unsigned char *, int);
+void SHA1_Final(struct sha1_context *, unsigned char *);
+
+#endif /* _KERNEL */
+#endif
Index: sys/netinet/sctp_structs.h
===================================================================
RCS file: sys/netinet/sctp_structs.h
diff -N sys/netinet/sctp_structs.h
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ sys/netinet/sctp_structs.h	18 Jun 2005 16:05:50 -0000
@@ -0,0 +1,654 @@
+/*	$KAME: sctp_structs.h,v 1.12 2004/08/17 04:06:19 itojun Exp $	*/
+
+#ifndef __sctp_structs_h__
+#define __sctp_structs_h__
+
+/*
+ * Copyright (c) 2001, 2002, 2003, 2004 Cisco Systems, Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *      This product includes software developed by Cisco Systems, Inc.
+ * 4. Neither the name of the project nor the names of its contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY CISCO SYSTEMS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL CISCO SYSTEMS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+#include <sys/queue.h>
+
+#if defined(__APPLE__)
+#include <netinet/sctp_callout.h>
+#elif defined(__OpenBSD__)
+#include <sys/timeout.h>
+#else
+#include <sys/callout.h>
+#endif
+
+#ifdef IPSEC
+#ifndef __OpenBSD__
+#include <netinet6/ipsec.h>
+#include <netkey/key.h>
+#endif
+#endif
+
+#include <netinet/sctp_header.h>
+#include <netinet/sctp_uio.h>
+
+struct sctp_timer {
+#if defined(__OpenBSD__)
+	struct timeout timer;
+#else
+	struct callout timer;
+#endif
+	int type;
+	/*
+	 * Depending on the timer type these will be setup and cast with
+	 * the appropriate entity.
+	 */
+	void *ep;
+	void *tcb;
+	void *net;
+};
+
+/*
+ * This is the information we track on each interface that we know about	* from the distant end.
+ */
+TAILQ_HEAD(sctpnetlisthead, sctp_nets);
+
+/*
+ * Users of the iterator need to malloc a iterator with a call to
+ * sctp_initiate_iterator(func, pcb_flags, asoc_state, void-ptr-arg, u_int32_t,
+ *                        u_int32-arg, end_func, inp);
+ *
+ * Use the following two defines if you don't care what pcb flags are on the
+ * EP and/or you don't care what state the association is in.
+ *
+ * Note that if you specify an INP as the last argument then ONLY each
+ * association of that single INP will be executed upon. Note that the
+ * pcb flags STILL apply so if the inp you specify has different pcb_flags
+ * then what you put in pcb_flags nothing will happen. use SCTP_PCB_ANY_FLAGS
+ * to assure the inp you specify gets treated.
+ */
+#define SCTP_PCB_ANY_FLAGS  0x00000000
+#define SCTP_ASOC_ANY_STATE 0x00000000
+
+typedef void (*asoc_func)(struct sctp_inpcb *, struct sctp_tcb *, void *ptr,
+			  u_int32_t val);
+typedef void (*end_func)(void *ptr, u_int32_t val);
+
+#define SCTP_ITERATOR_DO_ALL_INP	0x00000001
+#define SCTP_ITERATOR_DO_SINGLE_INP	0x00000002
+
+struct sctp_iterator {
+        LIST_ENTRY(sctp_iterator) sctp_nxt_itr;
+	struct sctp_timer tmr;
+	struct sctp_inpcb *inp;	/* ep */
+	struct sctp_tcb *stcb;	/* assoc */
+	asoc_func function_toapply;
+	end_func function_atend;
+	void *pointer;		/* pointer for apply func to use */
+	u_int32_t val;		/* value for apply func to use */
+	u_int32_t pcb_flags;
+	u_int32_t asoc_state;
+	u_int32_t iterator_flags;
+};
+
+LIST_HEAD(sctpiterators, sctp_iterator);
+
+struct sctp_copy_all {
+	struct sctp_inpcb *inp;	/* ep */
+	struct mbuf *m;
+	struct sctp_sndrcvinfo sndrcv;
+	int sndlen;
+	int cnt_sent;
+	int cnt_failed;
+};
+
+union sctp_sockstore {
+#ifdef AF_INET
+	struct sockaddr_in  sin;
+#endif
+#ifdef AF_INET6
+	struct sockaddr_in6 sin6;
+#endif
+	struct sockaddr     sa;
+};
+
+struct sctp_nets {
+	TAILQ_ENTRY(sctp_nets) sctp_next;	/* next link */
+
+        /* Things on the top half may be able to be split
+	 * into a common structure shared by all.
+	 */
+	struct sctp_timer pmtu_timer;
+
+	/*
+	 * The following two in combination equate to a route entry for
+	 * v6 or v4.
+	 */
+	struct sctp_route {
+		struct rtentry *ro_rt;
+		union sctp_sockstore _l_addr;	/* remote peer addr */
+		union sctp_sockstore _s_addr;	/* our selected src addr */
+	} ro;
+	/* mtu discovered so far */
+	u_int32_t mtu;
+        u_int32_t ssthresh;		/* not sure about this one for split */
+
+	/* smoothed average things for RTT and RTO itself */
+	int lastsa;
+	int lastsv;
+	unsigned int RTO;
+
+	/* This is used for SHUTDOWN/SHUTDOWN-ACK/SEND or INIT timers */
+	struct sctp_timer rxt_timer;
+
+	/* last time in seconds I sent to it */
+	struct timeval last_sent_time;
+	int ref_count;
+
+	/* Congestion stats per destination */
+	/*
+	 * flight size variables and such, sorry Vern, I could not avoid
+	 * this if I wanted performance :>
+	 */
+	u_int32_t flight_size;
+	u_int32_t cwnd; /* actual cwnd */
+	u_int32_t prev_cwnd; /* cwnd before any processing */
+	u_int32_t partial_bytes_acked; /* in CA tracks when to incr a MTU */
+
+	/* tracking variables to avoid the aloc/free in sack processing */
+	unsigned int net_ack;
+	unsigned int net_ack2;
+	/*
+	 * These only are valid if the primary dest_sstate holds the
+	 * SCTP_ADDR_SWITCH_PRIMARY flag
+	 */
+	u_int32_t next_tsn_at_change;
+	u_int32_t heartbeat_random1;
+	u_int32_t heartbeat_random2;
+
+	/* if this guy is ok or not ... status */
+	u_int16_t dest_state;
+	/* number of transmit failures to down this guy */
+	u_int16_t failure_threshold;
+	/* error stats on destination */
+	u_int16_t error_count;
+
+	/* Flags that probably can be combined into dest_state */
+	u_int8_t rto_pending;		/* is segment marked for RTO update  ** if we split?*/
+	u_int8_t fast_retran_ip;	/* fast retransmit in progress */
+	u_int8_t hb_responded;
+	u_int8_t cacc_saw_newack;	/* CACC algorithm flag */
+        u_int8_t src_addr_selected;	/* if we split we move */
+	u_int8_t indx_of_eligible_next_to_use;
+	u_int8_t addr_is_local;		/* its a local address (if known) could move in split */
+#ifdef SCTP_HIGH_SPEED
+	u_int8_t last_hs_used;		/* index into the last HS table entry we used */
+#endif
+};
+
+
+struct sctp_data_chunkrec {
+	u_int32_t TSN_seq;  /* the TSN of this transmit */
+	u_int16_t stream_seq; /* the stream sequence number of this transmit */
+	u_int16_t stream_number; /* the stream number of this guy */
+	u_int32_t payloadtype;
+	u_int32_t context;	/* from send */
+
+       /* ECN Nonce: Nonce Value for this chunk */
+        u_int8_t ect_nonce;
+
+	/* part of the Highest sacked algorithm to be able to
+	 * stroke counts on ones that are FR'd.
+	 */
+	u_int32_t fast_retran_tsn;	/* sending_seq at the time of FR */
+	struct timeval timetodrop;	/* time we drop it from queue */
+	u_int8_t doing_fast_retransmit;
+	u_int8_t rcv_flags; /* flags pulled from data chunk on inbound
+			   * for outbound holds sending flags.
+			   */
+	u_int8_t state_flags;
+};
+
+TAILQ_HEAD(sctpchunk_listhead, sctp_tmit_chunk);
+
+#define CHUNK_FLAGS_FRAGMENT_OK	0x0001
+
+struct sctp_tmit_chunk {
+	union {
+		struct sctp_data_chunkrec data;
+		int chunk_id;
+	} rec;
+	int32_t   sent;		/* the send status */
+	int32_t   snd_count;			/* number of times I sent */
+	u_int32_t flags;		/* flags, such as FRAGMENT_OK */
+	u_int32_t   send_size;
+	u_int32_t   book_size;
+	u_int32_t   mbcnt;
+	struct sctp_association *asoc;	/* bp to asoc this belongs to */
+	struct timeval sent_rcv_time;	/* filled in if RTT being calculated */
+	struct mbuf *data;		/* pointer to mbuf chain of data */
+	struct sctp_nets *whoTo;
+	TAILQ_ENTRY(sctp_tmit_chunk) sctp_next;	/* next link */
+	uint8_t do_rtt;
+};
+
+
+/*
+ * this struct contains info that is used to track inbound stream data
+ * and help with ordering.
+ */
+TAILQ_HEAD(sctpwheelunrel_listhead, sctp_stream_in);
+struct sctp_stream_in {
+	struct sctpchunk_listhead inqueue;
+	TAILQ_ENTRY(sctp_stream_in) next_spoke;
+	uint16_t stream_no;
+	uint16_t last_sequence_delivered;	/* used for re-order */
+};
+
+/* This struct is used to track the traffic on outbound streams */
+TAILQ_HEAD(sctpwheel_listhead, sctp_stream_out);
+struct sctp_stream_out {
+	struct sctpchunk_listhead outqueue;
+	TAILQ_ENTRY(sctp_stream_out) next_spoke; /* next link in wheel */
+	uint16_t stream_no;
+	uint16_t next_sequence_sent; /* next one I expect to send out */
+};
+
+/* used to keep track of the addresses yet to try to add/delete */
+TAILQ_HEAD(sctp_asconf_addrhead, sctp_asconf_addr);
+struct sctp_asconf_addr {
+	TAILQ_ENTRY(sctp_asconf_addr) next;
+	struct sctp_asconf_addr_param ap;
+	struct ifaddr *ifa;	/* save the ifa for add/del ip */
+	uint8_t	sent;		/* has this been sent yet? */
+};
+
+
+/*
+ * Here we have information about each individual association that we
+ * track. We probably in production would be more dynamic. But for ease
+ * of implementation we will have a fixed array that we hunt for in a
+ * linear fashion.
+ */
+struct sctp_association {
+	/* association state */
+	int state;
+	/* queue of pending addrs to add/delete */
+	struct sctp_asconf_addrhead asconf_queue;
+	struct timeval time_entered;		/* time we entered state */
+	struct timeval time_last_rcvd;
+	struct timeval time_last_sent;
+	struct timeval time_last_sat_advance;
+	struct sctp_sndrcvinfo def_send;	/* default send parameters */
+
+	/* timers and such */
+	struct sctp_timer hb_timer;		/* hb timer */
+	struct sctp_timer dack_timer;		/* Delayed ack timer */
+	struct sctp_timer asconf_timer;		/* Asconf */
+	struct sctp_timer strreset_timer;	/* stream reset */
+	struct sctp_timer shut_guard_timer;	/* guard */
+	struct sctp_timer autoclose_timer;	/* automatic close timer */
+	struct sctp_timer delayed_event_timer;	/* timer for delayed events */
+
+	/* list of local addresses when add/del in progress */
+	struct sctpladdr sctp_local_addr_list;
+	struct sctpnetlisthead nets;
+
+	/* Control chunk queue */
+	struct sctpchunk_listhead control_send_queue;
+
+	/* Once a TSN hits the wire it is moved to the sent_queue. We
+	 * maintain two counts here (don't know if any but retran_cnt
+	 * is needed). The idea is that the sent_queue_retran_cnt
+	 * reflects how many chunks have been marked for retranmission
+	 * by either T3-rxt or FR.
+	 */
+	struct sctpchunk_listhead sent_queue;
+	struct sctpchunk_listhead send_queue;
+
+
+	/* re-assembly queue for fragmented chunks on the inbound path */
+	struct sctpchunk_listhead reasmqueue;
+
+	/*
+	 * this queue is used when we reach a condition that we can NOT
+	 * put data into the socket buffer. We track the size of this
+	 * queue and set our rwnd to the space in the socket minus also
+	 * the size_on_delivery_queue.
+	 */
+	struct sctpchunk_listhead delivery_queue;
+
+	struct sctpwheel_listhead out_wheel;
+
+	/* If an iterator is looking at me, this is it */
+	struct sctp_iterator *stcb_starting_point_for_iterator;
+
+	/* ASCONF destination address last sent to */
+	struct sctp_nets *asconf_last_sent_to;
+
+	/* ASCONF save the last ASCONF-ACK so we can resend it if necessary */
+	struct mbuf *last_asconf_ack_sent;
+
+	/*
+	 * if Source Address Selection happening, this will rotate through
+	 * the link list.
+	 */
+	struct sctp_laddr *last_used_address;
+
+	/* stream arrays */
+        struct sctp_stream_in  *strmin;
+	struct sctp_stream_out *strmout;
+	u_int8_t *mapping_array;
+	/* primary destination to use */
+	struct sctp_nets *primary_destination;
+
+	/* last place I got a data chunk from */
+	struct sctp_nets *last_data_chunk_from;
+	/* last place I got a control from */
+	struct sctp_nets *last_control_chunk_from;
+
+	/* circular looking for output selection */
+	struct sctp_stream_out *last_out_stream;
+
+	/* wait to the point the cum-ack passes
+	 * pending_reply->sr_resp.reset_at_tsn.
+	 */
+	struct sctp_stream_reset_response *pending_reply;
+	struct sctpchunk_listhead pending_reply_queue;
+
+	u_int32_t cookie_preserve_req;
+	/* ASCONF next seq I am sending out, inits at init-tsn */
+	uint32_t asconf_seq_out;
+	/* ASCONF last received ASCONF from peer, starts at peer's TSN-1 */
+	uint32_t asconf_seq_in;
+
+	/* next seq I am sending in str reset messages */
+	uint32_t str_reset_seq_out;
+
+	/* next seq I am expecting in str reset messages */
+	uint32_t str_reset_seq_in;
+	u_int32_t str_reset_sending_seq;
+
+	/* various verification tag information */
+	u_int32_t my_vtag;	/*
+				 * The tag to be used. if assoc is
+				 * re-initited by remote end, and
+				 * I have unlocked this will be
+				 * regenrated to a new random value.
+				 */
+	u_int32_t peer_vtag;	/* The peers last tag */
+
+	u_int32_t my_vtag_nonce;
+	u_int32_t peer_vtag_nonce;
+
+
+	/* This is the SCTP fragmentation threshold */
+	u_int32_t smallest_mtu;
+
+	/*
+	 * Special hook for Fast retransmit, allows us to track the highest
+	 * TSN that is NEW in this SACK if gap ack blocks are present.
+	 */
+	u_int32_t this_sack_highest_gap;
+
+	/*
+	 * The highest consecutive TSN that has been acked by peer on my
+	 * sends
+	 */
+	u_int32_t last_acked_seq;
+
+	/* The next TSN that I will use in sending. */
+	u_int32_t sending_seq;
+
+	/* Original seq number I used ??questionable to keep?? */
+	u_int32_t init_seq_number;
+
+	/*
+	 * We use this value to know if FR's are allowed, i.e. did the
+	 * cum-ack pass this point or equal it so FR's are now allowed.
+	 */
+	u_int32_t t3timeout_highest_marked;
+
+	/* The Advanced Peer Ack Point, as required by the PR-SCTP */
+	/* (A1 in Section 4.2) */
+	u_int32_t advanced_peer_ack_point;
+
+	/*
+	 * The highest consequetive TSN at the bottom of the mapping
+	 * array (for his sends).
+	 */
+	u_int32_t cumulative_tsn;
+	/*
+	 * Used to track the mapping array and its offset bits. This
+	 * MAY be lower then cumulative_tsn.
+	 */
+	u_int32_t mapping_array_base_tsn;
+	/*
+	 * used to track highest TSN we have received and is listed in
+	 * the mapping array.
+	 */
+	u_int32_t highest_tsn_inside_map;
+
+	u_int32_t last_echo_tsn;
+	u_int32_t last_cwr_tsn;
+	u_int32_t fast_recovery_tsn;
+	u_int32_t sat_t3_recovery_tsn;
+
+	u_int32_t tsn_last_delivered;
+
+	/*
+	 * window state information and smallest MTU that I use to bound
+	 * segmentation
+	 */
+	u_int32_t peers_rwnd;
+	u_int32_t my_rwnd;
+	u_int32_t my_last_reported_rwnd;
+	u_int32_t my_rwnd_control_len;
+
+	u_int32_t total_output_queue_size;
+	u_int32_t total_output_mbuf_queue_size;
+
+	/* 32 bit nonce stuff */
+	u_int32_t nonce_resync_tsn;
+	u_int32_t nonce_wait_tsn;
+
+	int ctrl_queue_cnt; /* could be removed  REM */
+	/*
+	 * All outbound datagrams queue into this list from the
+	 * individual stream queue. Here they get assigned a TSN
+	 * and then await sending. The stream seq comes when it
+	 * is first put in the individual str queue
+	 */
+	unsigned int stream_queue_cnt;
+	unsigned int send_queue_cnt;
+	unsigned int sent_queue_cnt;
+	unsigned int sent_queue_cnt_removeable;
+	/*
+	 * Number on sent queue that are marked for retran until this
+	 * value is 0 we only send one packet of retran'ed data.
+	 */
+	unsigned int sent_queue_retran_cnt;
+
+	unsigned int size_on_reasm_queue;
+	unsigned int cnt_on_reasm_queue;
+	/* amount of data (bytes) currently in flight (on all destinations) */
+	unsigned int total_flight;
+	/* Total book size in flight */
+	unsigned int total_flight_count;	/* count of chunks used with book total */
+	/* count of destinaton nets and list of destination nets */
+	unsigned int numnets;
+
+	/* Total error count on this association */
+	unsigned int overall_error_count;
+
+	unsigned int size_on_delivery_queue;
+	unsigned int cnt_on_delivery_queue;
+
+	unsigned int cnt_msg_on_sb;
+
+	/* All stream count of chunks for delivery */
+	unsigned int size_on_all_streams;
+	unsigned int cnt_on_all_streams;
+
+	/* Heart Beat delay in ticks */
+	unsigned int heart_beat_delay;
+
+	/* autoclose */
+	unsigned int sctp_autoclose_ticks;
+
+	/* how many preopen streams we have */
+	unsigned int pre_open_streams;
+
+	/* How many streams I support coming into me */
+	unsigned int max_inbound_streams;
+
+	/* the cookie life I award for any cookie, in seconds */
+	unsigned int cookie_life;
+
+	unsigned int numduptsns;
+	int dup_tsns[SCTP_MAX_DUP_TSNS];
+	unsigned int initial_init_rto_max;	/* initial RTO for INIT's */
+	unsigned int initial_rto;		/* initial send RTO */
+	unsigned int minrto;			/* per assoc RTO-MIN */
+	unsigned int maxrto;			/* per assoc RTO-MAX */
+	/* Being that we have no bag to collect stale cookies, and
+	 * that we really would not want to anyway.. we will count
+	 * them in this counter. We of course feed them to the
+	 * pigeons right away (I have always thought of pigeons
+	 * as flying rats).
+	 */
+	u_int16_t stale_cookie_count;
+
+	/* For the partial delivery API, if up, invoked
+	 * this is what last TSN I delivered
+	 */
+	u_int16_t str_of_pdapi;
+	u_int16_t ssn_of_pdapi;
+
+
+	/* counts of actual built streams. Allocation may be more however */
+	/* could re-arrange to optimize space here. */
+	u_int16_t streamincnt;
+	u_int16_t streamoutcnt;
+
+	/* my maximum number of retrans of INIT and SEND */
+	/* copied from SCTP but should be individually setable */
+	u_int16_t max_init_times;
+	u_int16_t max_send_times;
+
+	u_int16_t def_net_failure;
+
+	/*
+	 * lock flag: 0 is ok to send, 1+ (duals as a retran count) is
+	 * awaiting ACK
+	 */
+	u_int16_t asconf_sent;   /* possibly removable REM */
+	u_int16_t mapping_array_size;
+
+	u_int16_t chunks_on_out_queue; /* total chunks floating around */
+	int16_t num_send_timers_up;
+	/*
+	 * This flag indicates that we need to send the first SACK. If
+	 * in place it says we have NOT yet sent a SACK and need to.
+	 */
+	u_int8_t first_ack_sent;
+
+	/* max burst after fast retransmit completes */
+	u_int8_t max_burst;
+
+	u_int8_t sat_network;	/* RTT is in range of sat net or greater */
+	u_int8_t sat_network_lockout;/* lockout code */
+	u_int8_t burst_limit_applied;	/* Burst limit in effect at last send? */
+	/* flag goes on when we are doing a partial delivery api */
+	u_int8_t hb_random_values[4];
+	u_int8_t fragmented_delivery_inprogress;
+	u_int8_t fragment_flags;
+	u_int8_t hb_ect_randombit;
+        u_int8_t hb_random_idx;
+
+	/* ECN Nonce stuff */
+	u_int8_t receiver_nonce_sum; /* nonce I sum and put in my sack */
+	u_int8_t ecn_nonce_allowed;  /* Tells us if ECN nonce is on */
+	u_int8_t nonce_sum_check;    /* On off switch used during re-sync */
+	u_int8_t nonce_wait_for_ecne;/* flag when we expect a ECN */
+	u_int8_t peer_supports_ecn_nonce;
+
+	/*
+	 * This value, plus all other ack'd but above cum-ack is added
+	 * together to cross check against the bit that we have yet to
+	 * define (probably in the SACK).
+	 * When the cum-ack is updated, this sum is updated as well.
+	 */
+	u_int8_t nonce_sum_expect_base;
+	/* Flag to tell if ECN is allowed */
+	u_int8_t ecn_allowed;
+
+	/* flag to indicate if peer can do asconf */
+	uint8_t peer_supports_asconf;
+	uint8_t peer_supports_asconf_setprim; /* possibly removable REM */
+	/* pr-sctp support flag */
+	uint8_t peer_supports_prsctp;
+
+	/* stream resets are supported by the peer */
+	uint8_t peer_supports_strreset;
+
+	/*
+	 * packet drop's are supported by the peer, we don't really care
+	 * about this but we bookkeep it anyway.
+	 */
+	uint8_t peer_supports_pktdrop;
+
+	/* Do we allow V6/V4? */
+	u_int8_t ipv4_addr_legal;
+	u_int8_t ipv6_addr_legal;
+	/* Address scoping flags */
+	/* scope value for IPv4 */
+	u_int8_t ipv4_local_scope;
+	/* scope values for IPv6 */
+	u_int8_t local_scope;
+	u_int8_t site_scope;
+	/* loopback scope */
+	u_int8_t loopback_scope;
+	/* flags to handle send alternate net tracking */
+	u_int8_t used_alt_onsack;
+	u_int8_t used_alt_asconfack;
+	u_int8_t fast_retran_loss_recovery;
+	u_int8_t sat_t3_loss_recovery;
+        u_int8_t dropped_special_cnt;
+	u_int8_t seen_a_sack_this_pkt;
+	u_int8_t stream_reset_outstanding;
+	u_int8_t delayed_connection;
+	u_int8_t ifp_had_enobuf;
+	u_int8_t saw_sack_with_frags;
+	/*
+	 * The mapping array is used to track out of order sequences above
+	 * last_acked_seq. 0 indicates packet missing 1 indicates packet
+	 * rec'd. We slide it up every time we raise last_acked_seq and 0
+	 * trailing locactions out.  If I get a TSN above the array
+	 * mappingArraySz, I discard the datagram and let retransmit happen.
+	 */
+};
+
+#endif
Index: sys/netinet/sctp_timer.c
===================================================================
RCS file: sys/netinet/sctp_timer.c
diff -N sys/netinet/sctp_timer.c
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ sys/netinet/sctp_timer.c	18 Jun 2005 16:05:50 -0000
@@ -0,0 +1,1560 @@
+/*	$KAME: sctp_timer.c,v 1.28 2004/08/17 04:06:20 itojun Exp $	*/
+
+/*
+ * Copyright (C) 2002, 2003, 2004 Cisco Systems Inc,
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the project nor the names of its contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+#if !(defined(__OpenBSD__) || defined(__APPLE__))
+#include "opt_ipsec.h"
+#endif
+#if defined(__FreeBSD__) || defined(__DragonFly__)
+#include "opt_compat.h"
+#include "opt_inet6.h"
+#include "opt_inet.h"
+#endif
+#if defined(__NetBSD__)
+#include "opt_inet.h"
+#endif
+#ifdef __APPLE__
+#include <sctp.h>
+#elif !defined(__OpenBSD__)
+#include "opt_sctp.h"
+#endif
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/malloc.h>
+#include <sys/mbuf.h>
+#ifndef __OpenBSD__
+#include <sys/domain.h>
+#endif
+#include <sys/protosw.h>
+#include <sys/socket.h>
+#include <sys/socketvar.h>
+#include <sys/proc.h>
+#include <sys/kernel.h>
+#include <sys/sysctl.h>
+#ifdef INET6
+#include <sys/domain.h>
+#endif
+
+#if (defined(__FreeBSD__) && __FreeBSD_version >= 500000)
+#include <sys/limits.h>
+#else
+#include <machine/limits.h>
+#endif
+
+#include <net/if.h>
+#include <net/if_types.h>
+#include <net/route.h>
+#include <netinet/in.h>
+#include <netinet/in_systm.h>
+#define _IP_VHL
+#include <netinet/ip.h>
+#include <netinet/in_pcb.h>
+#include <netinet/in_var.h>
+#include <netinet/ip_var.h>
+
+#ifdef INET6
+#include <netinet/ip6.h>
+#include <netinet6/ip6_var.h>
+#endif /* INET6 */
+
+#include <netinet/sctp_pcb.h>
+
+#ifdef IPSEC
+#ifndef __OpenBSD__
+#include <netinet6/ipsec.h>
+#include <netkey/key.h>
+#else
+#undef IPSEC
+#endif
+#endif /* IPSEC */
+#ifdef INET6
+#include <netinet6/sctp6_var.h>
+#endif
+#include <netinet/sctp_var.h>
+#include <netinet/sctp_timer.h>
+#include <netinet/sctputil.h>
+#include <netinet/sctp_output.h>
+#include <netinet/sctp_hashdriver.h>
+#include <netinet/sctp_header.h>
+#include <netinet/sctp_indata.h>
+#include <netinet/sctp_asconf.h>
+
+#include <netinet/sctp.h>
+#include <netinet/sctp_uio.h>
+
+#include <net/net_osdep.h>
+
+#ifdef SCTP_DEBUG
+extern u_int32_t sctp_debug_on;
+#endif /* SCTP_DEBUG */
+
+void
+sctp_audit_retranmission_queue(struct sctp_association *asoc)
+{
+	struct sctp_tmit_chunk *chk;
+
+#ifdef SCTP_DEBUG
+	if (sctp_debug_on & SCTP_DEBUG_TIMER4) {
+		printf("Audit invoked on send queue cnt:%d onqueue:%d\n",
+		    asoc->sent_queue_retran_cnt,
+		    asoc->sent_queue_cnt);
+	}
+#endif /* SCTP_DEBUG */
+	asoc->sent_queue_retran_cnt = 0;
+	asoc->sent_queue_cnt = 0;
+	TAILQ_FOREACH(chk, &asoc->sent_queue, sctp_next) {
+		if (chk->sent == SCTP_DATAGRAM_RESEND) {
+			asoc->sent_queue_retran_cnt++;
+		}
+		asoc->sent_queue_cnt++;
+	}
+	TAILQ_FOREACH(chk, &asoc->control_send_queue, sctp_next) {
+		if (chk->sent == SCTP_DATAGRAM_RESEND) {
+			asoc->sent_queue_retran_cnt++;
+		}
+	}
+#ifdef SCTP_DEBUG
+	if (sctp_debug_on & SCTP_DEBUG_TIMER4) {
+		printf("Audit completes retran:%d onqueue:%d\n",
+		    asoc->sent_queue_retran_cnt,
+		    asoc->sent_queue_cnt);
+	}
+#endif /* SCTP_DEBUG */
+}
+
+int
+sctp_threshold_management(struct sctp_inpcb *inp, struct sctp_tcb *stcb,
+    struct sctp_nets *net, uint16_t threshold)
+{
+	if (net) {
+		net->error_count++;
+#ifdef SCTP_DEBUG
+		if (sctp_debug_on & SCTP_DEBUG_TIMER4) {
+			printf("Error count for %p now %d thresh:%d\n",
+			    net, net->error_count,
+			    net->failure_threshold);
+		}
+#endif /* SCTP_DEBUG */
+		if (net->error_count >= net->failure_threshold) {
+			/* We had a threshold failure */
+			if (net->dest_state & SCTP_ADDR_REACHABLE) {
+				net->dest_state &= ~SCTP_ADDR_REACHABLE;
+				net->dest_state |= SCTP_ADDR_NOT_REACHABLE;
+				if (net == stcb->asoc.primary_destination) {
+					net->dest_state |= SCTP_ADDR_WAS_PRIMARY;
+				}
+				sctp_ulp_notify(SCTP_NOTIFY_INTERFACE_DOWN,
+						stcb,
+						SCTP_FAILED_THRESHOLD,
+						(void *)net);
+			}
+		}
+		/*********HOLD THIS COMMENT FOR PATCH OF ALTERNATE
+		 *********ROUTING CODE
+		 */
+		/*********HOLD THIS COMMENT FOR END OF PATCH OF ALTERNATE
+		 *********ROUTING CODE
+		 */
+	}
+	if (stcb == NULL)
+		return (0);
+
+	if (net) {
+		if ((net->dest_state & SCTP_ADDR_UNCONFIRMED) == 0) {
+			stcb->asoc.overall_error_count++;
+		}
+	} else {
+		stcb->asoc.overall_error_count++;
+	}
+#ifdef SCTP_DEBUG
+	if (sctp_debug_on & SCTP_DEBUG_TIMER4) {
+		printf("Overall error count for %p now %d thresh:%u state:%x\n",
+		       &stcb->asoc,
+		       stcb->asoc.overall_error_count,
+		       (u_int)threshold,
+		       ((net == NULL) ? (u_int)0 : (u_int)net->dest_state));
+	}
+#endif /* SCTP_DEBUG */
+	/* We specifically do not do >= to give the assoc one more
+	 * change before we fail it.
+	 */
+	if (stcb->asoc.overall_error_count > threshold) {
+		/* Abort notification sends a ULP notify */
+		struct mbuf *oper;
+		MGET(oper, MB_DONTWAIT, MT_DATA);
+		if (oper) {
+			struct sctp_paramhdr *ph;
+			u_int32_t *ippp;
+
+			oper->m_len = sizeof(struct sctp_paramhdr) +
+			    sizeof(*ippp);
+			ph = mtod(oper, struct sctp_paramhdr *);
+			ph->param_type = htons(SCTP_CAUSE_PROTOCOL_VIOLATION);
+			ph->param_length = htons(oper->m_len);
+			ippp = (u_int32_t *)(ph + 1);
+			*ippp = htonl(0x40000001);
+		}
+		sctp_abort_an_association(inp, stcb, SCTP_FAILED_THRESHOLD, oper);
+		return (1);
+	}
+	return (0);
+}
+
+struct sctp_nets *
+sctp_find_alternate_net(struct sctp_tcb *stcb,
+			struct sctp_nets *net)
+{
+	/* Find and return an alternate network if possible */
+	struct sctp_nets *alt, *mnet;
+	int once;
+
+	if (stcb->asoc.numnets == 1) {
+		/* No others but net */
+		return (TAILQ_FIRST(&stcb->asoc.nets));
+	}
+	mnet = net;
+	once = 0;
+
+	if (mnet == NULL) {
+		mnet = TAILQ_FIRST(&stcb->asoc.nets);
+	}
+	do {
+		alt = TAILQ_NEXT(mnet, sctp_next);
+		if (alt == NULL) {
+			once++;
+			if (once > 1) {
+				break;
+			}
+			alt = TAILQ_FIRST(&stcb->asoc.nets);
+		}
+		if (alt->ro.ro_rt == NULL) {
+#ifndef SCOPEDROUTING
+			struct sockaddr_in6 *sin6;
+			sin6 = (struct sockaddr_in6 *)&alt->ro._l_addr;
+			if (sin6->sin6_family == AF_INET6) {
+#if defined(SCTP_BASE_FREEBSD) || defined(__APPLE__) || defined(__DragonFly__)
+				(void)in6_embedscope(&sin6->sin6_addr, sin6,
+						     NULL, NULL);
+#else
+				(void)in6_embedscope(&sin6->sin6_addr, sin6);
+#endif
+			}
+#endif
+#if defined(__FreeBSD__) || defined(__APPLE__) || defined(__DragonFly__)
+			rtalloc_ign((struct route*)&alt->ro, 0UL);
+#else
+			rtalloc((struct route*)&alt->ro);
+#endif
+#ifndef SCOPEDROUTING
+			if (sin6->sin6_family == AF_INET6) {
+				(void)in6_recoverscope(sin6, &sin6->sin6_addr,
+				    NULL);
+			}
+#endif
+			alt->src_addr_selected = 0;
+		}
+		if (
+			((alt->dest_state & SCTP_ADDR_REACHABLE) == SCTP_ADDR_REACHABLE) &&
+			(alt->ro.ro_rt != NULL) &&
+			(!(alt->dest_state & SCTP_ADDR_UNCONFIRMED))
+			) {
+			/* Found a reachable address */
+			break;
+		}
+		mnet = alt;
+	} while (alt != NULL);
+
+	if (alt == NULL) {
+		/* Case where NO insv network exists (dormant state) */
+		/* we rotate destinations */
+		once = 0;
+		mnet = net;
+		do {
+			alt = TAILQ_NEXT(mnet, sctp_next);
+			if (alt == NULL) {
+				once++;
+				if (once > 1) {
+					break;
+				}
+				alt = TAILQ_FIRST(&stcb->asoc.nets);
+			}
+			if ((!(alt->dest_state & SCTP_ADDR_UNCONFIRMED)) &&
+			    (alt != net)) {
+				/* Found an alternate address */
+				break;
+			}
+			mnet = alt;
+		} while (alt != NULL);
+	}
+	if (alt == NULL) {
+		return (net);
+	}
+	return (alt);
+}
+
+static void
+sctp_backoff_on_timeout(struct sctp_tcb *stcb,
+			struct sctp_nets *net,
+			int win_probe,
+			int num_marked)
+{
+#ifdef SCTP_DEBUG
+	int oldRTO;
+
+	oldRTO = net->RTO;
+#endif /* SCTP_DEBUG */
+	net->RTO <<= 1;
+#ifdef SCTP_DEBUG
+	if (sctp_debug_on & SCTP_DEBUG_TIMER2) {
+		printf("Timer doubles from %d ms -to-> %d ms\n",
+		       oldRTO, net->RTO);
+	}
+#endif /* SCTP_DEBUG */
+
+	if (net->RTO > stcb->asoc.maxrto) {
+		net->RTO = stcb->asoc.maxrto;
+#ifdef SCTP_DEBUG
+		if (sctp_debug_on & SCTP_DEBUG_TIMER2) {
+			printf("Growth capped by maxrto %d\n",
+			       net->RTO);
+		}
+#endif /* SCTP_DEBUG */
+	}
+
+
+	if ((win_probe == 0) && num_marked) {
+		/* We don't apply penalty to window probe scenarios */
+#ifdef SCTP_CWND_LOGGING
+		int old_cwnd=net->cwnd;
+#endif
+		net->ssthresh = net->cwnd >> 1;
+		if (net->ssthresh < (net->mtu << 1)) {
+			net->ssthresh = (net->mtu << 1);
+		}
+		net->cwnd = net->mtu;
+		/* floor of 1 mtu */
+		if (net->cwnd < net->mtu)
+			net->cwnd = net->mtu;
+#ifdef SCTP_CWND_LOGGING
+		sctp_log_cwnd(net, net->cwnd-old_cwnd, SCTP_CWND_LOG_FROM_RTX);
+#endif
+
+		net->partial_bytes_acked = 0;
+#ifdef SCTP_DEBUG
+		if (sctp_debug_on & SCTP_DEBUG_TIMER1) {
+			printf("collapse cwnd to 1MTU ssthresh to %d\n",
+			       net->ssthresh);
+		}
+#endif
+
+	}
+}
+
+
+static int
+sctp_mark_all_for_resend(struct sctp_tcb *stcb,
+			 struct sctp_nets *net,
+			 struct sctp_nets *alt,
+			 int *num_marked)
+{
+
+	/*
+	 * Mark all chunks (well not all) that were sent to *net for retransmission.
+	 * Move them to alt for there destination as well... We only
+	 * mark chunks that have been outstanding long enough to have
+	 * received feed-back.
+	 */
+	struct sctp_tmit_chunk *chk, *tp2;
+	struct sctp_nets *lnets;
+	struct timeval now, min_wait, tv;
+	int cur_rto;
+	int win_probes, non_win_probes, orig_rwnd, audit_tf, num_mk, fir;
+	unsigned int cnt_mk;
+	u_int32_t orig_flight;
+	u_int32_t tsnlast, tsnfirst;
+
+	/* none in flight now */
+	audit_tf = 0;
+	fir=0;
+	/* figure out how long a data chunk must be pending
+	 * before we can mark it ..
+	 */
+	SCTP_GETTIME_TIMEVAL(&now);
+	/* get cur rto in micro-seconds */
+	cur_rto = (((net->lastsa >> 2) + net->lastsv) >> 1);
+#ifdef SCTP_FR_LOGGING
+	sctp_log_fr(cur_rto, 0, 0, SCTP_FR_T3_MARK_TIME);
+#endif
+	cur_rto *= 1000;
+#ifdef SCTP_FR_LOGGING
+	sctp_log_fr(cur_rto, 0, 0, SCTP_FR_T3_MARK_TIME);
+#endif
+	tv.tv_sec = cur_rto / 1000000;
+	tv.tv_usec = cur_rto % 1000000;
+#ifndef __FreeBSD__
+	timersub(&now, &tv, &min_wait);
+#else
+	min_wait = now;
+	timevalsub(&min_wait, &tv);
+#endif
+	if (min_wait.tv_sec < 0 || min_wait.tv_usec < 0) {
+		/*
+		 * if we hit here, we don't
+		 * have enough seconds on the clock to account
+		 * for the RTO. We just let the lower seconds
+		 * be the bounds and don't worry about it. This
+		 * may mean we will mark a lot more than we should.
+		 */
+		min_wait.tv_sec = min_wait.tv_usec = 0;
+	}
+#ifdef SCTP_FR_LOGGING
+	sctp_log_fr(cur_rto, now.tv_sec, now.tv_usec, SCTP_FR_T3_MARK_TIME);
+	sctp_log_fr(0, min_wait.tv_sec, min_wait.tv_usec, SCTP_FR_T3_MARK_TIME);
+#endif
+	stcb->asoc.total_flight -= net->flight_size;
+	if (stcb->asoc.total_flight < 0) {
+		audit_tf = 1;
+		stcb->asoc.total_flight = 0;
+	}
+        /* Our rwnd will be incorrect here since we are not adding
+	 * back the cnt * mbuf but we will fix that down below.
+	 */
+	orig_rwnd = stcb->asoc.peers_rwnd;
+	orig_flight = net->flight_size;
+	stcb->asoc.peers_rwnd += net->flight_size;
+	net->flight_size = 0;
+	net->rto_pending = 0;
+	net->fast_retran_ip= 0;
+	win_probes = non_win_probes = 0;
+#ifdef SCTP_DEBUG
+	if (sctp_debug_on & SCTP_DEBUG_TIMER2) {
+		printf("Marking ALL un-acked for retransmission at t3-timeout\n");
+	}
+#endif /* SCTP_DEBUG */
+	/* Now on to each chunk */
+	num_mk = cnt_mk = 0;
+	tsnfirst = tsnlast = 0;
+	chk = TAILQ_FIRST(&stcb->asoc.sent_queue);
+	for (;chk != NULL; chk = tp2) {
+		tp2 = TAILQ_NEXT(chk, sctp_next);
+		if ((compare_with_wrap(stcb->asoc.last_acked_seq,
+				       chk->rec.data.TSN_seq,
+				       MAX_TSN)) ||
+		    (stcb->asoc.last_acked_seq == chk->rec.data.TSN_seq)) {
+			/* Strange case our list got out of order? */
+			printf("Our list is out of order?\n");
+			TAILQ_REMOVE(&stcb->asoc.sent_queue, chk, sctp_next);
+			if (chk->data) {
+				sctp_release_pr_sctp_chunk(stcb, chk, 0xffff,
+				    &stcb->asoc.sent_queue);
+				if (chk->flags & SCTP_PR_SCTP_BUFFER) {
+					stcb->asoc.sent_queue_cnt_removeable--;
+				}
+			}
+			stcb->asoc.sent_queue_cnt--;
+			sctp_free_remote_addr(chk->whoTo);
+			sctppcbinfo.ipi_count_chunk--;
+			if ((int)sctppcbinfo.ipi_count_chunk < 0) {
+				panic("Chunk count is going negative");
+			}
+			SCTP_ZONE_FREE(sctppcbinfo.ipi_zone_chunk, chk);
+			sctppcbinfo.ipi_gencnt_chunk++;
+			continue;
+		}
+		if ((chk->whoTo == net) && (chk->sent < SCTP_DATAGRAM_ACKED)) {
+			/* found one to mark:
+			 * If it is less than DATAGRAM_ACKED it MUST
+			 * not be a skipped or marked TSN but instead
+			 * one that is either already set for retransmission OR
+			 * one that needs retransmission.
+			 */
+
+			/* validate its been outstanding long enough */
+#ifdef SCTP_FR_LOGGING
+			sctp_log_fr(chk->rec.data.TSN_seq,
+				    chk->sent_rcv_time.tv_sec,
+				    chk->sent_rcv_time.tv_usec,
+				    SCTP_FR_T3_MARK_TIME);
+#endif
+			if (chk->sent_rcv_time.tv_sec > min_wait.tv_sec) {
+				/* we have reached a chunk that was sent some
+				 * seconds past our min.. forget it we will
+				 * find no more to send.
+				 */
+#ifdef SCTP_FR_LOGGING
+				sctp_log_fr(0,
+					    chk->sent_rcv_time.tv_sec,
+					    chk->sent_rcv_time.tv_usec,
+					    SCTP_FR_T3_STOPPED);
+#endif
+				continue;
+			} else if (chk->sent_rcv_time.tv_sec == min_wait.tv_sec) {
+				/* we must look at the micro seconds to know.
+				 */
+				if (chk->sent_rcv_time.tv_usec >= min_wait.tv_usec) {
+					/* ok it was sent after our boundary time. */
+#ifdef SCTP_FR_LOGGING
+					sctp_log_fr(0,
+						    chk->sent_rcv_time.tv_sec,
+						    chk->sent_rcv_time.tv_usec,
+						    SCTP_FR_T3_STOPPED);
+#endif
+					continue;
+				}
+			}
+			stcb->asoc.total_flight_count--;
+			if (stcb->asoc.total_flight_count < 0) {
+				stcb->asoc.total_flight_count = 0;
+			}
+			if ((chk->flags & (SCTP_PR_SCTP_ENABLED|SCTP_PR_SCTP_BUFFER)) == SCTP_PR_SCTP_ENABLED) {
+				/* Is it expired? */
+				if ((now.tv_sec > chk->rec.data.timetodrop.tv_sec) ||
+				    ((chk->rec.data.timetodrop.tv_sec == now.tv_sec) &&
+				     (now.tv_usec > chk->rec.data.timetodrop.tv_usec))) {
+					/* Yes so drop it */
+					if (chk->data) {
+						sctp_release_pr_sctp_chunk(stcb,
+						    chk,
+						    (SCTP_RESPONSE_TO_USER_REQ|SCTP_NOTIFY_DATAGRAM_SENT),
+						    &stcb->asoc.sent_queue);
+					}
+				}
+				continue;
+			}
+			if (chk->sent != SCTP_DATAGRAM_RESEND) {
+ 				stcb->asoc.sent_queue_retran_cnt++;
+ 				num_mk++;
+				if (fir == 0) {
+					fir = 1;
+#ifdef SCTP_DEBUG
+					if (sctp_debug_on & SCTP_DEBUG_TIMER1) {
+						printf("First TSN marked was %x\n",
+						       chk->rec.data.TSN_seq);
+					}
+#endif
+					tsnfirst = chk->rec.data.TSN_seq;
+				}
+				tsnlast = chk->rec.data.TSN_seq;
+#ifdef SCTP_FR_LOGGING
+				sctp_log_fr(chk->rec.data.TSN_seq, chk->snd_count,
+					    0, SCTP_FR_T3_MARKED);
+
+#endif
+			}
+			chk->sent = SCTP_DATAGRAM_RESEND;
+			/* reset the TSN for striking and other FR stuff */
+			chk->rec.data.doing_fast_retransmit = 0;
+#ifdef SCTP_DEBUG
+			if (sctp_debug_on & SCTP_DEBUG_TIMER3) {
+				printf("mark TSN:%x for retransmission\n", chk->rec.data.TSN_seq);
+			}
+#endif /* SCTP_DEBUG */
+			/* Clear any time so NO RTT is being done */
+			chk->do_rtt = 0;
+			/* Bump up the count */
+			if (compare_with_wrap(chk->rec.data.TSN_seq,
+					      stcb->asoc.t3timeout_highest_marked,
+					      MAX_TSN)) {
+				/* TSN_seq > than t3timeout so update */
+				stcb->asoc.t3timeout_highest_marked = chk->rec.data.TSN_seq;
+			}
+			if (alt != net) {
+				sctp_free_remote_addr(chk->whoTo);
+				chk->whoTo = alt;
+				alt->ref_count++;
+			}
+			if ((chk->rec.data.state_flags & SCTP_WINDOW_PROBE) !=
+			    SCTP_WINDOW_PROBE) {
+				non_win_probes++;
+			} else {
+				chk->rec.data.state_flags &= ~SCTP_WINDOW_PROBE;
+				win_probes++;
+			}
+		}
+		if (chk->sent == SCTP_DATAGRAM_RESEND) {
+			cnt_mk++;
+		}
+	}
+
+#ifdef SCTP_FR_LOGGING
+	sctp_log_fr(tsnfirst, tsnlast, num_mk, SCTP_FR_T3_TIMEOUT);
+#endif
+	/* compensate for the number we marked */
+	stcb->asoc.peers_rwnd += (num_mk /* * sizeof(struct mbuf)*/);
+
+#ifdef SCTP_DEBUG
+	if (sctp_debug_on & SCTP_DEBUG_TIMER1) {
+		if (num_mk) {
+			printf("LAST TSN marked was %x\n", tsnlast);
+			printf("Num marked for retransmission was %d peer-rwd:%ld\n",
+			       num_mk, (u_long)stcb->asoc.peers_rwnd);
+			printf("LAST TSN marked was %x\n", tsnlast);
+			printf("Num marked for retransmission was %d peer-rwd:%d\n",
+			       num_mk,
+			       (int)stcb->asoc.peers_rwnd
+				);
+		}
+	}
+#endif
+	*num_marked = num_mk;
+	if (stcb->asoc.sent_queue_retran_cnt != cnt_mk) {
+		printf("Local Audit says there are %d for retran asoc cnt:%d\n",
+		       cnt_mk, stcb->asoc.sent_queue_retran_cnt);
+#ifndef SCTP_AUDITING_ENABLED
+		stcb->asoc.sent_queue_retran_cnt = cnt_mk;
+#endif
+	}
+#ifdef SCTP_DEBUG
+	if (sctp_debug_on & SCTP_DEBUG_TIMER3) {
+		printf("**************************\n");
+	}
+#endif /* SCTP_DEBUG */
+
+	/* Now check for a ECN Echo that may be stranded */
+	TAILQ_FOREACH(chk, &stcb->asoc.control_send_queue, sctp_next) {
+		if ((chk->whoTo == net) &&
+		    (chk->rec.chunk_id == SCTP_ECN_ECHO)) {
+			sctp_free_remote_addr(chk->whoTo);
+			chk->whoTo = alt;
+			if (chk->sent != SCTP_DATAGRAM_RESEND) {
+				chk->sent = SCTP_DATAGRAM_RESEND;
+				stcb->asoc.sent_queue_retran_cnt++;
+			}
+			alt->ref_count++;
+		}
+	}
+	if ((orig_rwnd == 0) && (stcb->asoc.total_flight == 0) &&
+	    (orig_flight <= net->mtu)) {
+		/*
+		 * If the LAST packet sent was not acked and our rwnd is 0
+		 * then we are in a win-probe state.
+		 */
+		win_probes = 1;
+		non_win_probes = 0;
+#ifdef SCTP_DEBUG
+		if (sctp_debug_on & SCTP_DEBUG_TIMER1) {
+			printf("WIN_PROBE set via o_rwnd=0 tf=0 and all:%d fit in mtu:%d\n",
+			       orig_flight, net->mtu);
+		}
+#endif
+	}
+
+	if (audit_tf) {
+#ifdef SCTP_DEBUG
+		if (sctp_debug_on & SCTP_DEBUG_TIMER4) {
+			printf("Audit total flight due to negative value net:%p\n",
+			    net);
+		}
+#endif /* SCTP_DEBUG */
+		stcb->asoc.total_flight = 0;
+		stcb->asoc.total_flight_count = 0;
+		/* Clear all networks flight size */
+		TAILQ_FOREACH(lnets, &stcb->asoc.nets, sctp_next) {
+			lnets->flight_size = 0;
+#ifdef SCTP_DEBUG
+			if (sctp_debug_on & SCTP_DEBUG_TIMER4) {
+				printf("Net:%p c-f cwnd:%d ssthresh:%d\n",
+				    lnets, lnets->cwnd, lnets->ssthresh);
+			}
+#endif /* SCTP_DEBUG */
+		}
+		TAILQ_FOREACH(chk, &stcb->asoc.sent_queue, sctp_next) {
+			if (chk->sent < SCTP_DATAGRAM_RESEND) {
+				stcb->asoc.total_flight += chk->book_size;
+				chk->whoTo->flight_size += chk->book_size;
+				stcb->asoc.total_flight_count++;
+			}
+		}
+	}
+	/* Setup the ecn nonce re-sync point. We
+	 * do this since retranmissions are NOT
+	 * setup for ECN. This means that do to
+	 * Karn's rule, we don't know the total
+	 * of the peers ecn bits.
+	 */
+	chk = TAILQ_FIRST(&stcb->asoc.send_queue);
+	if (chk == NULL) {
+		stcb->asoc.nonce_resync_tsn = stcb->asoc.sending_seq;
+	} else {
+		stcb->asoc.nonce_resync_tsn = chk->rec.data.TSN_seq;
+	}
+	stcb->asoc.nonce_wait_for_ecne = 0;
+	stcb->asoc.nonce_sum_check = 0;
+	/* We return 1 if we only have a window probe outstanding */
+	if (win_probes && (non_win_probes == 0)) {
+		return (1);
+	}
+	return (0);
+}
+
+static void
+sctp_move_all_chunks_to_alt(struct sctp_tcb *stcb,
+			    struct sctp_nets *net,
+			    struct sctp_nets *alt)
+{
+	struct sctp_association *asoc;
+	struct sctp_stream_out *outs;
+	struct sctp_tmit_chunk *chk;
+
+	if (net == alt)
+		/* nothing to do */
+		return;
+
+	asoc = &stcb->asoc;
+
+	/*
+	 * now through all the streams checking for chunks sent to our
+	 * bad network.
+	 */
+	TAILQ_FOREACH(outs, &asoc->out_wheel, next_spoke) {
+		/* now clean up any chunks here */
+		TAILQ_FOREACH(chk, &outs->outqueue, sctp_next) {
+			if (chk->whoTo == net) {
+				sctp_free_remote_addr(chk->whoTo);
+				chk->whoTo = alt;
+				alt->ref_count++;
+			}
+		}
+	}
+	/* Now check the pending queue */
+	TAILQ_FOREACH(chk, &asoc->send_queue, sctp_next) {
+		if (chk->whoTo == net) {
+			sctp_free_remote_addr(chk->whoTo);
+			chk->whoTo = alt;
+			alt->ref_count++;
+		}
+	}
+
+}
+
+int
+sctp_t3rxt_timer(struct sctp_inpcb *inp,
+		 struct sctp_tcb *stcb,
+		 struct sctp_nets *net)
+{
+	struct sctp_nets *alt;
+	int win_probe, num_mk;
+
+
+#ifdef SCTP_FR_LOGGING
+	sctp_log_fr(0, 0, 0, SCTP_FR_T3_TIMEOUT);
+#endif
+	/* Find an alternate and mark those for retransmission */
+	alt = sctp_find_alternate_net(stcb, net);
+	win_probe = sctp_mark_all_for_resend(stcb, net, alt, &num_mk);
+
+	/* FR Loss recovery just ended with the T3. */
+	stcb->asoc.fast_retran_loss_recovery = 0;
+
+	/* setup the sat loss recovery that prevents
+	 * satellite cwnd advance.
+	 */
+ 	stcb->asoc.sat_t3_loss_recovery = 1;
+	stcb->asoc.sat_t3_recovery_tsn = stcb->asoc.sending_seq;
+
+	/* Backoff the timer and cwnd */
+	sctp_backoff_on_timeout(stcb, net, win_probe, num_mk);
+	if (win_probe == 0) {
+		/* We don't do normal threshold management on window probes */
+		if (sctp_threshold_management(inp, stcb, net,
+					      stcb->asoc.max_send_times)) {
+			/* Association was destroyed */
+			return (1);
+		} else {
+			if (net != stcb->asoc.primary_destination) {
+				/* send a immediate HB if our RTO is stale */
+				struct  timeval now;
+				unsigned int ms_goneby;
+				SCTP_GETTIME_TIMEVAL(&now);
+				if (net->last_sent_time.tv_sec) {
+					ms_goneby = (now.tv_sec - net->last_sent_time.tv_sec) * 1000;
+				} else {
+					ms_goneby = 0;
+				}
+				if ((ms_goneby > net->RTO) || (net->RTO == 0)) {
+					/* no recent feed back in an RTO or more, request a RTT update */
+					sctp_send_hb(stcb, 1, net);
+				}
+			}
+		}
+	} else {
+		/*
+		 * For a window probe we don't penalize the net's but only
+		 * the association. This may fail it if SACKs are not coming
+		 * back. If sack's are coming with rwnd locked at 0, we will
+		 * continue to hold things waiting for rwnd to raise
+		 */
+		if (sctp_threshold_management(inp, stcb, NULL,
+					      stcb->asoc.max_send_times)) {
+			/* Association was destroyed */
+			return (1);
+		}
+	}
+	if (net->dest_state & SCTP_ADDR_NOT_REACHABLE) {
+		/* Move all pending over too */
+		sctp_move_all_chunks_to_alt(stcb, net, alt);
+		/* Was it our primary? */
+		if ((stcb->asoc.primary_destination == net) && (alt != net)) {
+			/*
+			 * Yes, note it as such and find an alternate
+			 * note: this means HB code must use this to resent
+			 * the primary if it goes active AND if someone does
+			 * a change-primary then this flag must be cleared
+			 * from any net structures.
+			 */
+			if (sctp_set_primary_addr(stcb,
+						 (struct sockaddr *)NULL,
+						 alt) == 0) {
+				net->dest_state |= SCTP_ADDR_WAS_PRIMARY;
+				net->src_addr_selected = 0;
+			}
+		}
+	}
+	/*
+	 * Special case for cookie-echo'ed case, we don't do output
+	 * but must await the COOKIE-ACK before retransmission
+	 */
+	if (SCTP_GET_STATE(&stcb->asoc) == SCTP_STATE_COOKIE_ECHOED) {
+		/*
+		 * Here we just reset the timer and start again since we
+		 * have not established the asoc
+		 */
+#ifdef SCTP_DEBUG
+		if (sctp_debug_on & SCTP_DEBUG_TIMER1) {
+			printf("Special cookie case return\n");
+		}
+#endif /* SCTP_DEBUG */
+		sctp_timer_start(SCTP_TIMER_TYPE_SEND, inp, stcb, net);
+		return (0);
+	}
+	if (stcb->asoc.peer_supports_prsctp) {
+		struct sctp_tmit_chunk *lchk;
+		lchk = sctp_try_advance_peer_ack_point(stcb, &stcb->asoc);
+		/* C3. See if we need to send a Fwd-TSN */
+		if (compare_with_wrap(stcb->asoc.advanced_peer_ack_point,
+				      stcb->asoc.last_acked_seq, MAX_TSN)) {
+			/*
+			 * ISSUE with ECN, see FWD-TSN processing for notes
+			 * on issues that will occur when the ECN NONCE stuff
+			 * is put into SCTP for cross checking.
+			 */
+#ifdef SCTP_DEBUG
+			if (sctp_debug_on & SCTP_DEBUG_TIMER1) {
+				printf("Forward TSN time\n");
+			}
+#endif /* SCTP_DEBUG */
+			send_forward_tsn(stcb, &stcb->asoc);
+			if (lchk) {
+				/* Assure a timer is up */
+				sctp_timer_start(SCTP_TIMER_TYPE_SEND, stcb->sctp_ep, stcb, lchk->whoTo);
+			}
+		}
+	}
+	return (0);
+}
+
+int
+sctp_t1init_timer(struct sctp_inpcb *inp,
+		  struct sctp_tcb *stcb,
+		  struct sctp_nets *net)
+{
+	/* bump the thresholds */
+	if (stcb->asoc.delayed_connection) {
+		/* special hook for delayed connection. The
+		 * library did NOT complete the rest of its
+		 * sends.
+		 */
+		stcb->asoc.delayed_connection = 0;
+		sctp_send_initiate(inp, stcb);
+		return (0);
+	}
+	if (sctp_threshold_management(inp, stcb, net,
+				      stcb->asoc.max_init_times)) {
+		/* Association was destroyed */
+		return (1);
+	}
+	stcb->asoc.dropped_special_cnt = 0;
+	sctp_backoff_on_timeout(stcb, stcb->asoc.primary_destination, 1, 0);
+	if (stcb->asoc.initial_init_rto_max < net->RTO) {
+		net->RTO = stcb->asoc.initial_init_rto_max;
+	}
+	if (stcb->asoc.numnets > 1) {
+		/* If we have more than one addr use it */
+		struct sctp_nets *alt;
+		alt = sctp_find_alternate_net(stcb, stcb->asoc.primary_destination);
+		if ((alt != NULL) && (alt != stcb->asoc.primary_destination)) {
+			sctp_move_all_chunks_to_alt(stcb, stcb->asoc.primary_destination, alt);
+			stcb->asoc.primary_destination = alt;
+		}
+	}
+	/* Send out a new init */
+	sctp_send_initiate(inp, stcb);
+	return (0);
+}
+
+/*
+ * For cookie and asconf we actually need to find and mark for resend,
+ * then increment the resend counter (after all the threshold management
+ * stuff of course).
+ */
+int  sctp_cookie_timer(struct sctp_inpcb *inp,
+		       struct sctp_tcb *stcb,
+		       struct sctp_nets *net)
+{
+	struct sctp_nets *alt;
+	struct sctp_tmit_chunk *cookie;
+	/* first before all else we must find the cookie */
+	TAILQ_FOREACH(cookie, &stcb->asoc.control_send_queue, sctp_next) {
+		if (cookie->rec.chunk_id == SCTP_COOKIE_ECHO) {
+			break;
+		}
+	}
+	if (cookie == NULL) {
+		if (SCTP_GET_STATE(&stcb->asoc) == SCTP_STATE_COOKIE_ECHOED) {
+			/* FOOBAR! */
+			struct mbuf *oper;
+			MGET(oper, MB_DONTWAIT, MT_DATA);
+			if (oper) {
+				struct sctp_paramhdr *ph;
+				u_int32_t *ippp;
+
+				oper->m_len = sizeof(struct sctp_paramhdr) +
+				    sizeof(*ippp);
+				ph = mtod(oper, struct sctp_paramhdr *);
+				ph->param_type = htons(SCTP_CAUSE_PROTOCOL_VIOLATION);
+				ph->param_length = htons(oper->m_len);
+				ippp = (u_int32_t *)(ph + 1);
+				*ippp = htonl(0x40000002);
+			}
+			sctp_abort_an_association(inp, stcb, SCTP_INTERNAL_ERROR,
+			    oper);
+		}
+		return (1);
+	}
+	/* Ok we found the cookie, threshold management next */
+	if (sctp_threshold_management(inp, stcb, cookie->whoTo,
+	    stcb->asoc.max_init_times)) {
+		/* Assoc is over */
+		return (1);
+	}
+	/*
+	 * cleared theshold management now lets backoff the address &
+	 * select an alternate
+	 */
+	stcb->asoc.dropped_special_cnt = 0;
+	sctp_backoff_on_timeout(stcb, cookie->whoTo, 1, 0);
+	alt = sctp_find_alternate_net(stcb, cookie->whoTo);
+	if (alt != cookie->whoTo) {
+		sctp_free_remote_addr(cookie->whoTo);
+		cookie->whoTo = alt;
+		alt->ref_count++;
+	}
+	/* Now mark the retran info */
+	if (cookie->sent != SCTP_DATAGRAM_RESEND) {
+		stcb->asoc.sent_queue_retran_cnt++;
+	}
+	cookie->sent = SCTP_DATAGRAM_RESEND;
+	/*
+	 * Now call the output routine to kick out the cookie again, Note we
+	 * don't mark any chunks for retran so that FR will need to kick in
+	 * to move these (or a send timer).
+	 */
+	return (0);
+}
+
+int sctp_strreset_timer(struct sctp_inpcb *inp, struct sctp_tcb *stcb,
+    struct sctp_nets *net)
+{
+	struct sctp_nets *alt;
+	struct sctp_tmit_chunk *strrst, *chk;
+	struct sctp_stream_reset_req *strreq;
+	/* find the existing STRRESET */
+	TAILQ_FOREACH(strrst, &stcb->asoc.control_send_queue,
+		      sctp_next) {
+		if (strrst->rec.chunk_id == SCTP_STREAM_RESET) {
+			/* is it what we want */
+			strreq = mtod(strrst->data, struct sctp_stream_reset_req *);
+			if (strreq->sr_req.ph.param_type == ntohs(SCTP_STR_RESET_REQUEST)) {
+				break;
+			}
+		}
+	}
+	if (strrst == NULL) {
+#ifdef SCTP_DEBUG
+		if (sctp_debug_on & SCTP_DEBUG_TIMER1) {
+			printf("Strange, strreset timer fires, but I can't find an str-reset?\n");
+		}
+#endif /* SCTP_DEBUG */
+		return (0);
+	}
+	/* do threshold management */
+	if (sctp_threshold_management(inp, stcb, strrst->whoTo,
+				      stcb->asoc.max_send_times)) {
+		/* Assoc is over */
+		return (1);
+	}
+
+	/*
+	 * cleared theshold management
+	 * now lets backoff the address & select an alternate
+	 */
+	sctp_backoff_on_timeout(stcb, strrst->whoTo, 1, 0);
+	alt = sctp_find_alternate_net(stcb, strrst->whoTo);
+	sctp_free_remote_addr(strrst->whoTo);
+	strrst->whoTo = alt;
+	alt->ref_count++;
+
+	/* See if a ECN Echo is also stranded */
+	TAILQ_FOREACH(chk, &stcb->asoc.control_send_queue, sctp_next) {
+		if ((chk->whoTo == net) &&
+		    (chk->rec.chunk_id == SCTP_ECN_ECHO)) {
+			sctp_free_remote_addr(chk->whoTo);
+			if (chk->sent != SCTP_DATAGRAM_RESEND) {
+				chk->sent = SCTP_DATAGRAM_RESEND;
+				stcb->asoc.sent_queue_retran_cnt++;
+			}
+			chk->whoTo = alt;
+			alt->ref_count++;
+		}
+	}
+	if (net->dest_state & SCTP_ADDR_NOT_REACHABLE) {
+		/*
+		 * If the address went un-reachable, we need to move
+		 * to alternates for ALL chk's in queue
+		 */
+		sctp_move_all_chunks_to_alt(stcb, net, alt);
+	}
+	/* mark the retran info */
+	if (strrst->sent != SCTP_DATAGRAM_RESEND)
+		stcb->asoc.sent_queue_retran_cnt++;
+	strrst->sent = SCTP_DATAGRAM_RESEND;
+
+	/* restart the timer */
+	sctp_timer_start(SCTP_TIMER_TYPE_STRRESET, inp, stcb, strrst->whoTo);
+	return (0);
+}
+
+int sctp_asconf_timer(struct sctp_inpcb *inp, struct sctp_tcb *stcb,
+    struct sctp_nets *net)
+{
+	struct sctp_nets *alt;
+	struct sctp_tmit_chunk *asconf, *chk;
+
+	/* is this the first send, or a retransmission? */
+	if (stcb->asoc.asconf_sent == 0) {
+		/* compose a new ASCONF chunk and send it */
+		sctp_send_asconf(stcb, net);
+	} else {
+		/* Retransmission of the existing ASCONF needed... */
+
+		/* find the existing ASCONF */
+		TAILQ_FOREACH(asconf, &stcb->asoc.control_send_queue,
+		    sctp_next) {
+			if (asconf->rec.chunk_id == SCTP_ASCONF) {
+				break;
+			}
+		}
+		if (asconf == NULL) {
+#ifdef SCTP_DEBUG
+			if (sctp_debug_on & SCTP_DEBUG_TIMER1) {
+				printf("Strange, asconf timer fires, but I can't find an asconf?\n");
+			}
+#endif /* SCTP_DEBUG */
+			return (0);
+		}
+		/* do threshold management */
+		if (sctp_threshold_management(inp, stcb, asconf->whoTo,
+		    stcb->asoc.max_send_times)) {
+			/* Assoc is over */
+			return (1);
+		}
+
+		/* PETER? FIX? How will the following code ever run? If
+		 * the max_send_times is hit, threshold managment will
+		 * blow away the association?
+		 */
+		if (asconf->snd_count > stcb->asoc.max_send_times) {
+			/*
+			 * Something is rotten, peer is not responding to
+			 * ASCONFs but maybe is to data etc.  e.g. it is not
+			 * properly handling the chunk type upper bits
+			 * Mark this peer as ASCONF incapable and cleanup
+			 */
+#ifdef SCTP_DEBUG
+			if (sctp_debug_on & SCTP_DEBUG_TIMER1) {
+				printf("asconf_timer: Peer has not responded to our repeated ASCONFs\n");
+			}
+#endif /* SCTP_DEBUG */
+			sctp_asconf_cleanup(stcb, net);
+			return (0);
+		}
+		/*
+		 * cleared theshold management
+		 * now lets backoff the address & select an alternate
+		 */
+		sctp_backoff_on_timeout(stcb, asconf->whoTo, 1, 0);
+		alt = sctp_find_alternate_net(stcb, asconf->whoTo);
+		sctp_free_remote_addr(asconf->whoTo);
+		asconf->whoTo = alt;
+		alt->ref_count++;
+
+		/* See if a ECN Echo is also stranded */
+		TAILQ_FOREACH(chk, &stcb->asoc.control_send_queue, sctp_next) {
+			if ((chk->whoTo == net) &&
+			    (chk->rec.chunk_id == SCTP_ECN_ECHO)) {
+				sctp_free_remote_addr(chk->whoTo);
+				chk->whoTo = alt;
+				if (chk->sent != SCTP_DATAGRAM_RESEND) {
+					chk->sent = SCTP_DATAGRAM_RESEND;
+					stcb->asoc.sent_queue_retran_cnt++;
+				}
+				alt->ref_count++;
+
+			}
+		}
+		if (net->dest_state & SCTP_ADDR_NOT_REACHABLE) {
+			/*
+			 * If the address went un-reachable, we need to move
+			 * to alternates for ALL chk's in queue
+			 */
+			sctp_move_all_chunks_to_alt(stcb, net, alt);
+		}
+		/* mark the retran info */
+		if (asconf->sent != SCTP_DATAGRAM_RESEND)
+			stcb->asoc.sent_queue_retran_cnt++;
+		asconf->sent = SCTP_DATAGRAM_RESEND;
+	}
+	return (0);
+}
+
+/*
+ * For the shutdown and shutdown-ack, we do not keep one around on the
+ * control queue. This means we must generate a new one and call the general
+ * chunk output routine, AFTER having done threshold
+ * management.
+ */
+int
+sctp_shutdown_timer(struct sctp_inpcb *inp, struct sctp_tcb *stcb,
+    struct sctp_nets *net)
+{
+	struct sctp_nets *alt;
+	/* first threshold managment */
+	if (sctp_threshold_management(inp, stcb, net, stcb->asoc.max_send_times)) {
+		/* Assoc is over */
+		return (1);
+	}
+	/* second select an alternative */
+	alt = sctp_find_alternate_net(stcb, net);
+
+	/* third generate a shutdown into the queue for out net */
+#ifdef SCTP_DEBUG
+	if (sctp_debug_on & SCTP_DEBUG_OUTPUT4) {
+		printf("%s:%d sends a shutdown\n",
+		       __FILE__,
+		       __LINE__
+			);
+	}
+#endif
+	if (alt) {
+		sctp_send_shutdown(stcb, alt);
+	} else {
+		/* if alt is NULL, there is no dest
+		 * to send to??
+		 */
+		return (0);
+	}
+	/* fourth restart timer */
+	sctp_timer_start(SCTP_TIMER_TYPE_SHUTDOWN, inp, stcb, alt);
+	return (0);
+}
+
+int sctp_shutdownack_timer(struct sctp_inpcb *inp, struct sctp_tcb *stcb,
+    struct sctp_nets *net)
+{
+	struct sctp_nets *alt;
+	/* first threshold managment */
+	if (sctp_threshold_management(inp, stcb, net, stcb->asoc.max_send_times)) {
+		/* Assoc is over */
+		return (1);
+	}
+	/* second select an alternative */
+	alt = sctp_find_alternate_net(stcb, net);
+
+	/* third generate a shutdown into the queue for out net */
+	sctp_send_shutdown_ack(stcb, alt);
+
+	/* fourth restart timer */
+	sctp_timer_start(SCTP_TIMER_TYPE_SHUTDOWNACK, inp, stcb, alt);
+	return (0);
+}
+
+static void
+sctp_audit_stream_queues_for_size(struct sctp_inpcb *inp,
+				  struct sctp_tcb *stcb)
+{
+	struct sctp_stream_out *outs;
+	struct sctp_tmit_chunk *chk;
+	unsigned int chks_in_queue=0;
+
+	if ((stcb == NULL) || (inp == NULL))
+		return;
+	if (TAILQ_EMPTY(&stcb->asoc.out_wheel)) {
+		printf("Strange, out_wheel empty nothing on sent/send and  tot=%lu?\n",
+		    (u_long)stcb->asoc.total_output_queue_size);
+		stcb->asoc.total_output_queue_size = 0;
+		return;
+	}
+	if (stcb->asoc.sent_queue_retran_cnt) {
+		printf("Hmm, sent_queue_retran_cnt is non-zero %d\n",
+		    stcb->asoc.sent_queue_retran_cnt);
+		stcb->asoc.sent_queue_retran_cnt = 0;
+	}
+	/* Check to see if some data queued, if so report it */
+	TAILQ_FOREACH(outs, &stcb->asoc.out_wheel, next_spoke) {
+		if (!TAILQ_EMPTY(&outs->outqueue)) {
+			TAILQ_FOREACH(chk, &outs->outqueue, sctp_next) {
+				chks_in_queue++;
+			}
+		}
+	}
+	if (chks_in_queue != stcb->asoc.stream_queue_cnt) {
+		printf("Hmm, stream queue cnt at %d I counted %d in stream out wheel\n",
+		       stcb->asoc.stream_queue_cnt, chks_in_queue);
+	}
+	if (chks_in_queue) {
+		/* call the output queue function */
+		sctp_chunk_output(inp, stcb, 1);
+		if ((TAILQ_EMPTY(&stcb->asoc.send_queue)) &&
+		    (TAILQ_EMPTY(&stcb->asoc.sent_queue))) {
+			/* Probably should go in and make it go back through and add fragments allowed */
+			printf("Still nothing moved %d chunks are stuck\n", chks_in_queue);
+		}
+	} else {
+		printf("Found no chunks on any queue tot:%lu\n",
+		    (u_long)stcb->asoc.total_output_queue_size);
+		stcb->asoc.total_output_queue_size = 0;
+	}
+}
+
+int
+sctp_heartbeat_timer(struct sctp_inpcb *inp, struct sctp_tcb *stcb,
+    struct sctp_nets *net)
+{
+	int cnt_of_unconf=0;
+
+	if (net) {
+		if (net->hb_responded == 0) {
+			sctp_backoff_on_timeout(stcb, net, 1, 0);
+		}
+		/* Zero PBA, if it needs it */
+		if (net->partial_bytes_acked) {
+			net->partial_bytes_acked = 0;
+		}
+	}
+	TAILQ_FOREACH(net, &stcb->asoc.nets, sctp_next) {
+		if ((net->dest_state & SCTP_ADDR_UNCONFIRMED) &&
+		    (net->dest_state & SCTP_ADDR_REACHABLE)) {
+			cnt_of_unconf++;
+		}
+	}
+	if ((stcb->asoc.total_output_queue_size > 0) &&
+	    (TAILQ_EMPTY(&stcb->asoc.send_queue)) &&
+	    (TAILQ_EMPTY(&stcb->asoc.sent_queue))) {
+		sctp_audit_stream_queues_for_size(inp, stcb);
+	}
+	/* Send a new HB, this will do threshold managment, pick a new dest */
+	if (sctp_send_hb(stcb, 0, NULL) < 0) {
+		return (1);
+	}
+	if (cnt_of_unconf > 1) {
+		/*
+		 * this will send out extra hb's up to maxburst if
+		 * there are any unconfirmed addresses.
+		 */
+		int cnt_sent = 1;
+		while ((cnt_sent < stcb->asoc.max_burst) && (cnt_of_unconf > 1)) {
+			if (sctp_send_hb(stcb, 0, NULL) == 0)
+				break;
+			cnt_of_unconf--;
+			cnt_sent++;
+		}
+	}
+	return (0);
+}
+
+#define SCTP_NUMBER_OF_MTU_SIZES 18
+static u_int32_t mtu_sizes[]={
+	68,
+	296,
+	508,
+	512,
+	544,
+	576,
+	1006,
+	1492,
+	1500,
+	1536,
+	2002,
+	2048,
+	4352,
+	4464,
+	8166,
+	17914,
+	32000,
+	65535
+};
+
+
+static u_int32_t
+sctp_getnext_mtu(struct sctp_inpcb *inp, u_int32_t cur_mtu)
+{
+	/* select another MTU that is just bigger than this one */
+	int i;
+
+	for (i = 0; i < SCTP_NUMBER_OF_MTU_SIZES; i++) {
+		if (cur_mtu < mtu_sizes[i]) {
+		    /* no max_mtu is bigger than this one */
+		    return (mtu_sizes[i]);
+		}
+	}
+	/* here return the highest allowable */
+	return (cur_mtu);
+}
+
+
+void sctp_pathmtu_timer(struct sctp_inpcb *inp,
+			struct sctp_tcb *stcb,
+			struct sctp_nets *net)
+{
+	u_int32_t next_mtu;
+
+	/* restart the timer in any case */
+	next_mtu = sctp_getnext_mtu(inp, net->mtu);
+	if (next_mtu <= net->mtu) {
+	    /* nothing to do */
+	    return;
+	}
+	if (net->ro.ro_rt != NULL) {
+		/* only if we have a route and interface do we
+		 * set anything. Note we always restart
+		 * the timer though just in case it is updated
+		 * (i.e. the ifp) or route/ifp is populated.
+		 */
+		if (net->ro.ro_rt->rt_ifp != NULL) {
+			if (net->ro.ro_rt->rt_ifp->if_mtu > next_mtu) {
+				/* ok it will fit out the door */
+				net->mtu = next_mtu;
+			}
+		}
+	}
+	/* restart the timer */
+	sctp_timer_start(SCTP_TIMER_TYPE_PATHMTURAISE, inp, stcb, net);
+}
+
+void sctp_autoclose_timer(struct sctp_inpcb *inp,
+			  struct sctp_tcb *stcb,
+			  struct sctp_nets *net)
+{
+	struct timeval tn, *tim_touse;
+	struct sctp_association *asoc;
+	int ticks_gone_by;
+
+	SCTP_GETTIME_TIMEVAL(&tn);
+	if (stcb->asoc.sctp_autoclose_ticks &&
+	    (inp->sctp_flags & SCTP_PCB_FLAGS_AUTOCLOSE)) {
+		/* Auto close is on */
+		asoc = &stcb->asoc;
+		/* pick the time to use */
+		if (asoc->time_last_rcvd.tv_sec >
+		    asoc->time_last_sent.tv_sec) {
+			tim_touse = &asoc->time_last_rcvd;
+		} else {
+			tim_touse = &asoc->time_last_sent;
+		}
+		/* Now has long enough transpired to autoclose? */
+		ticks_gone_by = ((tn.tv_sec - tim_touse->tv_sec) * hz);
+		if ((ticks_gone_by > 0) &&
+		    (ticks_gone_by >= (int)asoc->sctp_autoclose_ticks)) {
+			/*
+			 * autoclose time has hit, call the output routine,
+			 * which should do nothing just to be SURE we don't
+			 * have hanging data. We can then safely check the
+			 * queues and know that we are clear to send shutdown
+			 */
+			sctp_chunk_output(inp, stcb, 9);
+			/* Are we clean? */
+			if (TAILQ_EMPTY(&asoc->send_queue) &&
+			    TAILQ_EMPTY(&asoc->sent_queue)) {
+				/*
+				 * there is nothing queued to send,
+				 * so I'm done...
+				 */
+				if (SCTP_GET_STATE(asoc) !=
+				    SCTP_STATE_SHUTDOWN_SENT) {
+					/* only send SHUTDOWN 1st time thru */
+#ifdef SCTP_DEBUG
+					if (sctp_debug_on & SCTP_DEBUG_OUTPUT4) {
+						printf("%s:%d sends a shutdown\n",
+						       __FILE__,
+						       __LINE__
+							);
+					}
+#endif
+					sctp_send_shutdown(stcb, stcb->asoc.primary_destination);
+					asoc->state = SCTP_STATE_SHUTDOWN_SENT;
+					sctp_timer_start(SCTP_TIMER_TYPE_SHUTDOWN,
+					    stcb->sctp_ep, stcb,
+					    asoc->primary_destination);
+					sctp_timer_start(SCTP_TIMER_TYPE_SHUTDOWNGUARD,
+					    stcb->sctp_ep, stcb,
+					    asoc->primary_destination);
+				}
+			}
+		} else {
+			/*
+			 * No auto close at this time, reset t-o to
+			 * check later
+			 */
+			int tmp;
+			/* fool the timer startup to use the time left */
+			tmp = asoc->sctp_autoclose_ticks;
+			asoc->sctp_autoclose_ticks -= ticks_gone_by;
+			sctp_timer_start(SCTP_TIMER_TYPE_AUTOCLOSE, inp, stcb,
+					 net);
+			/* restore the real tick value */
+			asoc->sctp_autoclose_ticks = tmp;
+		}
+	}
+}
+
+void
+sctp_iterator_timer(struct sctp_iterator *it)
+{
+	int cnt= 0;
+	/* only one iterator can run at a
+	 * time. This is the only way we
+	 * can cleanly pull ep's from underneath
+	 * all the running interators when a
+	 * ep is freed.
+	 */
+ 	SCTP_ITERATOR_LOCK();
+	if (it->inp == NULL) {
+		/* iterator is complete */
+	done_with_iterator:
+		SCTP_ITERATOR_UNLOCK();
+		SCTP_INP_INFO_WLOCK();
+		LIST_REMOVE(it, sctp_nxt_itr);
+		/* stopping the callout is not needed, in theory,
+		 * but I am paranoid.
+		 */
+		SCTP_INP_INFO_WUNLOCK();
+		callout_stop(&it->tmr.timer);
+		if (it->function_atend != NULL) {
+			(*it->function_atend)(it->pointer, it->val);
+		}
+		FREE(it, M_PCB);
+		return;
+	}
+ select_a_new_ep:
+	SCTP_INP_WLOCK(it->inp);
+	while ((it->pcb_flags) && ((it->inp->sctp_flags & it->pcb_flags) != it->pcb_flags)) {
+		/* we do not like this ep */
+		if (it->iterator_flags & SCTP_ITERATOR_DO_SINGLE_INP) {
+			SCTP_INP_WUNLOCK(it->inp);
+			goto done_with_iterator;
+		}
+		SCTP_INP_WUNLOCK(it->inp);
+		it->inp = LIST_NEXT(it->inp, sctp_list);
+		if (it->inp == NULL) {
+			goto done_with_iterator;
+		}
+		SCTP_INP_WLOCK(it->inp);
+	}
+	if ((it->inp->inp_starting_point_for_iterator != NULL) &&
+	    (it->inp->inp_starting_point_for_iterator != it)) {
+		printf("Iterator collision, we must wait for other iterator at %x\n",
+		       (u_int)it->inp);
+		SCTP_INP_WUNLOCK(it->inp);
+		goto start_timer_return;
+	}
+	/* now we do the actual write to this guy */
+	it->inp->inp_starting_point_for_iterator = it;
+	SCTP_INP_WUNLOCK(it->inp);
+	SCTP_INP_RLOCK(it->inp);
+	/* if we reach here we found a inp acceptable, now through each
+	 * one that has the association in the right state
+	 */
+	if (it->stcb == NULL) {
+		it->stcb = LIST_FIRST(&it->inp->sctp_asoc_list);
+	}
+	if (it->stcb->asoc.stcb_starting_point_for_iterator == it) {
+		it->stcb->asoc.stcb_starting_point_for_iterator = NULL;
+	}
+	while (it->stcb) {
+		SCTP_TCB_LOCK(it->stcb);
+		if (it->asoc_state && ((it->stcb->asoc.state & it->asoc_state) != it->asoc_state)) {
+			SCTP_TCB_UNLOCK(it->stcb);
+			it->stcb = LIST_NEXT(it->stcb, sctp_tcblist);
+			continue;
+		}
+		cnt++;
+		/* run function on this one */
+		SCTP_INP_RUNLOCK(it->inp);
+		(*it->function_toapply)(it->inp, it->stcb, it->pointer, it->val);
+		sctp_chunk_output(it->inp, it->stcb, 1);
+		SCTP_TCB_UNLOCK(it->stcb);
+		/* see if we have limited out */
+		if (cnt > SCTP_MAX_ITERATOR_AT_ONCE) {
+			it->stcb->asoc.stcb_starting_point_for_iterator = it;
+		start_timer_return:
+			SCTP_ITERATOR_UNLOCK();
+			sctp_timer_start(SCTP_TIMER_TYPE_ITERATOR, (struct sctp_inpcb *)it, NULL, NULL);
+			return;
+		}
+		SCTP_INP_RLOCK(it->inp);
+		it->stcb = LIST_NEXT(it->stcb, sctp_tcblist);
+	}
+	/* if we reach here, we ran out of stcb's in the inp we are looking at */
+	SCTP_INP_RUNLOCK(it->inp);
+	SCTP_INP_WLOCK(it->inp);
+	it->inp->inp_starting_point_for_iterator = NULL;
+	SCTP_INP_WUNLOCK(it->inp);
+	if (it->iterator_flags & SCTP_ITERATOR_DO_SINGLE_INP) {
+		it->inp = NULL;
+	} else {
+		SCTP_INP_INFO_RLOCK();
+		it->inp = LIST_NEXT(it->inp, sctp_list);
+		SCTP_INP_INFO_RUNLOCK();
+	}
+	if (it->inp == NULL) {
+		goto done_with_iterator;
+	}
+	goto select_a_new_ep;
+}
Index: sys/netinet/sctp_timer.h
===================================================================
RCS file: sys/netinet/sctp_timer.h
diff -N sys/netinet/sctp_timer.h
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ sys/netinet/sctp_timer.h	18 Jun 2005 16:05:50 -0000
@@ -0,0 +1,73 @@
+/*	$KAME: sctp_timer.h,v 1.5 2004/08/17 04:06:20 itojun Exp $	*/
+
+#ifndef __sctp_timer_h__
+#define __sctp_timer_h__
+
+/*
+ * Copyright (C) 2002, 2004 Cisco Systems Inc,
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the project nor the names of its contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#if defined(_KERNEL) || (defined(__APPLE__) && defined(KERNEL))
+struct sctp_nets *sctp_find_alternate_net(struct sctp_tcb *,
+	struct sctp_nets *);
+
+int sctp_threshold_management(struct sctp_inpcb *, struct sctp_tcb *,
+	struct sctp_nets *, uint16_t);
+
+int  sctp_t3rxt_timer(struct sctp_inpcb *, struct sctp_tcb *,
+	struct sctp_nets *);
+int  sctp_t1init_timer(struct sctp_inpcb *, struct sctp_tcb *,
+	struct sctp_nets *);
+int  sctp_shutdown_timer(struct sctp_inpcb *, struct sctp_tcb *,
+	struct sctp_nets *);
+int  sctp_heartbeat_timer(struct sctp_inpcb *, struct sctp_tcb *,
+	struct sctp_nets *);
+
+int  sctp_cookie_timer(struct sctp_inpcb *, struct sctp_tcb *,
+	struct sctp_nets *);
+
+void sctp_pathmtu_timer(struct sctp_inpcb *, struct sctp_tcb *,
+	struct sctp_nets *);
+
+int  sctp_shutdownack_timer(struct sctp_inpcb *, struct sctp_tcb *,
+	struct sctp_nets *);
+int  sctp_strreset_timer(struct sctp_inpcb *inp, struct sctp_tcb *stcb,
+			 struct sctp_nets *net);
+
+int  sctp_asconf_timer(struct sctp_inpcb *, struct sctp_tcb *,
+	struct sctp_nets *);
+
+void sctp_autoclose_timer(struct sctp_inpcb *, struct sctp_tcb *,
+	struct sctp_nets *net);
+
+void sctp_audit_retranmission_queue(struct sctp_association *);
+
+void sctp_iterator_timer(struct sctp_iterator *it);
+
+#endif
+#endif
Index: sys/netinet/sctp_uio.h
===================================================================
RCS file: sys/netinet/sctp_uio.h
diff -N sys/netinet/sctp_uio.h
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ sys/netinet/sctp_uio.h	18 Jun 2005 16:05:50 -0000
@@ -0,0 +1,523 @@
+/*	$KAME: sctp_uio.h,v 1.10 2004/08/17 04:06:20 itojun Exp $	*/
+
+#ifndef __sctp_uio_h__
+#define __sctp_uio_h__
+
+/*
+ * Copyright (c) 2001, 2002, 2003, 2004 Cisco Systems, Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *      This product includes software developed by Cisco Systems, Inc.
+ * 4. Neither the name of the project nor the names of its contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY CISCO SYSTEMS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL CISCO SYSTEMS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/types.h>
+#include <sys/socket.h>
+
+typedef caddr_t sctp_assoc_t;
+
+/* On/Off setup for subscription to events */
+struct sctp_event_subscribe {
+	u_int8_t sctp_data_io_event;
+	u_int8_t sctp_association_event;
+	u_int8_t sctp_address_event;
+	u_int8_t sctp_send_failure_event;
+	u_int8_t sctp_peer_error_event;
+	u_int8_t sctp_shutdown_event;
+	u_int8_t sctp_partial_delivery_event;
+	u_int8_t sctp_adaption_layer_event;
+	u_int8_t sctp_stream_reset_events;
+};
+
+/* ancillary data types */
+#define SCTP_INIT	0x0001
+#define SCTP_SNDRCV	0x0002
+
+/*
+ * ancillary data structures
+ */
+struct sctp_initmsg {
+	u_int32_t sinit_num_ostreams;
+	u_int32_t sinit_max_instreams;
+	u_int16_t sinit_max_attempts;
+	u_int16_t sinit_max_init_timeo;
+};
+
+struct sctp_sndrcvinfo {
+	u_int16_t sinfo_stream;
+	u_int16_t sinfo_ssn;
+	u_int16_t sinfo_flags;
+	u_int32_t sinfo_ppid;
+	u_int32_t sinfo_context;
+	u_int32_t sinfo_timetolive;
+	u_int32_t sinfo_tsn;
+	u_int32_t sinfo_cumtsn;
+	sctp_assoc_t sinfo_assoc_id;
+};
+
+struct sctp_snd_all_completes {
+	u_int16_t sall_stream;
+	u_int16_t sall_flags;
+	u_int32_t sall_ppid;
+	u_int32_t sall_context;
+	u_int32_t sall_num_sent;
+	u_int32_t sall_num_failed;
+};
+
+/* send/recv flags */
+/* MSG_EOF (0x0100) is reused from sys/socket.h */
+#define MSG_SENDALL     0x0200
+#define MSG_PR_SCTP_TTL	0x0400	/* Partial Reliable on this msg */
+#define MSG_PR_SCTP_BUF	0x0800	/* Buffer based PR-SCTP */
+#ifndef MSG_EOF
+#define MSG_EOF 	0x1000	/* Start shutdown procedures */
+#endif
+#define MSG_UNORDERED 	0x2000	/* Message is un-ordered */
+#define MSG_ADDR_OVER	0x4000	/* Override the primary-address */
+#define MSG_ABORT	0x8000	/* Send an ABORT to peer */
+
+/* Stat's */
+struct sctp_pcbinfo {
+	u_int32_t ep_count;
+	u_int32_t asoc_count;
+	u_int32_t laddr_count;
+	u_int32_t raddr_count;
+	u_int32_t chk_count;
+	u_int32_t sockq_count;
+	u_int32_t mbuf_track;
+};
+
+struct sctp_sockstat {
+	sctp_assoc_t ss_assoc_id;
+	u_int32_t ss_total_sndbuf;
+	u_int32_t ss_total_mbuf_sndbuf;
+	u_int32_t ss_total_recv_buf;
+};
+
+/*
+ * notification event structures
+ */
+
+
+/* association change events */
+
+struct sctp_assoc_change {
+	u_int16_t sac_type;
+	u_int16_t sac_flags;
+	u_int32_t sac_length;
+	u_int16_t sac_state;
+	u_int16_t sac_error;
+	u_int16_t sac_outbound_streams;
+	u_int16_t sac_inbound_streams;
+	sctp_assoc_t sac_assoc_id;
+};
+/* sac_state values */
+
+#define SCTP_COMM_UP		0x0001
+#define SCTP_COMM_LOST		0x0002
+#define SCTP_RESTART		0x0003
+#define SCTP_SHUTDOWN_COMP	0x0004
+#define SCTP_CANT_STR_ASSOC	0x0005
+
+
+/* Address events */
+struct sctp_paddr_change {
+	u_int16_t spc_type;
+	u_int16_t spc_flags;
+	u_int32_t spc_length;
+	struct sockaddr_storage spc_aaddr;
+	u_int32_t spc_state;
+	u_int32_t spc_error;
+	sctp_assoc_t spc_assoc_id;
+};
+/* paddr state values */
+#define SCTP_ADDR_AVAILABLE	0x0001
+#define SCTP_ADDR_UNREACHABLE	0x0002
+#define SCTP_ADDR_REMOVED	0x0003
+#define SCTP_ADDR_ADDED		0x0004
+#define SCTP_ADDR_MADE_PRIM	0x0005
+#define SCTP_ADDR_CONFIRMED	0x0006
+
+/*
+ * CAUTION: these are user exposed SCTP addr reachability states
+ *          must be compatible with SCTP_ADDR states in sctp_constants.h
+ */
+#ifdef SCTP_ACTIVE
+#undef SCTP_ACTIVE
+#endif
+#define SCTP_ACTIVE		0x0001	/* SCTP_ADDR_REACHABLE */
+
+#ifdef SCTP_INACTIVE
+#undef SCTP_INACTIVE
+#endif
+#define SCTP_INACTIVE		0x0002	/* SCTP_ADDR_NOT_REACHABLE */
+
+
+#ifdef SCTP_UNCONFIRMED
+#undef SCTP_UNCONFIRMED
+#endif
+#define SCTP_UNCONFIRMED	0x0200  /* SCTP_ADDR_UNCONFIRMED */
+
+#ifdef SCTP_NOHEARTBEAT
+#undef SCTP_NOHEARTBEAT
+#endif
+#define SCTP_NOHEARTBEAT        0x0040 /* SCTP_ADDR_NOHB */
+
+
+
+
+/* remote error events */
+struct sctp_remote_error {
+	u_int16_t sre_type;
+	u_int16_t sre_flags;
+	u_int32_t sre_length;
+	u_int16_t sre_error;
+	sctp_assoc_t sre_assoc_id;
+	u_int8_t  sre_data[4];
+};
+
+/* data send failure event */
+struct sctp_send_failed {
+	u_int16_t ssf_type;
+	u_int16_t ssf_flags;
+	u_int32_t ssf_length;
+	u_int32_t ssf_error;
+	struct sctp_sndrcvinfo ssf_info;
+	sctp_assoc_t ssf_assoc_id;
+	u_int8_t ssf_data[4];
+};
+
+/* flag that indicates state of data */
+#define SCTP_DATA_UNSENT	0x0001	/* inqueue never on wire */
+#define SCTP_DATA_SENT		0x0002	/* on wire at failure */
+
+/* shutdown event */
+struct sctp_shutdown_event {
+	u_int16_t	sse_type;
+	u_int16_t	sse_flags;
+	u_int32_t	sse_length;
+	sctp_assoc_t	sse_assoc_id;
+};
+
+/* Adaption layer indication stuff */
+struct sctp_adaption_event {
+	u_int16_t	sai_type;
+	u_int16_t	sai_flags;
+	u_int32_t	sai_length;
+	u_int32_t	sai_adaption_ind;
+	sctp_assoc_t	sai_assoc_id;
+};
+
+struct sctp_setadaption {
+	u_int32_t	ssb_adaption_ind;
+};
+
+/* pdapi indications */
+struct sctp_pdapi_event {
+	u_int16_t	pdapi_type;
+	u_int16_t	pdapi_flags;
+	u_int32_t	pdapi_length;
+	u_int32_t	pdapi_indication;
+	sctp_assoc_t	pdapi_assoc_id;
+};
+
+
+#define SCTP_PARTIAL_DELIVERY_ABORTED	0x0001
+
+/* stream reset stuff */
+
+struct sctp_stream_reset_event {
+	u_int16_t	strreset_type;
+	u_int16_t	strreset_flags;
+	u_int32_t	strreset_length;
+	sctp_assoc_t    strreset_assoc_id;
+	u_int16_t       strreset_list[0];
+};
+
+/* flags in strreset_flags filed */
+#define SCTP_STRRESET_INBOUND_STR  0x0001
+#define SCTP_STRRESET_OUTBOUND_STR 0x0002
+#define SCTP_STRRESET_ALL_STREAMS  0x0004
+#define SCTP_STRRESET_STREAM_LIST  0x0008
+
+#define MAX_ASOC_IDS_RET 255
+
+struct sctp_assoc_ids {
+	u_int16_t asls_assoc_start;	/* array of index's start at 0 */
+	u_int8_t asls_numb_present;
+	u_int8_t asls_more_to_get;
+	sctp_assoc_t asls_assoc_id[MAX_ASOC_IDS_RET];
+};
+
+/* notification types */
+#define SCTP_ASSOC_CHANGE		0x0001
+#define SCTP_PEER_ADDR_CHANGE		0x0002
+#define SCTP_REMOTE_ERROR		0x0003
+#define SCTP_SEND_FAILED		0x0004
+#define SCTP_SHUTDOWN_EVENT		0x0005
+#define SCTP_ADAPTION_INDICATION	0x0006
+#define SCTP_PARTIAL_DELIVERY_EVENT	0x0007
+#define SCTP_STREAM_RESET_EVENT         0x0008
+
+
+
+struct sctp_tlv {
+	u_int16_t sn_type;
+	u_int16_t sn_flags;
+	u_int32_t sn_length;
+};
+
+
+/* notification event */
+union sctp_notification {
+	struct sctp_tlv sn_header;
+	struct sctp_assoc_change sn_assoc_change;
+	struct sctp_paddr_change sn_paddr_change;
+	struct sctp_remote_error sn_remote_error;
+	struct sctp_send_failed	sn_send_failed;
+	struct sctp_shutdown_event sn_shutdown_event;
+	struct sctp_adaption_event sn_adaption_event;
+	struct sctp_pdapi_event sn_pdapi_event;
+	struct sctp_stream_reset_event sn_strreset_event;
+};
+
+/*
+ * socket option structs
+ */
+#define SCTP_ISSUE_HB 0xffffffff	/* get a on-demand hb */
+#define SCTP_NO_HB    0x0		/* turn off hb's */
+
+struct sctp_paddrparams {
+	sctp_assoc_t spp_assoc_id;
+	struct sockaddr_storage spp_address;
+	u_int32_t spp_hbinterval;
+	u_int16_t spp_pathmaxrxt;
+};
+
+struct sctp_paddrinfo {
+	sctp_assoc_t spinfo_assoc_id;
+	struct sockaddr_storage spinfo_address;
+	int32_t spinfo_state;
+	u_int32_t spinfo_cwnd;
+	u_int32_t spinfo_srtt;
+	u_int32_t spinfo_rto;
+	u_int32_t spinfo_mtu;
+};
+
+struct sctp_rtoinfo {
+	sctp_assoc_t srto_assoc_id;
+	u_int32_t srto_initial;
+	u_int32_t srto_max;
+	u_int32_t srto_min;
+};
+
+struct sctp_assocparams {
+	sctp_assoc_t sasoc_assoc_id;
+	u_int16_t sasoc_asocmaxrxt;
+        u_int16_t sasoc_number_peer_destinations;
+        u_int32_t sasoc_peer_rwnd;
+        u_int32_t sasoc_local_rwnd;
+        u_int32_t sasoc_cookie_life;
+};
+
+struct sctp_setprim {
+	sctp_assoc_t ssp_assoc_id;
+	struct sockaddr_storage ssp_addr;
+};
+
+struct sctp_setpeerprim {
+	sctp_assoc_t sspp_assoc_id;
+	struct sockaddr_storage sspp_addr;
+};
+
+struct sctp_getaddresses {
+	sctp_assoc_t sget_assoc_id;
+	/* addr is filled in for N * sockaddr_storage */
+	struct sockaddr addr[1];
+};
+
+struct sctp_setstrm_timeout {
+	sctp_assoc_t ssto_assoc_id;
+	u_int32_t ssto_timeout;
+	u_int32_t ssto_streamid_start;
+	u_int32_t ssto_streamid_end;
+};
+
+struct sctp_status {
+	sctp_assoc_t sstat_assoc_id;
+	int32_t sstat_state;
+	u_int32_t sstat_rwnd;
+	u_int16_t sstat_unackdata;
+	u_int16_t sstat_penddata;
+        u_int16_t sstat_instrms;
+        u_int16_t sstat_outstrms;
+        u_int32_t sstat_fragmentation_point;
+	struct sctp_paddrinfo sstat_primary;
+};
+
+struct sctp_cwnd_args {
+	struct sctp_nets *net;		/* network to */
+	u_int32_t cwnd_new_value;	/* cwnd in k */
+	u_int32_t inflight;		/* flightsize in k */
+	int cwnd_augment;		/* increment to it */
+};
+
+struct sctp_blk_args {
+	u_int32_t onmb;			/* in 1k bytes */
+	u_int32_t onsb;			/* in 1k bytes */
+	u_int16_t maxmb;		/* in 1k bytes */
+	u_int16_t maxsb;		/* in 1k bytes */
+	u_int16_t send_sent_qcnt;	/* chnk cnt */
+	u_int16_t stream_qcnt;		/* chnk cnt */
+};
+
+/*
+ * Max we can reset in one setting, note this is dictated not by the
+ * define but the size of a mbuf cluster so don't change this define
+ * and think you can specify more. You must do multiple resets if you
+ * want to reset more than SCTP_MAX_EXPLICIT_STR_RESET.
+ */
+#define SCTP_MAX_EXPLICT_STR_RESET   1000
+
+#define SCTP_RESET_LOCAL_RECV  0x0001
+#define SCTP_RESET_LOCAL_SEND  0x0002
+#define SCTP_RESET_BOTH        0x0003
+
+struct sctp_stream_reset {
+	sctp_assoc_t strrst_assoc_id;
+	u_int16_t    strrst_flags;
+	u_int16_t    strrst_num_streams;	/* 0 == ALL */
+	u_int16_t    strrst_list[0];		/* list if strrst_num_streams is not 0*/
+};
+
+
+struct sctp_get_nonce_values {
+	sctp_assoc_t gn_assoc_id;
+	u_int32_t    gn_peers_tag;
+	u_int32_t    gn_local_tag;
+};
+
+/* Debugging logs */
+struct sctp_str_log{
+	u_int32_t n_tsn;
+	u_int32_t e_tsn;
+	u_int16_t n_sseq;
+	u_int16_t e_sseq;
+};
+
+struct sctp_fr_log {
+	u_int32_t largest_tsn;
+	u_int32_t largest_new_tsn;
+	u_int32_t tsn;
+};
+
+struct sctp_fr_map {
+	u_int32_t base;
+	u_int32_t cum;
+	u_int32_t high;
+};
+
+struct sctp_rwnd_log {
+	u_int32_t rwnd;
+	u_int32_t send_size;
+	u_int32_t overhead;
+	u_int32_t new_rwnd;
+};
+
+struct sctp_mbcnt_log {
+	u_int32_t total_queue_size;
+	u_int32_t size_change;
+	u_int32_t total_queue_mb_size;
+	u_int32_t mbcnt_change;
+};
+
+struct sctp_cwnd_log{
+	union {
+		struct sctp_blk_args blk;
+		struct sctp_cwnd_args cwnd;
+		struct sctp_str_log strlog;
+		struct sctp_fr_log fr;
+		struct sctp_fr_map map;
+		struct sctp_rwnd_log rwnd;
+		struct sctp_mbcnt_log mbcnt;
+	}x;
+	u_int8_t from;
+	u_int8_t event_type;
+
+};
+
+struct sctp_cwnd_log_req{
+	int num_in_log;     /* Number in log */
+	int num_ret;        /* Number returned */
+	int start_at;       /* start at this one */
+	int end_at;         /* end at this one */
+	struct sctp_cwnd_log log[0];
+};
+
+
+
+/*
+ * API system calls
+ */
+#if !(defined(_KERNEL) || (defined(__APPLE__) && defined(KERNEL)))
+
+__BEGIN_DECLS
+int	sctp_peeloff	__P((int, sctp_assoc_t));
+int	sctp_bindx	__P((int, struct sockaddr *, int, int));
+int     sctp_connectx   __P((int, struct sockaddr *, int));
+int	sctp_getpaddrs	__P((int, sctp_assoc_t, struct sockaddr **));
+void	sctp_freepaddrs	__P((struct sockaddr *));
+int	sctp_getladdrs	__P((int, sctp_assoc_t, struct sockaddr **));
+void	sctp_freeladdrs	__P((struct sockaddr *));
+int     sctp_opt_info   __P((int, sctp_assoc_t, int, void *, size_t *));
+
+ssize_t sctp_sendmsg    __P((int, const void *, size_t,
+	const struct sockaddr *,
+	socklen_t, u_int32_t, u_int32_t, u_int16_t, u_int32_t, u_int32_t));
+
+ssize_t sctp_send       __P((int sd, const void *msg, size_t len,
+	const struct sctp_sndrcvinfo *sinfo,int flags));
+
+ssize_t
+sctp_sendx __P((int sd, const void *msg, size_t len,
+		struct sockaddr *addrs, int addrcnt,
+		struct sctp_sndrcvinfo *sinfo, int flags));
+ssize_t
+sctp_sendmsgx __P((int sd, const void *, size_t,
+	           struct sockaddr *, int,
+	           u_int32_t, u_int32_t, u_int16_t, u_int32_t, u_int32_t));
+
+sctp_assoc_t
+sctp_getassocid __P((int sd, struct sockaddr *sa));
+
+ssize_t sctp_recvmsg	__P((int, void *, size_t, struct sockaddr *,
+        socklen_t *, struct sctp_sndrcvinfo *, int *));
+
+__END_DECLS
+
+#endif /* !_KERNEL */
+#endif /* !__sctp_uio_h__ */
Index: sys/netinet/sctp_usrreq.c
===================================================================
RCS file: sys/netinet/sctp_usrreq.c
diff -N sys/netinet/sctp_usrreq.c
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ sys/netinet/sctp_usrreq.c	18 Jun 2005 16:05:50 -0000
@@ -0,0 +1,4743 @@
+/*	$KAME: sctp_usrreq.c,v 1.47 2005/03/06 16:04:18 itojun Exp $	*/
+
+/*
+ * Copyright (c) 2001, 2002, 2003, 2004 Cisco Systems, Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *      This product includes software developed by Cisco Systems, Inc.
+ * 4. Neither the name of the project nor the names of its contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY CISCO SYSTEMS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL CISCO SYSTEMS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+#if !(defined(__OpenBSD__) || defined(__APPLE__))
+#include "opt_ipsec.h"
+#endif
+#if defined(__FreeBSD__) || defined(__DragonFly__)
+#include "opt_inet6.h"
+#include "opt_inet.h"
+#endif
+#if defined(__NetBSD__)
+#include "opt_inet.h"
+#endif
+
+#ifdef __APPLE__
+#include <sctp.h>
+#elif !defined(__OpenBSD__)
+#include "opt_sctp.h"
+#endif
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/kernel.h>
+#include <sys/malloc.h>
+#include <sys/mbuf.h>
+#include <sys/domain.h>
+#include <sys/proc.h>
+#include <sys/protosw.h>
+#include <sys/socket.h>
+#include <sys/socketvar.h>
+#include <sys/sysctl.h>
+#include <sys/syslog.h>
+#include <sys/thread2.h>
+#include <net/if.h>
+#include <net/if_types.h>
+#if defined(__FreeBSD__) || defined(__APPLE__) || defined(__DragonFly__)
+#include <net/if_var.h>
+#endif
+#include <net/route.h>
+#include <netinet/in.h>
+#include <netinet/in_systm.h>
+#include <netinet/ip.h>
+#include <netinet/ip6.h>
+#include <netinet/in_pcb.h>
+#include <netinet/in_var.h>
+#include <netinet/ip_var.h>
+#include <netinet6/ip6_var.h>
+#include <netinet6/in6_var.h>
+
+#include <netinet/ip_icmp.h>
+#include <netinet/icmp_var.h>
+#include <netinet/sctp_pcb.h>
+#include <netinet/sctp_header.h>
+#include <netinet/sctp_var.h>
+#include <netinet/sctp_output.h>
+#include <netinet/sctp_uio.h>
+#include <netinet/sctp_asconf.h>
+#include <netinet/sctputil.h>
+#include <netinet/sctp_indata.h>
+#include <netinet/sctp_asconf.h>
+#ifdef IPSEC
+#ifndef __OpenBSD__
+#include <netinet6/ipsec.h>
+#include <netkey/key.h>
+#else
+#undef IPSEC
+#endif
+#endif /* IPSEC */
+
+#include <net/net_osdep.h>
+
+#if defined(HAVE_NRL_INPCB) || defined(__FreeBSD__) || defined(__DragonFly__)
+#ifndef in6pcb
+#define in6pcb		inpcb
+#endif
+#ifndef sotoin6pcb
+#define sotoin6pcb      sotoinpcb
+#endif
+#endif
+
+#ifdef SCTP_DEBUG
+extern u_int32_t sctp_debug_on;
+#endif /* SCTP_DEBUG */
+
+/*
+ * sysctl tunable variables
+ */
+int sctp_auto_asconf = SCTP_DEFAULT_AUTO_ASCONF;
+int sctp_max_burst_default = SCTP_DEF_MAX_BURST;
+int sctp_peer_chunk_oh = sizeof(struct mbuf);
+int sctp_strict_init = 1;
+int sctp_no_csum_on_loopback = 1;
+unsigned int sctp_max_chunks_on_queue = SCTP_ASOC_MAX_CHUNKS_ON_QUEUE;
+int sctp_sendspace = (128 * 1024);
+int sctp_recvspace = 128 * (1024 +
+#ifdef INET6
+				sizeof(struct sockaddr_in6)
+#else
+				sizeof(struct sockaddr_in)
+#endif
+	);
+int sctp_strict_sacks = 0;
+int sctp_ecn = 1;
+int sctp_ecn_nonce = 0;
+
+unsigned int sctp_delayed_sack_time_default = SCTP_RECV_MSEC;
+unsigned int sctp_heartbeat_interval_default = SCTP_HB_DEFAULT_MSEC;
+unsigned int sctp_pmtu_raise_time_default = SCTP_DEF_PMTU_RAISE_SEC;
+unsigned int sctp_shutdown_guard_time_default = SCTP_DEF_MAX_SHUTDOWN_SEC;
+unsigned int sctp_secret_lifetime_default = SCTP_DEFAULT_SECRET_LIFE_SEC;
+unsigned int sctp_rto_max_default = SCTP_RTO_UPPER_BOUND;
+unsigned int sctp_rto_min_default = SCTP_RTO_LOWER_BOUND;
+unsigned int sctp_rto_initial_default = SCTP_RTO_INITIAL;
+unsigned int sctp_init_rto_max_default = SCTP_RTO_UPPER_BOUND;
+unsigned int sctp_valid_cookie_life_default = SCTP_DEFAULT_COOKIE_LIFE;
+unsigned int sctp_init_rtx_max_default = SCTP_DEF_MAX_INIT;
+unsigned int sctp_assoc_rtx_max_default = SCTP_DEF_MAX_SEND;
+unsigned int sctp_path_rtx_max_default = SCTP_DEF_MAX_SEND/2;
+unsigned int sctp_nr_outgoing_streams_default = SCTP_OSTREAM_INITIAL;
+
+void
+sctp_init(void)
+{
+#ifdef __OpenBSD__
+#define nmbclusters	nmbclust
+#endif
+	/* Init the SCTP pcb in sctp_pcb.c */
+	u_long sb_max_adj;
+
+	sctp_pcb_init();
+
+#ifndef __OpenBSD__
+	if (nmbclusters > SCTP_ASOC_MAX_CHUNKS_ON_QUEUE)
+		sctp_max_chunks_on_queue = nmbclusters;
+#else
+/*	if (nmbclust > SCTP_ASOC_MAX_CHUNKS_ON_QUEUE)
+	sctp_max_chunks_on_queue = nmbclust; FIX ME */
+	sctp_max_chunks_on_queue = nmbclust * 2;
+#endif
+	/*
+	 * Allow a user to take no more than 1/2 the number of clusters
+	 * or the SB_MAX whichever is smaller for the send window.
+	 */
+	sb_max_adj = (u_long)((u_quad_t)(SB_MAX) * MCLBYTES / (MSIZE + MCLBYTES));
+	sctp_sendspace = min((min(SB_MAX, sb_max_adj)),
+#ifndef __OpenBSD__
+			     ((nmbclusters/2) * SCTP_DEFAULT_MAXSEGMENT));
+#else
+			     ((nmbclust/2) * SCTP_DEFAULT_MAXSEGMENT));
+#endif
+	/*
+	 * Now for the recv window, should we take the same amount?
+	 * or should I do 1/2 the SB_MAX instead in the SB_MAX min above.
+	 * For now I will just copy.
+	 */
+	sctp_recvspace = sctp_sendspace;
+#ifdef __OpenBSD__
+#undef nmbclusters
+#endif
+}
+
+#ifdef INET6
+void
+ip_2_ip6_hdr(struct ip6_hdr *ip6, struct ip *ip)
+{
+	bzero(ip6, sizeof(*ip6));
+
+	ip6->ip6_vfc = IPV6_VERSION;
+	ip6->ip6_plen = ip->ip_len;
+	ip6->ip6_nxt = ip->ip_p;
+	ip6->ip6_hlim = ip->ip_ttl;
+	ip6->ip6_src.s6_addr32[2] = ip6->ip6_dst.s6_addr32[2] =
+		IPV6_ADDR_INT32_SMP;
+	ip6->ip6_src.s6_addr32[3] = ip->ip_src.s_addr;
+	ip6->ip6_dst.s6_addr32[3] = ip->ip_dst.s_addr;
+}
+#endif /* INET6 */
+
+static void
+sctp_split_chunks(struct sctp_association *asoc,
+		  struct sctp_stream_out *strm,
+		  struct sctp_tmit_chunk *chk)
+{
+	struct sctp_tmit_chunk *new_chk;
+
+	/* First we need a chunk */
+	new_chk = (struct sctp_tmit_chunk *)SCTP_ZONE_GET(sctppcbinfo.ipi_zone_chunk);
+	if (new_chk == NULL) {
+		chk->flags |= CHUNK_FLAGS_FRAGMENT_OK;
+		return;
+	}
+	sctppcbinfo.ipi_count_chunk++;
+	sctppcbinfo.ipi_gencnt_chunk++;
+	/* Copy it all */
+	*new_chk = *chk;
+	/*  split the data */
+	new_chk->data = m_split(chk->data, (chk->send_size>>1), MB_DONTWAIT);
+	if (new_chk->data == NULL) {
+		/* Can't split */
+		chk->flags |= CHUNK_FLAGS_FRAGMENT_OK;
+		SCTP_ZONE_FREE(sctppcbinfo.ipi_zone_chunk, new_chk);
+		sctppcbinfo.ipi_count_chunk--;
+		if ((int)sctppcbinfo.ipi_count_chunk < 0) {
+			panic("Chunk count is negative");
+		}
+		sctppcbinfo.ipi_gencnt_chunk++;
+		return;
+
+	}
+	/* Data is now split adjust sizes */
+	chk->send_size >>= 1;
+	new_chk->send_size >>= 1;
+
+	chk->book_size >>= 1;
+	new_chk->book_size >>= 1;
+
+	/* now adjust the marks */
+	chk->rec.data.rcv_flags |= SCTP_DATA_FIRST_FRAG;
+	chk->rec.data.rcv_flags &= ~SCTP_DATA_LAST_FRAG;
+
+	new_chk->rec.data.rcv_flags &= ~SCTP_DATA_FIRST_FRAG;
+	new_chk->rec.data.rcv_flags |= SCTP_DATA_LAST_FRAG;
+
+	/* Increase ref count if dest is set */
+	if (chk->whoTo) {
+		new_chk->whoTo->ref_count++;
+	}
+	/* now drop it on the end of the list*/
+	asoc->stream_queue_cnt++;
+	TAILQ_INSERT_AFTER(&strm->outqueue, chk, new_chk, sctp_next);
+}
+
+static void
+sctp_notify_mbuf(struct sctp_inpcb *inp,
+		 struct sctp_tcb *stcb,
+		 struct sctp_nets *net,
+		 struct ip *ip,
+		 struct sctphdr *sh)
+
+{
+	struct icmp *icmph;
+	int totsz;
+	uint16_t nxtsz;
+
+	/* protection */
+	if ((inp == NULL) || (stcb == NULL) || (net == NULL) ||
+	    (ip == NULL) || (sh == NULL)) {
+		if (stcb != NULL)
+			SCTP_TCB_UNLOCK(stcb);
+		return;
+	}
+	/* First job is to verify the vtag matches what I would send */
+	if (ntohl(sh->v_tag) != (stcb->asoc.peer_vtag)) {
+		SCTP_TCB_UNLOCK(stcb);
+		return;
+	}
+	icmph = (struct icmp *)((caddr_t)ip - (sizeof(struct icmp) -
+					       sizeof(struct ip)));
+	if (icmph->icmp_type != ICMP_UNREACH) {
+		/* We only care about unreachable */
+		SCTP_TCB_UNLOCK(stcb);
+		return;
+	}
+	if (icmph->icmp_code != ICMP_UNREACH_NEEDFRAG) {
+		/* not a unreachable message due to frag. */
+		SCTP_TCB_UNLOCK(stcb);
+		return;
+	}
+	totsz = ip->ip_len;
+	nxtsz = ntohs(icmph->icmp_seq);
+	if (nxtsz == 0) {
+		/*
+		 * old type router that does not tell us what the next size
+		 * mtu is. Rats we will have to guess (in a educated fashion
+		 * of course)
+		 */
+		nxtsz = find_next_best_mtu(totsz);
+	}
+
+	/* Stop any PMTU timer */
+	sctp_timer_stop(SCTP_TIMER_TYPE_PATHMTURAISE, inp, stcb, NULL);
+
+	/* Adjust destination size limit */
+	if (net->mtu > nxtsz) {
+		net->mtu = nxtsz;
+	}
+	/* now what about the ep? */
+	if (stcb->asoc.smallest_mtu > nxtsz) {
+		struct sctp_tmit_chunk *chk, *nchk;
+		struct sctp_stream_out *strm;
+		/* Adjust that too */
+		stcb->asoc.smallest_mtu = nxtsz;
+		/* now off to subtract IP_DF flag if needed */
+
+		TAILQ_FOREACH(chk, &stcb->asoc.send_queue, sctp_next) {
+			if ((chk->send_size+IP_HDR_SIZE) > nxtsz) {
+				chk->flags |= CHUNK_FLAGS_FRAGMENT_OK;
+			}
+		}
+		TAILQ_FOREACH(chk, &stcb->asoc.sent_queue, sctp_next) {
+			if ((chk->send_size+IP_HDR_SIZE) > nxtsz) {
+				/*
+				 * For this guy we also mark for immediate
+				 * resend since we sent to big of chunk
+				 */
+				chk->flags |= CHUNK_FLAGS_FRAGMENT_OK;
+				if (chk->sent != SCTP_DATAGRAM_RESEND) {
+					stcb->asoc.sent_queue_retran_cnt++;
+				}
+				chk->sent = SCTP_DATAGRAM_RESEND;
+				chk->rec.data.doing_fast_retransmit = 0;
+
+				/* Clear any time so NO RTT is being done */
+				chk->do_rtt = 0;
+				stcb->asoc.total_flight -= chk->book_size;
+				if (stcb->asoc.total_flight < 0) {
+					stcb->asoc.total_flight = 0;
+				}
+				stcb->asoc.total_flight_count--;
+				if (stcb->asoc.total_flight_count < 0) {
+					stcb->asoc.total_flight_count = 0;
+				}
+				net->flight_size -= chk->book_size;
+				if (net->flight_size < 0) {
+					net->flight_size = 0;
+				}
+			}
+		}
+		TAILQ_FOREACH(strm, &stcb->asoc.out_wheel, next_spoke) {
+			chk = TAILQ_FIRST(&strm->outqueue);
+			while (chk) {
+				nchk = TAILQ_NEXT(chk, sctp_next);
+				if ((chk->send_size+SCTP_MED_OVERHEAD) > nxtsz) {
+					sctp_split_chunks(&stcb->asoc, strm, chk);
+				}
+				chk = nchk;
+			}
+		}
+	}
+	sctp_timer_start(SCTP_TIMER_TYPE_PATHMTURAISE, inp, stcb, NULL);
+	SCTP_TCB_UNLOCK(stcb);
+}
+
+
+void
+sctp_notify(struct sctp_inpcb *inp,
+	    int errno,
+	    struct sctphdr *sh,
+	    struct sockaddr *to,
+	    struct sctp_tcb *stcb,
+	    struct sctp_nets *net)
+{
+	/* protection */
+	if ((inp == NULL) || (stcb == NULL) || (net == NULL) ||
+	    (sh == NULL) || (to == NULL)) {
+#ifdef SCTP_DEBUG
+		if (sctp_debug_on & SCTP_DEBUG_USRREQ1) {
+			printf("sctp-notify, bad call\n");
+		}
+#endif /* SCTP_DEBUG */
+		return;
+	}
+	/* First job is to verify the vtag matches what I would send */
+	if (ntohl(sh->v_tag) != (stcb->asoc.peer_vtag)) {
+		return;
+	}
+
+/* FIX ME FIX ME PROTOPT i.e. no SCTP should ALWAYS be an ABORT */
+
+	if ((errno == EHOSTUNREACH) ||  /* Host is not reachable */
+	    (errno == EHOSTDOWN) ||	/* Host is down */
+	    (errno == ECONNREFUSED) ||	/* Host refused the connection, (not an abort?) */
+	    (errno == ENOPROTOOPT)	/* SCTP is not present on host */
+		) {
+		/*
+		 * Hmm reachablity problems we must examine closely.
+		 * If its not reachable, we may have lost a network.
+		 * Or if there is NO protocol at the other end named SCTP.
+		 * well we consider it a OOTB abort.
+		 */
+		if ((errno == EHOSTUNREACH) || (errno == EHOSTDOWN)) {
+			if (net->dest_state & SCTP_ADDR_REACHABLE) {
+				/* Ok that destination is NOT reachable */
+				net->dest_state &= ~SCTP_ADDR_REACHABLE;
+				net->dest_state |= SCTP_ADDR_NOT_REACHABLE;
+				net->error_count = net->failure_threshold + 1;
+				sctp_ulp_notify(SCTP_NOTIFY_INTERFACE_DOWN,
+						stcb, SCTP_FAILED_THRESHOLD,
+						(void *)net);
+			}
+			if (stcb)
+				SCTP_TCB_UNLOCK(stcb);
+		} else {
+			/*
+			 * Here the peer is either playing tricks on us,
+			 * including an address that belongs to someone who
+			 * does not support SCTP OR was a userland
+			 * implementation that shutdown and now is dead. In
+			 * either case treat it like a OOTB abort with no TCB
+			 */
+			sctp_abort_notification(stcb, SCTP_PEER_FAULTY);
+			sctp_free_assoc(inp, stcb);
+			/* no need to unlock here, since the TCB is gone */
+		}
+	} else {
+		/* Send all others to the app */
+		if (inp->sctp_socket) {
+			SOCK_LOCK(inp->sctp_socket);
+			inp->sctp_socket->so_error = errno;
+			sctp_sowwakeup(inp, inp->sctp_socket);
+			SOCK_UNLOCK(inp->sctp_socket);
+		}
+	        if (stcb)
+			SCTP_TCB_UNLOCK(stcb);
+	}
+}
+
+#if defined(__FreeBSD__) || defined(__APPLE__) || defined(__DragonFly__)
+void
+#else
+void *
+#endif
+sctp_ctlinput(cmd, sa, vip)
+	int cmd;
+	struct sockaddr *sa;
+	void *vip;
+{
+	struct ip *ip = vip;
+	struct sctphdr *sh;
+
+
+	if (sa->sa_family != AF_INET ||
+	    ((struct sockaddr_in *)sa)->sin_addr.s_addr == INADDR_ANY) {
+#if defined(__FreeBSD__) || defined(__APPLE__) || defined(__DragonFly__)
+		return;
+#else
+		return (NULL);
+#endif
+	}
+
+	if (PRC_IS_REDIRECT(cmd)) {
+		ip = 0;
+	} else if ((unsigned)cmd >= PRC_NCMDS || inetctlerrmap[cmd] == 0) {
+#if defined(__FreeBSD__) || defined(__APPLE__) || defined(__DragonFly__)
+		return;
+#else
+		return (NULL);
+#endif
+	}
+	if (ip) {
+		struct sctp_inpcb *inp;
+		struct sctp_tcb *stcb;
+		struct sctp_nets *net;
+		struct sockaddr_in to, from;
+
+		sh = (struct sctphdr *)((caddr_t)ip + (ip->ip_hl << 2));
+		bzero(&to, sizeof(to));
+		bzero(&from, sizeof(from));
+		from.sin_family = to.sin_family = AF_INET;
+		from.sin_len = to.sin_len = sizeof(to);
+		from.sin_port = sh->src_port;
+		from.sin_addr = ip->ip_src;
+		to.sin_port = sh->dest_port;
+		to.sin_addr = ip->ip_dst;
+
+		/*
+		 * 'to' holds the dest of the packet that failed to be sent.
+		 * 'from' holds our local endpoint address.
+		 * Thus we reverse the to and the from in the lookup.
+		 */
+		crit_enter();
+		stcb = sctp_findassociation_addr_sa((struct sockaddr *)&from,
+						    (struct sockaddr *)&to,
+						    &inp, &net, 1);
+		if (stcb != NULL && inp && (inp->sctp_socket != NULL)) {
+			if (cmd != PRC_MSGSIZE) {
+				int cm;
+				if (cmd == PRC_HOSTDEAD) {
+					cm = EHOSTUNREACH;
+				} else {
+					cm = inetctlerrmap[cmd];
+				}
+				sctp_notify(inp, cm, sh,
+					    (struct sockaddr *)&to, stcb,
+					    net);
+			} else {
+				/* handle possible ICMP size messages */
+				sctp_notify_mbuf(inp, stcb, net, ip, sh);
+			}
+		} else {
+#if (defined(__FreeBSD__) && __FreeBSD_version < 500000) || defined(__DragonFly__)
+                        /* XXX must be fixed for 5.x and higher, leave for 4.x */
+			if (PRC_IS_REDIRECT(cmd) && inp) {
+				in_rtchange((struct inpcb *)inp,
+					    inetctlerrmap[cmd]);
+			}
+#endif
+			if ((stcb == NULL) && (inp != NULL)) {
+				/* reduce ref-count */
+				SCTP_INP_WLOCK(inp);
+				SCTP_INP_DECR_REF(inp);
+				SCTP_INP_WUNLOCK(inp);
+			}
+
+		}
+		crit_exit();
+	}
+#if defined(__FreeBSD__) || defined(__APPLE__) || defined(__DragonFly__)
+	return;
+#else
+	return (NULL);
+#endif
+}
+
+#if defined(__FreeBSD__) || defined(__DragonFly__)
+static int
+sctp_getcred(SYSCTL_HANDLER_ARGS)
+{
+	struct sockaddr_in addrs[2];
+	struct sctp_inpcb *inp;
+	struct sctp_nets *net;
+	struct sctp_tcb *stcb;
+	int error;
+
+#if __FreeBSD_version >= 500000 || defined(__DragonFly__)
+	error = suser(req->td);
+#else
+	error = suser(req->p);
+#endif
+	if (error)
+		return (error);
+	error = SYSCTL_IN(req, addrs, sizeof(addrs));
+	if (error)
+		return (error);
+
+	crit_enter();
+	stcb = sctp_findassociation_addr_sa(sintosa(&addrs[0]),
+					   sintosa(&addrs[1]),
+					   &inp, &net, 1);
+	if (stcb == NULL || inp == NULL || inp->sctp_socket == NULL) {
+		if ((inp != NULL) && (stcb == NULL)) {
+			/* reduce ref-count */
+			SCTP_INP_WLOCK(inp);
+			SCTP_INP_DECR_REF(inp);
+			SCTP_INP_WUNLOCK(inp);
+		}
+		error = ENOENT;
+		goto out;
+	}
+	error = SYSCTL_OUT(req, inp->sctp_socket->so_cred, sizeof(struct ucred));
+	SCTP_TCB_UNLOCK(stcb);
+out:
+	crit_exit();
+	return (error);
+}
+
+SYSCTL_PROC(_net_inet_sctp, OID_AUTO, getcred, CTLTYPE_OPAQUE|CTLFLAG_RW,
+	    0, 0, sctp_getcred, "S,ucred", "Get the ucred of a SCTP connection");
+#endif /* #if defined(__FreeBSD__) || defined(__DragonFly__) */
+
+/*
+ * sysctl definitions
+ */
+#if defined(__FreeBSD__) || defined (__APPLE__) || defined(__DragonFly__)
+
+SYSCTL_DECL(_net_inet);
+
+SYSCTL_NODE(_net_inet, OID_AUTO, sctp, CTLFLAG_RD, 0,
+	"sctp values");
+
+SYSCTL_INT(_net_inet_sctp, OID_AUTO, maxdgram, CTLFLAG_RW,
+	   &sctp_sendspace, 0, "Maximum outgoing SCTP buffer size");
+
+SYSCTL_INT(_net_inet_sctp, OID_AUTO, recvspace, CTLFLAG_RW,
+	   &sctp_recvspace, 0, "Maximum incoming SCTP buffer size");
+
+SYSCTL_INT(_net_inet_sctp, OID_AUTO, auto_asconf, CTLFLAG_RW,
+	   &sctp_auto_asconf, 0, "Enable SCTP Auto-ASCONF");
+
+SYSCTL_INT(_net_inet_sctp, OID_AUTO, ecn_enable, CTLFLAG_RW,
+	   &sctp_ecn, 0, "Enable SCTP ECN");
+
+SYSCTL_INT(_net_inet_sctp, OID_AUTO, ecn_nonce, CTLFLAG_RW,
+	   &sctp_ecn_nonce, 0, "Enable SCTP ECN Nonce");
+
+SYSCTL_INT(_net_inet_sctp, OID_AUTO, strict_sacks, CTLFLAG_RW,
+	   &sctp_strict_sacks, 0, "Enable SCTP Strict SACK checking");
+
+SYSCTL_INT(_net_inet_sctp, OID_AUTO, loopback_nocsum, CTLFLAG_RW,
+	   &sctp_no_csum_on_loopback, 0,
+	   "Enable NO Csum on packets sent on loopback");
+
+SYSCTL_INT(_net_inet_sctp, OID_AUTO, strict_init, CTLFLAG_RW,
+	   &sctp_strict_init, 0,
+	   "Enable strict INIT/INIT-ACK singleton enforcement");
+
+SYSCTL_INT(_net_inet_sctp, OID_AUTO, peer_chkoh, CTLFLAG_RW,
+	   &sctp_peer_chunk_oh, 0,
+	   "Amount to debit peers rwnd per chunk sent");
+
+SYSCTL_INT(_net_inet_sctp, OID_AUTO, maxburst, CTLFLAG_RW,
+	   &sctp_max_burst_default, 0,
+	   "Default max burst for sctp endpoints");
+
+SYSCTL_INT(_net_inet_sctp, OID_AUTO, maxchunks, CTLFLAG_RW,
+	   &sctp_max_chunks_on_queue, 0,
+	   "Default max chunks on queue per asoc");
+
+SYSCTL_UINT(_net_inet_sctp, OID_AUTO, delayed_sack_time, CTLFLAG_RW,
+	    &sctp_delayed_sack_time_default, 0,
+	    "Default delayed SACK timer in msec");
+
+SYSCTL_UINT(_net_inet_sctp, OID_AUTO, heartbeat_interval, CTLFLAG_RW,
+	    &sctp_heartbeat_interval_default, 0,
+	    "Default heartbeat interval in msec");
+
+SYSCTL_UINT(_net_inet_sctp, OID_AUTO, pmtu_raise_time, CTLFLAG_RW,
+	    &sctp_pmtu_raise_time_default, 0,
+	    "Default PMTU raise timer in sec");
+
+SYSCTL_UINT(_net_inet_sctp, OID_AUTO, shutdown_guard_time, CTLFLAG_RW,
+	    &sctp_shutdown_guard_time_default, 0,
+	    "Default shutdown guard timer in sec");
+
+SYSCTL_UINT(_net_inet_sctp, OID_AUTO, secret_lifetime, CTLFLAG_RW,
+	    &sctp_secret_lifetime_default, 0,
+	    "Default secret lifetime in sec");
+
+SYSCTL_UINT(_net_inet_sctp, OID_AUTO, rto_max, CTLFLAG_RW,
+	    &sctp_rto_max_default, 0,
+	    "Default maximum retransmission timeout in msec");
+
+SYSCTL_UINT(_net_inet_sctp, OID_AUTO, rto_min, CTLFLAG_RW,
+	    &sctp_rto_min_default, 0,
+	    "Default minimum retransmission timeout in msec");
+
+SYSCTL_UINT(_net_inet_sctp, OID_AUTO, rto_initial, CTLFLAG_RW,
+	    &sctp_rto_initial_default, 0,
+	    "Default initial retransmission timeout in msec");
+
+SYSCTL_UINT(_net_inet_sctp, OID_AUTO, init_rto_max, CTLFLAG_RW,
+	    &sctp_init_rto_max_default, 0,
+	    "Default maximum retransmission timeout during association setup in msec");
+
+SYSCTL_UINT(_net_inet_sctp, OID_AUTO, valid_cookie_life, CTLFLAG_RW,
+	    &sctp_valid_cookie_life_default, 0,
+	    "Default cookie lifetime in sec");
+
+SYSCTL_UINT(_net_inet_sctp, OID_AUTO, init_rtx_max, CTLFLAG_RW,
+	    &sctp_init_rtx_max_default, 0,
+	    "Default maximum number of retransmission for INIT chunks");
+
+SYSCTL_UINT(_net_inet_sctp, OID_AUTO, assoc_rtx_max, CTLFLAG_RW,
+	    &sctp_assoc_rtx_max_default, 0,
+	    "Default maximum number of retransmissions per association");
+
+SYSCTL_UINT(_net_inet_sctp, OID_AUTO, path_rtx_max, CTLFLAG_RW,
+	    &sctp_path_rtx_max_default, 0,
+	    "Default maximum of retransmissions per path");
+
+SYSCTL_UINT(_net_inet_sctp, OID_AUTO, nr_outgoing_streams, CTLFLAG_RW,
+	    &sctp_nr_outgoing_streams_default, 0,
+	    "Default number of outgoing streams");
+
+#ifdef SCTP_DEBUG
+SYSCTL_INT(_net_inet_sctp, OID_AUTO, debug, CTLFLAG_RW,
+	   &sctp_debug_on, 0, "Configure debug output");
+#endif /* SCTP_DEBUG */
+#endif
+
+static int
+sctp_abort(struct socket *so)
+{
+	struct sctp_inpcb *inp;
+
+	inp = (struct sctp_inpcb *)so->so_pcb;
+	if (inp == 0)
+		return EINVAL;	/* ??? possible? panic instead? */
+
+	crit_enter();
+	sctp_inpcb_free(inp, 1);
+	crit_exit();
+	return 0;
+}
+
+static int
+#if defined(__FreeBSD__) && __FreeBSD_version >= 500000
+sctp_attach(struct socket *so, int proto, struct thread *p)
+#elif defined(__DragonFly__)
+sctp_attach(struct socket *so, int proto, struct pru_attach_info *ai)
+#else
+sctp_attach(struct socket *so, int proto, struct proc *p)
+#endif
+{
+	struct sctp_inpcb *inp;
+	struct inpcb *ip_inp;
+	int error;
+
+	crit_enter();
+	inp = (struct sctp_inpcb *)so->so_pcb;
+	if (inp != 0) {
+		crit_exit();
+		return EINVAL;
+	}
+	error = soreserve(so, sctp_sendspace, sctp_recvspace, NULL);
+	if (error) {
+		crit_exit();
+		return error;
+	}
+	error = sctp_inpcb_alloc(so);
+	if (error) {
+		crit_exit();
+		return error;
+	}
+	inp = (struct sctp_inpcb *)so->so_pcb;
+	SCTP_INP_WLOCK(inp);
+
+	inp->sctp_flags &= ~SCTP_PCB_FLAGS_BOUND_V6;	/* I'm not v6! */
+	ip_inp = &inp->ip_inp.inp;
+#if defined(__FreeBSD__) || defined(__APPLE__) || defined(__DragonFly__)
+	ip_inp->inp_vflag |= INP_IPV4;
+	ip_inp->inp_ip_ttl = ip_defttl;
+#else
+	inp->inp_vflag |= INP_IPV4;
+	inp->inp_ip_ttl = ip_defttl;
+#endif
+
+#ifdef IPSEC
+#if !(defined(__OpenBSD__) || defined(__APPLE__))
+	error = ipsec_init_pcbpolicy(so, &ip_inp->inp_sp);
+	if (error != 0) {
+		sctp_inpcb_free(inp, 1);
+		return error;
+	}
+#endif
+#endif /*IPSEC*/
+	SCTP_INP_WUNLOCK(inp);
+#if defined(__NetBSD__)
+	so->so_send = sctp_sosend;
+#endif
+	crit_exit();
+	return 0;
+}
+
+static int
+#if (defined(__FreeBSD__) && __FreeBSD_version >= 500000) || defined(__DragonFly__)
+sctp_bind(struct socket *so, struct sockaddr *addr, struct thread *p)
+{
+#elif defined(__FreeBSD__) || defined(__APPLE__)
+sctp_bind(struct socket *so, struct sockaddr *addr, struct proc *p)
+{
+#else
+sctp_bind(struct socket *so, struct mbuf *nam, struct proc *p)
+{
+	struct sockaddr *addr = nam ? mtod(nam, struct sockaddr *) : NULL;
+#endif
+	struct sctp_inpcb *inp;
+	int error;
+
+#ifdef INET6
+	if (addr && addr->sa_family != AF_INET)
+		/* must be a v4 address! */
+		return EINVAL;
+#endif /* INET6 */
+
+	inp = (struct sctp_inpcb *)so->so_pcb;
+	if (inp == 0)
+		return EINVAL;
+
+	crit_enter();
+	error = sctp_inpcb_bind(so, addr, p);
+	crit_exit();
+	return error;
+}
+
+
+static int
+sctp_detach(struct socket *so)
+{
+	struct sctp_inpcb *inp;
+
+	inp = (struct sctp_inpcb *)so->so_pcb;
+	if (inp == 0)
+		return EINVAL;
+	crit_enter();
+	if (((so->so_options & SO_LINGER) && (so->so_linger == 0)) ||
+	    (so->so_rcv.sb_cc > 0)) {
+		sctp_inpcb_free(inp, 1);
+	} else {
+		sctp_inpcb_free(inp, 0);
+	}
+	crit_exit();
+	return 0;
+}
+
+int
+#if (defined(__FreeBSD__) && __FreeBSD_version >= 500000) || defined(__DragonFly__)
+sctp_send(struct socket *so, int flags, struct mbuf *m, struct sockaddr *addr,
+	  struct mbuf *control, struct thread *p);
+#else
+sctp_send(struct socket *so, int flags, struct mbuf *m, struct sockaddr *addr,
+	  struct mbuf *control, struct proc *p);
+#endif
+
+int
+#if (defined(__FreeBSD__) && __FreeBSD_version >= 500000) || defined(__DragonFly__)
+sctp_send(struct socket *so, int flags, struct mbuf *m, struct sockaddr *addr,
+	  struct mbuf *control, struct thread *p)
+{
+#else
+sctp_send(struct socket *so, int flags, struct mbuf *m, struct sockaddr *addr,
+	  struct mbuf *control, struct proc *p)
+{
+#endif
+	struct sctp_inpcb *inp;
+	int error;
+	inp = (struct sctp_inpcb *)so->so_pcb;
+	if (inp == 0) {
+		if (control) {
+			sctp_m_freem(control);
+			control = NULL;
+		}
+		sctp_m_freem(m);
+		return EINVAL;
+	}
+	/* Got to have an to address if we are NOT a connected socket */
+	if ((addr == NULL) &&
+	    ((inp->sctp_flags & SCTP_PCB_FLAGS_CONNECTED) ||
+	     (inp->sctp_flags & SCTP_PCB_FLAGS_TCPTYPE))
+		) {
+		goto connected_type;
+	} else if (addr == NULL) {
+		error = EDESTADDRREQ;
+		sctp_m_freem(m);
+		if (control) {
+			sctp_m_freem(control);
+			control = NULL;
+		}
+		return (error);
+	}
+#ifdef INET6
+	if (addr->sa_family != AF_INET) {
+		/* must be a v4 address! */
+		sctp_m_freem(m);
+		if (control) {
+			sctp_m_freem(control);
+			control = NULL;
+		}
+		error = EDESTADDRREQ;
+		return EINVAL;
+	}
+#endif /* INET6 */
+ connected_type:
+	/* now what about control */
+	if (control) {
+		if (inp->control) {
+			printf("huh? control set?\n");
+			sctp_m_freem(inp->control);
+			inp->control = NULL;
+		}
+		inp->control = control;
+	}
+	/* add it in possibly */
+	if ((inp->pkt) && (inp->pkt->m_flags & M_PKTHDR)) {
+		struct mbuf *x;
+		int c_len;
+
+		c_len = 0;
+		/* How big is it */
+		for (x=m;x;x = x->m_next) {
+			c_len += x->m_len;
+		}
+		inp->pkt->m_pkthdr.len += c_len;
+	}
+	/* Place the data */
+	if (inp->pkt) {
+		inp->pkt_last->m_next = m;
+		inp->pkt_last = m;
+	} else {
+		inp->pkt_last = inp->pkt = m;
+	}
+	if (
+#if defined (__FreeBSD__) || defined(__APPLE__) || defined(__DragonFly__)
+	    /* FreeBSD uses a flag passed */
+	    ((flags & PRUS_MORETOCOME) == 0)
+#elif defined( __NetBSD__)
+	    /* NetBSD uses the so_state field */
+	    ((so->so_state & SS_MORETOCOME) == 0)
+#else
+	    1   /* Open BSD does not have any "more to come" indication */
+#endif
+	    ) {
+		/*
+		 * note with the current version this code will only be used
+		 * by OpenBSD-- NetBSD, FreeBSD, and MacOS have methods for
+		 * re-defining sosend to use the sctp_sosend. One can
+		 * optionally switch back to this code (by changing back the
+		 * definitions) but this is not advisable.
+	     */
+		int ret;
+		ret = sctp_output(inp, inp->pkt, addr, inp->control, p, flags);
+		inp->pkt = NULL;
+		inp->control = NULL;
+		return (ret);
+	} else {
+		return (0);
+	}
+}
+
+static int
+sctp_disconnect(struct socket *so)
+{
+	struct sctp_inpcb *inp;
+
+	crit_enter();
+	inp = (struct sctp_inpcb *)so->so_pcb;
+	if (inp == NULL) {
+		crit_exit();
+		return (ENOTCONN);
+	}
+	SCTP_INP_RLOCK(inp);
+	if (inp->sctp_flags & SCTP_PCB_FLAGS_TCPTYPE) {
+		if (LIST_EMPTY(&inp->sctp_asoc_list)) {
+			/* No connection */
+			crit_exit();
+			SCTP_INP_RUNLOCK(inp);
+			return (0);
+		} else {
+			int some_on_streamwheel = 0;
+			struct sctp_association *asoc;
+			struct sctp_tcb *stcb;
+
+			stcb = LIST_FIRST(&inp->sctp_asoc_list);
+			if (stcb == NULL) {
+				crit_exit();
+				SCTP_INP_RUNLOCK(inp);
+				return (EINVAL);
+			}
+			asoc = &stcb->asoc;
+			SCTP_TCB_LOCK(stcb);
+			if (((so->so_options & SO_LINGER) &&
+			     (so->so_linger == 0)) ||
+			    (so->so_rcv.sb_cc > 0)) {
+				if (SCTP_GET_STATE(asoc) !=
+				    SCTP_STATE_COOKIE_WAIT) {
+					/* Left with Data unread */
+					struct mbuf *err;
+					err = NULL;
+					MGET(err, MB_DONTWAIT, MT_DATA);
+					if (err) {
+						/* Fill in the user initiated abort */
+						struct sctp_paramhdr *ph;
+						ph = mtod(err, struct sctp_paramhdr *);
+						err->m_len = sizeof(struct sctp_paramhdr);
+						ph->param_type = htons(SCTP_CAUSE_USER_INITIATED_ABT);
+						ph->param_length = htons(err->m_len);
+					}
+					sctp_send_abort_tcb(stcb, err);
+				}
+				SCTP_INP_RUNLOCK(inp);
+				sctp_free_assoc(inp, stcb);
+				/* No unlock tcb assoc is gone */
+				crit_exit();
+				return (0);
+			}
+			if (!TAILQ_EMPTY(&asoc->out_wheel)) {
+				/* Check to see if some data queued */
+				struct sctp_stream_out *outs;
+				TAILQ_FOREACH(outs, &asoc->out_wheel,
+					      next_spoke) {
+					if (!TAILQ_EMPTY(&outs->outqueue)) {
+						some_on_streamwheel = 1;
+						break;
+					}
+				}
+			}
+
+			if (TAILQ_EMPTY(&asoc->send_queue) &&
+			    TAILQ_EMPTY(&asoc->sent_queue) &&
+			    (some_on_streamwheel == 0)) {
+				/* there is nothing queued to send, so done */
+				if ((SCTP_GET_STATE(asoc) !=
+				     SCTP_STATE_SHUTDOWN_SENT) &&
+				    (SCTP_GET_STATE(asoc) !=
+				     SCTP_STATE_SHUTDOWN_ACK_SENT)) {
+					/* only send SHUTDOWN 1st time thru */
+#ifdef SCTP_DEBUG
+					if (sctp_debug_on & SCTP_DEBUG_OUTPUT4) {
+						printf("%s:%d sends a shutdown\n",
+						       __FILE__,
+						       __LINE__
+							);
+					}
+#endif
+					sctp_send_shutdown(stcb,
+							   stcb->asoc.primary_destination);
+					sctp_chunk_output(stcb->sctp_ep, stcb, 1);
+					asoc->state = SCTP_STATE_SHUTDOWN_SENT;
+					sctp_timer_start(SCTP_TIMER_TYPE_SHUTDOWN,
+							 stcb->sctp_ep, stcb,
+							 asoc->primary_destination);
+					sctp_timer_start(SCTP_TIMER_TYPE_SHUTDOWNGUARD,
+							 stcb->sctp_ep, stcb,
+							 asoc->primary_destination);
+				}
+			} else {
+				/*
+				 * we still got (or just got) data to send,
+				 * so set SHUTDOWN_PENDING
+				 */
+				/*
+				 * XXX sockets draft says that MSG_EOF should
+				 * be sent with no data.
+				 * currently, we will allow user data to be
+				 * sent first and move to SHUTDOWN-PENDING
+				 */
+				asoc->state |= SCTP_STATE_SHUTDOWN_PENDING;
+			}
+			SCTP_TCB_UNLOCK(stcb);
+			SCTP_INP_RUNLOCK(inp);
+			crit_exit();
+			return (0);
+		}
+		/* not reached */
+	} else {
+		/* UDP model does not support this */
+		SCTP_INP_RUNLOCK(inp);
+		crit_exit();
+		return EOPNOTSUPP;
+	}
+}
+
+int
+sctp_shutdown(struct socket *so)
+{
+	struct sctp_inpcb *inp;
+
+	crit_enter();
+	inp = (struct sctp_inpcb *)so->so_pcb;
+	if (inp == 0) {
+		crit_exit();
+		return EINVAL;
+	}
+	SCTP_INP_RLOCK(inp);
+	/* For UDP model this is a invalid call */
+	if (inp->sctp_flags & SCTP_PCB_FLAGS_UDPTYPE) {
+		/* Restore the flags that the soshutdown took away. */
+#if defined(__FreeBSD__) && __FreeBSD_version >= 502115
+		so->so_rcv.sb_state &= ~SBS_CANTRCVMORE;
+#else
+		so->so_state &= ~SS_CANTRCVMORE;
+#endif
+		/* This proc will wakeup for read and do nothing (I hope) */
+		crit_exit();
+		SCTP_INP_RUNLOCK(inp);
+		return (EOPNOTSUPP);
+	}
+	/*
+	 * Ok if we reach here its the TCP model and it is either a SHUT_WR
+	 * or SHUT_RDWR. This means we put the shutdown flag against it.
+	 */
+	{
+		int some_on_streamwheel = 0;
+		struct sctp_tcb *stcb;
+		struct sctp_association *asoc;
+		socantsendmore(so);
+
+		stcb = LIST_FIRST(&inp->sctp_asoc_list);
+		if (stcb == NULL) {
+			/*
+			 * Ok we hit the case that the shutdown call was made
+			 * after an abort or something. Nothing to do now.
+			 */
+			crit_exit();
+			return (0);
+		}
+		SCTP_TCB_LOCK(stcb);
+		asoc = &stcb->asoc;
+
+		if (!TAILQ_EMPTY(&asoc->out_wheel)) {
+			/* Check to see if some data queued */
+			struct sctp_stream_out *outs;
+			TAILQ_FOREACH(outs, &asoc->out_wheel, next_spoke) {
+				if (!TAILQ_EMPTY(&outs->outqueue)) {
+					some_on_streamwheel = 1;
+					break;
+				}
+			}
+		}
+		if (TAILQ_EMPTY(&asoc->send_queue) &&
+		    TAILQ_EMPTY(&asoc->sent_queue) &&
+		    (some_on_streamwheel == 0)) {
+			/* there is nothing queued to send, so I'm done... */
+			if (SCTP_GET_STATE(asoc) != SCTP_STATE_SHUTDOWN_SENT) {
+				/* only send SHUTDOWN the first time through */
+#ifdef SCTP_DEBUG
+				if (sctp_debug_on & SCTP_DEBUG_OUTPUT4) {
+					printf("%s:%d sends a shutdown\n",
+					       __FILE__,
+					       __LINE__
+						);
+				}
+#endif
+				sctp_send_shutdown(stcb,
+						   stcb->asoc.primary_destination);
+				sctp_chunk_output(stcb->sctp_ep, stcb, 1);
+				asoc->state = SCTP_STATE_SHUTDOWN_SENT;
+				sctp_timer_start(SCTP_TIMER_TYPE_SHUTDOWN,
+						 stcb->sctp_ep, stcb,
+						 asoc->primary_destination);
+				sctp_timer_start(SCTP_TIMER_TYPE_SHUTDOWNGUARD,
+						 stcb->sctp_ep, stcb,
+						 asoc->primary_destination);
+			}
+		} else {
+			/*
+			 * we still got (or just got) data to send, so
+			 * set SHUTDOWN_PENDING
+			 */
+			asoc->state |= SCTP_STATE_SHUTDOWN_PENDING;
+		}
+		SCTP_TCB_UNLOCK(stcb);
+	}
+	SCTP_INP_RUNLOCK(inp);
+	crit_exit();
+	return 0;
+}
+
+/*
+ * copies a "user" presentable address and removes embedded scope, etc.
+ * returns 0 on success, 1 on error
+ */
+static uint32_t
+sctp_fill_user_address(struct sockaddr_storage *ss, struct sockaddr *sa)
+{
+	struct sockaddr_in6 lsa6;
+	sa = (struct sockaddr *)sctp_recover_scope((struct sockaddr_in6 *)sa,
+						   &lsa6);
+	memcpy(ss, sa, sa->sa_len);
+	return (0);
+}
+
+
+#if defined(__NetBSD__) || defined(__OpenBSD__)
+/*
+ * On NetBSD and OpenBSD in6_sin_2_v4mapsin6() not used and not exported,
+ * so we have to export it here.
+ */
+void    in6_sin_2_v4mapsin6 __P((struct sockaddr_in *sin,
+                                 struct sockaddr_in6 *sin6));
+#endif
+
+static int
+sctp_fill_up_addresses(struct sctp_inpcb *inp,
+		       struct sctp_tcb *stcb,
+		       int limit,
+		       struct sockaddr_storage *sas)
+{
+	struct ifnet *ifn;
+	struct ifaddr *ifa;
+	int loopback_scope, ipv4_local_scope, local_scope, site_scope, actual;
+	int ipv4_addr_legal, ipv6_addr_legal;
+	actual = 0;
+	if (limit <= 0)
+		return (actual);
+
+	if (stcb) {
+		/* Turn on all the appropriate scope */
+		loopback_scope = stcb->asoc.loopback_scope;
+		ipv4_local_scope = stcb->asoc.ipv4_local_scope;
+		local_scope = stcb->asoc.local_scope;
+		site_scope = stcb->asoc.site_scope;
+	} else {
+		/* Turn on ALL scope, since we look at the EP */
+		loopback_scope = ipv4_local_scope = local_scope =
+			site_scope = 1;
+	}
+	ipv4_addr_legal = ipv6_addr_legal = 0;
+	if (inp->sctp_flags & SCTP_PCB_FLAGS_BOUND_V6) {
+		ipv6_addr_legal = 1;
+		if (
+#if defined(__OpenBSD__)
+		(0) /* we always do dual bind */
+#elif defined (__NetBSD__)
+		(((struct in6pcb *)inp)->in6p_flags & IN6P_IPV6_V6ONLY)
+#else
+		(((struct in6pcb *)inp)->inp_flags & IN6P_IPV6_V6ONLY)
+#endif
+		== 0) {
+			ipv4_addr_legal = 1;
+		}
+	} else {
+		ipv4_addr_legal = 1;
+	}
+
+	if (inp->sctp_flags & SCTP_PCB_FLAGS_BOUNDALL) {
+		TAILQ_FOREACH(ifn, &ifnet, if_list) {
+			if ((loopback_scope == 0) &&
+			    (ifn->if_type == IFT_LOOP)) {
+				/* Skip loopback if loopback_scope not set */
+				continue;
+			}
+			TAILQ_FOREACH(ifa, &ifn->if_addrlist, ifa_list) {
+				if (stcb) {
+				/*
+				 * For the BOUND-ALL case, the list
+				 * associated with a TCB is Always
+				 * considered a reverse list.. i.e.
+				 * it lists addresses that are NOT
+				 * part of the association. If this
+				 * is one of those we must skip it.
+				 */
+					if (sctp_is_addr_restricted(stcb,
+								    ifa->ifa_addr)) {
+						continue;
+					}
+				}
+				if ((ifa->ifa_addr->sa_family == AF_INET) &&
+				    (ipv4_addr_legal)) {
+					struct sockaddr_in *sin;
+					sin = (struct sockaddr_in *)ifa->ifa_addr;
+					if (sin->sin_addr.s_addr == 0) {
+						/* we skip unspecifed addresses */
+						continue;
+					}
+					if ((ipv4_local_scope == 0) &&
+					    (IN4_ISPRIVATE_ADDRESS(&sin->sin_addr))) {
+						continue;
+					}
+					if (inp->sctp_flags & SCTP_PCB_FLAGS_NEEDS_MAPPED_V4) {
+						in6_sin_2_v4mapsin6(sin, (struct sockaddr_in6 *)sas);
+						((struct sockaddr_in6 *)sas)->sin6_port = inp->sctp_lport;
+						sas = (struct sockaddr_storage *)((caddr_t)sas + sizeof(struct sockaddr_in6));
+						actual += sizeof(sizeof(struct sockaddr_in6));
+					} else {
+						memcpy(sas, sin, sizeof(*sin));
+						((struct sockaddr_in *)sas)->sin_port = inp->sctp_lport;
+						sas = (struct sockaddr_storage *)((caddr_t)sas + sizeof(*sin));
+						actual += sizeof(*sin);
+					}
+					if (actual >= limit) {
+						return (actual);
+					}
+				} else if ((ifa->ifa_addr->sa_family == AF_INET6) &&
+					   (ipv6_addr_legal)) {
+					struct sockaddr_in6 *sin6, lsa6;
+					sin6 = (struct sockaddr_in6 *)ifa->ifa_addr;
+					if (IN6_IS_ADDR_UNSPECIFIED(&sin6->sin6_addr)) {
+						/* we skip unspecifed addresses */
+						continue;
+					}
+					if (IN6_IS_ADDR_LINKLOCAL(&sin6->sin6_addr)) {
+						if (local_scope == 0)
+							continue;
+						if (sin6->sin6_scope_id == 0) {
+							lsa6 = *sin6;
+							if (in6_recoverscope(&lsa6,
+									     &lsa6.sin6_addr,
+									     NULL))
+								/* bad link local address */
+								continue;
+							sin6 = &lsa6;
+						}
+					}
+					if ((site_scope == 0) &&
+					    (IN6_IS_ADDR_SITELOCAL(&sin6->sin6_addr))) {
+						continue;
+					}
+					memcpy(sas, sin6, sizeof(*sin6));
+					((struct sockaddr_in6 *)sas)->sin6_port = inp->sctp_lport;
+					sas = (struct sockaddr_storage *)((caddr_t)sas + sizeof(*sin6));
+					actual += sizeof(*sin6);
+					if (actual >= limit) {
+						return (actual);
+					}
+				}
+			}
+		}
+	} else {
+		struct sctp_laddr *laddr;
+		/*
+		 * If we have a TCB and we do NOT support ASCONF (it's
+		 * turned off or otherwise) then the list is always the
+		 * true list of addresses (the else case below).  Otherwise
+		 * the list on the association is a list of addresses that
+		 * are NOT part of the association.
+		 */
+		if (inp->sctp_flags & SCTP_PCB_FLAGS_DO_ASCONF) {
+			/* The list is a NEGATIVE list */
+			LIST_FOREACH(laddr, &inp->sctp_addr_list, sctp_nxt_addr) {
+				if (stcb) {
+					if (sctp_is_addr_restricted(stcb, laddr->ifa->ifa_addr)) {
+						continue;
+					}
+				}
+				if (sctp_fill_user_address(sas, laddr->ifa->ifa_addr))
+					continue;
+
+				((struct sockaddr_in6 *)sas)->sin6_port = inp->sctp_lport;
+				sas = (struct sockaddr_storage *)((caddr_t)sas +
+								  laddr->ifa->ifa_addr->sa_len);
+				actual += laddr->ifa->ifa_addr->sa_len;
+				if (actual >= limit) {
+					return (actual);
+				}
+			}
+		} else {
+			/* The list is a positive list if present */
+			if (stcb) {
+				/* Must use the specific association list */
+				LIST_FOREACH(laddr, &stcb->asoc.sctp_local_addr_list,
+					     sctp_nxt_addr) {
+					if (sctp_fill_user_address(sas,
+								   laddr->ifa->ifa_addr))
+						continue;
+					((struct sockaddr_in6 *)sas)->sin6_port = inp->sctp_lport;
+					sas = (struct sockaddr_storage *)((caddr_t)sas +
+									  laddr->ifa->ifa_addr->sa_len);
+					actual += laddr->ifa->ifa_addr->sa_len;
+					if (actual >= limit) {
+						return (actual);
+					}
+				}
+			} else {
+				/* No endpoint so use the endpoints individual list */
+				LIST_FOREACH(laddr, &inp->sctp_addr_list,
+					     sctp_nxt_addr) {
+					if (sctp_fill_user_address(sas,
+								   laddr->ifa->ifa_addr))
+						continue;
+					((struct sockaddr_in6 *)sas)->sin6_port = inp->sctp_lport;
+					sas = (struct sockaddr_storage *)((caddr_t)sas +
+									  laddr->ifa->ifa_addr->sa_len);
+					actual += laddr->ifa->ifa_addr->sa_len;
+					if (actual >= limit) {
+						return (actual);
+					}
+				}
+			}
+		}
+	}
+	return (actual);
+}
+
+static int
+sctp_count_max_addresses(struct sctp_inpcb *inp)
+{
+	int cnt = 0;
+	/*
+	 * In both sub-set bound an bound_all cases we return the MAXIMUM
+	 * number of addresses that you COULD get. In reality the sub-set
+	 * bound may have an exclusion list for a given TCB OR in the
+	 * bound-all case a TCB may NOT include the loopback or other
+	 * addresses as well.
+	 */
+	if (inp->sctp_flags & SCTP_PCB_FLAGS_BOUNDALL) {
+		struct ifnet *ifn;
+		struct ifaddr *ifa;
+
+		TAILQ_FOREACH(ifn, &ifnet, if_list) {
+			TAILQ_FOREACH(ifa, &ifn->if_addrlist, ifa_list) {
+				/* Count them if they are the right type */
+				if (ifa->ifa_addr->sa_family == AF_INET) {
+					if (inp->sctp_flags & SCTP_PCB_FLAGS_NEEDS_MAPPED_V4)
+						cnt += sizeof(struct sockaddr_in6);
+					else
+						cnt += sizeof(struct sockaddr_in);
+
+				} else if (ifa->ifa_addr->sa_family == AF_INET6)
+					cnt += sizeof(struct sockaddr_in6);
+			}
+		}
+	} else {
+		struct sctp_laddr *laddr;
+		LIST_FOREACH(laddr, &inp->sctp_addr_list, sctp_nxt_addr) {
+			if (laddr->ifa->ifa_addr->sa_family == AF_INET) {
+				if (inp->sctp_flags & SCTP_PCB_FLAGS_NEEDS_MAPPED_V4)
+					cnt += sizeof(struct sockaddr_in6);
+				else
+					cnt += sizeof(struct sockaddr_in);
+
+			} else if (laddr->ifa->ifa_addr->sa_family == AF_INET6)
+				cnt += sizeof(struct sockaddr_in6);
+		}
+	}
+	return (cnt);
+}
+
+static int
+sctp_do_connect_x(struct socket *so,
+		  struct sctp_inpcb *inp,
+		  struct mbuf *m,
+#if (defined(__FreeBSD__) && __FreeBSD_version >= 500000) || defined(__DragonFly__)
+		  struct thread *p,
+#else
+  	          struct proc *p,
+#endif
+		  int delay
+	)
+{
+        int error = 0;
+	struct sctp_tcb *stcb = NULL;
+	struct sockaddr *sa;
+	int num_v6=0, num_v4=0, *totaddrp, totaddr, i, incr, at;
+#ifdef SCTP_DEBUG
+	if (sctp_debug_on & SCTP_DEBUG_PCB1) {
+		printf("Connectx called\n");
+	}
+#endif /* SCTP_DEBUG */
+
+	crit_enter();
+	if ((inp->sctp_flags & SCTP_PCB_FLAGS_TCPTYPE) &&
+	    (inp->sctp_flags & SCTP_PCB_FLAGS_CONNECTED)) {
+		/* We are already connected AND the TCP model */
+		crit_exit();
+		return (EADDRINUSE);
+	}
+	if (inp->sctp_flags & SCTP_PCB_FLAGS_CONNECTED) {
+		SCTP_INP_RLOCK(inp);
+		stcb = LIST_FIRST(&inp->sctp_asoc_list);
+		SCTP_INP_RUNLOCK(inp);
+	}
+	if (stcb) {
+		crit_exit();
+		return (EALREADY);
+
+	}
+	SCTP_ASOC_CREATE_LOCK(inp);
+	if ((inp->sctp_flags & SCTP_PCB_FLAGS_SOCKET_GONE) ||
+	    (inp->sctp_flags & SCTP_PCB_FLAGS_SOCKET_GONE)) {
+		SCTP_ASOC_CREATE_UNLOCK(inp);
+		crit_exit();
+		return (EFAULT);
+	}
+
+	totaddrp = mtod(m, int *);
+	totaddr = *totaddrp;
+	sa = (struct sockaddr *)(totaddrp + 1);
+	at = incr = 0;
+	/* account and validate addresses */
+	SCTP_INP_WLOCK(inp);
+	SCTP_INP_INCR_REF(inp);
+	SCTP_INP_WUNLOCK(inp);
+	for (i = 0; i < totaddr; i++) {
+		if (sa->sa_family == AF_INET) {
+			num_v4++;
+			incr = sizeof(struct sockaddr_in);
+		} else if (sa->sa_family == AF_INET6) {
+			struct sockaddr_in6 *sin6;
+			sin6 = (struct sockaddr_in6 *)sa;
+			if (IN6_IS_ADDR_V4MAPPED(&sin6->sin6_addr)) {
+				/* Must be non-mapped for connectx */
+				SCTP_ASOC_CREATE_UNLOCK(inp);
+				crit_exit();
+				return EINVAL;
+			}
+			num_v6++;
+			incr = sizeof(struct sockaddr_in6);
+		} else {
+			totaddr = i;
+			break;
+		}
+		stcb = sctp_findassociation_ep_addr(&inp, sa, NULL, NULL, NULL);
+		if (stcb != NULL) {
+			/* Already have or am bring up an association */
+			SCTP_ASOC_CREATE_UNLOCK(inp);
+			SCTP_TCB_UNLOCK(stcb);
+			crit_exit();
+			return (EALREADY);
+		}
+		if ((at + incr) > m->m_len) {
+			totaddr = i;
+			break;
+		}
+		sa = (struct sockaddr *)((caddr_t)sa + incr);
+	}
+	sa = (struct sockaddr *)(totaddrp + 1);
+	SCTP_INP_WLOCK(inp);
+	SCTP_INP_DECR_REF(inp);
+	SCTP_INP_WUNLOCK(inp);
+#ifdef INET6
+	if (((inp->sctp_flags & SCTP_PCB_FLAGS_BOUND_V6) == 0) &&
+	    (num_v6 > 0)) {
+		crit_exit();
+		SCTP_INP_WUNLOCK(inp);
+		SCTP_ASOC_CREATE_UNLOCK(inp);
+		return (EINVAL);
+	}
+	if ((inp->sctp_flags & SCTP_PCB_FLAGS_BOUND_V6) &&
+	    (num_v4 > 0)) {
+		struct in6pcb *inp6;
+		inp6 = (struct in6pcb *)inp;
+		if (
+#if defined(__OpenBSD__)
+			(0) /* we always do dual bind */
+#elif defined (__NetBSD__)
+			(inp6->in6p_flags & IN6P_IPV6_V6ONLY)
+#else
+			(inp6->inp_flags & IN6P_IPV6_V6ONLY)
+#endif
+			) {
+			/*
+			 * if IPV6_V6ONLY flag, ignore connections
+			 * destined to a v4 addr or v4-mapped addr
+			 */
+			SCTP_INP_WUNLOCK(inp);
+			SCTP_ASOC_CREATE_UNLOCK(inp);
+			crit_exit();
+			return EINVAL;
+		}
+	}
+#endif /* INET6 */
+	if ((inp->sctp_flags & SCTP_PCB_FLAGS_UNBOUND) ==
+	    SCTP_PCB_FLAGS_UNBOUND) {
+		/* Bind a ephemeral port */
+		SCTP_INP_WUNLOCK(inp);
+		error = sctp_inpcb_bind(so, NULL, p);
+		if (error) {
+			SCTP_ASOC_CREATE_UNLOCK(inp);
+			crit_exit();
+			return (error);
+		}
+	} else {
+		SCTP_INP_WUNLOCK(inp);
+	}
+        /* We are GOOD to go */
+	stcb = sctp_aloc_assoc(inp, sa, 1, &error, 0);
+	if (stcb == NULL) {
+		/* Gak! no memory */
+		SCTP_ASOC_CREATE_UNLOCK(inp);
+		crit_exit();
+		return (error);
+	}
+	/* move to second address */
+	if (sa->sa_family == AF_INET)
+		sa = (struct sockaddr *)((caddr_t)sa + sizeof(struct sockaddr_in));
+	else
+		sa = (struct sockaddr *)((caddr_t)sa + sizeof(struct sockaddr_in6));
+
+	for (i = 1; i < totaddr; i++) {
+		if (sa->sa_family == AF_INET) {
+			incr = sizeof(struct sockaddr_in);
+			if (sctp_add_remote_addr(stcb, sa, 0, 8)) {
+				/* assoc gone no un-lock */
+				sctp_free_assoc(inp, stcb);
+				SCTP_ASOC_CREATE_UNLOCK(inp);
+				crit_exit();
+				return (ENOBUFS);
+			}
+
+		} else if (sa->sa_family == AF_INET6) {
+			incr = sizeof(struct sockaddr_in6);
+			if (sctp_add_remote_addr(stcb, sa, 0, 8)) {
+				/* assoc gone no un-lock */
+				sctp_free_assoc(inp, stcb);
+				SCTP_ASOC_CREATE_UNLOCK(inp);
+				crit_exit();
+				return (ENOBUFS);
+			}
+		}
+		sa = (struct sockaddr *)((caddr_t)sa + incr);
+	}
+	stcb->asoc.state = SCTP_STATE_COOKIE_WAIT;
+	if (delay) {
+		/* doing delayed connection */
+		stcb->asoc.delayed_connection = 1;
+		sctp_timer_start(SCTP_TIMER_TYPE_INIT, inp, stcb, stcb->asoc.primary_destination);
+	} else {
+		SCTP_GETTIME_TIMEVAL(&stcb->asoc.time_entered);
+		sctp_send_initiate(inp, stcb);
+	}
+	SCTP_TCB_UNLOCK(stcb);
+	if (stcb->sctp_ep->sctp_flags & SCTP_PCB_FLAGS_TCPTYPE) {
+		stcb->sctp_ep->sctp_flags |= SCTP_PCB_FLAGS_CONNECTED;
+		/* Set the connected flag so we can queue data */
+		soisconnecting(so);
+	}
+	SCTP_ASOC_CREATE_UNLOCK(inp);
+	crit_exit();
+	return error;
+}
+
+
+static int
+sctp_optsget(struct socket *so,
+	     int opt,
+	     struct mbuf **mp,
+#if (defined(__FreeBSD__) && __FreeBSD_version >= 500000) || defined(__DragonFly__)
+	     struct thread *p
+#else
+	     struct proc *p
+#endif
+	)
+{
+	struct sctp_inpcb *inp;
+	struct mbuf *m;
+	int error, optval=0;
+	struct sctp_tcb *stcb = NULL;
+
+        inp = (struct sctp_inpcb *)so->so_pcb;
+	if (inp == 0)
+		return EINVAL;
+	error = 0;
+
+	if (mp == NULL) {
+#ifdef SCTP_DEBUG
+		if (sctp_debug_on & SCTP_DEBUG_USRREQ1) {
+			printf("optsget:MP is NULL EINVAL\n");
+		}
+#endif /* SCTP_DEBUG */
+		return (EINVAL);
+	}
+	m = *mp;
+	if (m == NULL) {
+		/* Got to have a mbuf */
+#ifdef SCTP_DEBUG
+		if (sctp_debug_on & SCTP_DEBUG_USRREQ1) {
+			printf("Huh no mbuf\n");
+		}
+#endif /* SCTP_DEBUG */
+		return (EINVAL);
+	}
+#ifdef SCTP_DEBUG
+	if (sctp_debug_on & SCTP_DEBUG_USRREQ2) {
+		printf("optsget opt:%lxx sz:%u\n", (unsigned long)opt,
+		       m->m_len);
+	}
+#endif /* SCTP_DEBUG */
+
+	switch (opt) {
+	case SCTP_NODELAY:
+	case SCTP_AUTOCLOSE:
+	case SCTP_AUTO_ASCONF:
+	case SCTP_DISABLE_FRAGMENTS:
+	case SCTP_I_WANT_MAPPED_V4_ADDR:
+#ifdef SCTP_DEBUG
+		if (sctp_debug_on & SCTP_DEBUG_USRREQ2) {
+			printf("other stuff\n");
+		}
+#endif /* SCTP_DEBUG */
+		SCTP_INP_RLOCK(inp);
+		switch (opt) {
+		case SCTP_DISABLE_FRAGMENTS:
+			optval = inp->sctp_flags & SCTP_PCB_FLAGS_NO_FRAGMENT;
+			break;
+		case SCTP_I_WANT_MAPPED_V4_ADDR:
+			optval = inp->sctp_flags & SCTP_PCB_FLAGS_NEEDS_MAPPED_V4;
+			break;
+		case SCTP_AUTO_ASCONF:
+			optval = inp->sctp_flags & SCTP_PCB_FLAGS_AUTO_ASCONF;
+			break;
+		case SCTP_NODELAY:
+			optval = inp->sctp_flags & SCTP_PCB_FLAGS_NODELAY;
+			break;
+		case SCTP_AUTOCLOSE:
+			if ((inp->sctp_flags & SCTP_PCB_FLAGS_AUTOCLOSE) ==
+			    SCTP_PCB_FLAGS_AUTOCLOSE)
+				optval = inp->sctp_ep.auto_close_time;
+			else
+				optval = 0;
+			break;
+
+		default:
+			error = ENOPROTOOPT;
+		} /* end switch (sopt->sopt_name) */
+		if (opt != SCTP_AUTOCLOSE) {
+			/* make it an "on/off" value */
+			optval = (optval != 0);
+		}
+		if ((size_t)m->m_len < sizeof(int)) {
+			error = EINVAL;
+		}
+		SCTP_INP_RUNLOCK(inp);
+		if (error == 0) {
+			/* return the option value */
+			*mtod(m, int *) = optval;
+			m->m_len = sizeof(optval);
+		}
+		break;
+	case SCTP_GET_ASOC_ID_LIST:
+	{
+		struct sctp_assoc_ids *ids;
+		int cnt, at;
+		u_int16_t orig;
+
+		if ((size_t)m->m_len < sizeof(struct sctp_assoc_ids)) {
+			error = EINVAL;
+			break;
+		}
+		ids = mtod(m, struct sctp_assoc_ids *);
+		cnt = 0;
+		SCTP_INP_RLOCK(inp);
+		stcb = LIST_FIRST(&inp->sctp_asoc_list);
+		if (stcb == NULL) {
+		none_out_now:
+			ids->asls_numb_present = 0;
+			ids->asls_more_to_get = 0;
+			SCTP_INP_RUNLOCK(inp);
+			break;
+		}
+		orig = ids->asls_assoc_start;
+		stcb = LIST_FIRST(&inp->sctp_asoc_list);
+		while( orig ) {
+			stcb = LIST_NEXT(stcb , sctp_tcblist);
+			orig--;
+			cnt--;
+		}
+		if ( stcb == NULL)
+			goto none_out_now;
+
+		at = 0;
+		ids->asls_numb_present = 0;
+		ids->asls_more_to_get = 1;
+		while(at < MAX_ASOC_IDS_RET) {
+			ids->asls_assoc_id[at] = sctp_get_associd(stcb);
+			at++;
+			ids->asls_numb_present++;
+			stcb = LIST_NEXT(stcb , sctp_tcblist);
+			if (stcb == NULL) {
+				ids->asls_more_to_get = 0;
+				break;
+			}
+		}
+		SCTP_INP_RUNLOCK(inp);
+	}
+	break;
+	case SCTP_GET_NONCE_VALUES:
+	{
+		struct sctp_get_nonce_values *gnv;
+		if ((size_t)m->m_len < sizeof(struct sctp_get_nonce_values)) {
+			error = EINVAL;
+			break;
+		}
+		gnv = mtod(m, struct sctp_get_nonce_values *);
+		stcb = sctp_findassociation_ep_asocid(inp, gnv->gn_assoc_id);
+		if (stcb == NULL) {
+			error = ENOTCONN;
+		} else {
+			gnv->gn_peers_tag = stcb->asoc.peer_vtag;
+			gnv->gn_local_tag = stcb->asoc.my_vtag;
+			SCTP_TCB_UNLOCK(stcb);
+		}
+
+	}
+	break;
+	case SCTP_PEER_PUBLIC_KEY:
+	case SCTP_MY_PUBLIC_KEY:
+	case SCTP_SET_AUTH_CHUNKS:
+	case SCTP_SET_AUTH_SECRET:
+		/* not supported yet and until we refine the draft */
+		error = EOPNOTSUPP;
+		break;
+
+	case SCTP_DELAYED_ACK_TIME:
+	{
+		int32_t *tm;
+		if ((size_t)m->m_len < sizeof(int32_t)) {
+			error = EINVAL;
+			break;
+		}
+		tm = mtod(m, int32_t *);
+
+		*tm = TICKS_TO_MSEC(inp->sctp_ep.sctp_timeoutticks[SCTP_TIMER_RECV]);
+	}
+	break;
+
+	case SCTP_GET_SNDBUF_USE:
+		if ((size_t)m->m_len < sizeof(struct sctp_sockstat)) {
+			error = EINVAL;
+		} else {
+			struct sctp_sockstat *ss;
+			struct sctp_tcb *stcb;
+			struct sctp_association *asoc;
+			ss = mtod(m, struct sctp_sockstat *);
+   		        stcb = sctp_findassociation_ep_asocid(inp, ss->ss_assoc_id);
+			if (stcb == NULL) {
+				error = ENOTCONN;
+			} else {
+				asoc = &stcb->asoc;
+				ss->ss_total_sndbuf = (u_int32_t)asoc->total_output_queue_size;
+				ss->ss_total_mbuf_sndbuf = (u_int32_t)asoc->total_output_mbuf_queue_size;
+				ss->ss_total_recv_buf = (u_int32_t)(asoc->size_on_delivery_queue +
+								    asoc->size_on_reasm_queue +
+								    asoc->size_on_all_streams);
+				SCTP_TCB_UNLOCK(stcb);
+				error = 0;
+				m->m_len = sizeof(struct sctp_sockstat);
+			}
+		}
+		break;
+	case SCTP_MAXBURST:
+	{
+		u_int8_t *burst;
+		burst = mtod(m, u_int8_t *);
+		SCTP_INP_RLOCK(inp);
+		*burst = inp->sctp_ep.max_burst;
+		SCTP_INP_RUNLOCK(inp);
+		m->m_len = sizeof(u_int8_t);
+	}
+	break;
+	case SCTP_MAXSEG:
+	{
+		u_int32_t *segsize;
+		sctp_assoc_t *assoc_id;
+		int ovh;
+
+		if ((size_t)m->m_len < sizeof(u_int32_t)) {
+			error = EINVAL;
+			break;
+		}
+		if ((size_t)m->m_len < sizeof(sctp_assoc_t)) {
+			error = EINVAL;
+			break;
+		}
+		assoc_id = mtod(m, sctp_assoc_t *);
+		segsize = mtod(m, u_int32_t *);
+		m->m_len = sizeof(u_int32_t);
+
+		if (((inp->sctp_flags & SCTP_PCB_FLAGS_TCPTYPE) &&
+		     (inp->sctp_flags & SCTP_PCB_FLAGS_CONNECTED)) ||
+		    (inp->sctp_flags & SCTP_PCB_FLAGS_IN_TCPPOOL)) {
+			struct sctp_tcb *stcb;
+			SCTP_INP_RLOCK(inp);
+			stcb = LIST_FIRST(&inp->sctp_asoc_list);
+			if (stcb) {
+				SCTP_TCB_LOCK(stcb);
+				SCTP_INP_RUNLOCK(inp);
+				*segsize = sctp_get_frag_point(stcb, &stcb->asoc);
+				SCTP_TCB_UNLOCK(stcb);
+			} else {
+				SCTP_INP_RUNLOCK(inp);
+				goto skipit;
+			}
+		} else {
+			stcb = sctp_findassociation_ep_asocid(inp, *assoc_id);
+			if (stcb) {
+				*segsize = sctp_get_frag_point(stcb, &stcb->asoc);
+				SCTP_TCB_UNLOCK(stcb);
+				break;
+			}
+		skipit:
+			/* default is to get the max, if I
+			 * can't calculate from an existing association.
+			 */
+			if (inp->sctp_flags & SCTP_PCB_FLAGS_BOUND_V6) {
+				ovh = SCTP_MED_OVERHEAD;
+			} else {
+				ovh = SCTP_MED_V4_OVERHEAD;
+			}
+			*segsize = inp->sctp_frag_point - ovh;
+		}
+	}
+	break;
+
+	case SCTP_SET_DEBUG_LEVEL:
+#ifdef SCTP_DEBUG
+	{
+		u_int32_t *level;
+		if ((size_t)m->m_len < sizeof(u_int32_t)) {
+			error = EINVAL;
+			break;
+		}
+		level = mtod(m, u_int32_t *);
+		error = 0;
+		*level = sctp_debug_on;
+		m->m_len = sizeof(u_int32_t);
+		printf("Returning DEBUG LEVEL %x is set\n",
+		       (u_int)sctp_debug_on);
+	}
+#else /* SCTP_DEBUG */
+	error = EOPNOTSUPP;
+#endif
+	break;
+	case SCTP_GET_STAT_LOG:
+#ifdef SCTP_STAT_LOGGING
+		error = sctp_fill_stat_log(m);
+#else /* SCTP_DEBUG */
+		error = EOPNOTSUPP;
+#endif
+		break;
+	case SCTP_GET_PEGS:
+	{
+		u_int32_t *pt;
+		if ((size_t)m->m_len < sizeof(sctp_pegs)) {
+			error = EINVAL;
+			break;
+		}
+		pt = mtod(m, u_int32_t *);
+		memcpy(pt, sctp_pegs, sizeof(sctp_pegs));
+		m->m_len = sizeof(sctp_pegs);
+	}
+	break;
+	case SCTP_EVENTS:
+	{
+		struct sctp_event_subscribe *events;
+#ifdef SCTP_DEBUG
+		if (sctp_debug_on & SCTP_DEBUG_USRREQ2) {
+			printf("get events\n");
+		}
+#endif /* SCTP_DEBUG */
+		if ((size_t)m->m_len < sizeof(struct sctp_event_subscribe)) {
+#ifdef SCTP_DEBUG
+			if (sctp_debug_on & SCTP_DEBUG_USRREQ2) {
+				printf("M->M_LEN is %d not %d\n",
+				       (int)m->m_len,
+				       (int)sizeof(struct sctp_event_subscribe));
+			}
+#endif /* SCTP_DEBUG */
+			error = EINVAL;
+			break;
+		}
+		events = mtod(m, struct sctp_event_subscribe *);
+		memset(events, 0, sizeof(events));
+		SCTP_INP_RLOCK(inp);
+		if (inp->sctp_flags & SCTP_PCB_FLAGS_RECVDATAIOEVNT)
+			events->sctp_data_io_event = 1;
+
+		if (inp->sctp_flags & SCTP_PCB_FLAGS_RECVASSOCEVNT)
+			events->sctp_association_event = 1;
+
+		if (inp->sctp_flags & SCTP_PCB_FLAGS_RECVPADDREVNT)
+			events->sctp_address_event = 1;
+
+		if (inp->sctp_flags & SCTP_PCB_FLAGS_RECVSENDFAILEVNT)
+			events->sctp_send_failure_event = 1;
+
+		if (inp->sctp_flags & SCTP_PCB_FLAGS_RECVPEERERR)
+			events->sctp_peer_error_event = 1;
+
+		if (inp->sctp_flags & SCTP_PCB_FLAGS_RECVSHUTDOWNEVNT)
+			events->sctp_shutdown_event = 1;
+
+		if (inp->sctp_flags & SCTP_PCB_FLAGS_PDAPIEVNT)
+			events->sctp_partial_delivery_event = 1;
+
+		if (inp->sctp_flags & SCTP_PCB_FLAGS_ADAPTIONEVNT)
+			events->sctp_adaption_layer_event = 1;
+
+		if (inp->sctp_flags & SCTP_PCB_FLAGS_STREAM_RESETEVNT)
+			events->sctp_stream_reset_events = 1;
+		SCTP_INP_RUNLOCK(inp);
+		m->m_len = sizeof(struct sctp_event_subscribe);
+
+	}
+	break;
+
+	case SCTP_ADAPTION_LAYER:
+		if ((size_t)m->m_len < sizeof(int)) {
+			error = EINVAL;
+			break;
+		}
+#ifdef SCTP_DEBUG
+		if (sctp_debug_on & SCTP_DEBUG_USRREQ1) {
+			printf("getadaption ind\n");
+		}
+#endif /* SCTP_DEBUG */
+		SCTP_INP_RLOCK(inp);
+		*mtod(m, int *) = inp->sctp_ep.adaption_layer_indicator;
+		SCTP_INP_RUNLOCK(inp);
+		m->m_len = sizeof(int);
+		break;
+	case SCTP_SET_INITIAL_DBG_SEQ:
+		if ((size_t)m->m_len < sizeof(int)) {
+			error = EINVAL;
+			break;
+		}
+#ifdef SCTP_DEBUG
+		if (sctp_debug_on & SCTP_DEBUG_USRREQ1) {
+			printf("get initial dbg seq\n");
+		}
+#endif /* SCTP_DEBUG */
+		SCTP_INP_RLOCK(inp);
+		*mtod(m, int *) = inp->sctp_ep.initial_sequence_debug;
+		SCTP_INP_RUNLOCK(inp);
+		m->m_len = sizeof(int);
+		break;
+	case SCTP_GET_LOCAL_ADDR_SIZE:
+		if ((size_t)m->m_len < sizeof(int)) {
+			error = EINVAL;
+			break;
+		}
+#ifdef SCTP_DEBUG
+		if (sctp_debug_on & SCTP_DEBUG_USRREQ1) {
+			printf("get local sizes\n");
+		}
+#endif /* SCTP_DEBUG */
+		SCTP_INP_RLOCK(inp);
+		*mtod(m, int *) = sctp_count_max_addresses(inp);
+		SCTP_INP_RUNLOCK(inp);
+		m->m_len = sizeof(int);
+		break;
+	case SCTP_GET_REMOTE_ADDR_SIZE:
+	{
+		sctp_assoc_t *assoc_id;
+		u_int32_t *val, sz;
+		struct sctp_nets *net;
+#ifdef SCTP_DEBUG
+		if (sctp_debug_on & SCTP_DEBUG_USRREQ1) {
+			printf("get remote size\n");
+		}
+#endif /* SCTP_DEBUG */
+		if ((size_t)m->m_len < sizeof(sctp_assoc_t)) {
+#ifdef SCTP_DEBUG
+			printf("m->m_len:%d not %d\n",
+			       m->m_len, sizeof(sctp_assoc_t));
+#endif /* SCTP_DEBUG */
+			error = EINVAL;
+			break;
+		}
+		stcb = NULL;
+		val = mtod(m, u_int32_t *);
+		if (inp->sctp_flags & SCTP_PCB_FLAGS_CONNECTED) {
+			SCTP_INP_RLOCK(inp);
+			stcb = LIST_FIRST(&inp->sctp_asoc_list);
+			if (stcb)
+				SCTP_TCB_LOCK(stcb);
+			SCTP_INP_RUNLOCK(inp);
+		}
+		if (stcb == NULL) {
+			assoc_id = mtod(m, sctp_assoc_t *);
+			stcb = sctp_findassociation_ep_asocid(inp, *assoc_id);
+		}
+
+		if (stcb == NULL) {
+			error = EINVAL;
+			break;
+		}
+		*val = 0;
+		sz = 0;
+		/* Count the sizes */
+		TAILQ_FOREACH(net, &stcb->asoc.nets, sctp_next) {
+			if ((stcb->sctp_ep->sctp_flags & SCTP_PCB_FLAGS_NEEDS_MAPPED_V4) ||
+			    (((struct sockaddr *)&net->ro._l_addr)->sa_family == AF_INET6)) {
+				sz += sizeof(struct sockaddr_in6);
+			} else if (((struct sockaddr *)&net->ro._l_addr)->sa_family == AF_INET) {
+				sz += sizeof(struct sockaddr_in);
+			} else {
+				/* huh */
+				break;
+			}
+		}
+		SCTP_TCB_UNLOCK(stcb);
+		*val = sz;
+		m->m_len = sizeof(u_int32_t);
+	}
+	break;
+	case SCTP_GET_PEER_ADDRESSES:
+		/*
+		 * Get the address information, an array
+		 * is passed in to fill up we pack it.
+		 */
+	{
+		int cpsz, left;
+		struct sockaddr_storage *sas;
+		struct sctp_nets *net;
+		struct sctp_getaddresses *saddr;
+#ifdef SCTP_DEBUG
+		if (sctp_debug_on & SCTP_DEBUG_USRREQ1) {
+			printf("get peer addresses\n");
+		}
+#endif /* SCTP_DEBUG */
+		if ((size_t)m->m_len < sizeof(struct sctp_getaddresses)) {
+			error = EINVAL;
+			break;
+		}
+		left = m->m_len - sizeof(struct sctp_getaddresses);
+		saddr = mtod(m, struct sctp_getaddresses *);
+		if (inp->sctp_flags & SCTP_PCB_FLAGS_CONNECTED) {
+			SCTP_INP_RLOCK(inp);
+			stcb = LIST_FIRST(&inp->sctp_asoc_list);
+			if (stcb)
+				SCTP_TCB_LOCK(stcb);
+			SCTP_INP_RUNLOCK(inp);
+		} else
+			stcb = sctp_findassociation_ep_asocid(inp,
+							      saddr->sget_assoc_id);
+		if (stcb == NULL) {
+			error = ENOENT;
+			break;
+		}
+		m->m_len = sizeof(struct sctp_getaddresses);
+		sas = (struct sockaddr_storage *)&saddr->addr[0];
+
+		TAILQ_FOREACH(net, &stcb->asoc.nets, sctp_next) {
+			if ((stcb->sctp_ep->sctp_flags & SCTP_PCB_FLAGS_NEEDS_MAPPED_V4) ||
+			    (((struct sockaddr *)&net->ro._l_addr)->sa_family == AF_INET6)) {
+				cpsz = sizeof(struct sockaddr_in6);
+			} else if (((struct sockaddr *)&net->ro._l_addr)->sa_family == AF_INET) {
+				cpsz = sizeof(struct sockaddr_in);
+			} else {
+				/* huh */
+				break;
+			}
+			if (left < cpsz) {
+				/* not enough room. */
+#ifdef SCTP_DEBUG
+				if (sctp_debug_on & SCTP_DEBUG_USRREQ1) {
+					printf("Out of room\n");
+				}
+#endif /* SCTP_DEBUG */
+				break;
+			}
+			if ((stcb->sctp_ep->sctp_flags & SCTP_PCB_FLAGS_NEEDS_MAPPED_V4) &&
+			    (((struct sockaddr *)&net->ro._l_addr)->sa_family == AF_INET)) {
+				/* Must map the address */
+				in6_sin_2_v4mapsin6((struct sockaddr_in *)&net->ro._l_addr,
+						    (struct sockaddr_in6 *)sas);
+			} else {
+				memcpy(sas, &net->ro._l_addr, cpsz);
+			}
+			((struct sockaddr_in *)sas)->sin_port = stcb->rport;
+
+			sas = (struct sockaddr_storage *)((caddr_t)sas + cpsz);
+			left -= cpsz;
+			m->m_len += cpsz;
+#ifdef SCTP_DEBUG
+			if (sctp_debug_on & SCTP_DEBUG_USRREQ2) {
+				printf("left now:%d mlen:%d\n",
+				       left, m->m_len);
+			}
+#endif /* SCTP_DEBUG */
+		}
+		SCTP_TCB_UNLOCK(stcb);
+	}
+#ifdef SCTP_DEBUG
+	if (sctp_debug_on & SCTP_DEBUG_USRREQ1) {
+		printf("All done\n");
+	}
+#endif /* SCTP_DEBUG */
+	break;
+	case SCTP_GET_LOCAL_ADDRESSES:
+	{
+		int limit, actual;
+		struct sockaddr_storage *sas;
+		struct sctp_getaddresses *saddr;
+#ifdef SCTP_DEBUG
+		if (sctp_debug_on & SCTP_DEBUG_USRREQ1) {
+			printf("get local addresses\n");
+		}
+#endif /* SCTP_DEBUG */
+		if ((size_t)m->m_len < sizeof(struct sctp_getaddresses)) {
+			error = EINVAL;
+			break;
+		}
+		saddr = mtod(m, struct sctp_getaddresses *);
+
+		if (saddr->sget_assoc_id) {
+			if (inp->sctp_flags & SCTP_PCB_FLAGS_CONNECTED) {
+				SCTP_INP_RLOCK(inp);
+				stcb = LIST_FIRST(&inp->sctp_asoc_list);
+				if (stcb)
+					SCTP_TCB_LOCK(stcb);
+				SCTP_INP_RUNLOCK(inp);
+			} else
+				stcb = sctp_findassociation_ep_asocid(inp, saddr->sget_assoc_id);
+
+		} else {
+			stcb = NULL;
+		}
+		/*
+		 * assure that the TCP model does not need a assoc id
+		 * once connected.
+		 */
+		if ( (inp->sctp_flags & SCTP_PCB_FLAGS_CONNECTED) &&
+		     (stcb == NULL) ) {
+			SCTP_INP_RLOCK(inp);
+			stcb = LIST_FIRST(&inp->sctp_asoc_list);
+			if (stcb)
+				SCTP_TCB_LOCK(stcb);
+			SCTP_INP_RUNLOCK(inp);
+		}
+		sas = (struct sockaddr_storage *)&saddr->addr[0];
+		limit = m->m_len - sizeof(sctp_assoc_t);
+		actual = sctp_fill_up_addresses(inp, stcb, limit, sas);
+		SCTP_TCB_UNLOCK(stcb);
+		m->m_len = sizeof(struct sockaddr_storage) + actual;
+	}
+	break;
+	case SCTP_PEER_ADDR_PARAMS:
+	{
+		struct sctp_paddrparams *paddrp;
+		struct sctp_nets *net;
+
+#ifdef SCTP_DEBUG
+		if (sctp_debug_on & SCTP_DEBUG_USRREQ1) {
+			printf("Getting peer_addr_params\n");
+		}
+#endif /* SCTP_DEBUG */
+		if ((size_t)m->m_len < sizeof(struct sctp_paddrparams)) {
+#ifdef SCTP_DEBUG
+			if (sctp_debug_on & SCTP_DEBUG_USRREQ2) {
+				printf("Hmm m->m_len:%d is to small\n",
+				       m->m_len);
+			}
+#endif /* SCTP_DEBUG */
+			error = EINVAL;
+			break;
+		}
+		paddrp = mtod(m, struct sctp_paddrparams *);
+
+		net = NULL;
+		if (paddrp->spp_assoc_id) {
+#ifdef SCTP_DEBUG
+			if (sctp_debug_on & SCTP_DEBUG_USRREQ1) {
+				printf("In spp_assoc_id find type\n");
+			}
+#endif /* SCTP_DEBUG */
+			if (inp->sctp_flags & SCTP_PCB_FLAGS_CONNECTED) {
+				SCTP_INP_RLOCK(inp);
+				stcb = LIST_FIRST(&inp->sctp_asoc_list);
+				if (stcb) {
+					SCTP_TCB_LOCK(stcb);
+					net = sctp_findnet(stcb, (struct sockaddr *)&paddrp->spp_address);
+				}
+				SCTP_INP_RLOCK(inp);
+			} else {
+				stcb = sctp_findassociation_ep_asocid(inp, paddrp->spp_assoc_id);
+			}
+			if (stcb == NULL) {
+				error = ENOENT;
+				break;
+			}
+		}
+		if (	(stcb == NULL) &&
+			((((struct sockaddr *)&paddrp->spp_address)->sa_family == AF_INET) ||
+			 (((struct sockaddr *)&paddrp->spp_address)->sa_family == AF_INET6))) {
+			/* Lookup via address */
+#ifdef SCTP_DEBUG
+			if (sctp_debug_on & SCTP_DEBUG_USRREQ1) {
+				printf("Ok we need to lookup a param\n");
+			}
+#endif /* SCTP_DEBUG */
+			if (inp->sctp_flags & SCTP_PCB_FLAGS_CONNECTED) {
+				SCTP_INP_RLOCK(inp);
+				stcb = LIST_FIRST(&inp->sctp_asoc_list);
+				if (stcb) {
+					SCTP_TCB_LOCK(stcb);
+					net = sctp_findnet(stcb, (struct sockaddr *)&paddrp->spp_address);
+				}
+				SCTP_INP_RUNLOCK(inp);
+			} else {
+				SCTP_INP_WLOCK(inp);
+				SCTP_INP_INCR_REF(inp);
+				SCTP_INP_WUNLOCK(inp);
+				stcb = sctp_findassociation_ep_addr(&inp,
+								    (struct sockaddr *)&paddrp->spp_address,
+								    &net, NULL, NULL);
+				if (stcb == NULL) {
+					SCTP_INP_WLOCK(inp);
+					SCTP_INP_DECR_REF(inp);
+					SCTP_INP_WUNLOCK(inp);
+				}
+			}
+
+			if (stcb == NULL) {
+				error = ENOENT;
+				break;
+			}
+		} else {
+			/* Effects the Endpoint */
+#ifdef SCTP_DEBUG
+			if (sctp_debug_on & SCTP_DEBUG_USRREQ1) {
+				printf("User wants EP level info\n");
+			}
+#endif /* SCTP_DEBUG */
+			stcb = NULL;
+		}
+		if (stcb) {
+			/* Applys to the specific association */
+#ifdef SCTP_DEBUG
+			if (sctp_debug_on & SCTP_DEBUG_USRREQ1) {
+				printf("In TCB side\n");
+			}
+#endif /* SCTP_DEBUG */
+			if (net) {
+				paddrp->spp_pathmaxrxt = net->failure_threshold;
+			} else {
+				/* No destination so return default value */
+				paddrp->spp_pathmaxrxt = stcb->asoc.def_net_failure;
+			}
+			paddrp->spp_hbinterval = stcb->asoc.heart_beat_delay;
+			paddrp->spp_assoc_id = sctp_get_associd(stcb);
+			SCTP_TCB_UNLOCK(stcb);
+		} else {
+			/* Use endpoint defaults */
+			SCTP_INP_RLOCK(inp);
+#ifdef SCTP_DEBUG
+			if (sctp_debug_on & SCTP_DEBUG_USRREQ1) {
+				printf("In EP levle info\n");
+			}
+#endif /* SCTP_DEBUG */
+			paddrp->spp_pathmaxrxt = inp->sctp_ep.def_net_failure;
+			paddrp->spp_hbinterval = inp->sctp_ep.sctp_timeoutticks[SCTP_TIMER_HEARTBEAT];
+			paddrp->spp_assoc_id = (sctp_assoc_t)0;
+			SCTP_INP_RUNLOCK(inp);
+		}
+		m->m_len = sizeof(struct sctp_paddrparams);
+	}
+	break;
+	case SCTP_GET_PEER_ADDR_INFO:
+	{
+		struct sctp_paddrinfo *paddri;
+		struct sctp_nets *net;
+#ifdef SCTP_DEBUG
+		if (sctp_debug_on & SCTP_DEBUG_USRREQ1) {
+			printf("GetPEER ADDR_INFO\n");
+		}
+#endif /* SCTP_DEBUG */
+		if ((size_t)m->m_len < sizeof(struct sctp_paddrinfo)) {
+			error = EINVAL;
+			break;
+		}
+		paddri = mtod(m, struct sctp_paddrinfo *);
+		net = NULL;
+		if ((((struct sockaddr *)&paddri->spinfo_address)->sa_family == AF_INET) ||
+		    (((struct sockaddr *)&paddri->spinfo_address)->sa_family == AF_INET6)) {
+			/* Lookup via address */
+			if (inp->sctp_flags & SCTP_PCB_FLAGS_CONNECTED) {
+				SCTP_INP_RLOCK(inp);
+				stcb = LIST_FIRST(&inp->sctp_asoc_list);
+				if (stcb) {
+					SCTP_TCB_LOCK(stcb);
+					net = sctp_findnet(stcb,
+							    (struct sockaddr *)&paddri->spinfo_address);
+				}
+				SCTP_INP_RUNLOCK(inp);
+			} else {
+				SCTP_INP_WLOCK(inp);
+				SCTP_INP_INCR_REF(inp);
+				SCTP_INP_WUNLOCK(inp);
+				stcb = sctp_findassociation_ep_addr(&inp,
+				    (struct sockaddr *)&paddri->spinfo_address,
+				    &net, NULL, NULL);
+				if (stcb == NULL) {
+					SCTP_INP_WLOCK(inp);
+					SCTP_INP_DECR_REF(inp);
+					SCTP_INP_WUNLOCK(inp);
+				}
+			}
+
+		} else {
+			stcb = NULL;
+		}
+		if ((stcb == NULL) || (net == NULL)) {
+			error = ENOENT;
+			break;
+		}
+		m->m_len = sizeof(struct sctp_paddrinfo);
+		paddri->spinfo_state = net->dest_state & (SCTP_REACHABLE_MASK|SCTP_ADDR_NOHB);
+		paddri->spinfo_cwnd = net->cwnd;
+		paddri->spinfo_srtt = ((net->lastsa >> 2) + net->lastsv) >> 1;
+		paddri->spinfo_rto = net->RTO;
+		paddri->spinfo_assoc_id = sctp_get_associd(stcb);
+		SCTP_TCB_UNLOCK(stcb);
+	}
+	break;
+	case SCTP_PCB_STATUS:
+	{
+		struct sctp_pcbinfo *spcb;
+#ifdef SCTP_DEBUG
+		if (sctp_debug_on & SCTP_DEBUG_USRREQ1) {
+			printf("PCB status\n");
+		}
+#endif /* SCTP_DEBUG */
+		if ((size_t)m->m_len < sizeof(struct sctp_pcbinfo)) {
+			error = EINVAL;
+			break;
+		}
+		spcb = mtod(m, struct sctp_pcbinfo *);
+		sctp_fill_pcbinfo(spcb);
+		m->m_len = sizeof(struct sctp_pcbinfo);
+	}
+	break;
+	case SCTP_STATUS:
+	{
+		struct sctp_nets *net;
+		struct sctp_status *sstat;
+#ifdef SCTP_DEBUG
+		if (sctp_debug_on & SCTP_DEBUG_USRREQ1) {
+			printf("SCTP status\n");
+		}
+#endif /* SCTP_DEBUG */
+
+		if ((size_t)m->m_len < sizeof(struct sctp_status)) {
+			error = EINVAL;
+			break;
+		}
+		sstat = mtod(m, struct sctp_status *);
+
+		if (inp->sctp_flags & SCTP_PCB_FLAGS_CONNECTED) {
+			SCTP_INP_RLOCK(inp);
+			stcb = LIST_FIRST(&inp->sctp_asoc_list);
+			if (stcb)
+				SCTP_TCB_LOCK(stcb);
+			SCTP_INP_RUNLOCK(inp);
+		} else
+			stcb = sctp_findassociation_ep_asocid(inp, sstat->sstat_assoc_id);
+
+		if (stcb == NULL) {
+			error = EINVAL;
+			break;
+		}
+		/*
+		 * I think passing the state is fine since
+		 * sctp_constants.h will be available to the user
+		 * land.
+		 */
+		sstat->sstat_state = stcb->asoc.state;
+		sstat->sstat_rwnd = stcb->asoc.peers_rwnd;
+		sstat->sstat_unackdata = stcb->asoc.sent_queue_cnt;
+		/*
+		 * We can't include chunks that have been passed
+		 * to the socket layer. Only things in queue.
+		 */
+		sstat->sstat_penddata = (stcb->asoc.cnt_on_delivery_queue +
+					 stcb->asoc.cnt_on_reasm_queue +
+					 stcb->asoc.cnt_on_all_streams);
+
+
+		sstat->sstat_instrms = stcb->asoc.streamincnt;
+		sstat->sstat_outstrms = stcb->asoc.streamoutcnt;
+		sstat->sstat_fragmentation_point = sctp_get_frag_point(stcb, &stcb->asoc);
+		memcpy(&sstat->sstat_primary.spinfo_address,
+		       &stcb->asoc.primary_destination->ro._l_addr,
+		       ((struct sockaddr *)(&stcb->asoc.primary_destination->ro._l_addr))->sa_len);
+		net = stcb->asoc.primary_destination;
+		((struct sockaddr_in *)&sstat->sstat_primary.spinfo_address)->sin_port = stcb->rport;
+		/*
+		 * Again the user can get info from sctp_constants.h
+		 * for what the state of the network is.
+		 */
+		sstat->sstat_primary.spinfo_state = net->dest_state & SCTP_REACHABLE_MASK;
+		sstat->sstat_primary.spinfo_cwnd = net->cwnd;
+		sstat->sstat_primary.spinfo_srtt = net->lastsa;
+		sstat->sstat_primary.spinfo_rto = net->RTO;
+		sstat->sstat_primary.spinfo_mtu = net->mtu;
+		sstat->sstat_primary.spinfo_assoc_id = sctp_get_associd(stcb);
+		SCTP_TCB_UNLOCK(stcb);
+		m->m_len = sizeof(*sstat);
+	}
+	break;
+	case SCTP_RTOINFO:
+	{
+		struct sctp_rtoinfo *srto;
+#ifdef SCTP_DEBUG
+		if (sctp_debug_on & SCTP_DEBUG_USRREQ1) {
+			printf("RTO Info\n");
+		}
+#endif /* SCTP_DEBUG */
+		if ((size_t)m->m_len < sizeof(struct sctp_rtoinfo)) {
+			error = EINVAL;
+			break;
+		}
+		srto = mtod(m, struct sctp_rtoinfo *);
+		if (srto->srto_assoc_id == 0) {
+			/* Endpoint only please */
+			SCTP_INP_RLOCK(inp);
+			srto->srto_initial = inp->sctp_ep.initial_rto;
+			srto->srto_max = inp->sctp_ep.sctp_maxrto;
+			srto->srto_min = inp->sctp_ep.sctp_minrto;
+			SCTP_INP_RUNLOCK(inp);
+			break;
+		}
+		if (inp->sctp_flags & SCTP_PCB_FLAGS_CONNECTED) {
+			SCTP_INP_RLOCK(inp);
+			stcb = LIST_FIRST(&inp->sctp_asoc_list);
+			if (stcb)
+				SCTP_TCB_LOCK(stcb);
+			SCTP_INP_RUNLOCK(inp);
+		} else
+			stcb = sctp_findassociation_ep_asocid(inp, srto->srto_assoc_id);
+
+		if (stcb == NULL) {
+			error = EINVAL;
+			break;
+		}
+		srto->srto_initial = stcb->asoc.initial_rto;
+		srto->srto_max = stcb->asoc.maxrto;
+		srto->srto_min = stcb->asoc.minrto;
+		SCTP_TCB_UNLOCK(stcb);
+		m->m_len = sizeof(*srto);
+	}
+	break;
+	case SCTP_ASSOCINFO:
+	{
+		struct sctp_assocparams *sasoc;
+#ifdef SCTP_DEBUG
+		if (sctp_debug_on & SCTP_DEBUG_USRREQ1) {
+			printf("Associnfo\n");
+		}
+#endif /* SCTP_DEBUG */
+		if ((size_t)m->m_len < sizeof(struct sctp_assocparams)) {
+			error = EINVAL;
+			break;
+		}
+		sasoc = mtod(m, struct sctp_assocparams *);
+		stcb = NULL;
+
+		if (inp->sctp_flags & SCTP_PCB_FLAGS_CONNECTED) {
+			SCTP_INP_RLOCK(inp);
+			stcb = LIST_FIRST(&inp->sctp_asoc_list);
+			if (stcb)
+				SCTP_TCB_LOCK(stcb);
+			SCTP_INP_RUNLOCK(inp);
+		}
+		if ((sasoc->sasoc_assoc_id) && (stcb == NULL)) {
+			stcb = sctp_findassociation_ep_asocid(inp,
+							     sasoc->sasoc_assoc_id);
+			if (stcb == NULL) {
+				error = ENOENT;
+				break;
+			}
+		} else {
+			stcb = NULL;
+		}
+
+		if (stcb) {
+			sasoc->sasoc_asocmaxrxt = stcb->asoc.max_send_times;
+			sasoc->sasoc_number_peer_destinations = stcb->asoc.numnets;
+			sasoc->sasoc_peer_rwnd = stcb->asoc.peers_rwnd;
+			sasoc->sasoc_local_rwnd = stcb->asoc.my_rwnd;
+			sasoc->sasoc_cookie_life = stcb->asoc.cookie_life;
+			SCTP_TCB_UNLOCK(stcb);
+		} else {
+			SCTP_INP_RLOCK(inp);
+			sasoc->sasoc_asocmaxrxt = inp->sctp_ep.max_send_times;
+			sasoc->sasoc_number_peer_destinations = 0;
+			sasoc->sasoc_peer_rwnd = 0;
+			sasoc->sasoc_local_rwnd = sbspace(&inp->sctp_socket->so_rcv);
+			sasoc->sasoc_cookie_life = inp->sctp_ep.def_cookie_life;
+			SCTP_INP_RUNLOCK(inp);
+		}
+		m->m_len = sizeof(*sasoc);
+	}
+	break;
+	case SCTP_DEFAULT_SEND_PARAM:
+	{
+		struct sctp_sndrcvinfo *s_info;
+
+		if (m->m_len != sizeof(struct sctp_sndrcvinfo)) {
+			error = EINVAL;
+			break;
+		}
+		s_info = mtod(m, struct sctp_sndrcvinfo *);
+		if (inp->sctp_flags & SCTP_PCB_FLAGS_CONNECTED) {
+			SCTP_INP_RLOCK(inp);
+			stcb = LIST_FIRST(&inp->sctp_asoc_list);
+			if (stcb)
+				SCTP_TCB_LOCK(stcb);
+			SCTP_INP_RUNLOCK(inp);
+		} else
+			stcb = sctp_findassociation_ep_asocid(inp, s_info->sinfo_assoc_id);
+
+		if (stcb == NULL) {
+			error = ENOENT;
+			break;
+		}
+		/* Copy it out */
+		*s_info = stcb->asoc.def_send;
+		SCTP_TCB_UNLOCK(stcb);
+		m->m_len = sizeof(*s_info);
+	}
+	case SCTP_INITMSG:
+	{
+		struct sctp_initmsg *sinit;
+#ifdef SCTP_DEBUG
+		if (sctp_debug_on & SCTP_DEBUG_USRREQ1) {
+			printf("initmsg\n");
+		}
+#endif /* SCTP_DEBUG */
+		if ((size_t)m->m_len < sizeof(struct sctp_initmsg)) {
+			error = EINVAL;
+			break;
+		}
+		sinit = mtod(m, struct sctp_initmsg *);
+		SCTP_INP_RLOCK(inp);
+		sinit->sinit_num_ostreams = inp->sctp_ep.pre_open_stream_count;
+		sinit->sinit_max_instreams = inp->sctp_ep.max_open_streams_intome;
+		sinit->sinit_max_attempts = inp->sctp_ep.max_init_times;
+		sinit->sinit_max_init_timeo = inp->sctp_ep.initial_init_rto_max;
+		SCTP_INP_RUNLOCK(inp);
+		m->m_len = sizeof(*sinit);
+	}
+	break;
+	case SCTP_PRIMARY_ADDR:
+		/* we allow a "get" operation on this */
+	{
+		struct sctp_setprim *ssp;
+
+#ifdef SCTP_DEBUG
+		if (sctp_debug_on & SCTP_DEBUG_USRREQ1) {
+			printf("setprimary\n");
+		}
+#endif /* SCTP_DEBUG */
+		if ((size_t)m->m_len < sizeof(struct sctp_setprim)) {
+			error = EINVAL;
+			break;
+		}
+		ssp = mtod(m, struct sctp_setprim *);
+		if (inp->sctp_flags & SCTP_PCB_FLAGS_CONNECTED) {
+			SCTP_INP_RLOCK(inp);
+			stcb = LIST_FIRST(&inp->sctp_asoc_list);
+			if (stcb)
+				SCTP_TCB_LOCK(stcb);
+			SCTP_INP_RUNLOCK(inp);
+		} else {
+			stcb = sctp_findassociation_ep_asocid(inp, ssp->ssp_assoc_id);
+			if (stcb == NULL) {
+				/* one last shot, try it by the address in */
+				struct sctp_nets *net;
+
+				SCTP_INP_WLOCK(inp);
+				SCTP_INP_INCR_REF(inp);
+				SCTP_INP_WUNLOCK(inp);
+				stcb = sctp_findassociation_ep_addr(&inp,
+							    (struct sockaddr *)&ssp->ssp_addr,
+							    &net, NULL, NULL);
+				if (stcb == NULL) {
+					SCTP_INP_WLOCK(inp);
+					SCTP_INP_DECR_REF(inp);
+					SCTP_INP_WUNLOCK(inp);
+				}
+			}
+			if (stcb == NULL) {
+				error = EINVAL;
+				break;
+			}
+		}
+		/* simply copy out the sockaddr_storage... */
+		memcpy(&ssp->ssp_addr,
+		       &stcb->asoc.primary_destination->ro._l_addr,
+		       ((struct sockaddr *)&stcb->asoc.primary_destination->ro._l_addr)->sa_len);
+		SCTP_TCB_UNLOCK(stcb);
+		m->m_len = sizeof(*ssp);
+	}
+	break;
+	default:
+		error = ENOPROTOOPT;
+		m->m_len = 0;
+		break;
+	} /* end switch (sopt->sopt_name) */
+        return (error);
+}
+
+static int
+sctp_optsset(struct socket *so,
+	     int opt,
+	     struct mbuf **mp,
+#if (defined(__FreeBSD__) && __FreeBSD_version >= 500000) || defined(__DragonFly__)
+	     struct thread *p
+#else
+	     struct proc *p
+#endif
+	)
+{
+	int error, *mopt, set_opt;
+	struct mbuf *m;
+	struct sctp_tcb *stcb = NULL;
+        struct sctp_inpcb *inp;
+
+	if (mp == NULL) {
+#ifdef SCTP_DEBUG
+		if (sctp_debug_on & SCTP_DEBUG_USRREQ1) {
+			printf("optsset:MP is NULL EINVAL\n");
+		}
+#endif /* SCTP_DEBUG */
+		return (EINVAL);
+	}
+	m = *mp;
+	if (m == NULL)
+		return (EINVAL);
+
+	inp = (struct sctp_inpcb *)so->so_pcb;
+	if (inp == 0)
+		return EINVAL;
+
+	error = 0;
+	switch (opt) {
+	case SCTP_NODELAY:
+	case SCTP_AUTOCLOSE:
+	case SCTP_AUTO_ASCONF:
+	case SCTP_DISABLE_FRAGMENTS:
+	case SCTP_I_WANT_MAPPED_V4_ADDR:
+		/* copy in the option value */
+		if ((size_t)m->m_len < sizeof(int)) {
+			error = EINVAL;
+			break;
+		}
+		mopt = mtod(m, int *);
+		set_opt = 0;
+		if (error)
+			break;
+		switch (opt) {
+		case SCTP_DISABLE_FRAGMENTS:
+			set_opt = SCTP_PCB_FLAGS_NO_FRAGMENT;
+			break;
+		case SCTP_AUTO_ASCONF:
+			set_opt = SCTP_PCB_FLAGS_AUTO_ASCONF;
+			break;
+
+		case SCTP_I_WANT_MAPPED_V4_ADDR:
+			if (inp->sctp_flags & SCTP_PCB_FLAGS_BOUND_V6) {
+				set_opt = SCTP_PCB_FLAGS_NEEDS_MAPPED_V4;
+			} else {
+				return (EINVAL);
+			}
+			break;
+		case SCTP_NODELAY:
+			set_opt = SCTP_PCB_FLAGS_NODELAY;
+			break;
+		case SCTP_AUTOCLOSE:
+			set_opt = SCTP_PCB_FLAGS_AUTOCLOSE;
+			/*
+			 * The value is in ticks.
+			 * Note this does not effect old associations, only
+			 * new ones.
+			 */
+			inp->sctp_ep.auto_close_time = (*mopt * hz);
+			break;
+		}
+		SCTP_INP_WLOCK(inp);
+		if (*mopt != 0) {
+			inp->sctp_flags |= set_opt;
+		} else {
+			inp->sctp_flags &= ~set_opt;
+		}
+		SCTP_INP_WUNLOCK(inp);
+		break;
+	case SCTP_MY_PUBLIC_KEY:    /* set my public key */
+	case SCTP_SET_AUTH_CHUNKS:  /* set the authenticated chunks required */
+	case SCTP_SET_AUTH_SECRET:  /* set the actual secret for the endpoint */
+		/* not supported yet and until we refine the draft */
+		error = EOPNOTSUPP;
+		break;
+
+	case SCTP_CLR_STAT_LOG:
+#ifdef SCTP_STAT_LOGGING
+		sctp_clr_stat_log();
+#else
+		error = EOPNOTSUPP;
+#endif
+		break;
+	case SCTP_DELAYED_ACK_TIME:
+	{
+		int32_t *tm;
+		if ((size_t)m->m_len < sizeof(int32_t)) {
+			error = EINVAL;
+			break;
+		}
+		tm = mtod(m, int32_t *);
+
+		if ((*tm < 10) || (*tm > 500)) {
+			/* can't be smaller than 10ms */
+			/* MUST NOT be larger than 500ms */
+			error = EINVAL;
+			break;
+		}
+		inp->sctp_ep.sctp_timeoutticks[SCTP_TIMER_RECV] = MSEC_TO_TICKS(*tm);
+	}
+		break;
+	case SCTP_RESET_STREAMS:
+	{
+		struct sctp_stream_reset *strrst;
+		uint8_t two_way, not_peer;
+
+		if ((size_t)m->m_len < sizeof(struct sctp_stream_reset)) {
+			error = EINVAL;
+			break;
+		}
+		strrst = mtod(m, struct sctp_stream_reset *);
+
+		if (inp->sctp_flags & SCTP_PCB_FLAGS_CONNECTED) {
+			SCTP_INP_RLOCK(inp);
+			stcb = LIST_FIRST(&inp->sctp_asoc_list);
+			if (stcb)
+				SCTP_TCB_LOCK(stcb);
+			SCTP_INP_RUNLOCK(inp);
+		} else
+			stcb = sctp_findassociation_ep_asocid(inp, strrst->strrst_assoc_id);
+		if (stcb == NULL) {
+			error = ENOENT;
+			break;
+		}
+		if (stcb->asoc.peer_supports_strreset == 0) {
+			/* Peer does not support it,
+			 * we return protocol not supported since
+			 * this is true for this feature and this
+			 * peer, not the socket request in general.
+			 */
+			error = EPROTONOSUPPORT;
+			SCTP_TCB_UNLOCK(stcb);
+			break;
+		}
+
+/* Having re-thought this code I added as I write the I-D there
+ * is NO need for it. The peer, if we are requesting a stream-reset
+ * will send a request to us but will itself do what we do, take
+ * and copy off the "reset information" we send and queue TSN's
+ * larger than the send-next in our response message. Thus they
+ * will handle it.
+ */
+/*		if (stcb->asoc.sending_seq != (stcb->asoc.last_acked_seq + 1)) {*/
+		/* Must have all sending data ack'd before we
+		 * start this procedure. This is a bit restrictive
+		 * and we SHOULD work on changing this so ONLY the
+		 * streams being RESET get held up. So, a reset-all
+		 * would require this.. but a reset specific just
+		 * needs to be sure that the ones being reset have
+		 * nothing on the send_queue. For now we will
+		 * skip this more detailed method and do a course
+		 * way.. i.e. nothing pending ... for future FIX ME!
+		 */
+/*			error = EBUSY;*/
+/*			break;*/
+/*		}*/
+
+		if (stcb->asoc.stream_reset_outstanding) {
+			error = EALREADY;
+			SCTP_TCB_UNLOCK(stcb);
+			break;
+		}
+		if (strrst->strrst_flags == SCTP_RESET_LOCAL_RECV) {
+			two_way = 0;
+			not_peer = 0;
+		} else if (strrst->strrst_flags == SCTP_RESET_LOCAL_SEND) {
+			two_way = 1;
+			not_peer = 1;
+		} else if (strrst->strrst_flags == SCTP_RESET_BOTH) {
+			two_way = 1;
+			not_peer = 0;
+		} else {
+			error = EINVAL;
+			SCTP_TCB_UNLOCK(stcb);
+			break;
+		}
+		sctp_send_str_reset_req(stcb, strrst->strrst_num_streams,
+					strrst->strrst_list, two_way, not_peer);
+		crit_enter();
+		sctp_chunk_output(inp, stcb, 12);
+		SCTP_TCB_UNLOCK(stcb);
+		crit_exit();
+
+	}
+	break;
+	case SCTP_RESET_PEGS:
+		memset(sctp_pegs, 0, sizeof(sctp_pegs));
+		error = 0;
+		break;
+	case SCTP_CONNECT_X:
+		if ((size_t)m->m_len < (sizeof(int) + sizeof(struct sockaddr_in))) {
+			error = EINVAL;
+			break;
+		}
+		error = sctp_do_connect_x(so, inp, m, p, 0);
+		break;
+
+	case SCTP_CONNECT_X_DELAYED:
+		if ((size_t)m->m_len < (sizeof(int) + sizeof(struct sockaddr_in))) {
+			error = EINVAL;
+			break;
+		}
+		error = sctp_do_connect_x(so, inp, m, p, 1);
+		break;
+
+	case SCTP_CONNECT_X_COMPLETE:
+	{
+		struct sockaddr *sa;
+		struct sctp_nets *net;
+		if ((size_t)m->m_len < sizeof(struct sockaddr_in)) {
+			error = EINVAL;
+			break;
+		}
+		sa = mtod(m, struct sockaddr *);
+		/* find tcb */
+		if (inp->sctp_flags & SCTP_PCB_FLAGS_CONNECTED) {
+			SCTP_INP_RLOCK(inp);
+			stcb = LIST_FIRST(&inp->sctp_asoc_list);
+			if (stcb) {
+				SCTP_TCB_LOCK(stcb);
+				net = sctp_findnet(stcb, sa);
+			}
+			SCTP_INP_RUNLOCK(inp);
+		} else {
+			SCTP_INP_WLOCK(inp);
+			SCTP_INP_INCR_REF(inp);
+			SCTP_INP_WUNLOCK(inp);
+			stcb = sctp_findassociation_ep_addr(&inp, sa, &net, NULL, NULL);
+			if (stcb == NULL) {
+				SCTP_INP_WLOCK(inp);
+				SCTP_INP_DECR_REF(inp);
+				SCTP_INP_WUNLOCK(inp);
+			}
+		}
+
+		if (stcb == NULL) {
+			error = ENOENT;
+			break;
+		}
+		if (stcb->asoc.delayed_connection == 1) {
+			stcb->asoc.delayed_connection = 0;
+			SCTP_GETTIME_TIMEVAL(&stcb->asoc.time_entered);
+			sctp_timer_stop(SCTP_TIMER_TYPE_INIT, inp, stcb, stcb->asoc.primary_destination);
+			sctp_send_initiate(inp, stcb);
+		} else {
+			/* already expired or did not use delayed connectx */
+			error = EALREADY;
+		}
+		SCTP_TCB_UNLOCK(stcb);
+	}
+	break;
+	case SCTP_MAXBURST:
+	{
+		u_int8_t *burst;
+		SCTP_INP_WLOCK(inp);
+		burst = mtod(m, u_int8_t *);
+		if (*burst) {
+			inp->sctp_ep.max_burst = *burst;
+		}
+		SCTP_INP_WUNLOCK(inp);
+	}
+	break;
+	case SCTP_MAXSEG:
+	{
+		u_int32_t *segsize;
+		int ovh;
+		if (inp->sctp_flags & SCTP_PCB_FLAGS_BOUND_V6) {
+			ovh = SCTP_MED_OVERHEAD;
+		} else {
+			ovh = SCTP_MED_V4_OVERHEAD;
+		}
+		segsize = mtod(m, u_int32_t *);
+		if (*segsize < 1) {
+			error = EINVAL;
+			break;
+		}
+		SCTP_INP_WLOCK(inp);
+		inp->sctp_frag_point = (*segsize+ovh);
+		if (inp->sctp_frag_point < MHLEN) {
+			inp->sctp_frag_point = MHLEN;
+		}
+		SCTP_INP_WUNLOCK(inp);
+	}
+	break;
+	case SCTP_SET_DEBUG_LEVEL:
+#ifdef SCTP_DEBUG
+	{
+		u_int32_t *level;
+		if ((size_t)m->m_len < sizeof(u_int32_t)) {
+			error = EINVAL;
+			break;
+		}
+		level = mtod(m, u_int32_t *);
+		error = 0;
+		sctp_debug_on = (*level & (SCTP_DEBUG_ALL |
+					   SCTP_DEBUG_NOISY));
+		printf("SETTING DEBUG LEVEL to %x\n",
+		       (u_int)sctp_debug_on);
+
+	}
+#else
+	error = EOPNOTSUPP;
+#endif /* SCTP_DEBUG */
+	break;
+	case SCTP_EVENTS:
+	{
+		struct sctp_event_subscribe *events;
+		if ((size_t)m->m_len < sizeof(struct sctp_event_subscribe)) {
+			error = EINVAL;
+			break;
+		}
+		SCTP_INP_WLOCK(inp);
+		events = mtod(m, struct sctp_event_subscribe *);
+		if (events->sctp_data_io_event) {
+			inp->sctp_flags |= SCTP_PCB_FLAGS_RECVDATAIOEVNT;
+		} else {
+			inp->sctp_flags &= ~SCTP_PCB_FLAGS_RECVDATAIOEVNT;
+		}
+
+		if (events->sctp_association_event) {
+			inp->sctp_flags |= SCTP_PCB_FLAGS_RECVASSOCEVNT;
+		} else {
+			inp->sctp_flags &= ~SCTP_PCB_FLAGS_RECVASSOCEVNT;
+		}
+
+		if (events->sctp_address_event) {
+			inp->sctp_flags |= SCTP_PCB_FLAGS_RECVPADDREVNT;
+		} else {
+			inp->sctp_flags &= ~SCTP_PCB_FLAGS_RECVPADDREVNT;
+		}
+
+		if (events->sctp_send_failure_event) {
+			inp->sctp_flags |= SCTP_PCB_FLAGS_RECVSENDFAILEVNT;
+		} else {
+			inp->sctp_flags &= ~SCTP_PCB_FLAGS_RECVSENDFAILEVNT;
+		}
+
+		if (events->sctp_peer_error_event) {
+			inp->sctp_flags |= SCTP_PCB_FLAGS_RECVPEERERR;
+		} else {
+			inp->sctp_flags &= ~SCTP_PCB_FLAGS_RECVPEERERR;
+		}
+
+		if (events->sctp_shutdown_event) {
+			inp->sctp_flags |= SCTP_PCB_FLAGS_RECVSHUTDOWNEVNT;
+		} else {
+			inp->sctp_flags &= ~SCTP_PCB_FLAGS_RECVSHUTDOWNEVNT;
+		}
+
+		if (events->sctp_partial_delivery_event) {
+			inp->sctp_flags |= SCTP_PCB_FLAGS_PDAPIEVNT;
+		} else {
+			inp->sctp_flags &= ~SCTP_PCB_FLAGS_PDAPIEVNT;
+		}
+
+		if (events->sctp_adaption_layer_event) {
+			inp->sctp_flags |= SCTP_PCB_FLAGS_ADAPTIONEVNT;
+		} else {
+			inp->sctp_flags &= ~SCTP_PCB_FLAGS_ADAPTIONEVNT;
+		}
+
+		if (events->sctp_stream_reset_events) {
+			inp->sctp_flags |= SCTP_PCB_FLAGS_STREAM_RESETEVNT;
+		} else {
+			inp->sctp_flags &= ~SCTP_PCB_FLAGS_STREAM_RESETEVNT;
+		}
+		SCTP_INP_WUNLOCK(inp);
+	}
+	break;
+
+	case SCTP_ADAPTION_LAYER:
+	{
+		struct sctp_setadaption *adap_bits;
+		if ((size_t)m->m_len < sizeof(struct sctp_setadaption)) {
+			error = EINVAL;
+			break;
+		}
+		SCTP_INP_WLOCK(inp);
+		adap_bits = mtod(m, struct sctp_setadaption *);
+		inp->sctp_ep.adaption_layer_indicator = adap_bits->ssb_adaption_ind;
+		SCTP_INP_WUNLOCK(inp);
+	}
+	break;
+	case SCTP_SET_INITIAL_DBG_SEQ:
+	{
+		u_int32_t *vvv;
+		if ((size_t)m->m_len < sizeof(u_int32_t)) {
+			error = EINVAL;
+			break;
+		}
+		SCTP_INP_WLOCK(inp);
+		vvv = mtod(m, u_int32_t *);
+		inp->sctp_ep.initial_sequence_debug = *vvv;
+		SCTP_INP_WUNLOCK(inp);
+	}
+	break;
+	case SCTP_DEFAULT_SEND_PARAM:
+	{
+		struct sctp_sndrcvinfo *s_info;
+
+		if (m->m_len != sizeof(struct sctp_sndrcvinfo)) {
+			error = EINVAL;
+			break;
+		}
+		s_info = mtod(m, struct sctp_sndrcvinfo *);
+
+		if (inp->sctp_flags & SCTP_PCB_FLAGS_CONNECTED) {
+			SCTP_INP_RLOCK(inp);
+			stcb = LIST_FIRST(&inp->sctp_asoc_list);
+			if (stcb)
+				SCTP_TCB_LOCK(stcb);
+			SCTP_INP_RUNLOCK(inp);
+		} else
+			stcb = sctp_findassociation_ep_asocid(inp, s_info->sinfo_assoc_id);
+
+		if (stcb == NULL) {
+			error = ENOENT;
+			break;
+		}
+		/* Validate things */
+		if (s_info->sinfo_stream > stcb->asoc.streamoutcnt) {
+			SCTP_TCB_UNLOCK(stcb);
+			error = EINVAL;
+			break;
+		}
+		/* Mask off the flags that are allowed */
+		s_info->sinfo_flags = (s_info->sinfo_flags &
+				       (MSG_UNORDERED | MSG_ADDR_OVER |
+					MSG_PR_SCTP_TTL | MSG_PR_SCTP_BUF));
+		/* Copy it in */
+		stcb->asoc.def_send = *s_info;
+		SCTP_TCB_UNLOCK(stcb);
+	}
+	break;
+	case SCTP_PEER_ADDR_PARAMS:
+	{
+		struct sctp_paddrparams *paddrp;
+		struct sctp_nets *net;
+		if ((size_t)m->m_len < sizeof(struct sctp_paddrparams)) {
+			error = EINVAL;
+			break;
+		}
+		paddrp = mtod(m, struct sctp_paddrparams *);
+		net = NULL;
+		if (paddrp->spp_assoc_id) {
+			if (inp->sctp_flags & SCTP_PCB_FLAGS_CONNECTED) {
+				SCTP_INP_RLOCK(inp);
+				stcb = LIST_FIRST(&inp->sctp_asoc_list);
+				if (stcb) {
+					SCTP_TCB_LOCK(stcb);
+					net = sctp_findnet(stcb, (struct sockaddr *)&paddrp->spp_address);
+				}
+				SCTP_INP_RUNLOCK(inp);
+			} else
+				stcb = sctp_findassociation_ep_asocid(inp, paddrp->spp_assoc_id);
+			if (stcb == NULL) {
+				error = ENOENT;
+				break;
+			}
+
+		}
+		if ((stcb == NULL) &&
+		    ((((struct sockaddr *)&paddrp->spp_address)->sa_family == AF_INET) ||
+		     (((struct sockaddr *)&paddrp->spp_address)->sa_family == AF_INET6))) {
+			/* Lookup via address */
+			if (inp->sctp_flags & SCTP_PCB_FLAGS_CONNECTED) {
+				SCTP_INP_RLOCK(inp);
+				stcb = LIST_FIRST(&inp->sctp_asoc_list);
+				if (stcb) {
+					SCTP_TCB_LOCK(stcb);
+					net = sctp_findnet(stcb,
+							   (struct sockaddr *)&paddrp->spp_address);
+				}
+				SCTP_INP_RUNLOCK(inp);
+			} else {
+				SCTP_INP_WLOCK(inp);
+				SCTP_INP_INCR_REF(inp);
+				SCTP_INP_WUNLOCK(inp);
+				stcb = sctp_findassociation_ep_addr(&inp,
+								    (struct sockaddr *)&paddrp->spp_address,
+								    &net, NULL, NULL);
+				if (stcb == NULL) {
+					SCTP_INP_WLOCK(inp);
+					SCTP_INP_DECR_REF(inp);
+					SCTP_INP_WUNLOCK(inp);
+				}
+			}
+		} else {
+			/* Effects the Endpoint */
+			stcb = NULL;
+		}
+		if (stcb) {
+			/* Applies to the specific association */
+			if (paddrp->spp_pathmaxrxt) {
+				if (net) {
+					if (paddrp->spp_pathmaxrxt)
+						net->failure_threshold = paddrp->spp_pathmaxrxt;
+				} else {
+					if (paddrp->spp_pathmaxrxt)
+						stcb->asoc.def_net_failure = paddrp->spp_pathmaxrxt;
+				}
+			}
+			if ((paddrp->spp_hbinterval != 0) && (paddrp->spp_hbinterval != 0xffffffff)) {
+				/* Just a set */
+				int old;
+				if (net) {
+					net->dest_state &= ~SCTP_ADDR_NOHB;
+				} else {
+					old = stcb->asoc.heart_beat_delay;
+					stcb->asoc.heart_beat_delay = paddrp->spp_hbinterval;
+					if (old == 0) {
+						/* Turn back on the timer */
+						sctp_timer_start(SCTP_TIMER_TYPE_HEARTBEAT, inp, stcb, net);
+					}
+				}
+			} else if (paddrp->spp_hbinterval == 0xffffffff) {
+				/* on demand HB */
+				sctp_send_hb(stcb, 1, net);
+			} else {
+				if (net == NULL) {
+					/* off on association */
+					if (stcb->asoc.heart_beat_delay) {
+						int cnt_of_unconf = 0;
+						struct sctp_nets *lnet;
+						TAILQ_FOREACH(lnet, &stcb->asoc.nets, sctp_next) {
+							if (lnet->dest_state & SCTP_ADDR_UNCONFIRMED) {
+								cnt_of_unconf++;
+							}
+						}
+						/* stop the timer ONLY if we have no unconfirmed addresses
+						 */
+						if (cnt_of_unconf == 0)
+							sctp_timer_stop(SCTP_TIMER_TYPE_HEARTBEAT, inp, stcb, net);
+					}
+					stcb->asoc.heart_beat_delay = 0;
+				} else {
+					net->dest_state |= SCTP_ADDR_NOHB;
+				}
+			}
+			SCTP_TCB_UNLOCK(stcb);
+		} else {
+			/* Use endpoint defaults */
+			SCTP_INP_WLOCK(inp);
+			if (paddrp->spp_pathmaxrxt)
+				inp->sctp_ep.def_net_failure = paddrp->spp_pathmaxrxt;
+			if (paddrp->spp_hbinterval != SCTP_ISSUE_HB)
+				inp->sctp_ep.sctp_timeoutticks[SCTP_TIMER_HEARTBEAT] = paddrp->spp_hbinterval;
+			SCTP_INP_WUNLOCK(inp);
+		}
+	}
+	break;
+	case SCTP_RTOINFO:
+	{
+		struct sctp_rtoinfo *srto;
+		if ((size_t)m->m_len < sizeof(struct sctp_rtoinfo)) {
+			error = EINVAL;
+			break;
+		}
+		srto = mtod(m, struct sctp_rtoinfo *);
+		if (srto->srto_assoc_id == 0) {
+			SCTP_INP_WLOCK(inp);
+			/* If we have a null asoc, its default for the endpoint */
+			if (srto->srto_initial > 10)
+				inp->sctp_ep.initial_rto = srto->srto_initial;
+			if (srto->srto_max > 10)
+				inp->sctp_ep.sctp_maxrto = srto->srto_max;
+			if (srto->srto_min > 10)
+				inp->sctp_ep.sctp_minrto = srto->srto_min;
+			SCTP_INP_WUNLOCK(inp);
+			break;
+		}
+		if (inp->sctp_flags & SCTP_PCB_FLAGS_CONNECTED) {
+			SCTP_INP_RLOCK(inp);
+			stcb = LIST_FIRST(&inp->sctp_asoc_list);
+			if (stcb)
+				SCTP_TCB_LOCK(stcb);
+			SCTP_INP_RUNLOCK(inp);
+		} else
+			stcb = sctp_findassociation_ep_asocid(inp, srto->srto_assoc_id);
+		if (stcb == NULL) {
+			error = EINVAL;
+			break;
+		}
+		/* Set in ms we hope :-) */
+		if (srto->srto_initial > 10)
+			stcb->asoc.initial_rto = srto->srto_initial;
+		if (srto->srto_max > 10)
+			stcb->asoc.maxrto = srto->srto_max;
+		if (srto->srto_min > 10)
+			stcb->asoc.minrto = srto->srto_min;
+		SCTP_TCB_UNLOCK(stcb);
+	}
+	break;
+	case SCTP_ASSOCINFO:
+	{
+		struct sctp_assocparams *sasoc;
+
+		if ((size_t)m->m_len < sizeof(struct sctp_assocparams)) {
+			error = EINVAL;
+			break;
+		}
+		sasoc = mtod(m, struct sctp_assocparams *);
+		if (sasoc->sasoc_assoc_id) {
+			if (inp->sctp_flags & SCTP_PCB_FLAGS_CONNECTED) {
+				SCTP_INP_RLOCK(inp);
+				stcb = LIST_FIRST(&inp->sctp_asoc_list);
+				if (stcb)
+					SCTP_TCB_LOCK(stcb);
+				SCTP_INP_RUNLOCK(inp);
+			} else
+				stcb = sctp_findassociation_ep_asocid(inp,
+								      sasoc->sasoc_assoc_id);
+			if (stcb == NULL) {
+				error = ENOENT;
+				break;
+			}
+
+		} else {
+			stcb = NULL;
+		}
+		if (stcb) {
+			if (sasoc->sasoc_asocmaxrxt)
+				stcb->asoc.max_send_times = sasoc->sasoc_asocmaxrxt;
+			sasoc->sasoc_number_peer_destinations = stcb->asoc.numnets;
+			sasoc->sasoc_peer_rwnd = 0;
+			sasoc->sasoc_local_rwnd = 0;
+			if (stcb->asoc.cookie_life)
+				stcb->asoc.cookie_life = sasoc->sasoc_cookie_life;
+			SCTP_TCB_UNLOCK(stcb);
+		} else {
+			SCTP_INP_WLOCK(inp);
+                        if (sasoc->sasoc_asocmaxrxt)
+				inp->sctp_ep.max_send_times = sasoc->sasoc_asocmaxrxt;
+			sasoc->sasoc_number_peer_destinations = 0;
+			sasoc->sasoc_peer_rwnd = 0;
+			sasoc->sasoc_local_rwnd = 0;
+			if (sasoc->sasoc_cookie_life)
+				inp->sctp_ep.def_cookie_life = sasoc->sasoc_cookie_life;
+			SCTP_INP_WUNLOCK(inp);
+		}
+	}
+	break;
+	case SCTP_INITMSG:
+	{
+                struct sctp_initmsg *sinit;
+
+		if ((size_t)m->m_len < sizeof(struct sctp_initmsg)) {
+			error = EINVAL;
+			break;
+		}
+		sinit = mtod(m, struct sctp_initmsg *);
+		SCTP_INP_WLOCK(inp);
+		if (sinit->sinit_num_ostreams)
+			inp->sctp_ep.pre_open_stream_count = sinit->sinit_num_ostreams;
+
+		if (sinit->sinit_max_instreams)
+			inp->sctp_ep.max_open_streams_intome = sinit->sinit_max_instreams;
+
+		if (sinit->sinit_max_attempts)
+			inp->sctp_ep.max_init_times = sinit->sinit_max_attempts;
+
+		if (sinit->sinit_max_init_timeo > 10)
+			/* We must be at least a 100ms (we set in ticks) */
+			inp->sctp_ep.initial_init_rto_max = sinit->sinit_max_init_timeo;
+		SCTP_INP_WUNLOCK(inp);
+	}
+	break;
+	case SCTP_PRIMARY_ADDR:
+	{
+		struct sctp_setprim *spa;
+		struct sctp_nets *net, *lnet;
+		if ((size_t)m->m_len < sizeof(struct sctp_setprim)) {
+			error = EINVAL;
+			break;
+		}
+		spa = mtod(m, struct sctp_setprim *);
+
+		if (inp->sctp_flags & SCTP_PCB_FLAGS_CONNECTED) {
+ 			SCTP_INP_RLOCK(inp);
+			stcb = LIST_FIRST(&inp->sctp_asoc_list);
+			if (stcb) {
+				SCTP_TCB_LOCK(stcb);
+			} else {
+				error = EINVAL;
+				break;
+			}
+ 			SCTP_INP_RUNLOCK(inp);
+		} else
+			stcb = sctp_findassociation_ep_asocid(inp, spa->ssp_assoc_id);
+		if (stcb == NULL) {
+			/* One last shot */
+			SCTP_INP_WLOCK(inp);
+			SCTP_INP_INCR_REF(inp);
+			SCTP_INP_WUNLOCK(inp);
+			stcb = sctp_findassociation_ep_addr(&inp,
+							    (struct sockaddr *)&spa->ssp_addr,
+							    &net, NULL, NULL);
+			if (stcb == NULL) {
+				SCTP_INP_WLOCK(inp);
+				SCTP_INP_DECR_REF(inp);
+				SCTP_INP_WUNLOCK(inp);
+				error = EINVAL;
+				break;
+			}
+		} else {
+			/* find the net, associd or connected lookup type */
+			net = sctp_findnet(stcb, (struct sockaddr *)&spa->ssp_addr);
+			if (net == NULL) {
+				SCTP_TCB_UNLOCK(stcb);
+				error = EINVAL;
+				break;
+			}
+                }
+                if ((net != stcb->asoc.primary_destination) &&
+  		    (!(net->dest_state & SCTP_ADDR_UNCONFIRMED))) {
+			/* Ok we need to set it */
+			lnet = stcb->asoc.primary_destination;
+                        lnet->next_tsn_at_change = net->next_tsn_at_change = stcb->asoc.sending_seq;
+		        if (sctp_set_primary_addr(stcb,
+						  (struct sockaddr *)NULL,
+						  net) == 0) {
+			        if (net->dest_state & SCTP_ADDR_SWITCH_PRIMARY) {
+   				        net->dest_state |= SCTP_ADDR_DOUBLE_SWITCH;
+                                }
+                                net->dest_state |= SCTP_ADDR_SWITCH_PRIMARY;
+                        }
+		}
+		SCTP_TCB_UNLOCK(stcb);
+        }
+	break;
+
+	case SCTP_SET_PEER_PRIMARY_ADDR:
+	{
+		struct sctp_setpeerprim *sspp;
+		if ((size_t)m->m_len < sizeof(struct sctp_setpeerprim)) {
+			error = EINVAL;
+			break;
+		}
+		sspp = mtod(m, struct sctp_setpeerprim *);
+
+
+		if (inp->sctp_flags & SCTP_PCB_FLAGS_CONNECTED) {
+			SCTP_INP_RLOCK(inp);
+			stcb = LIST_FIRST(&inp->sctp_asoc_list);
+			if (stcb)
+				SCTP_TCB_UNLOCK(stcb);
+			SCTP_INP_RUNLOCK(inp);
+		} else
+			stcb = sctp_findassociation_ep_asocid(inp, sspp->sspp_assoc_id);
+		if (stcb == NULL) {
+			error = EINVAL;
+			break;
+		}
+		if (sctp_set_primary_ip_address_sa(stcb, (struct sockaddr *)&sspp->sspp_addr) != 0) {
+			error = EINVAL;
+		}
+		SCTP_TCB_UNLOCK(stcb);
+	}
+	break;
+	case SCTP_BINDX_ADD_ADDR:
+	{
+		struct sctp_getaddresses *addrs;
+		struct sockaddr *addr_touse;
+		struct sockaddr_in sin;
+		/* see if we're bound all already! */
+		if (inp->sctp_flags & SCTP_PCB_FLAGS_BOUNDALL) {
+			error = EINVAL;
+			break;
+		}
+		if ((size_t)m->m_len < sizeof(struct sctp_getaddresses)) {
+			error = EINVAL;
+			break;
+		}
+		addrs = mtod(m, struct sctp_getaddresses *);
+		addr_touse = addrs->addr;
+		if (addrs->addr->sa_family == AF_INET6) {
+			struct sockaddr_in6 *sin6;
+			sin6 = (struct sockaddr_in6 *)addr_touse;
+			if (IN6_IS_ADDR_V4MAPPED(&sin6->sin6_addr)) {
+				in6_sin6_2_sin(&sin, sin6);
+				addr_touse = (struct sockaddr *)&sin;
+			}
+		}
+		if (inp->sctp_flags & SCTP_PCB_FLAGS_UNBOUND) {
+			if (p == NULL) {
+				/* Can't get proc for Net/Open BSD */
+				error = EINVAL;
+				break;
+			}
+			error = sctp_inpcb_bind(so, addr_touse, p);
+			break;
+		}
+		/* No locks required here since bind and mgmt_ep_sa all
+		 * do their own locking. If we do something for the FIX:
+		 * below we may need to lock in that case.
+		 */
+		if (addrs->sget_assoc_id == 0) {
+			/* add the address */
+			struct sctp_inpcb  *lep;
+			((struct sockaddr_in *)addr_touse)->sin_port = inp->sctp_lport;
+			lep = sctp_pcb_findep(addr_touse, 1, 0);
+			if (lep != NULL) {
+				/* We must decrement the refcount
+				 * since we have the ep already and
+				 * are binding. No remove going on
+				 * here.
+				 */
+				SCTP_INP_WLOCK(inp);
+				SCTP_INP_DECR_REF(inp);
+				SCTP_INP_WUNLOCK(inp);
+			}
+			if (lep == inp) {
+				/* already bound to it.. ok */
+				break;
+			} else if (lep == NULL) {
+				((struct sockaddr_in *)addr_touse)->sin_port = 0;
+				error = sctp_addr_mgmt_ep_sa(inp, addr_touse,
+							     SCTP_ADD_IP_ADDRESS);
+			} else {
+				error = EADDRNOTAVAIL;
+			}
+			if (error)
+				break;
+
+		} else {
+			/* FIX: decide whether we allow assoc based bindx */
+		}
+	}
+	break;
+	case SCTP_BINDX_REM_ADDR:
+	{
+		struct sctp_getaddresses *addrs;
+		struct sockaddr *addr_touse;
+		struct sockaddr_in sin;
+		/* see if we're bound all already! */
+		if (inp->sctp_flags & SCTP_PCB_FLAGS_BOUNDALL) {
+			error = EINVAL;
+			break;
+		}
+		if ((size_t)m->m_len < sizeof(struct sctp_getaddresses)) {
+			error = EINVAL;
+			break;
+		}
+		addrs = mtod(m, struct sctp_getaddresses *);
+		addr_touse = addrs->addr;
+		if (addrs->addr->sa_family == AF_INET6) {
+			struct sockaddr_in6 *sin6;
+			sin6 = (struct sockaddr_in6 *)addr_touse;
+			if (IN6_IS_ADDR_V4MAPPED(&sin6->sin6_addr)) {
+				in6_sin6_2_sin(&sin, sin6);
+				addr_touse = (struct sockaddr *)&sin;
+			}
+		}
+                /* No lock required mgmt_ep_sa does its own locking. If
+		 * the FIX: below is ever changed we may need to
+		 * lock before calling association level binding.
+		 */
+		if (addrs->sget_assoc_id == 0) {
+			/* delete the address */
+			sctp_addr_mgmt_ep_sa(inp, addr_touse,
+					     SCTP_DEL_IP_ADDRESS);
+		} else {
+			/* FIX: decide whether we allow assoc based bindx */
+		}
+	}
+	break;
+	default:
+		error = ENOPROTOOPT;
+		break;
+	} /* end switch (opt) */
+	return (error);
+}
+
+
+#if defined(__FreeBSD__) || defined(__APPLE__) || defined(__DragonFly__)
+int
+sctp_ctloutput(struct socket *so, struct sockopt *sopt)
+{
+	struct mbuf *m = NULL;
+	struct sctp_inpcb *inp;
+	int error;
+
+	inp = (struct sctp_inpcb *)so->so_pcb;
+	crit_enter();
+	if (inp == 0) {
+		crit_exit();
+		/* I made the same as TCP since we are not setup? */
+		return (ECONNRESET);
+	}
+	if (sopt->sopt_level != IPPROTO_SCTP) {
+		/* wrong proto level... send back up to IP */
+#ifdef INET6
+		if (INP_CHECK_SOCKAF(so, AF_INET6))
+			error = ip6_ctloutput(so, sopt);
+		else
+#endif /* INET6 */
+			error = ip_ctloutput(so, sopt);
+		crit_exit();
+		return (error);
+	}
+	if (sopt->sopt_valsize > MCLBYTES) {
+		/*
+		 * Restrict us down to a cluster size, that's all we can
+		 * pass either way...
+		 */
+		sopt->sopt_valsize = MCLBYTES;
+	}
+	if (sopt->sopt_valsize) {
+
+		m = m_get(MB_WAIT, MT_DATA);
+		if (sopt->sopt_valsize > MLEN) {
+			MCLGET(m, MB_DONTWAIT);
+			if ((m->m_flags & M_EXT) == 0) {
+				sctp_m_freem(m);
+				crit_exit();
+				return (ENOBUFS);
+			}
+		}
+		error = sooptcopyin(sopt, mtod(m, caddr_t), sopt->sopt_valsize,
+				    sopt->sopt_valsize);
+		if (error) {
+			(void) m_free(m);
+			goto out;
+		}
+		m->m_len = sopt->sopt_valsize;
+	}
+	if (sopt->sopt_dir == SOPT_SET) {
+#if (defined(__FreeBSD__) && __FreeBSD_version >= 500000) || defined(__DragonFly__)
+		error = sctp_optsset(so, sopt->sopt_name, &m, sopt->sopt_td);
+#else
+		error = sctp_optsset(so, sopt->sopt_name, &m, sopt->sopt_p);
+#endif
+	} else if (sopt->sopt_dir == SOPT_GET) {
+#if (defined (__FreeBSD__) && __FreeBSD_version >= 500000) || defined(__DragonFly__)
+		error = sctp_optsget(so, sopt->sopt_name, &m, sopt->sopt_td);
+#else
+		error = sctp_optsget(so, sopt->sopt_name, &m, sopt->sopt_p);
+#endif
+	} else {
+		error = EINVAL;
+	}
+	if ( (error == 0) && (m != NULL)) {
+		error = sooptcopyout(sopt, mtod(m, caddr_t), m->m_len);
+		sctp_m_freem(m);
+	} else if (m != NULL) {
+		sctp_m_freem(m);
+	}
+ out:
+	crit_exit();
+	return (error);
+}
+
+#else
+/* NetBSD and OpenBSD */
+int
+sctp_ctloutput(op, so, level, optname, mp)
+     int op;
+     struct socket *so;
+     int level, optname;
+     struct mbuf **mp;
+{
+	int s, error;
+	struct inpcb *inp;
+#ifdef INET6
+	struct in6pcb *in6p;
+#endif
+	int family;	/* family of the socket */
+
+	family = so->so_proto->pr_domain->dom_family;
+	error = 0;
+	crit_exit();
+	switch (family) {
+	case PF_INET:
+		inp = sotoinpcb(so);
+#ifdef INET6
+		in6p = NULL;
+#endif
+		break;
+#ifdef INET6
+	case PF_INET6:
+		inp = NULL;
+		in6p = sotoin6pcb(so);
+		break;
+#endif
+	default:
+		crit_exit();
+		return EAFNOSUPPORT;
+	}
+#ifndef INET6
+	if (inp == NULL)
+#else
+		if (inp == NULL && in6p == NULL)
+#endif
+		{
+			crit_exit();
+			if (op == PRCO_SETOPT && *mp)
+				(void) m_free(*mp);
+			return (ECONNRESET);
+		}
+	if (level != IPPROTO_SCTP) {
+		switch (family) {
+		case PF_INET:
+			error = ip_ctloutput(op, so, level, optname, mp);
+			break;
+#ifdef INET6
+		case PF_INET6:
+			error = ip6_ctloutput(op, so, level, optname, mp);
+			break;
+#endif
+		}
+		crit_exit();
+		return (error);
+	}
+	/* Ok if we reach here it is a SCTP option we hope */
+	if (op == PRCO_SETOPT) {
+		error = sctp_optsset(so, optname, mp, (struct proc *)NULL);
+		if (*mp)
+			(void) m_free(*mp);
+	} else if (op ==  PRCO_GETOPT) {
+		error = sctp_optsget(so, optname, mp, (struct proc *)NULL);
+	} else {
+		error = EINVAL;
+	}
+	crit_exit();
+	return (error);
+}
+
+#endif
+
+static int
+#if (defined(__FreeBSD__) && __FreeBSD_version >= 500000) || defined(__DragonFly__)
+sctp_connect(struct socket *so, struct sockaddr *addr, struct thread *p)
+{
+#else
+#if defined(__FreeBSD__) || defined(__APPLE__)
+sctp_connect(struct socket *so, struct sockaddr *addr, struct proc *p)
+{
+#else
+sctp_connect(struct socket *so, struct mbuf *nam, struct proc *p)
+{
+	struct sockaddr *addr = mtod(nam, struct sockaddr *);
+#endif
+#endif
+	int error = 0;
+	struct sctp_inpcb *inp;
+	struct sctp_tcb *stcb;
+
+#ifdef SCTP_DEBUG
+	if (sctp_debug_on & SCTP_DEBUG_PCB1) {
+		printf("Connect called in SCTP to ");
+		sctp_print_address(addr);
+		printf("Port %d\n", ntohs(((struct sockaddr_in *)addr)->sin_port));
+	}
+#endif /* SCTP_DEBUG */
+	crit_enter();
+	inp = (struct sctp_inpcb *)so->so_pcb;
+	if (inp == 0) {
+		crit_exit();
+		/* I made the same as TCP since we are not setup? */
+		return (ECONNRESET);
+	}
+	SCTP_ASOC_CREATE_LOCK(inp);
+	SCTP_INP_WLOCK(inp);
+	if ((inp->sctp_flags & SCTP_PCB_FLAGS_SOCKET_GONE) ||
+	    (inp->sctp_flags & SCTP_PCB_FLAGS_SOCKET_GONE)) {
+		/* Should I really unlock ? */
+		SCTP_INP_WUNLOCK(inp);
+		SCTP_ASOC_CREATE_UNLOCK(inp);
+		crit_exit();
+		return (EFAULT);
+	}
+#ifdef INET6
+	if (((inp->sctp_flags & SCTP_PCB_FLAGS_BOUND_V6) == 0) &&
+	    (addr->sa_family == AF_INET6)) {
+		SCTP_INP_WUNLOCK(inp);
+		SCTP_ASOC_CREATE_UNLOCK(inp);
+		crit_exit();
+		return (EINVAL);
+	}
+#endif /* INET6 */
+	if ((inp->sctp_flags & SCTP_PCB_FLAGS_UNBOUND) ==
+	    SCTP_PCB_FLAGS_UNBOUND) {
+		/* Bind a ephemeral port */
+		SCTP_INP_WUNLOCK(inp);
+		error = sctp_inpcb_bind(so, NULL, p);
+		if (error) {
+			SCTP_ASOC_CREATE_UNLOCK(inp);
+			crit_exit();
+			return (error);
+		}
+		SCTP_INP_WLOCK(inp);
+	}
+	/* Now do we connect? */
+	if ((inp->sctp_flags & SCTP_PCB_FLAGS_TCPTYPE) &&
+	    (inp->sctp_flags & SCTP_PCB_FLAGS_CONNECTED)) {
+		/* We are already connected AND the TCP model */
+		crit_exit();
+		SCTP_INP_WUNLOCK(inp);
+		SCTP_ASOC_CREATE_UNLOCK(inp);
+		return (EADDRINUSE);
+	}
+	if (inp->sctp_flags & SCTP_PCB_FLAGS_CONNECTED) {
+		stcb = LIST_FIRST(&inp->sctp_asoc_list);
+		if (stcb)
+			SCTP_TCB_UNLOCK(stcb);
+		SCTP_INP_WUNLOCK(inp);
+	} else {
+		SCTP_INP_INCR_REF(inp);
+		SCTP_INP_WUNLOCK(inp);
+		stcb = sctp_findassociation_ep_addr(&inp, addr, NULL, NULL, NULL);
+		if (stcb == NULL) {
+			SCTP_INP_WLOCK(inp);
+			SCTP_INP_DECR_REF(inp);
+			SCTP_INP_WUNLOCK(inp);
+		}
+	}
+	if (stcb != NULL) {
+		/* Already have or am bring up an association */
+		SCTP_ASOC_CREATE_UNLOCK(inp);
+		SCTP_TCB_UNLOCK(stcb);
+		crit_exit();
+		return (EALREADY);
+	}
+	/* We are GOOD to go */
+	stcb = sctp_aloc_assoc(inp, addr, 1, &error, 0);
+	if (stcb == NULL) {
+		/* Gak! no memory */
+		crit_exit();
+		return (error);
+	}
+	if (stcb->sctp_ep->sctp_flags & SCTP_PCB_FLAGS_TCPTYPE) {
+		stcb->sctp_ep->sctp_flags |= SCTP_PCB_FLAGS_CONNECTED;
+		/* Set the connected flag so we can queue data */
+		soisconnecting(so);
+	}
+	stcb->asoc.state = SCTP_STATE_COOKIE_WAIT;
+	SCTP_GETTIME_TIMEVAL(&stcb->asoc.time_entered);
+	sctp_send_initiate(inp, stcb);
+	SCTP_ASOC_CREATE_UNLOCK(inp);
+	SCTP_TCB_UNLOCK(stcb);
+	crit_exit();
+	return error;
+}
+
+int
+sctp_usr_recvd(struct socket *so, int flags)
+{
+	struct sctp_socket_q_list *sq=NULL;
+	/*
+	 * The user has received some data, we may be able to stuff more
+	 * up the socket. And we need to possibly update the rwnd.
+	 */
+	struct sctp_inpcb *inp;
+	struct sctp_tcb *stcb=NULL;
+
+	inp = (struct sctp_inpcb *)so->so_pcb;
+#ifdef SCTP_DEBUG
+	if (sctp_debug_on & SCTP_DEBUG_USRREQ2)
+		printf("Read for so:%x inp:%x Flags:%x\n",
+		       (u_int)so, (u_int)inp, (u_int)flags);
+#endif
+
+	if (inp == 0) {
+		/* I made the same as TCP since we are not setup? */
+#ifdef SCTP_DEBUG
+		if (sctp_debug_on & SCTP_DEBUG_USRREQ2)
+			printf("Nope, connection reset\n");
+#endif
+		return (ECONNRESET);
+	}
+	crit_enter();
+	/*
+	 * Grab the first one on the list. It will re-insert itself if
+	 * it runs out of room
+	 */
+	SCTP_INP_WLOCK(inp);
+	if ((flags & MSG_EOR) && ((inp->sctp_flags & SCTP_PCB_FLAGS_IN_TCPPOOL) == 0)
+	    && ((inp->sctp_flags & SCTP_PCB_FLAGS_CONNECTED) == 0)) {
+		/* Ok the other part of our grubby tracking
+		 * stuff for our horrible layer violation that
+		 * the tsvwg thinks is ok for sctp_peeloff.. gak!
+		 * We must update the next vtag pending on the
+		 * socket buffer (if any).
+		 */
+		inp->sctp_vtag_first = sctp_get_first_vtag_from_sb(so);
+		sq = TAILQ_FIRST(&inp->sctp_queue_list);
+		if (sq) {
+			stcb = sq->tcb;
+		} else {
+			stcb = NULL;
+		}
+	} else {
+		stcb = LIST_FIRST(&inp->sctp_asoc_list);
+	}
+	if (stcb)
+		SCTP_TCB_LOCK(stcb);
+	if (stcb) {
+		long incr;
+		/* all code in normal stcb path assumes
+		 * that you have a tcb_lock only. Thus
+		 * we must release the inp write lock.
+		 */
+		if (flags & MSG_EOR) {
+			if (((inp->sctp_flags & SCTP_PCB_FLAGS_IN_TCPPOOL) == 0)
+			   && ((inp->sctp_flags & SCTP_PCB_FLAGS_CONNECTED) == 0)) {
+				stcb = sctp_remove_from_socket_q(inp);
+			}
+#ifdef SCTP_DEBUG
+			if (sctp_debug_on & SCTP_DEBUG_USRREQ2)
+				printf("remove from socket queue for inp:%x tcbret:%x\n",
+				       (u_int)inp, (u_int)stcb);
+#endif
+
+ 			stcb->asoc.my_rwnd_control_len = sctp_sbspace_sub(stcb->asoc.my_rwnd_control_len,
+ 									  sizeof(struct mbuf));
+			if (inp->sctp_flags & SCTP_PCB_FLAGS_RECVDATAIOEVNT) {
+ 				stcb->asoc.my_rwnd_control_len = sctp_sbspace_sub(stcb->asoc.my_rwnd_control_len,
+ 										  CMSG_LEN(sizeof(struct sctp_sndrcvinfo)));
+			}
+		}
+		if ((TAILQ_EMPTY(&stcb->asoc.delivery_queue) == 0) ||
+		    (TAILQ_EMPTY(&stcb->asoc.reasmqueue) == 0)) {
+			/* Deliver if there is something to be delivered */
+			sctp_service_queues(stcb, &stcb->asoc, 1);
+		}
+		sctp_set_rwnd(stcb, &stcb->asoc);
+		/* if we increase by 1 or more MTU's (smallest MTUs of all
+		 * nets) we send a window update sack
+		 */
+		incr = stcb->asoc.my_rwnd - stcb->asoc.my_last_reported_rwnd;
+		if (incr < 0) {
+			incr = 0;
+		}
+		if (((uint32_t)incr >= (stcb->asoc.smallest_mtu * SCTP_SEG_TO_RWND_UPD)) ||
+		    ((((uint32_t)incr)*SCTP_SCALE_OF_RWND_TO_UPD) >= so->so_rcv.sb_hiwat)) {
+			if (callout_pending(&stcb->asoc.dack_timer.timer)) {
+				/* If the timer is up, stop it */
+				sctp_timer_stop(SCTP_TIMER_TYPE_RECV,
+						stcb->sctp_ep, stcb, NULL);
+			}
+			/* Send the sack, with the new rwnd */
+			sctp_send_sack(stcb);
+			/* Now do the output */
+			sctp_chunk_output(inp, stcb, 10);
+		}
+	} else {
+		if ((( sq ) && (flags & MSG_EOR) && ((inp->sctp_flags & SCTP_PCB_FLAGS_IN_TCPPOOL) == 0))
+		    && ((inp->sctp_flags & SCTP_PCB_FLAGS_CONNECTED) == 0)) {
+			stcb = sctp_remove_from_socket_q(inp);
+		}
+	}
+	SOCKBUF_LOCK(&so->so_rcv);
+	if (( so->so_rcv.sb_mb == NULL ) &&
+	    (TAILQ_EMPTY(&inp->sctp_queue_list) == 0)) {
+		int sq_cnt=0;
+#ifdef SCTP_DEBUG
+		if (sctp_debug_on & SCTP_DEBUG_USRREQ2)
+			printf("Something off, inp:%x so->so_rcv->sb_mb is empty and sockq is not.. cleaning\n",
+			       (u_int)inp);
+#endif
+		if (((inp->sctp_flags & SCTP_PCB_FLAGS_IN_TCPPOOL) == 0)
+		   && ((inp->sctp_flags & SCTP_PCB_FLAGS_CONNECTED) == 0)) {
+			int done_yet;
+			done_yet = TAILQ_EMPTY(&inp->sctp_queue_list);
+			while (!done_yet) {
+				sq_cnt++;
+				(void)sctp_remove_from_socket_q(inp);
+				done_yet = TAILQ_EMPTY(&inp->sctp_queue_list);
+			}
+		}
+#ifdef SCTP_DEBUG
+		if (sctp_debug_on & SCTP_DEBUG_USRREQ2)
+			printf("Cleaned up %d sockq's\n", sq_cnt);
+#endif
+	}
+	SOCKBUF_UNLOCK(&so->so_rcv);
+	if (stcb)
+		SCTP_TCB_UNLOCK(stcb);
+	SCTP_INP_WUNLOCK(inp);
+	crit_exit();
+	return (0);
+}
+
+int
+#if (defined(__FreeBSD__) && __FreeBSD_version >= 500000) || defined(__DragonFly__)
+sctp_listen(struct socket *so, struct thread *p)
+#else
+sctp_listen(struct socket *so, struct proc *p)
+#endif
+{
+	/*
+	 * Note this module depends on the protocol processing being
+	 * called AFTER any socket level flags and backlog are applied
+	 * to the socket. The traditional way that the socket flags are
+	 * applied is AFTER protocol processing. We have made a change
+	 * to the sys/kern/uipc_socket.c module to reverse this but this
+	 * MUST be in place if the socket API for SCTP is to work properly.
+	 */
+	int error = 0;
+	struct sctp_inpcb *inp;
+
+	crit_enter();
+	inp = (struct sctp_inpcb *)so->so_pcb;
+	if (inp == 0) {
+		crit_exit();
+		/* I made the same as TCP since we are not setup? */
+		return (ECONNRESET);
+	}
+	SCTP_INP_RLOCK(inp);
+	if ((inp->sctp_flags & SCTP_PCB_FLAGS_TCPTYPE) &&
+	    (inp->sctp_flags & SCTP_PCB_FLAGS_CONNECTED)) {
+		/* We are already connected AND the TCP model */
+		crit_exit();
+		SCTP_INP_RUNLOCK(inp);
+		return (EADDRINUSE);
+	}
+	if (inp->sctp_flags & SCTP_PCB_FLAGS_UNBOUND) {
+		/* We must do a bind. */
+		SCTP_INP_RUNLOCK(inp);
+		if ((error = sctp_inpcb_bind(so, NULL, p))) {
+			/* bind error, probably perm */
+			crit_exit();
+			return (error);
+		}
+	} else {
+		SCTP_INP_RUNLOCK(inp);
+	}
+	SOCK_LOCK(so);
+	SCTP_INP_WLOCK(inp);
+	if (inp->sctp_socket->so_qlimit) {
+		if (inp->sctp_flags & SCTP_PCB_FLAGS_UDPTYPE) {
+			/*
+			 * For the UDP model we must TURN OFF the ACCEPT
+			 * flags since we do NOT allow the accept() call.
+			 * The TCP model (when present) will do accept which
+			 * then prohibits connect().
+			 */
+			inp->sctp_socket->so_options &= ~SO_ACCEPTCONN;
+		}
+		inp->sctp_flags |= SCTP_PCB_FLAGS_ACCEPTING;
+	} else {
+		if (inp->sctp_flags & SCTP_PCB_FLAGS_ACCEPTING) {
+			/*
+			 * Turning off the listen flags if the backlog is
+			 * set to 0 (i.e. qlimit is 0).
+			 */
+			inp->sctp_flags &= ~SCTP_PCB_FLAGS_ACCEPTING;
+		}
+		inp->sctp_socket->so_options &= ~SO_ACCEPTCONN;
+	}
+	SCTP_INP_WUNLOCK(inp);
+	SOCK_UNLOCK(so);
+	crit_exit();
+	return (error);
+}
+
+int
+#if defined(__FreeBSD__) || defined(__APPLE__) || defined(__DragonFly__)
+sctp_accept(struct socket *so, struct sockaddr **addr)
+{
+#else
+sctp_accept(struct socket *so, struct mbuf *nam)
+{
+	struct sockaddr *addr = mtod(nam, struct sockaddr *);
+#endif
+	struct sctp_tcb *stcb;
+	struct sockaddr *prim;
+	struct sctp_inpcb *inp;
+
+	crit_enter();
+	inp = (struct sctp_inpcb *)so->so_pcb;
+
+	if (inp == 0) {
+		crit_exit();
+		return (ECONNRESET);
+	}
+	SCTP_INP_RLOCK(inp);
+	if (so->so_state & SS_ISDISCONNECTED) {
+		crit_exit();
+		SCTP_INP_RUNLOCK(inp);
+		return (ECONNABORTED);
+	}
+	stcb = LIST_FIRST(&inp->sctp_asoc_list);
+	if (stcb == NULL) {
+		crit_exit();
+		SCTP_INP_RUNLOCK(inp);
+		return (ECONNRESET);
+	}
+	SCTP_TCB_LOCK(stcb);
+	SCTP_INP_RUNLOCK(inp);
+	prim = (struct sockaddr *)&stcb->asoc.primary_destination->ro._l_addr;
+	if (prim->sa_family == AF_INET) {
+		struct sockaddr_in *sin;
+#if defined(__FreeBSD__) || defined(__APPLE__) || defined(__DragonFly__)
+		MALLOC(sin, struct sockaddr_in *, sizeof *sin, M_SONAME,
+		       M_WAITOK | M_ZERO);
+#else
+		sin = (struct sockaddr_in *)addr;
+		bzero((caddr_t)sin, sizeof (*sin));
+#endif
+		sin->sin_family = AF_INET;
+		sin->sin_len = sizeof(*sin);
+		sin->sin_port = ((struct sockaddr_in *)prim)->sin_port;
+		sin->sin_addr = ((struct sockaddr_in *)prim)->sin_addr;
+#if defined(__FreeBSD__) || defined(__APPLE__) || defined(__DragonFly__)
+		*addr = (struct sockaddr *)sin;
+#else
+		nam->m_len = sizeof(*sin);
+#endif
+	} else {
+		struct sockaddr_in6 *sin6;
+#if defined(__FreeBSD__) || defined(__APPLE__) || defined(__DragonFly__)
+		MALLOC(sin6, struct sockaddr_in6 *, sizeof *sin6, M_SONAME,
+		       M_WAITOK | M_ZERO);
+#else
+		sin6 = (struct sockaddr_in6 *)addr;
+#endif
+		bzero((caddr_t)sin6, sizeof (*sin6));
+		sin6->sin6_family = AF_INET6;
+		sin6->sin6_len = sizeof(*sin6);
+		sin6->sin6_port = ((struct sockaddr_in6 *)prim)->sin6_port;
+
+		sin6->sin6_addr = ((struct sockaddr_in6 *)prim)->sin6_addr;
+		if (IN6_IS_SCOPE_LINKLOCAL(&sin6->sin6_addr))
+			/*      sin6->sin6_scope_id = ntohs(sin6->sin6_addr.s6_addr16[1]);*/
+			in6_recoverscope(sin6, &sin6->sin6_addr, NULL);  /* skip ifp check */
+		else
+			sin6->sin6_scope_id = 0;	/*XXX*/
+#if defined(__FreeBSD__) || defined (__APPLE__) || defined(__DragonFly__)
+		*addr= (struct sockaddr *)sin6;
+#else
+		nam->m_len = sizeof(*sin6);
+#endif
+	}
+	/* Wake any delayed sleep action */
+	SCTP_TCB_UNLOCK(stcb);
+	SCTP_INP_WLOCK(inp);
+	if (inp->sctp_flags & SCTP_PCB_FLAGS_DONT_WAKE) {
+		inp->sctp_flags &= ~SCTP_PCB_FLAGS_DONT_WAKE;
+		if (inp->sctp_flags & SCTP_PCB_FLAGS_WAKEOUTPUT) {
+			inp->sctp_flags &= ~SCTP_PCB_FLAGS_WAKEOUTPUT;
+#if defined(__NetBSD__)
+			if (sowritable(inp->sctp_socket))
+				sowwakeup(inp->sctp_socket);
+#else
+			if (sowriteable(inp->sctp_socket))
+				sowwakeup(inp->sctp_socket);
+#endif
+		}
+		if (inp->sctp_flags & SCTP_PCB_FLAGS_WAKEINPUT) {
+			inp->sctp_flags &= ~SCTP_PCB_FLAGS_WAKEINPUT;
+			if (soreadable(inp->sctp_socket))
+				sorwakeup(inp->sctp_socket);
+		}
+
+	}
+	SCTP_INP_WUNLOCK(inp);
+	crit_exit();
+	return (0);
+}
+
+int
+#if defined(__FreeBSD__) || defined(__APPLE__) || defined(__DragonFly__)
+sctp_ingetaddr(struct socket *so, struct sockaddr **addr)
+#else
+sctp_ingetaddr(struct socket *so, struct mbuf *nam)
+#endif
+{
+#if defined(__FreeBSD__) || defined(__APPLE__) || defined(__DragonFly__)
+	struct sockaddr_in *sin;
+#else
+	struct sockaddr_in *sin = mtod(nam, struct sockaddr_in *);
+#endif
+	struct sctp_inpcb *inp;
+	/*
+	 * Do the malloc first in case it blocks.
+	 */
+#if defined(__FreeBSD__) || defined(__APPLE__) || defined(__DragonFly__)
+	MALLOC(sin, struct sockaddr_in *, sizeof *sin, M_SONAME, M_WAITOK |
+	       M_ZERO);
+#else
+	nam->m_len = sizeof(*sin);
+	memset(sin, 0, sizeof(*sin));
+#endif
+	sin->sin_family = AF_INET;
+	sin->sin_len = sizeof(*sin);
+	crit_enter();
+	inp = (struct sctp_inpcb *)so->so_pcb;
+	if (!inp) {
+		crit_exit();
+#if defined(__FreeBSD__) || defined(__APPLE__) || defined(__DragonFly__)
+		FREE(sin, M_SONAME);
+#endif
+		return ECONNRESET;
+	}
+	SCTP_INP_RLOCK(inp);
+	sin->sin_port = inp->sctp_lport;
+	if (inp->sctp_flags & SCTP_PCB_FLAGS_BOUNDALL) {
+		if (inp->sctp_flags & SCTP_PCB_FLAGS_CONNECTED) {
+			struct sctp_tcb *stcb;
+			struct sockaddr_in *sin_a;
+			struct sctp_nets *net;
+			int fnd;
+
+			stcb = LIST_FIRST(&inp->sctp_asoc_list);
+			if (stcb == NULL) {
+				goto notConn;
+			}
+			fnd = 0;
+			sin_a = NULL;
+			SCTP_TCB_LOCK(stcb);
+			TAILQ_FOREACH(net, &stcb->asoc.nets, sctp_next) {
+				sin_a = (struct sockaddr_in *)&net->ro._l_addr;
+				if (sin_a->sin_family == AF_INET) {
+					fnd = 1;
+					break;
+				}
+			}
+			if ((!fnd) || (sin_a == NULL)) {
+				/* punt */
+				SCTP_TCB_UNLOCK(stcb);
+				goto notConn;
+			}
+			sin->sin_addr = sctp_ipv4_source_address_selection(inp,
+			    stcb, (struct route *)&net->ro, net, 0);
+			SCTP_TCB_UNLOCK(stcb);
+		} else {
+			/* For the bound all case you get back 0 */
+		notConn:
+			sin->sin_addr.s_addr = 0;
+		}
+
+	} else {
+		/* Take the first IPv4 address in the list */
+		struct sctp_laddr *laddr;
+		int fnd = 0;
+		LIST_FOREACH(laddr, &inp->sctp_addr_list, sctp_nxt_addr) {
+			if (laddr->ifa->ifa_addr->sa_family == AF_INET) {
+				struct sockaddr_in *sin_a;
+				sin_a = (struct sockaddr_in *)laddr->ifa->ifa_addr;
+				sin->sin_addr = sin_a->sin_addr;
+				fnd = 1;
+				break;
+			}
+		}
+		if (!fnd) {
+			crit_exit();
+#if defined(__FreeBSD__) || defined(__APPLE__) || defined(__DragonFly__)
+			FREE(sin, M_SONAME);
+#endif
+			SCTP_INP_RUNLOCK(inp);
+			return ENOENT;
+		}
+	}
+	SCTP_INP_RUNLOCK(inp);
+	crit_exit();
+#if defined(__FreeBSD__) || defined(__APPLE__) || defined(__DragonFly__)
+	(*addr) = (struct sockaddr *)sin;
+#endif
+	return (0);
+}
+
+int
+#if defined(__FreeBSD__) || defined(__APPLE__) || defined(__DragonFly__)
+sctp_peeraddr(struct socket *so, struct sockaddr **addr)
+{
+	struct sockaddr_in *sin = (struct sockaddr_in *)*addr;
+#else
+sctp_peeraddr(struct socket *so, struct mbuf *nam)
+{
+	struct sockaddr_in *sin = mtod(nam, struct sockaddr_in *);
+#endif
+	int fnd;
+	struct sockaddr_in *sin_a;
+	struct sctp_inpcb *inp;
+	struct sctp_tcb *stcb;
+	struct sctp_nets *net;
+
+	/* Do the malloc first in case it blocks. */
+	inp = (struct sctp_inpcb *)so->so_pcb;
+	if ((inp == NULL) ||
+	    ((inp->sctp_flags & SCTP_PCB_FLAGS_CONNECTED) == 0)) {
+		/* UDP type and listeners will drop out here */
+		return (ENOTCONN);
+	}
+	crit_enter();
+
+#if defined(__FreeBSD__) || defined(__APPLE__) || defined(__DragonFly__)
+	MALLOC(sin, struct sockaddr_in *, sizeof *sin, M_SONAME, M_WAITOK |
+	       M_ZERO);
+#else
+	nam->m_len = sizeof(*sin);
+	memset(sin, 0, sizeof(*sin));
+#endif
+	sin->sin_family = AF_INET;
+	sin->sin_len = sizeof(*sin);
+
+	/* We must recapture incase we blocked */
+	inp = (struct sctp_inpcb *)so->so_pcb;
+	if (!inp) {
+		crit_exit();
+#if defined(__FreeBSD__) || defined(__APPLE__) || defined(__DragonFly__)
+		FREE(sin, M_SONAME);
+#endif
+		return ECONNRESET;
+	}
+	SCTP_INP_RLOCK(inp);
+	stcb = LIST_FIRST(&inp->sctp_asoc_list);
+	if (stcb)
+		SCTP_TCB_LOCK(stcb);
+	SCTP_INP_RUNLOCK(inp);
+	if (stcb == NULL) {
+		crit_exit();
+#if defined(__FreeBSD__) || defined(__APPLE__) || defined(__DragonFly__)
+		FREE(sin, M_SONAME);
+#endif
+		return ECONNRESET;
+	}
+	fnd = 0;
+	TAILQ_FOREACH(net, &stcb->asoc.nets, sctp_next) {
+		sin_a = (struct sockaddr_in *)&net->ro._l_addr;
+		if (sin_a->sin_family == AF_INET) {
+			fnd = 1;
+			sin->sin_port = stcb->rport;
+			sin->sin_addr = sin_a->sin_addr;
+			break;
+		}
+	}
+	SCTP_TCB_UNLOCK(stcb);
+	if (!fnd) {
+		/* No IPv4 address */
+		crit_exit();
+#if defined(__FreeBSD__) || defined(__APPLE__) || defined(__DragonFly__)
+		FREE(sin, M_SONAME);
+#endif
+		return ENOENT;
+	}
+	crit_exit();
+	return (0);
+}
+
+#if defined(__FreeBSD__) || defined(__APPLE__) || defined(__DragonFly__)
+struct pr_usrreqs sctp_usrreqs = {
+	sctp_abort,
+	sctp_accept,
+	sctp_attach,
+	sctp_bind,
+	sctp_connect,
+	pru_connect2_notsupp,
+	in_control,
+	sctp_detach,
+	sctp_disconnect,
+	sctp_listen,
+	sctp_peeraddr,
+	sctp_usr_recvd,
+	pru_rcvoob_notsupp,
+	sctp_send,
+	pru_sense_null,
+	sctp_shutdown,
+	sctp_ingetaddr,
+	sctp_sosend,
+	soreceive,
+	sopoll
+};
+
+#else
+#if defined(__NetBSD__)
+int
+sctp_usrreq(so, req, m, nam, control, p)
+     struct socket *so;
+     int req;
+     struct mbuf *m, *nam, *control;
+     struct proc *p;
+{
+#else
+int
+sctp_usrreq(so, req, m, nam, control)
+     struct socket *so;
+     int req;
+     struct mbuf *m, *nam, *control;
+{
+	struct proc *p = curproc;
+#endif
+	int error = 0;
+	int family;
+
+	family = so->so_proto->pr_domain->dom_family;
+
+	crit_enter();
+	if (req == PRU_CONTROL) {
+		switch (family) {
+		case PF_INET:
+			error = in_control(so, (long)m, (caddr_t)nam,
+			    (struct ifnet *)control
+#if defined(__NetBSD__)
+			    , p
+#endif
+			    );
+			break;
+#ifdef INET6
+		case PF_INET6:
+			error = in6_control(so, (long)m, (caddr_t)nam,
+			    (struct ifnet *)control, p);
+			break;
+#endif
+		default:
+			error =  EAFNOSUPPORT;
+		}
+		crit_exit();
+		return (error);
+	}
+#ifdef __NetBSD__
+	if (req == PRU_PURGEIF) {
+		struct ifnet *ifn;
+		struct ifaddr *ifa;
+		ifn = (struct ifnet *)control;
+		TAILQ_FOREACH(ifa, &ifn->if_addrlist, ifa_list) {
+			if (ifa->ifa_addr->sa_family == family) {
+				sctp_delete_ip_address(ifa);
+			}
+		}
+		switch (family) {
+		case PF_INET:
+			in_purgeif (ifn);
+			break;
+#ifdef INET6
+		case PF_INET6:
+			in6_purgeif (ifn);
+			break;
+#endif /* INET6 */
+		default:
+			crit_exit();
+			return (EAFNOSUPPORT);
+		}
+		crit_exit();
+		return (0);
+	}
+#endif
+	switch (req) {
+	case PRU_ATTACH:
+		error = sctp_attach(so, family, p);
+		break;
+	case PRU_DETACH:
+		error = sctp_detach(so);
+		break;
+	case PRU_BIND:
+		if (nam == NULL) {
+			crit_exit();
+			return (EINVAL);
+		}
+		error  = sctp_bind(so, nam, p);
+		break;
+	case PRU_LISTEN:
+		error = sctp_listen(so, p);
+		break;
+	case PRU_CONNECT:
+		if (nam == NULL) {
+			crit_exit();
+			return (EINVAL);
+		}
+		error = sctp_connect(so, nam, p);
+		break;
+	case PRU_DISCONNECT:
+		error = sctp_disconnect(so);
+		break;
+	case PRU_ACCEPT:
+		if (nam == NULL) {
+			crit_exit();
+			return (EINVAL);
+		}
+		error = sctp_accept(so, nam);
+	break;
+	case PRU_SHUTDOWN:
+		error = sctp_shutdown(so);
+		break;
+
+	case PRU_RCVD:
+		/*
+		 * For Open and Net BSD, this is real
+		 * ugly. The mbuf *nam that is passed
+		 * (by soreceive()) is the int flags c
+		 * ast as a (mbuf *) yuck!
+		 */
+ 		error = sctp_usr_recvd(so, (int)((long)nam));
+		break;
+
+	case PRU_SEND:
+		/* Flags are ignored */
+#ifdef SCTP_DEBUG
+		if (sctp_debug_on & SCTP_DEBUG_USRREQ1) {
+			printf("Send called on V4 side\n");
+		}
+#endif
+		{
+		    struct sockaddr *addr;
+		    if (nam == NULL)
+			addr = NULL;
+		    else
+			addr = mtod(nam, struct sockaddr *);
+
+		    error = sctp_send(so, 0, m, addr, control, p);
+		}
+		break;
+	case PRU_ABORT:
+		error = sctp_abort(so);
+		break;
+
+	case PRU_SENSE:
+		error = 0;
+		break;
+	case PRU_RCVOOB:
+		error = EAFNOSUPPORT;
+		break;
+	case PRU_SENDOOB:
+		error = EAFNOSUPPORT;
+		break;
+	case PRU_PEERADDR:
+		error = sctp_peeraddr(so, nam);
+		break;
+	case PRU_SOCKADDR:
+		error = sctp_ingetaddr(so, nam);
+		break;
+	case PRU_SLOWTIMO:
+		error = 0;
+		break;
+	default:
+		break;
+	}
+	crit_exit();
+	return (error);
+}
+#endif
+
+/* #if defined(__NetBSD__) || defined(__OpenBSD__) */
+#if __OpenBSD__
+/*
+ * Sysctl for sctp variables.
+ */
+int
+sctp_sysctl(name, namelen, oldp, oldlenp, newp, newlen)
+	int *name;
+	u_int namelen;
+	void *oldp;
+	size_t *oldlenp;
+	void *newp;
+	size_t newlen;
+{
+
+	/* All sysctl names at this level are terminal. */
+	if (namelen != 1)
+		return (ENOTDIR);
+sysctl_int();
+
+	switch (name[0]) {
+	case SCTPCTL_MAXDGRAM:
+		return (sysctl_int(oldp, oldlenp, newp, newlen,
+		     &sctp_sendspace));
+	case SCTPCTL_RECVSPACE:
+		return (sysctl_int(oldp, oldlenp, newp, newlen,
+		    &sctp_recvspace));
+	case SCTPCTL_AUTOASCONF:
+		return (sysctl_int(oldp, oldlenp, newp, newlen,
+		    &sctp_auto_asconf));
+	case SCTPCTL_ECN_ENABLE:
+		return (sysctl_int(oldp, oldlenp, newp, newlen,
+				   &sctp_ecn));
+	case SCTPCTL_ECN_NONCE:
+		return (sysctl_int(oldp, oldlenp, newp, newlen,
+				   &sctp_ecn_nonce));
+	case SCTPCTL_STRICT_SACK:
+		return (sysctl_int(oldp, oldlenp, newp, newlen,
+				   &sctp_strict_sacks));
+	case SCTPCTL_NOCSUM_LO:
+		return (sysctl_int(oldp, oldlenp, newp, newlen,
+				   &sctp_no_csum_on_loopback));
+	case SCTPCTL_STRICT_INIT:
+		return (sysctl_int(oldp, oldlenp, newp, newlen,
+				   &sctp_strict_init));
+ 	case SCTPCTL_PEER_CHK_OH:
+ 		return (sysctl_int(oldp, oldlenp, newp, newlen,
+ 				   &sctp_peer_chunk_oh));
+ 	case SCTPCTL_MAXBURST:
+ 		return (sysctl_int(oldp, oldlenp, newp, newlen,
+ 				   &sctp_max_burst_default));
+ 	case SCTPCTL_MAXCHUNKONQ:
+ 		return (sysctl_int(oldp, oldlenp, newp, newlen,
+ 				   &sctp_max_chunks_on_queue));
+ 	case SCTPCTL_DELAYED_SACK:
+ 		return (sysctl_int(oldp, oldlenp, newp, newlen,
+ 				   &sctp_delayed_sack_time_default));
+ 	case SCTPCTL_HB_INTERVAL:
+ 		return (sysctl_int(oldp, oldlenp, newp, newlen,
+ 				   &sctp_heartbeat_interval_default));
+ 	case SCTPCTL_PMTU_RAISE:
+ 		return (sysctl_int(oldp, oldlenp, newp, newlen,
+ 				   &sctp_pmtu_raise_time_default));
+ 	case SCTPCTL_SHUTDOWN_GUARD:
+ 		return (sysctl_int(oldp, oldlenp, newp, newlen,
+ 				   &sctp_shutdown_guard_time_default));
+ 	case SCTPCTL_SECRET_LIFETIME:
+ 		return (sysctl_int(oldp, oldlenp, newp, newlen,
+ 				   &sctp_secret_lifetime_default));
+ 	case SCTPCTL_RTO_MAX:
+ 		return (sysctl_int(oldp, oldlenp, newp, newlen,
+ 				   &sctp_rto_max_default));
+ 	case SCTPCTL_RTO_MIN:
+ 		return (sysctl_int(oldp, oldlenp, newp, newlen,
+ 				   &sctp_rto_min_default));
+ 	case SCTPCTL_RTO_INITIAL:
+ 		return (sysctl_int(oldp, oldlenp, newp, newlen,
+ 				   &sctp_rto_initial_default));
+ 	case SCTPCTL_INIT_RTO_MAX:
+ 		return (sysctl_int(oldp, oldlenp, newp, newlen,
+ 				   &sctp_init_rto_max_default));
+ 	case SCTPCTL_COOKIE_LIFE:
+ 		return (sysctl_int(oldp, oldlenp, newp, newlen,
+ 				   &sctp_valid_cookie_life_default));
+ 	case SCTPCTL_INIT_RTX_MAX:
+ 		return (sysctl_int(oldp, oldlenp, newp, newlen,
+ 				   &sctp_init_rtx_max_default));
+ 	case SCTPCTL_ASSOC_RTX_MAX:
+ 		return (sysctl_int(oldp, oldlenp, newp, newlen,
+ 				   &sctp_assoc_rtx_max_default));
+ 	case SCTPCTL_PATH_RTX_MAX:
+ 		return (sysctl_int(oldp, oldlenp, newp, newlen,
+ 				   &sctp_path_rtx_max_default));
+ 	case SCTPCTL_NR_OUTGOING_STREAMS:
+ 		return (sysctl_int(oldp, oldlenp, newp, newlen,
+ 				   &sctp_nr_outgoing_streams_default));
+#ifdef SCTP_DEBUG
+ 	case SCTPCTL_DEBUG:
+ 		return (sysctl_int(oldp, oldlenp, newp, newlen,
+ 				   &sctp_debug_on));
+#endif
+	default:
+		return (ENOPROTOOPT);
+	}
+	/* NOTREACHED */
+}
+#endif
+#if __NetBSD__
+/*
+ * Sysctl for sctp variables.
+ */
+SYSCTL_SETUP(sysctl_net_inet_sctp_setup, "sysctl net.inet.sctp subtree setup")
+{
+
+	sysctl_createv(clog, 0, NULL, NULL,
+		       CTLFLAG_PERMANENT,
+	               CTLTYPE_NODE, "net", NULL,
+                       NULL, 0, NULL, 0,
+                       CTL_NET, CTL_EOL);
+        sysctl_createv(clog, 0, NULL, NULL,
+                       CTLFLAG_PERMANENT,
+                       CTLTYPE_NODE, "inet", NULL,
+                       NULL, 0, NULL, 0,
+                       CTL_NET, PF_INET, CTL_EOL);
+        sysctl_createv(clog, 0, NULL, NULL,
+                       CTLFLAG_PERMANENT,
+                       CTLTYPE_NODE, "sctp",
+                       SYSCTL_DESCR("sctp related settings"),
+                       NULL, 0, NULL, 0,
+                       CTL_NET, PF_INET, IPPROTO_SCTP, CTL_EOL);
+
+       sysctl_createv(clog, 0, NULL, NULL,
+                       CTLFLAG_PERMANENT|CTLFLAG_READWRITE,
+                       CTLTYPE_INT, "maxdgram",
+                       SYSCTL_DESCR("Maximum outgoing SCTP buffer size"),
+                       NULL, 0, &sctp_sendspace, 0,
+                       CTL_NET, PF_INET, IPPROTO_SCTP, SCTPCTL_MAXDGRAM,
+                       CTL_EOL);
+
+       sysctl_createv(clog, 0, NULL, NULL,
+                       CTLFLAG_PERMANENT|CTLFLAG_READWRITE,
+                       CTLTYPE_INT, "recvspace",
+                       SYSCTL_DESCR("Maximum incoming SCTP buffer size"),
+                       NULL, 0, &sctp_recvspace, 0,
+                       CTL_NET, PF_INET, IPPROTO_SCTP, SCTPCTL_RECVSPACE,
+                       CTL_EOL);
+
+       sysctl_createv(clog, 0, NULL, NULL,
+                       CTLFLAG_PERMANENT|CTLFLAG_READWRITE,
+                       CTLTYPE_INT, "autoasconf",
+                       SYSCTL_DESCR("Enable SCTP Auto-ASCONF"),
+                       NULL, 0, &sctp_auto_asconf, 0,
+                       CTL_NET, PF_INET, IPPROTO_SCTP, SCTPCTL_AUTOASCONF,
+                       CTL_EOL);
+
+       sysctl_createv(clog, 0, NULL, NULL,
+                       CTLFLAG_PERMANENT|CTLFLAG_READWRITE,
+                       CTLTYPE_INT, "ecn_enable",
+                       SYSCTL_DESCR("Enable SCTP ECN"),
+                       NULL, 0, &sctp_ecn, 0,
+                       CTL_NET, PF_INET, IPPROTO_SCTP, SCTPCTL_ECN_ENABLE,
+                       CTL_EOL);
+
+       sysctl_createv(clog, 0, NULL, NULL,
+                       CTLFLAG_PERMANENT|CTLFLAG_READWRITE,
+                       CTLTYPE_INT, "ecn_nonce",
+                       SYSCTL_DESCR("Enable SCTP ECN Nonce"),
+                       NULL, 0, &sctp_ecn_nonce, 0,
+                       CTL_NET, PF_INET, IPPROTO_SCTP, SCTPCTL_ECN_NONCE,
+                       CTL_EOL);
+
+       sysctl_createv(clog, 0, NULL, NULL,
+                       CTLFLAG_PERMANENT|CTLFLAG_READWRITE,
+                       CTLTYPE_INT, "strict_sack",
+                       SYSCTL_DESCR("Enable SCTP Strict SACK checking"),
+                       NULL, 0, &sctp_strict_sacks, 0,
+                       CTL_NET, PF_INET, IPPROTO_SCTP, SCTPCTL_STRICT_SACK,
+                       CTL_EOL);
+
+       sysctl_createv(clog, 0, NULL, NULL,
+                       CTLFLAG_PERMANENT|CTLFLAG_READWRITE,
+                       CTLTYPE_INT, "loopback_nocsum",
+                       SYSCTL_DESCR("Enable NO Csum on packets sent on loopback"),
+                       NULL, 0, &sctp_no_csum_on_loopback, 0,
+                       CTL_NET, PF_INET, IPPROTO_SCTP, SCTPCTL_NOCSUM_LO,
+                       CTL_EOL);
+
+       sysctl_createv(clog, 0, NULL, NULL,
+                       CTLFLAG_PERMANENT|CTLFLAG_READWRITE,
+                       CTLTYPE_INT, "strict_init",
+                       SYSCTL_DESCR("Enable strict INIT/INIT-ACK singleton enforcement"),
+                       NULL, 0, &sctp_strict_init, 0,
+                       CTL_NET, PF_INET, IPPROTO_SCTP, SCTPCTL_STRICT_INIT,
+                       CTL_EOL);
+
+       sysctl_createv(clog, 0, NULL, NULL,
+                       CTLFLAG_PERMANENT|CTLFLAG_READWRITE,
+                       CTLTYPE_INT, "peer_chkoh",
+                       SYSCTL_DESCR("Amount to debit peers rwnd per chunk sent"),
+                       NULL, 0, &sctp_peer_chunk_oh, 0,
+                       CTL_NET, PF_INET, IPPROTO_SCTP, SCTPCTL_PEER_CHK_OH,
+                       CTL_EOL);
+
+       sysctl_createv(clog, 0, NULL, NULL,
+                       CTLFLAG_PERMANENT|CTLFLAG_READWRITE,
+                       CTLTYPE_INT, "maxburst",
+                       SYSCTL_DESCR("Default max burst for sctp endpoints"),
+                       NULL, 0, &sctp_max_burst_default, 0,
+                       CTL_NET, PF_INET, IPPROTO_SCTP, SCTPCTL_MAXBURST,
+                       CTL_EOL);
+
+       sysctl_createv(clog, 0, NULL, NULL,
+                       CTLFLAG_PERMANENT|CTLFLAG_READWRITE,
+                       CTLTYPE_INT, "maxchunks",
+                       SYSCTL_DESCR("Default max chunks on queue per asoc"),
+                       NULL, 0, &sctp_max_chunks_on_queue, 0,
+                       CTL_NET, PF_INET, IPPROTO_SCTP, SCTPCTL_MAXCHUNKONQ,
+                       CTL_EOL);
+
+}
+#endif
Index: sys/netinet/sctp_var.h
===================================================================
RCS file: sys/netinet/sctp_var.h
diff -N sys/netinet/sctp_var.h
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ sys/netinet/sctp_var.h	18 Jun 2005 16:05:50 -0000
@@ -0,0 +1,278 @@
+/*	$KAME: sctp_var.h,v 1.23 2004/10/27 07:57:49 itojun Exp $	*/
+
+/*
+ * Copyright (c) 2001, 2002, 2003, 2004 Cisco Systems, Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *      This product includes software developed by Cisco Systems, Inc.
+ * 4. Neither the name of the project nor the names of its contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY CISCO SYSTEMS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL CISCO SYSTEMS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef _NETINET_SCTP_VAR_H_
+#define _NETINET_SCTP_VAR_H_
+
+#ifndef __OpenBSD__
+#include <sys/socketvar.h>
+#endif
+#include <netinet/sctp_uio.h>
+
+/* SCTP Kernel structures */
+
+/*
+ * Names for SCTP sysctl objects
+ */
+#ifndef __APPLE__
+#define	SCTPCTL_MAXDGRAM	    1	/* max datagram size */
+#define	SCTPCTL_RECVSPACE	    2	/* default receive buffer space */
+#define SCTPCTL_AUTOASCONF          3   /* auto asconf enable/disable flag */
+#define SCTPCTL_ECN_ENABLE          4	/* Is ecn allowed */
+#define SCTPCTL_ECN_NONCE           5   /* Is ecn nonce allowed */
+#define SCTPCTL_STRICT_SACK         6	/* strictly require sack'd TSN's to be
+					 * smaller than sndnxt.
+					 */
+#define SCTPCTL_NOCSUM_LO           7   /* Require that the Loopback NOT have
+				         * the crc32 checksum on packets routed over
+					 * it.
+				         */
+#define SCTPCTL_STRICT_INIT         8
+#define SCTPCTL_PEER_CHK_OH         9
+#define SCTPCTL_MAXBURST            10
+#define SCTPCTL_MAXCHUNKONQ         11
+#define SCTPCTL_DELAYED_SACK        12
+#define SCTPCTL_HB_INTERVAL         13
+#define SCTPCTL_PMTU_RAISE          14
+#define SCTPCTL_SHUTDOWN_GUARD      15
+#define SCTPCTL_SECRET_LIFETIME     16
+#define SCTPCTL_RTO_MAX             17
+#define SCTPCTL_RTO_MIN             18
+#define SCTPCTL_RTO_INITIAL         19
+#define SCTPCTL_INIT_RTO_MAX        20
+#define SCTPCTL_COOKIE_LIFE         21
+#define SCTPCTL_INIT_RTX_MAX        22
+#define SCTPCTL_ASSOC_RTX_MAX       23
+#define SCTPCTL_PATH_RTX_MAX        24
+#define SCTPCTL_NR_OUTGOING_STREAMS 25
+#ifdef SCTP_DEBUG
+#define SCTPCTL_DEBUG               26
+#define SCTPCTL_MAXID		    27
+#else
+#define SCTPCTL_MAXID		    26
+#endif
+
+#endif
+
+#ifdef SCTP_DEBUG
+#define SCTPCTL_NAMES { \
+	{ 0, 0 }, \
+	{ "maxdgram", CTLTYPE_INT }, \
+	{ "recvspace", CTLTYPE_INT }, \
+	{ "autoasconf", CTLTYPE_INT }, \
+	{ "ecn_enable", CTLTYPE_INT }, \
+	{ "ecn_nonce", CTLTYPE_INT }, \
+	{ "strict_sack", CTLTYPE_INT }, \
+	{ "looback_nocsum", CTLTYPE_INT }, \
+	{ "strict_init", CTLTYPE_INT }, \
+	{ "peer_chkoh", CTLTYPE_INT }, \
+	{ "maxburst", CTLTYPE_INT }, \
+	{ "maxchunks", CTLTYPE_INT }, \
+	{ "delayed_sack_time", CTLTYPE_INT }, \
+	{ "heartbeat_interval", CTLTYPE_INT }, \
+	{ "pmtu_raise_time", CTLTYPE_INT }, \
+	{ "shutdown_guard_time", CTLTYPE_INT }, \
+	{ "secret_lifetime", CTLTYPE_INT }, \
+	{ "rto_max", CTLTYPE_INT }, \
+	{ "rto_min", CTLTYPE_INT }, \
+	{ "rto_initial", CTLTYPE_INT }, \
+	{ "init_rto_max", CTLTYPE_INT }, \
+	{ "valid_cookie_life", CTLTYPE_INT }, \
+	{ "init_rtx_max", CTLTYPE_INT }, \
+	{ "assoc_rtx_max", CTLTYPE_INT }, \
+	{ "path_rtx_max", CTLTYPE_INT }, \
+	{ "nr_outgoing_streams", CTLTYPE_INT }, \
+	{ "debug", CTLTYPE_INT }, \
+}
+#else
+#define SCTPCTL_NAMES { \
+	{ 0, 0 }, \
+	{ "maxdgram", CTLTYPE_INT }, \
+	{ "recvspace", CTLTYPE_INT }, \
+	{ "autoasconf", CTLTYPE_INT }, \
+	{ "ecn_enable", CTLTYPE_INT }, \
+	{ "ecn_nonce", CTLTYPE_INT }, \
+	{ "strict_sack", CTLTYPE_INT }, \
+	{ "looback_nocsum", CTLTYPE_INT }, \
+	{ "strict_init", CTLTYPE_INT }, \
+	{ "peer_chkoh", CTLTYPE_INT }, \
+	{ "maxburst", CTLTYPE_INT }, \
+	{ "maxchunks", CTLTYPE_INT }, \
+	{ "delayed_sack_time", CTLTYPE_INT }, \
+	{ "heartbeat_interval", CTLTYPE_INT }, \
+	{ "pmtu_raise_time", CTLTYPE_INT }, \
+	{ "shutdown_guard_time", CTLTYPE_INT }, \
+	{ "secret_lifetime", CTLTYPE_INT }, \
+	{ "rto_max", CTLTYPE_INT }, \
+	{ "rto_min", CTLTYPE_INT }, \
+	{ "rto_initial", CTLTYPE_INT }, \
+	{ "init_rto_max", CTLTYPE_INT }, \
+	{ "valid_cookie_life", CTLTYPE_INT }, \
+	{ "init_rtx_max", CTLTYPE_INT }, \
+	{ "assoc_rtx_max", CTLTYPE_INT }, \
+	{ "path_rtx_max", CTLTYPE_INT }, \
+	{ "nr_outgoing_streams", CTLTYPE_INT }, \
+}
+#endif
+
+#if defined(_KERNEL) || (defined(__APPLE__) && defined(KERNEL))
+
+#if defined(__FreeBSD__) || defined(__APPLE__) || defined(__DragonFly__)
+#ifdef SYSCTL_DECL
+SYSCTL_DECL(_net_inet_sctp);
+#endif
+extern struct	pr_usrreqs sctp_usrreqs;
+#elif defined(__NetBSD__)
+int sctp_usrreq __P((struct socket *, int, struct mbuf *, struct mbuf *,
+		      struct mbuf *, struct proc *));
+#else
+int sctp_usrreq __P((struct socket *, int, struct mbuf *, struct mbuf *,
+		      struct mbuf *));
+#endif
+
+#define	sctp_sbspace(sb) ((long) (((sb)->sb_hiwat > (sb)->sb_cc) ? ((sb)->sb_hiwat - (sb)->sb_cc) : 0))
+
+#define sctp_sbspace_sub(a,b) ((a > b) ? (a - b) : 0)
+
+extern int	sctp_sendspace;
+extern int	sctp_recvspace;
+extern int      sctp_ecn;
+extern int      sctp_ecn_nonce;
+
+struct sctp_nets;
+struct sctp_inpcb;
+struct sctp_tcb;
+struct sctphdr;
+
+#if defined(__OpenBSD__)
+void sctp_fasttim(void);
+#endif
+
+#if defined(__FreeBSD__) || defined(__APPLE__)
+void	sctp_ctlinput __P((int, struct sockaddr *, void *));
+int	sctp_ctloutput __P((struct socket *, struct sockopt *));
+void	sctp_input __P((struct mbuf *, int));
+#elif defined(__DragonFly__)
+void	sctp_ctlinput __P((int, struct sockaddr *, void *));
+int	sctp_ctloutput __P((struct socket *, struct sockopt *));
+void	sctp_input __P((struct mbuf *, ... ));
+#else
+void*	sctp_ctlinput __P((int, struct sockaddr *, void *));
+int	sctp_ctloutput __P((int, struct socket *, int, int, struct mbuf **));
+void	sctp_input __P((struct mbuf *, ... ));
+#endif
+void	sctp_drain __P((void));
+void	sctp_init __P((void));
+int	sctp_shutdown __P((struct socket *));
+void	sctp_notify __P((struct sctp_inpcb *, int, struct sctphdr *,
+			 struct sockaddr *, struct sctp_tcb *,
+			 struct sctp_nets *));
+int sctp_usr_recvd __P((struct socket *, int));
+
+#if defined(INET6)
+void ip_2_ip6_hdr __P((struct ip6_hdr *, struct ip *));
+#endif
+
+int sctp_bindx(struct socket *, int, struct sockaddr_storage *,
+	int, int, struct proc *);
+
+/* can't use sctp_assoc_t here */
+int sctp_peeloff(struct socket *, struct socket *, int, caddr_t, int *);
+
+
+sctp_assoc_t sctp_getassocid(struct sockaddr *);
+
+
+
+int sctp_ingetaddr(struct socket *,
+#if defined(__FreeBSD__) || defined(__APPLE__) || defined(__DragonFly__)
+		   struct sockaddr **
+#else
+		   struct mbuf *
+#endif
+);
+
+int sctp_peeraddr(struct socket *,
+#if defined(__FreeBSD__) || defined(__APPLE__) || defined(__DragonFly__)
+		  struct sockaddr **
+#else
+		  struct mbuf *
+#endif
+);
+
+#if (defined(__FreeBSD__) && __FreeBSD_version >= 500000) || defined(__DragonFly__)
+int sctp_listen(struct socket *, struct thread *);
+#else
+int sctp_listen(struct socket *, struct proc *);
+#endif
+
+#if defined(__FreeBSD__) || defined(__APPLE__) || defined(__DragonFly__)
+int sctp_accept(struct socket *, struct sockaddr **);
+#else
+int sctp_accept(struct socket *, struct mbuf *);
+#endif
+
+#if defined(__NetBSD__) || defined(__OpenBSD__)
+int sctp_sysctl(int *, u_int, void *, size_t *, void *, size_t);
+#endif
+
+/*
+ * compatibility defines for OpenBSD, Apple
+ */
+
+/* map callout into timeout for OpenBSD */
+#ifdef __OpenBSD__
+#ifndef callout_init
+#define callout_init(args)
+#define callout_reset(c, ticks, func, arg) \
+do { \
+	timeout_set((c), (func), (arg)); \
+	timeout_add((c), (ticks)); \
+} while (0)
+#define callout_stop(c) timeout_del(c)
+#define callout_pending(c) timeout_pending(c)
+#define callout_active(c) timeout_initialized(c)
+#endif
+#endif
+
+/* XXX: Hopefully temporary until APPLE changes to newer defs like other BSDs */
+#if defined(__APPLE__)
+#define if_addrlist	if_addrhead
+#define if_list		if_link
+#define ifa_list	ifa_link
+#endif /* __APPLE__ **/
+
+#endif /* _KERNEL */
+
+#endif /* !_NETINET_SCTP_VAR_H_ */
Index: sys/netinet/sctputil.c
===================================================================
RCS file: sys/netinet/sctputil.c
diff -N sys/netinet/sctputil.c
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ sys/netinet/sctputil.c	18 Jun 2005 16:05:50 -0000
@@ -0,0 +1,3835 @@
+/*	$KAME: sctputil.c,v 1.36 2005/03/06 16:04:19 itojun Exp $	*/
+
+/*
+ * Copyright (c) 2001, 2002, 2003, 2004 Cisco Systems, Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *      This product includes software developed by Cisco Systems, Inc.
+ * 4. Neither the name of the project nor the names of its contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY CISCO SYSTEMS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL CISCO SYSTEMS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#if !(defined(__OpenBSD__) || defined(__APPLE__))
+#include "opt_ipsec.h"
+#endif
+#if defined(__FreeBSD__) || defined(__DragonFly__)
+#include "opt_compat.h"
+#include "opt_inet6.h"
+#include "opt_inet.h"
+#if !(defined(SCTP_BASE_FREEBSD) || defined(__DragonFly__))
+#include "opt_mpath.h"
+#endif /* SCTP_BASE_FREEBSD || __DragonFly__ */
+#endif /* FreeBSD */
+#if defined(__NetBSD__)
+#include "opt_inet.h"
+#endif
+#ifdef __APPLE__
+#include <sctp.h>
+#elif !defined(__OpenBSD__)
+#include "opt_sctp.h"
+#endif
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/malloc.h>
+#include <sys/mbuf.h>
+#include <sys/domain.h>
+#include <sys/protosw.h>
+#include <sys/socket.h>
+#include <sys/socketvar.h>
+#include <sys/proc.h>
+#include <sys/kernel.h>
+#include <sys/sysctl.h>
+#include <sys/thread2.h>
+
+#if defined(__FreeBSD__) || defined(__DragonFly__)
+#include <sys/callout.h>
+#else
+#include <netinet/sctp_callout.h>	/* for callout_active() */
+#endif
+
+#include <net/radix.h>
+#include <net/route.h>
+
+#ifdef INET6
+#ifndef __OpenBSD__
+#include <sys/domain.h>
+#endif
+#endif
+
+#if (defined(__FreeBSD__) && __FreeBSD_version >= 500000)
+#include <sys/limits.h>
+#else
+#include <machine/limits.h>
+#endif
+
+#include <net/if.h>
+#include <net/if_types.h>
+#include <net/route.h>
+
+#include <netinet/in.h>
+#include <netinet/in_systm.h>
+#include <netinet/ip.h>
+#include <netinet/in_pcb.h>
+#include <netinet/in_var.h>
+#include <netinet/ip_var.h>
+
+#ifdef INET6
+#include <netinet/ip6.h>
+#include <netinet6/ip6_var.h>
+
+#if defined(__FreeBSD__) || defined(__NetBSD__) || defined(__APPLE__) || defined(__DragonFly_)
+#include <netinet6/in6_pcb.h>
+#elif defined(__OpenBSD__)
+#include <netinet/in_pcb.h>
+#endif
+
+#endif /* INET6 */
+
+#include <netinet/sctp_pcb.h>
+
+#ifdef IPSEC
+#ifndef __OpenBSD__
+#include <netinet6/ipsec.h>
+#include <netkey/key.h>
+#else
+#undef IPSEC
+#endif
+#endif /* IPSEC */
+
+#include <netinet/sctputil.h>
+#include <netinet/sctp_var.h>
+#ifdef INET6
+#include <netinet6/sctp6_var.h>
+#endif
+#include <netinet/sctp_header.h>
+#include <netinet/sctp_output.h>
+#include <netinet/sctp_hashdriver.h>
+#include <netinet/sctp_uio.h>
+#include <netinet/sctp_timer.h>
+#include <netinet/sctp_crc32.h>
+#include <netinet/sctp_indata.h>	/* for sctp_deliver_data() */
+#define NUMBER_OF_MTU_SIZES 18
+
+#ifdef SCTP_DEBUG
+extern u_int32_t sctp_debug_on;
+#endif
+
+#ifdef SCTP_STAT_LOGGING
+int sctp_cwnd_log_at=0;
+int sctp_cwnd_log_rolled=0;
+struct sctp_cwnd_log sctp_clog[SCTP_STAT_LOG_SIZE];
+
+void sctp_clr_stat_log(void)
+{
+	sctp_cwnd_log_at=0;
+	sctp_cwnd_log_rolled=0;
+}
+
+void
+sctp_log_strm_del_alt(u_int32_t tsn, u_int16_t sseq, int from)
+{
+
+	sctp_clog[sctp_cwnd_log_at].from = (u_int8_t)from;
+	sctp_clog[sctp_cwnd_log_at].event_type = (u_int8_t)SCTP_LOG_EVENT_STRM;
+	sctp_clog[sctp_cwnd_log_at].x.strlog.n_tsn = tsn;
+	sctp_clog[sctp_cwnd_log_at].x.strlog.n_sseq = sseq;
+	sctp_clog[sctp_cwnd_log_at].x.strlog.e_tsn = 0;
+	sctp_clog[sctp_cwnd_log_at].x.strlog.e_sseq = 0;
+	sctp_cwnd_log_at++;
+	if (sctp_cwnd_log_at >= SCTP_STAT_LOG_SIZE) {
+		sctp_cwnd_log_at = 0;
+		sctp_cwnd_log_rolled = 1;
+	}
+
+}
+
+void
+sctp_log_map(uint32_t map, uint32_t cum, uint32_t high, int from)
+{
+
+	sctp_clog[sctp_cwnd_log_at].from = (u_int8_t)from;
+	sctp_clog[sctp_cwnd_log_at].event_type = (u_int8_t)SCTP_LOG_EVENT_MAP;
+	sctp_clog[sctp_cwnd_log_at].x.map.base = map;
+	sctp_clog[sctp_cwnd_log_at].x.map.cum = cum;
+	sctp_clog[sctp_cwnd_log_at].x.map.high = high;
+	sctp_cwnd_log_at++;
+	if (sctp_cwnd_log_at >= SCTP_STAT_LOG_SIZE) {
+		sctp_cwnd_log_at = 0;
+		sctp_cwnd_log_rolled = 1;
+	}
+}
+
+void
+sctp_log_fr(uint32_t biggest_tsn, uint32_t biggest_new_tsn, uint32_t tsn,
+    int from)
+{
+
+	sctp_clog[sctp_cwnd_log_at].from = (u_int8_t)from;
+	sctp_clog[sctp_cwnd_log_at].event_type = (u_int8_t)SCTP_LOG_EVENT_FR;
+	sctp_clog[sctp_cwnd_log_at].x.fr.largest_tsn = biggest_tsn;
+	sctp_clog[sctp_cwnd_log_at].x.fr.largest_new_tsn = biggest_new_tsn;
+	sctp_clog[sctp_cwnd_log_at].x.fr.tsn = tsn;
+	sctp_cwnd_log_at++;
+	if (sctp_cwnd_log_at >= SCTP_STAT_LOG_SIZE) {
+		sctp_cwnd_log_at = 0;
+		sctp_cwnd_log_rolled = 1;
+	}
+}
+
+void
+sctp_log_strm_del(struct sctp_tmit_chunk *chk, struct sctp_tmit_chunk *poschk,
+    int from)
+{
+
+	if (chk == NULL) {
+		printf("Gak log of NULL?\n");
+		return;
+	}
+	sctp_clog[sctp_cwnd_log_at].from = (u_int8_t)from;
+	sctp_clog[sctp_cwnd_log_at].event_type = (u_int8_t)SCTP_LOG_EVENT_STRM;
+	sctp_clog[sctp_cwnd_log_at].x.strlog.n_tsn = chk->rec.data.TSN_seq;
+	sctp_clog[sctp_cwnd_log_at].x.strlog.n_sseq = chk->rec.data.stream_seq;
+	if (poschk != NULL) {
+		sctp_clog[sctp_cwnd_log_at].x.strlog.e_tsn =
+		    poschk->rec.data.TSN_seq;
+		sctp_clog[sctp_cwnd_log_at].x.strlog.e_sseq =
+		    poschk->rec.data.stream_seq;
+	} else {
+		sctp_clog[sctp_cwnd_log_at].x.strlog.e_tsn = 0;
+		sctp_clog[sctp_cwnd_log_at].x.strlog.e_sseq = 0;
+	}
+	sctp_cwnd_log_at++;
+	if (sctp_cwnd_log_at >= SCTP_STAT_LOG_SIZE) {
+		sctp_cwnd_log_at = 0;
+		sctp_cwnd_log_rolled = 1;
+	}
+}
+
+void
+sctp_log_cwnd(struct sctp_nets *net, int augment, uint8_t from)
+{
+
+	sctp_clog[sctp_cwnd_log_at].from = (u_int8_t)from;
+	sctp_clog[sctp_cwnd_log_at].event_type = (u_int8_t)SCTP_LOG_EVENT_CWND;
+	sctp_clog[sctp_cwnd_log_at].x.cwnd.net = net;
+	sctp_clog[sctp_cwnd_log_at].x.cwnd.cwnd_new_value = net->cwnd;
+	sctp_clog[sctp_cwnd_log_at].x.cwnd.inflight = net->flight_size;
+	sctp_clog[sctp_cwnd_log_at].x.cwnd.cwnd_augment = augment;
+	sctp_cwnd_log_at++;
+	if (sctp_cwnd_log_at >= SCTP_STAT_LOG_SIZE) {
+		sctp_cwnd_log_at = 0;
+		sctp_cwnd_log_rolled = 1;
+	}
+}
+
+void
+sctp_log_maxburst(struct sctp_nets *net, int error, int burst, uint8_t from)
+{
+	sctp_clog[sctp_cwnd_log_at].from = (u_int8_t)from;
+	sctp_clog[sctp_cwnd_log_at].event_type = (u_int8_t)SCTP_LOG_EVENT_MAXBURST;
+	sctp_clog[sctp_cwnd_log_at].x.cwnd.net = net;
+	sctp_clog[sctp_cwnd_log_at].x.cwnd.cwnd_new_value = error;
+	sctp_clog[sctp_cwnd_log_at].x.cwnd.inflight = net->flight_size;
+	sctp_clog[sctp_cwnd_log_at].x.cwnd.cwnd_augment = burst;
+	sctp_cwnd_log_at++;
+	if (sctp_cwnd_log_at >= SCTP_STAT_LOG_SIZE) {
+		sctp_cwnd_log_at = 0;
+		sctp_cwnd_log_rolled = 1;
+	}
+}
+
+void
+sctp_log_rwnd(uint8_t from, u_int32_t peers_rwnd , u_int32_t snd_size, u_int32_t overhead)
+{
+	sctp_clog[sctp_cwnd_log_at].from = (u_int8_t)from;
+	sctp_clog[sctp_cwnd_log_at].event_type = (u_int8_t)SCTP_LOG_EVENT_RWND;
+	sctp_clog[sctp_cwnd_log_at].x.rwnd.rwnd = peers_rwnd;
+	sctp_clog[sctp_cwnd_log_at].x.rwnd.send_size = snd_size;
+	sctp_clog[sctp_cwnd_log_at].x.rwnd.overhead = overhead;
+	sctp_clog[sctp_cwnd_log_at].x.rwnd.new_rwnd = 0;
+	sctp_cwnd_log_at++;
+	if (sctp_cwnd_log_at >= SCTP_STAT_LOG_SIZE) {
+		sctp_cwnd_log_at = 0;
+		sctp_cwnd_log_rolled = 1;
+	}
+}
+
+void
+sctp_log_rwnd_set(uint8_t from, u_int32_t peers_rwnd , u_int32_t flight_size, u_int32_t overhead, u_int32_t a_rwndval)
+{
+	sctp_clog[sctp_cwnd_log_at].from = (u_int8_t)from;
+	sctp_clog[sctp_cwnd_log_at].event_type = (u_int8_t)SCTP_LOG_EVENT_RWND;
+	sctp_clog[sctp_cwnd_log_at].x.rwnd.rwnd = peers_rwnd;
+	sctp_clog[sctp_cwnd_log_at].x.rwnd.send_size = flight_size;
+	sctp_clog[sctp_cwnd_log_at].x.rwnd.overhead = overhead;
+	sctp_clog[sctp_cwnd_log_at].x.rwnd.new_rwnd = a_rwndval;
+	sctp_cwnd_log_at++;
+	if (sctp_cwnd_log_at >= SCTP_STAT_LOG_SIZE) {
+		sctp_cwnd_log_at = 0;
+		sctp_cwnd_log_rolled = 1;
+	}
+}
+
+void
+sctp_log_mbcnt(uint8_t from, u_int32_t total_oq , u_int32_t book, u_int32_t total_mbcnt_q, u_int32_t mbcnt)
+{
+	sctp_clog[sctp_cwnd_log_at].from = (u_int8_t)from;
+	sctp_clog[sctp_cwnd_log_at].event_type = (u_int8_t)SCTP_LOG_EVENT_MBCNT;
+	sctp_clog[sctp_cwnd_log_at].x.mbcnt.total_queue_size = total_oq;
+	sctp_clog[sctp_cwnd_log_at].x.mbcnt.size_change  = book;
+	sctp_clog[sctp_cwnd_log_at].x.mbcnt.total_queue_mb_size = total_mbcnt_q;
+	sctp_clog[sctp_cwnd_log_at].x.mbcnt.mbcnt_change = mbcnt;
+	sctp_cwnd_log_at++;
+	if (sctp_cwnd_log_at >= SCTP_STAT_LOG_SIZE) {
+		sctp_cwnd_log_at = 0;
+		sctp_cwnd_log_rolled = 1;
+	}
+}
+
+void
+sctp_log_block(uint8_t from, struct socket *so, struct sctp_association *asoc)
+{
+
+	sctp_clog[sctp_cwnd_log_at].from = (u_int8_t)from;
+	sctp_clog[sctp_cwnd_log_at].event_type = (u_int8_t)SCTP_LOG_EVENT_BLOCK;
+	sctp_clog[sctp_cwnd_log_at].x.blk.maxmb = (u_int16_t)(so->so_snd.sb_mbmax/1024);
+	sctp_clog[sctp_cwnd_log_at].x.blk.onmb = asoc->total_output_mbuf_queue_size;
+	sctp_clog[sctp_cwnd_log_at].x.blk.maxsb = (u_int16_t)(so->so_snd.sb_hiwat/1024);
+	sctp_clog[sctp_cwnd_log_at].x.blk.onsb = asoc->total_output_queue_size;
+	sctp_clog[sctp_cwnd_log_at].x.blk.send_sent_qcnt = (u_int16_t)(asoc->send_queue_cnt + asoc->sent_queue_cnt);
+	sctp_clog[sctp_cwnd_log_at].x.blk.stream_qcnt = (u_int16_t)asoc->stream_queue_cnt;
+	sctp_cwnd_log_at++;
+	if (sctp_cwnd_log_at >= SCTP_STAT_LOG_SIZE) {
+		sctp_cwnd_log_at = 0;
+		sctp_cwnd_log_rolled = 1;
+	}
+}
+
+int
+sctp_fill_stat_log(struct mbuf *m)
+{
+	struct sctp_cwnd_log_req *req;
+	int size_limit, num, i, at, cnt_out=0;
+
+	if (m == NULL)
+		return (EINVAL);
+
+	size_limit = (m->m_len - sizeof(struct sctp_cwnd_log_req));
+	if (size_limit < sizeof(struct sctp_cwnd_log)) {
+		return (EINVAL);
+	}
+	req = mtod(m, struct sctp_cwnd_log_req *);
+	num = size_limit/sizeof(struct sctp_cwnd_log);
+	if (sctp_cwnd_log_rolled) {
+		req->num_in_log = SCTP_STAT_LOG_SIZE;
+	} else {
+		req->num_in_log = sctp_cwnd_log_at;
+		/* if the log has not rolled, we don't
+		 * let you have old data.
+		 */
+ 		if (req->end_at > sctp_cwnd_log_at) {
+			req->end_at = sctp_cwnd_log_at;
+		}
+	}
+	if ((num < SCTP_STAT_LOG_SIZE) &&
+	    ((sctp_cwnd_log_rolled) || (sctp_cwnd_log_at > num))) {
+		/* we can't return all of it */
+		if (((req->start_at == 0) && (req->end_at == 0)) ||
+		    (req->start_at >= SCTP_STAT_LOG_SIZE) ||
+		    (req->end_at >= SCTP_STAT_LOG_SIZE)) {
+			/* No user request or user is wacked. */
+			req->num_ret = num;
+			req->end_at = sctp_cwnd_log_at - 1;
+			if ((sctp_cwnd_log_at - num) < 0) {
+				int cc;
+				cc = num - sctp_cwnd_log_at;
+				req->start_at = SCTP_STAT_LOG_SIZE - cc;
+			} else {
+				req->start_at = sctp_cwnd_log_at - num;
+			}
+		} else {
+			/* a user request */
+			int cc;
+			if (req->start_at > req->end_at) {
+				cc = (SCTP_STAT_LOG_SIZE - req->start_at) +
+				    (req->end_at + 1);
+			} else {
+
+				cc = req->end_at - req->start_at;
+			}
+			if (cc < num) {
+				num = cc;
+			}
+			req->num_ret = num;
+		}
+	} else {
+		/* We can return all  of it */
+		req->start_at = 0;
+		req->end_at = sctp_cwnd_log_at - 1;
+		req->num_ret = sctp_cwnd_log_at;
+	}
+	for (i = 0, at = req->start_at; i < req->num_ret; i++) {
+		req->log[i] = sctp_clog[at];
+		cnt_out++;
+		at++;
+		if (at >= SCTP_STAT_LOG_SIZE)
+			at = 0;
+	}
+	m->m_len = (cnt_out * sizeof(struct sctp_cwnd_log_req)) + sizeof(struct sctp_cwnd_log_req);
+	return (0);
+}
+
+#endif
+
+#ifdef SCTP_AUDITING_ENABLED
+u_int8_t sctp_audit_data[SCTP_AUDIT_SIZE][2];
+static int sctp_audit_indx = 0;
+
+static
+void sctp_print_audit_report(void)
+{
+	int i;
+	int cnt;
+	cnt = 0;
+	for (i=sctp_audit_indx;i<SCTP_AUDIT_SIZE;i++) {
+		if ((sctp_audit_data[i][0] == 0xe0) &&
+		    (sctp_audit_data[i][1] == 0x01)) {
+			cnt = 0;
+			printf("\n");
+		} else if (sctp_audit_data[i][0] == 0xf0) {
+			cnt = 0;
+			printf("\n");
+		} else if ((sctp_audit_data[i][0] == 0xc0) &&
+		    (sctp_audit_data[i][1] == 0x01)) {
+			printf("\n");
+			cnt = 0;
+		}
+		printf("%2.2x%2.2x ", (uint32_t)sctp_audit_data[i][0],
+		    (uint32_t)sctp_audit_data[i][1]);
+		cnt++;
+		if ((cnt % 14) == 0)
+			printf("\n");
+	}
+	for (i=0;i<sctp_audit_indx;i++) {
+		if ((sctp_audit_data[i][0] == 0xe0) &&
+		    (sctp_audit_data[i][1] == 0x01)) {
+			cnt = 0;
+			printf("\n");
+		} else if (sctp_audit_data[i][0] == 0xf0) {
+			cnt = 0;
+			printf("\n");
+		} else if ((sctp_audit_data[i][0] == 0xc0) &&
+			 (sctp_audit_data[i][1] == 0x01)) {
+			printf("\n");
+			cnt = 0;
+		}
+		printf("%2.2x%2.2x ", (uint32_t)sctp_audit_data[i][0],
+		    (uint32_t)sctp_audit_data[i][1]);
+		cnt++;
+		if ((cnt % 14) == 0)
+			printf("\n");
+	}
+	printf("\n");
+}
+
+void sctp_auditing(int from, struct sctp_inpcb *inp, struct sctp_tcb *stcb,
+    struct sctp_nets *net)
+{
+	int resend_cnt, tot_out, rep, tot_book_cnt;
+	struct sctp_nets *lnet;
+	struct sctp_tmit_chunk *chk;
+
+	sctp_audit_data[sctp_audit_indx][0] = 0xAA;
+	sctp_audit_data[sctp_audit_indx][1] = 0x000000ff & from;
+	sctp_audit_indx++;
+	if (sctp_audit_indx >= SCTP_AUDIT_SIZE) {
+		sctp_audit_indx = 0;
+	}
+	if (inp == NULL) {
+		sctp_audit_data[sctp_audit_indx][0] = 0xAF;
+		sctp_audit_data[sctp_audit_indx][1] = 0x01;
+		sctp_audit_indx++;
+		if (sctp_audit_indx >= SCTP_AUDIT_SIZE) {
+			sctp_audit_indx = 0;
+		}
+		return;
+	}
+	if (stcb == NULL) {
+		sctp_audit_data[sctp_audit_indx][0] = 0xAF;
+		sctp_audit_data[sctp_audit_indx][1] = 0x02;
+		sctp_audit_indx++;
+		if (sctp_audit_indx >= SCTP_AUDIT_SIZE) {
+			sctp_audit_indx = 0;
+		}
+		return;
+	}
+	sctp_audit_data[sctp_audit_indx][0] = 0xA1;
+	sctp_audit_data[sctp_audit_indx][1] =
+	    (0x000000ff & stcb->asoc.sent_queue_retran_cnt);
+	sctp_audit_indx++;
+	if (sctp_audit_indx >= SCTP_AUDIT_SIZE) {
+		sctp_audit_indx = 0;
+	}
+	rep = 0;
+	tot_book_cnt = 0;
+	resend_cnt = tot_out = 0;
+	TAILQ_FOREACH(chk, &stcb->asoc.sent_queue, sctp_next) {
+		if (chk->sent == SCTP_DATAGRAM_RESEND) {
+			resend_cnt++;
+		} else if (chk->sent < SCTP_DATAGRAM_RESEND) {
+			tot_out += chk->book_size;
+			tot_book_cnt++;
+		}
+	}
+	if (resend_cnt != stcb->asoc.sent_queue_retran_cnt) {
+		sctp_audit_data[sctp_audit_indx][0] = 0xAF;
+		sctp_audit_data[sctp_audit_indx][1] = 0xA1;
+		sctp_audit_indx++;
+		if (sctp_audit_indx >= SCTP_AUDIT_SIZE) {
+			sctp_audit_indx = 0;
+		}
+		printf("resend_cnt:%d asoc-tot:%d\n",
+		    resend_cnt, stcb->asoc.sent_queue_retran_cnt);
+		rep = 1;
+		stcb->asoc.sent_queue_retran_cnt = resend_cnt;
+		sctp_audit_data[sctp_audit_indx][0] = 0xA2;
+		sctp_audit_data[sctp_audit_indx][1] =
+		    (0x000000ff & stcb->asoc.sent_queue_retran_cnt);
+		sctp_audit_indx++;
+		if (sctp_audit_indx >= SCTP_AUDIT_SIZE) {
+			sctp_audit_indx = 0;
+		}
+	}
+	if (tot_out != stcb->asoc.total_flight) {
+		sctp_audit_data[sctp_audit_indx][0] = 0xAF;
+		sctp_audit_data[sctp_audit_indx][1] = 0xA2;
+		sctp_audit_indx++;
+		if (sctp_audit_indx >= SCTP_AUDIT_SIZE) {
+			sctp_audit_indx = 0;
+		}
+		rep = 1;
+		printf("tot_flt:%d asoc_tot:%d\n", tot_out,
+		    (int)stcb->asoc.total_flight);
+		stcb->asoc.total_flight = tot_out;
+	}
+	if (tot_book_cnt != stcb->asoc.total_flight_count) {
+		sctp_audit_data[sctp_audit_indx][0] = 0xAF;
+		sctp_audit_data[sctp_audit_indx][1] = 0xA5;
+		sctp_audit_indx++;
+		if (sctp_audit_indx >= SCTP_AUDIT_SIZE) {
+			sctp_audit_indx = 0;
+		}
+		rep = 1;
+		printf("tot_flt_book:%d\n", tot_book);
+
+		stcb->asoc.total_flight_count = tot_book_cnt;
+	}
+	tot_out = 0;
+	TAILQ_FOREACH(lnet, &stcb->asoc.nets, sctp_next) {
+		tot_out += lnet->flight_size;
+	}
+	if (tot_out != stcb->asoc.total_flight) {
+		sctp_audit_data[sctp_audit_indx][0] = 0xAF;
+		sctp_audit_data[sctp_audit_indx][1] = 0xA3;
+		sctp_audit_indx++;
+		if (sctp_audit_indx >= SCTP_AUDIT_SIZE) {
+			sctp_audit_indx = 0;
+		}
+		rep = 1;
+		printf("real flight:%d net total was %d\n",
+		    stcb->asoc.total_flight, tot_out);
+		/* now corrective action */
+		TAILQ_FOREACH(lnet, &stcb->asoc.nets, sctp_next) {
+			tot_out = 0;
+			TAILQ_FOREACH(chk, &stcb->asoc.sent_queue, sctp_next) {
+				if ((chk->whoTo == lnet) &&
+				    (chk->sent < SCTP_DATAGRAM_RESEND)) {
+					tot_out += chk->book_size;
+				}
+			}
+			if (lnet->flight_size != tot_out) {
+				printf("net:%x flight was %d corrected to %d\n",
+				    (uint32_t)lnet, lnet->flight_size, tot_out);
+				lnet->flight_size = tot_out;
+			}
+
+		}
+	}
+
+	if (rep) {
+		sctp_print_audit_report();
+	}
+}
+
+void
+sctp_audit_log(u_int8_t ev, u_int8_t fd)
+{
+	crit_enter();
+	sctp_audit_data[sctp_audit_indx][0] = ev;
+	sctp_audit_data[sctp_audit_indx][1] = fd;
+	sctp_audit_indx++;
+	if (sctp_audit_indx >= SCTP_AUDIT_SIZE) {
+		sctp_audit_indx = 0;
+	}
+	crit_exit();
+}
+
+#endif
+
+/*
+ * a list of sizes based on typical mtu's, used only if next hop
+ * size not returned.
+ */
+static int sctp_mtu_sizes[] = {
+	68,
+	296,
+	508,
+	512,
+	544,
+	576,
+	1006,
+	1492,
+	1500,
+	1536,
+	2002,
+	2048,
+	4352,
+	4464,
+	8166,
+	17914,
+	32000,
+	65535
+};
+
+int
+find_next_best_mtu(int totsz)
+{
+	int i, perfer;
+	/*
+	 * if we are in here we must find the next best fit based on the
+	 * size of the dg that failed to be sent.
+	 */
+	perfer = 0;
+	for (i = 0; i < NUMBER_OF_MTU_SIZES; i++) {
+		if (totsz < sctp_mtu_sizes[i]) {
+			perfer = i - 1;
+			if (perfer < 0)
+				perfer = 0;
+			break;
+		}
+	}
+	return (sctp_mtu_sizes[perfer]);
+}
+
+void
+sctp_fill_random_store(struct sctp_pcb *m)
+{
+	/*
+	 * Here we use the MD5/SHA-1 to hash with our good randomNumbers
+	 * and our counter. The result becomes our good random numbers and
+	 * we then setup to give these out. Note that we do no lockig
+	 * to protect this. This is ok, since if competing folks call
+	 * this we will get more gobbled gook in the random store whic
+	 * is what we want. There is a danger that two guys will use
+	 * the same random numbers, but thats ok too since that
+	 * is random as well :->
+	 */
+	m->store_at = 0;
+	sctp_hash_digest((char *)m->random_numbers, sizeof(m->random_numbers),
+			 (char *)&m->random_counter, sizeof(m->random_counter),
+			 (char *)m->random_store);
+	m->random_counter++;
+}
+
+uint32_t
+sctp_select_initial_TSN(struct sctp_pcb *m)
+{
+	/*
+	 * A true implementation should use random selection process to
+	 * get the initial stream sequence number, using RFC1750 as a
+	 * good guideline
+	 */
+	u_long x, *xp;
+	uint8_t *p;
+
+	if (m->initial_sequence_debug != 0) {
+		u_int32_t ret;
+		ret = m->initial_sequence_debug;
+		m->initial_sequence_debug++;
+		return (ret);
+	}
+	if ((m->store_at+sizeof(u_long)) > SCTP_SIGNATURE_SIZE) {
+		/* Refill the random store */
+		sctp_fill_random_store(m);
+	}
+	p = &m->random_store[(int)m->store_at];
+	xp = (u_long *)p;
+	x = *xp;
+	m->store_at += sizeof(u_long);
+	return (x);
+}
+
+u_int32_t sctp_select_a_tag(struct sctp_inpcb *m)
+{
+	u_long x, not_done;
+	struct timeval now;
+
+	SCTP_GETTIME_TIMEVAL(&now);
+	not_done = 1;
+	while (not_done) {
+		x = sctp_select_initial_TSN(&m->sctp_ep);
+		if (x == 0) {
+			/* we never use 0 */
+			continue;
+		}
+		if (sctp_is_vtag_good(m, x, &now)) {
+			not_done = 0;
+		}
+	}
+	return (x);
+}
+
+
+int
+sctp_init_asoc(struct sctp_inpcb *m, struct sctp_association *asoc,
+	       int for_a_init, uint32_t override_tag )
+{
+	/*
+	 * Anything set to zero is taken care of by the allocation
+	 * routine's bzero
+	 */
+
+	/*
+	 * Up front select what scoping to apply on addresses I tell my peer
+	 * Not sure what to do with these right now, we will need to come up
+	 * with a way to set them. We may need to pass them through from the
+	 * caller in the sctp_aloc_assoc() function.
+	 */
+	int i;
+	/* init all variables to a known value.*/
+	asoc->state = SCTP_STATE_INUSE;
+	asoc->max_burst = m->sctp_ep.max_burst;
+	asoc->heart_beat_delay = m->sctp_ep.sctp_timeoutticks[SCTP_TIMER_HEARTBEAT];
+	asoc->cookie_life = m->sctp_ep.def_cookie_life;
+
+	if (override_tag) {
+		asoc->my_vtag = override_tag;
+	} else {
+		asoc->my_vtag = sctp_select_a_tag(m);
+	}
+	asoc->asconf_seq_out = asoc->str_reset_seq_out = asoc->init_seq_number = asoc->sending_seq =
+		sctp_select_initial_TSN(&m->sctp_ep);
+	asoc->t3timeout_highest_marked = asoc->asconf_seq_out;
+	/* we are opptimisitic here */
+	asoc->peer_supports_asconf = 1;
+	asoc->peer_supports_asconf_setprim = 1;
+	asoc->peer_supports_pktdrop = 1;
+
+	asoc->sent_queue_retran_cnt = 0;
+	/* This will need to be adjusted */
+	asoc->last_cwr_tsn = asoc->init_seq_number - 1;
+	asoc->last_acked_seq = asoc->init_seq_number - 1;
+	asoc->advanced_peer_ack_point = asoc->last_acked_seq;
+	asoc->asconf_seq_in = asoc->last_acked_seq;
+
+	/* here we are different, we hold the next one we expect */
+	asoc->str_reset_seq_in = asoc->last_acked_seq + 1;
+
+	asoc->initial_init_rto_max = m->sctp_ep.initial_init_rto_max;
+	asoc->initial_rto = m->sctp_ep.initial_rto;
+
+	asoc->max_init_times = m->sctp_ep.max_init_times;
+	asoc->max_send_times = m->sctp_ep.max_send_times;
+	asoc->def_net_failure = m->sctp_ep.def_net_failure;
+
+	/* ECN Nonce initialization */
+	asoc->ecn_nonce_allowed = 0;
+	asoc->receiver_nonce_sum = 1;
+	asoc->nonce_sum_expect_base = 1;
+	asoc->nonce_sum_check = 1;
+	asoc->nonce_resync_tsn = 0;
+	asoc->nonce_wait_for_ecne = 0;
+	asoc->nonce_wait_tsn = 0;
+
+	if (m->sctp_flags & SCTP_PCB_FLAGS_BOUND_V6) {
+		struct in6pcb *inp6;
+
+
+		/* Its a V6 socket */
+		inp6 = (struct in6pcb *)m;
+		asoc->ipv6_addr_legal = 1;
+		/* Now look at the binding flag to see if V4 will be legal */
+	if (
+#if defined(__OpenBSD__)
+		(0) /* we always do dual bind */
+#elif defined (__NetBSD__)
+		(inp6->in6p_flags & IN6P_IPV6_V6ONLY)
+#else
+		(inp6->inp_flags & IN6P_IPV6_V6ONLY)
+#endif
+	     == 0) {
+			asoc->ipv4_addr_legal = 1;
+		} else {
+			/* V4 addresses are NOT legal on the association */
+			asoc->ipv4_addr_legal = 0;
+		}
+	} else {
+		/* Its a V4 socket, no - V6 */
+		asoc->ipv4_addr_legal = 1;
+		asoc->ipv6_addr_legal = 0;
+	}
+
+
+	asoc->my_rwnd = max(m->sctp_socket->so_rcv.sb_hiwat, SCTP_MINIMAL_RWND);
+	asoc->peers_rwnd = m->sctp_socket->so_rcv.sb_hiwat;
+
+	asoc->smallest_mtu = m->sctp_frag_point;
+	asoc->minrto = m->sctp_ep.sctp_minrto;
+	asoc->maxrto = m->sctp_ep.sctp_maxrto;
+
+	LIST_INIT(&asoc->sctp_local_addr_list);
+	TAILQ_INIT(&asoc->nets);
+	TAILQ_INIT(&asoc->pending_reply_queue);
+	asoc->last_asconf_ack_sent = NULL;
+	/* Setup to fill the hb random cache at first HB */
+	asoc->hb_random_idx = 4;
+
+	asoc->sctp_autoclose_ticks = m->sctp_ep.auto_close_time;
+
+	/*
+	 * Now the stream parameters, here we allocate space for all
+	 * streams that we request by default.
+	 */
+	asoc->streamoutcnt = asoc->pre_open_streams =
+	    m->sctp_ep.pre_open_stream_count;
+	MALLOC(asoc->strmout, struct sctp_stream_out *, asoc->streamoutcnt *
+	    sizeof(struct sctp_stream_out), M_PCB, M_NOWAIT);
+	if (asoc->strmout == NULL) {
+		/* big trouble no memory */
+		return (ENOMEM);
+	}
+	for (i = 0; i < asoc->streamoutcnt; i++) {
+		/*
+		 * inbound side must be set to 0xffff,
+		 * also NOTE when we get the INIT-ACK back (for INIT sender)
+		 * we MUST reduce the count (streamoutcnt) but first check
+		 * if we sent to any of the upper streams that were dropped
+		 * (if some were). Those that were dropped must be notified
+		 * to the upper layer as failed to send.
+		 */
+		asoc->strmout[i].next_sequence_sent = 0x0;
+		TAILQ_INIT(&asoc->strmout[i].outqueue);
+		asoc->strmout[i].stream_no = i;
+		asoc->strmout[i].next_spoke.tqe_next = 0;
+		asoc->strmout[i].next_spoke.tqe_prev = 0;
+	}
+	/* Now the mapping array */
+	asoc->mapping_array_size = SCTP_INITIAL_MAPPING_ARRAY;
+#ifdef __NetBSD__
+	MALLOC(asoc->mapping_array, u_int8_t *, SCTP_INITIAL_MAPPING_ARRAY,
+	       M_PCB, M_NOWAIT);
+#else
+	MALLOC(asoc->mapping_array, u_int8_t *, asoc->mapping_array_size,
+	       M_PCB, M_NOWAIT);
+#endif
+	if (asoc->mapping_array == NULL) {
+		FREE(asoc->strmout, M_PCB);
+		return (ENOMEM);
+	}
+	memset(asoc->mapping_array, 0, asoc->mapping_array_size);
+	/* Now the init of the other outqueues */
+	TAILQ_INIT(&asoc->out_wheel);
+	TAILQ_INIT(&asoc->control_send_queue);
+	TAILQ_INIT(&asoc->send_queue);
+	TAILQ_INIT(&asoc->sent_queue);
+	TAILQ_INIT(&asoc->reasmqueue);
+	TAILQ_INIT(&asoc->delivery_queue);
+	asoc->max_inbound_streams = m->sctp_ep.max_open_streams_intome;
+
+	TAILQ_INIT(&asoc->asconf_queue);
+	return (0);
+}
+
+int
+sctp_expand_mapping_array(struct sctp_association *asoc)
+{
+	/* mapping array needs to grow */
+	u_int8_t *new_array;
+	uint16_t new_size;
+
+	new_size = asoc->mapping_array_size + SCTP_MAPPING_ARRAY_INCR;
+#ifdef __NetBSD__
+	MALLOC(new_array, u_int8_t *, asoc->mapping_array_size
+		+ SCTP_MAPPING_ARRAY_INCR, M_PCB, M_NOWAIT);
+#else
+	MALLOC(new_array, u_int8_t *, new_size, M_PCB, M_NOWAIT);
+#endif
+	if (new_array == NULL) {
+		/* can't get more, forget it */
+		printf("No memory for expansion of SCTP mapping array %d\n",
+		       new_size);
+		return (-1);
+	}
+	memset(new_array, 0, new_size);
+	memcpy(new_array, asoc->mapping_array, asoc->mapping_array_size);
+	FREE(asoc->mapping_array, M_PCB);
+	asoc->mapping_array = new_array;
+	asoc->mapping_array_size = new_size;
+	return (0);
+}
+
+static void
+sctp_timeout_handler(void *t)
+{
+	struct sctp_inpcb *inp;
+	struct sctp_tcb *stcb;
+	struct sctp_nets *net;
+	struct sctp_timer *tmr;
+	int did_output, typ;
+#if defined(__APPLE__)
+	boolean_t funnel_state;
+
+	/* get BSD kernel funnel/mutex */
+	funnel_state = thread_funnel_set(network_flock, TRUE);
+#endif
+
+	crit_enter();
+	tmr = (struct sctp_timer *)t;
+	inp = (struct sctp_inpcb *)tmr->ep;
+	stcb = (struct sctp_tcb *)tmr->tcb;
+	net = (struct sctp_nets *)tmr->net;
+	did_output = 1;
+
+
+#ifdef SCTP_AUDITING_ENABLED
+	sctp_audit_log(0xF0, (u_int8_t)tmr->type);
+	sctp_auditing(3, inp, stcb, net);
+#endif
+	sctp_pegs[SCTP_TIMERS_EXP]++;
+
+	if (inp == NULL) {
+		return;
+	}
+
+	SCTP_INP_WLOCK(inp);
+	if (inp->sctp_socket == 0) {
+		crit_exit();
+#if defined(__APPLE__)
+		/* release BSD kernel funnel/mutex */
+		(void) thread_funnel_set(network_flock, FALSE);
+#endif
+		SCTP_INP_WUNLOCK(inp);
+		return;
+	}
+	if (stcb) {
+		if (stcb->asoc.state == 0) {
+			crit_exit();
+#if defined(__APPLE__)
+			/* release BSD kernel funnel/mutex */
+			(void) thread_funnel_set(network_flock, FALSE);
+#endif
+			SCTP_INP_WUNLOCK(inp);
+			return;
+		}
+	}
+#ifdef SCTP_DEBUG
+	if (sctp_debug_on & SCTP_DEBUG_TIMER1) {
+		printf("Timer type %d goes off\n", tmr->type);
+	}
+#endif /* SCTP_DEBUG */
+#ifndef __NetBSD__
+	if (!callout_active(&tmr->timer)) {
+		crit_exit();
+#if defined(__APPLE__)
+		/* release BSD kernel funnel/mutex */
+		(void) thread_funnel_set(network_flock, FALSE);
+#endif
+		SCTP_INP_WUNLOCK(inp);
+		return;
+	}
+#endif
+#if defined(__APPLE__)
+	/* clear the callout pending status here */
+	callout_stop(&tmr->timer);
+#endif
+	if (stcb) {
+		SCTP_TCB_LOCK(stcb);
+	}
+	SCTP_INP_INCR_REF(inp);
+	SCTP_INP_WUNLOCK(inp);
+
+	typ = tmr->type;
+	switch (tmr->type) {
+	case SCTP_TIMER_TYPE_ITERATOR:
+	{
+		struct sctp_iterator *it;
+		it = (struct sctp_iterator *)inp;
+		sctp_iterator_timer(it);
+	}
+	break;
+	/* call the handler for the appropriate timer type */
+	case SCTP_TIMER_TYPE_SEND:
+		sctp_pegs[SCTP_TMIT_TIMER]++;
+		stcb->asoc.num_send_timers_up--;
+		if (stcb->asoc.num_send_timers_up < 0) {
+			stcb->asoc.num_send_timers_up = 0;
+		}
+		if (sctp_t3rxt_timer(inp, stcb, net)) {
+			/* no need to unlock on tcb its gone */
+
+			goto out_decr;
+		}
+#ifdef SCTP_AUDITING_ENABLED
+		sctp_auditing(4, inp, stcb, net);
+#endif
+		sctp_chunk_output(inp, stcb, 1);
+		if ((stcb->asoc.num_send_timers_up == 0) &&
+		    (stcb->asoc.sent_queue_cnt > 0)
+			) {
+			struct sctp_tmit_chunk *chk;
+			/*
+			 * safeguard. If there on some on the sent queue
+			 * somewhere but no timers running something is
+			 * wrong... so we start a timer on the first chunk
+			 * on the send queue on whatever net it is sent to.
+			 */
+			sctp_pegs[SCTP_T3_SAFEGRD]++;
+			chk = TAILQ_FIRST(&stcb->asoc.sent_queue);
+			sctp_timer_start(SCTP_TIMER_TYPE_SEND, inp, stcb,
+					 chk->whoTo);
+		}
+		break;
+	case SCTP_TIMER_TYPE_INIT:
+		if (sctp_t1init_timer(inp, stcb, net)) {
+			/* no need to unlock on tcb its gone */
+			goto out_decr;
+		}
+		/* We do output but not here */
+		did_output = 0;
+		break;
+	case SCTP_TIMER_TYPE_RECV:
+		sctp_pegs[SCTP_RECV_TIMER]++;
+		sctp_send_sack(stcb);
+#ifdef SCTP_AUDITING_ENABLED
+		sctp_auditing(4, inp, stcb, net);
+#endif
+		sctp_chunk_output(inp, stcb, 4);
+		break;
+	case SCTP_TIMER_TYPE_SHUTDOWN:
+		if (sctp_shutdown_timer(inp, stcb, net) ) {
+			/* no need to unlock on tcb its gone */
+			goto out_decr;
+		}
+#ifdef SCTP_AUDITING_ENABLED
+		sctp_auditing(4, inp, stcb, net);
+#endif
+		sctp_chunk_output(inp, stcb, 5);
+		break;
+	case SCTP_TIMER_TYPE_HEARTBEAT:
+		if (sctp_heartbeat_timer(inp, stcb, net)) {
+			/* no need to unlock on tcb its gone */
+			goto out_decr;
+		}
+#ifdef SCTP_AUDITING_ENABLED
+		sctp_auditing(4, inp, stcb, net);
+#endif
+		sctp_chunk_output(inp, stcb, 6);
+		break;
+	case SCTP_TIMER_TYPE_COOKIE:
+		if (sctp_cookie_timer(inp, stcb, net)) {
+			/* no need to unlock on tcb its gone */
+			goto out_decr;
+		}
+#ifdef SCTP_AUDITING_ENABLED
+		sctp_auditing(4, inp, stcb, net);
+#endif
+		sctp_chunk_output(inp, stcb, 1);
+		break;
+	case SCTP_TIMER_TYPE_NEWCOOKIE:
+	{
+		struct timeval tv;
+		int i, secret;
+		SCTP_GETTIME_TIMEVAL(&tv);
+		SCTP_INP_WLOCK(inp);
+		inp->sctp_ep.time_of_secret_change = tv.tv_sec;
+		inp->sctp_ep.last_secret_number =
+			inp->sctp_ep.current_secret_number;
+		inp->sctp_ep.current_secret_number++;
+		if (inp->sctp_ep.current_secret_number >=
+		    SCTP_HOW_MANY_SECRETS) {
+			inp->sctp_ep.current_secret_number = 0;
+		}
+		secret = (int)inp->sctp_ep.current_secret_number;
+		for (i = 0; i < SCTP_NUMBER_OF_SECRETS; i++) {
+			inp->sctp_ep.secret_key[secret][i] =
+				sctp_select_initial_TSN(&inp->sctp_ep);
+		}
+		SCTP_INP_WUNLOCK(inp);
+		sctp_timer_start(SCTP_TIMER_TYPE_NEWCOOKIE, inp, stcb, net);
+	}
+	did_output = 0;
+	break;
+	case SCTP_TIMER_TYPE_PATHMTURAISE:
+		sctp_pathmtu_timer(inp, stcb, net);
+		did_output = 0;
+		break;
+	case SCTP_TIMER_TYPE_SHUTDOWNACK:
+		if (sctp_shutdownack_timer(inp, stcb, net)) {
+			/* no need to unlock on tcb its gone */
+			goto out_decr;
+		}
+#ifdef SCTP_AUDITING_ENABLED
+		sctp_auditing(4, inp, stcb, net);
+#endif
+		sctp_chunk_output(inp, stcb, 7);
+		break;
+	case SCTP_TIMER_TYPE_SHUTDOWNGUARD:
+		sctp_abort_an_association(inp, stcb,
+					  SCTP_SHUTDOWN_GUARD_EXPIRES, NULL);
+		/* no need to unlock on tcb its gone */
+		goto out_decr;
+		break;
+
+	case SCTP_TIMER_TYPE_STRRESET:
+		if (sctp_strreset_timer(inp, stcb, net)) {
+			/* no need to unlock on tcb its gone */
+			goto out_decr;
+		}
+		sctp_chunk_output(inp, stcb, 9);
+		break;
+
+	case SCTP_TIMER_TYPE_ASCONF:
+		if (sctp_asconf_timer(inp, stcb, net)) {
+			/* no need to unlock on tcb its gone */
+			goto out_decr;
+		}
+#ifdef SCTP_AUDITING_ENABLED
+		sctp_auditing(4, inp, stcb, net);
+#endif
+		sctp_chunk_output(inp, stcb, 8);
+		break;
+
+	case SCTP_TIMER_TYPE_AUTOCLOSE:
+		sctp_autoclose_timer(inp, stcb, net);
+		sctp_chunk_output(inp, stcb, 10);
+		did_output = 0;
+		break;
+	case SCTP_TIMER_TYPE_INPKILL:
+		/* special case, take away our
+		 * increment since WE are the killer
+		 */
+		SCTP_INP_WLOCK(inp);
+		SCTP_INP_DECR_REF(inp);
+		SCTP_INP_WUNLOCK(inp);
+		sctp_timer_stop(SCTP_TIMER_TYPE_INPKILL, inp, NULL, NULL);
+		sctp_inpcb_free(inp, 1);
+		goto out_no_decr;
+		break;
+	default:
+#ifdef SCTP_DEBUG
+		if (sctp_debug_on & SCTP_DEBUG_TIMER1) {
+			printf("sctp_timeout_handler:unknown timer %d\n",
+			       tmr->type);
+		}
+#endif /* SCTP_DEBUG */
+		break;
+	};
+#ifdef SCTP_AUDITING_ENABLED
+	sctp_audit_log(0xF1, (u_int8_t)tmr->type);
+	sctp_auditing(5, inp, stcb, net);
+#endif
+	if (did_output) {
+		/*
+		 * Now we need to clean up the control chunk chain if an
+		 * ECNE is on it. It must be marked as UNSENT again so next
+		 * call will continue to send it until such time that we get
+		 * a CWR, to remove it. It is, however, less likely that we
+		 * will find a ecn echo on the chain though.
+		 */
+		sctp_fix_ecn_echo(&stcb->asoc);
+	}
+	if (stcb) {
+		SCTP_TCB_UNLOCK(stcb);
+	}
+ out_decr:
+	SCTP_INP_WLOCK(inp);
+	SCTP_INP_DECR_REF(inp);
+	SCTP_INP_WUNLOCK(inp);
+
+ out_no_decr:
+
+#ifdef SCTP_DEBUG
+	if (sctp_debug_on & SCTP_DEBUG_TIMER1) {
+		printf("Timer now complete (type %d)\n", typ);
+	}
+#endif /* SCTP_DEBUG */
+
+	crit_exit();
+#if defined(__APPLE__)
+	/* release BSD kernel funnel/mutex */
+	(void) thread_funnel_set(network_flock, FALSE);
+#endif
+}
+
+int
+sctp_timer_start(int t_type, struct sctp_inpcb *inp, struct sctp_tcb *stcb,
+    struct sctp_nets *net)
+{
+	int to_ticks;
+	struct sctp_timer *tmr;
+
+	if (inp == NULL)
+		return (EFAULT);
+
+	to_ticks = 0;
+
+	tmr = NULL;
+	switch (t_type) {
+	case SCTP_TIMER_TYPE_ITERATOR:
+	{
+		struct sctp_iterator *it;
+		it = (struct sctp_iterator *)inp;
+		tmr = &it->tmr;
+		to_ticks = SCTP_ITERATOR_TICKS;
+	}
+	break;
+	case SCTP_TIMER_TYPE_SEND:
+		/* Here we use the RTO timer */
+	{
+		int rto_val;
+		if ((stcb == NULL) || (net == NULL)) {
+			return (EFAULT);
+		}
+		tmr = &net->rxt_timer;
+		if (net->RTO == 0) {
+			rto_val = stcb->asoc.initial_rto;
+		} else {
+			rto_val = net->RTO;
+		}
+		to_ticks = MSEC_TO_TICKS(rto_val);
+	}
+	break;
+	case SCTP_TIMER_TYPE_INIT:
+		/*
+		 * Here we use the INIT timer default
+		 * usually about 1 minute.
+		 */
+		if ((stcb == NULL) || (net == NULL)) {
+			return (EFAULT);
+		}
+		tmr = &net->rxt_timer;
+		if (net->RTO == 0) {
+			to_ticks = MSEC_TO_TICKS(stcb->asoc.initial_rto);
+		} else {
+			to_ticks = MSEC_TO_TICKS(net->RTO);
+		}
+		break;
+	case SCTP_TIMER_TYPE_RECV:
+		/*
+		 * Here we use the Delayed-Ack timer value from the inp
+		 * ususually about 200ms.
+		 */
+		if (stcb == NULL) {
+			return (EFAULT);
+		}
+		tmr = &stcb->asoc.dack_timer;
+		to_ticks = inp->sctp_ep.sctp_timeoutticks[SCTP_TIMER_RECV];
+		break;
+	case SCTP_TIMER_TYPE_SHUTDOWN:
+		/* Here we use the RTO of the destination. */
+		if ((stcb == NULL) || (net == NULL)) {
+			return (EFAULT);
+		}
+
+		if (net->RTO == 0) {
+			to_ticks = MSEC_TO_TICKS(stcb->asoc.initial_rto);
+		} else {
+			to_ticks = MSEC_TO_TICKS(net->RTO);
+		}
+		tmr = &net->rxt_timer;
+		break;
+	case SCTP_TIMER_TYPE_HEARTBEAT:
+		/*
+		 * the net is used here so that we can add in the RTO.
+		 * Even though we use a different timer. We also add the
+		 * HB timer PLUS a random jitter.
+		 */
+		if (stcb == NULL) {
+			return (EFAULT);
+		}
+		{
+			uint32_t rndval;
+			uint8_t this_random;
+			int cnt_of_unconf=0;
+			struct sctp_nets *lnet;
+
+			TAILQ_FOREACH(lnet, &stcb->asoc.nets, sctp_next) {
+				if (lnet->dest_state & SCTP_ADDR_UNCONFIRMED) {
+					cnt_of_unconf++;
+				}
+			}
+#ifdef SCTP_DEBUG
+			if (sctp_debug_on & SCTP_DEBUG_TIMER1) {
+				printf("HB timer to start unconfirmed:%d hb_delay:%d\n",
+				       cnt_of_unconf, stcb->asoc.heart_beat_delay);
+			}
+#endif
+			if (stcb->asoc.hb_random_idx > 3) {
+				rndval = sctp_select_initial_TSN(&inp->sctp_ep);
+				memcpy(stcb->asoc.hb_random_values, &rndval,
+				       sizeof(stcb->asoc.hb_random_values));
+				this_random = stcb->asoc.hb_random_values[0];
+				stcb->asoc.hb_random_idx = 0;
+				stcb->asoc.hb_ect_randombit = 0;
+			} else {
+				this_random = stcb->asoc.hb_random_values[stcb->asoc.hb_random_idx];
+				stcb->asoc.hb_random_idx++;
+				stcb->asoc.hb_ect_randombit = 0;
+			}
+			/*
+			 * this_random will be 0 - 256 ms
+			 * RTO is in ms.
+			 */
+			if ((stcb->asoc.heart_beat_delay == 0) &&
+			    (cnt_of_unconf == 0)) {
+				/* no HB on this inp after confirmations */
+				return (0);
+			}
+			if (net) {
+				struct sctp_nets *lnet;
+				int delay;
+				delay = stcb->asoc.heart_beat_delay;
+				TAILQ_FOREACH(lnet, &stcb->asoc.nets, sctp_next) {
+					if ((lnet->dest_state & SCTP_ADDR_UNCONFIRMED) &&
+					    ((lnet->dest_state & SCTP_ADDR_OUT_OF_SCOPE) == 0) &&
+					    (lnet->dest_state & SCTP_ADDR_REACHABLE)) {
+					    delay = 0;
+					}
+				}
+				if (net->RTO == 0) {
+					/* Never been checked */
+					to_ticks = this_random + stcb->asoc.initial_rto + delay;
+				} else {
+					/* set rto_val to the ms */
+					to_ticks = delay + net->RTO + this_random;
+				}
+			} else {
+				if (cnt_of_unconf) {
+					to_ticks = this_random + stcb->asoc.initial_rto;
+				} else {
+					to_ticks = stcb->asoc.heart_beat_delay + this_random + stcb->asoc.initial_rto;
+				}
+			}
+			/*
+			 * Now we must convert the to_ticks that are now in
+			 * ms to ticks.
+			 */
+			to_ticks *= hz;
+			to_ticks /= 1000;
+#ifdef SCTP_DEBUG
+			if (sctp_debug_on & SCTP_DEBUG_TIMER1) {
+				printf("Timer to expire in %d ticks\n", to_ticks);
+			}
+#endif
+			tmr = &stcb->asoc.hb_timer;
+		}
+		break;
+	case SCTP_TIMER_TYPE_COOKIE:
+		/*
+		 * Here we can use the RTO timer from the network since
+		 * one RTT was compelete. If a retran happened then we will
+		 * be using the RTO initial value.
+		 */
+		if ((stcb == NULL) || (net == NULL)) {
+			return (EFAULT);
+		}
+		if (net->RTO == 0) {
+			to_ticks = MSEC_TO_TICKS(stcb->asoc.initial_rto);
+		} else {
+			to_ticks = MSEC_TO_TICKS(net->RTO);
+		}
+		tmr = &net->rxt_timer;
+		break;
+	case SCTP_TIMER_TYPE_NEWCOOKIE:
+		/*
+		 * nothing needed but the endpoint here
+		 * ususually about 60 minutes.
+		 */
+		tmr = &inp->sctp_ep.signature_change;
+		to_ticks = inp->sctp_ep.sctp_timeoutticks[SCTP_TIMER_SIGNATURE];
+		break;
+	case SCTP_TIMER_TYPE_INPKILL:
+		/*
+		 * The inp is setup to die. We re-use the
+		 * signature_chage timer since that has
+		 * stopped and we are in the GONE state.
+		 */
+		tmr = &inp->sctp_ep.signature_change;
+		to_ticks = (SCTP_INP_KILL_TIMEOUT * hz) / 1000;
+		break;
+	case SCTP_TIMER_TYPE_PATHMTURAISE:
+		/*
+		 * Here we use the value found in the EP for PMTU
+		 * ususually about 10 minutes.
+		 */
+		if (stcb == NULL) {
+			return (EFAULT);
+		}
+		if (net == NULL) {
+			return (EFAULT);
+		}
+		to_ticks = inp->sctp_ep.sctp_timeoutticks[SCTP_TIMER_PMTU];
+		tmr = &net->pmtu_timer;
+		break;
+	case SCTP_TIMER_TYPE_SHUTDOWNACK:
+		/* Here we use the RTO of the destination */
+		if ((stcb == NULL) || (net == NULL)) {
+			return (EFAULT);
+		}
+		if (net->RTO == 0) {
+			to_ticks = MSEC_TO_TICKS(stcb->asoc.initial_rto);
+		} else {
+			to_ticks = MSEC_TO_TICKS(net->RTO);
+		}
+		tmr = &net->rxt_timer;
+		break;
+	case SCTP_TIMER_TYPE_SHUTDOWNGUARD:
+		/*
+		 * Here we use the endpoints shutdown guard timer
+		 * usually about 3 minutes.
+		 */
+		if (stcb == NULL) {
+			return (EFAULT);
+		}
+		to_ticks = inp->sctp_ep.sctp_timeoutticks[SCTP_TIMER_MAXSHUTDOWN];
+		tmr = &stcb->asoc.shut_guard_timer;
+		break;
+	case SCTP_TIMER_TYPE_STRRESET:
+		/*
+		 * Here the timer comes from the inp
+		 * but its value is from the RTO.
+		 */
+		if ((stcb == NULL) || (net == NULL)) {
+			return (EFAULT);
+		}
+		if (net->RTO == 0) {
+			to_ticks = MSEC_TO_TICKS(stcb->asoc.initial_rto);
+		} else {
+			to_ticks = MSEC_TO_TICKS(net->RTO);
+		}
+		tmr = &stcb->asoc.strreset_timer;
+		break;
+
+	case SCTP_TIMER_TYPE_ASCONF:
+		/*
+		 * Here the timer comes from the inp
+		 * but its value is from the RTO.
+		 */
+		if ((stcb == NULL) || (net == NULL)) {
+			return (EFAULT);
+		}
+		if (net->RTO == 0) {
+			to_ticks = MSEC_TO_TICKS(stcb->asoc.initial_rto);
+		} else {
+			to_ticks = MSEC_TO_TICKS(net->RTO);
+		}
+		tmr = &stcb->asoc.asconf_timer;
+		break;
+	case SCTP_TIMER_TYPE_AUTOCLOSE:
+		if (stcb == NULL) {
+			return (EFAULT);
+		}
+		if (stcb->asoc.sctp_autoclose_ticks == 0) {
+			/* Really an error since stcb is NOT set to autoclose */
+			return (0);
+		}
+		to_ticks = stcb->asoc.sctp_autoclose_ticks;
+		tmr = &stcb->asoc.autoclose_timer;
+		break;
+	default:
+#ifdef SCTP_DEBUG
+		if (sctp_debug_on & SCTP_DEBUG_TIMER1) {
+			printf("sctp_timer_start:Unknown timer type %d\n",
+			       t_type);
+		}
+#endif /* SCTP_DEBUG */
+		return (EFAULT);
+		break;
+	};
+	if ((to_ticks <= 0) || (tmr == NULL)) {
+#ifdef SCTP_DEBUG
+		if (sctp_debug_on & SCTP_DEBUG_TIMER1) {
+			printf("sctp_timer_start:%d:software error to_ticks:%d tmr:%p not set ??\n",
+			       t_type, to_ticks, tmr);
+		}
+#endif /* SCTP_DEBUG */
+		return (EFAULT);
+	}
+	if (callout_pending(&tmr->timer)) {
+		/*
+		 * we do NOT allow you to have it already running.
+		 * if it is we leave the current one up unchanged
+		 */
+		return (EALREADY);
+	}
+	/* At this point we can proceed */
+	if (t_type == SCTP_TIMER_TYPE_SEND) {
+		stcb->asoc.num_send_timers_up++;
+	}
+	tmr->type = t_type;
+	tmr->ep = (void *)inp;
+	tmr->tcb = (void *)stcb;
+	tmr->net = (void *)net;
+	callout_reset(&tmr->timer, to_ticks, sctp_timeout_handler, tmr);
+	return (0);
+}
+
+int
+sctp_timer_stop(int t_type, struct sctp_inpcb *inp, struct sctp_tcb *stcb,
+		struct sctp_nets *net)
+{
+	struct sctp_timer *tmr;
+
+	if (inp == NULL)
+		return (EFAULT);
+
+	tmr = NULL;
+	switch (t_type) {
+	case SCTP_TIMER_TYPE_ITERATOR:
+	{
+		struct sctp_iterator *it;
+		it = (struct sctp_iterator *)inp;
+		tmr = &it->tmr;
+	}
+	break;
+	case SCTP_TIMER_TYPE_SEND:
+		if ((stcb == NULL) || (net == NULL)) {
+			return (EFAULT);
+		}
+		tmr = &net->rxt_timer;
+		break;
+	case SCTP_TIMER_TYPE_INIT:
+		if ((stcb == NULL) || (net == NULL)) {
+			return (EFAULT);
+		}
+		tmr = &net->rxt_timer;
+		break;
+	case SCTP_TIMER_TYPE_RECV:
+		if (stcb == NULL) {
+			return (EFAULT);
+		}
+		tmr = &stcb->asoc.dack_timer;
+		break;
+	case SCTP_TIMER_TYPE_SHUTDOWN:
+		if ((stcb == NULL) || (net == NULL)) {
+			return (EFAULT);
+		}
+		tmr = &net->rxt_timer;
+		break;
+	case SCTP_TIMER_TYPE_HEARTBEAT:
+		if (stcb == NULL) {
+			return (EFAULT);
+		}
+		tmr = &stcb->asoc.hb_timer;
+		break;
+	case SCTP_TIMER_TYPE_COOKIE:
+		if ((stcb == NULL) || (net == NULL)) {
+			return (EFAULT);
+		}
+		tmr = &net->rxt_timer;
+		break;
+	case SCTP_TIMER_TYPE_NEWCOOKIE:
+		/* nothing needed but the endpoint here */
+		tmr = &inp->sctp_ep.signature_change;
+		/* We re-use the newcookie timer for
+		 * the INP kill timer. We must assure
+		 * that we do not kill it by accident.
+		 */
+		break;
+	case SCTP_TIMER_TYPE_INPKILL:
+		/*
+		 * The inp is setup to die. We re-use the
+		 * signature_chage timer since that has
+		 * stopped and we are in the GONE state.
+		 */
+		tmr = &inp->sctp_ep.signature_change;
+		break;
+	case SCTP_TIMER_TYPE_PATHMTURAISE:
+		if (stcb == NULL) {
+			return (EFAULT);
+		}
+		if (net == NULL) {
+			return (EFAULT);
+		}
+		tmr = &net->pmtu_timer;
+		break;
+	case SCTP_TIMER_TYPE_SHUTDOWNACK:
+		if ((stcb == NULL) || (net == NULL)) {
+			return (EFAULT);
+		}
+		tmr = &net->rxt_timer;
+		break;
+	case SCTP_TIMER_TYPE_SHUTDOWNGUARD:
+		if (stcb == NULL) {
+			return (EFAULT);
+		}
+		tmr = &stcb->asoc.shut_guard_timer;
+		break;
+	case SCTP_TIMER_TYPE_STRRESET:
+		if (stcb == NULL) {
+			return (EFAULT);
+		}
+		tmr = &stcb->asoc.strreset_timer;
+		break;
+	case SCTP_TIMER_TYPE_ASCONF:
+		if (stcb == NULL) {
+			return (EFAULT);
+		}
+		tmr = &stcb->asoc.asconf_timer;
+		break;
+	case SCTP_TIMER_TYPE_AUTOCLOSE:
+		if (stcb == NULL) {
+			return (EFAULT);
+		}
+		tmr = &stcb->asoc.autoclose_timer;
+		break;
+	default:
+#ifdef SCTP_DEBUG
+		if (sctp_debug_on & SCTP_DEBUG_TIMER1) {
+			printf("sctp_timer_stop:Unknown timer type %d\n",
+			       t_type);
+		}
+#endif /* SCTP_DEBUG */
+		break;
+	};
+	if (tmr == NULL)
+		return (EFAULT);
+
+	if ((tmr->type != t_type) && tmr->type) {
+		/*
+		 * Ok we have a timer that is under joint use. Cookie timer
+		 * per chance with the SEND timer. We therefore are NOT
+		 * running the timer that the caller wants stopped.  So just
+		 * return.
+		 */
+		return (0);
+	}
+	if (t_type == SCTP_TIMER_TYPE_SEND) {
+		stcb->asoc.num_send_timers_up--;
+		if (stcb->asoc.num_send_timers_up < 0) {
+			stcb->asoc.num_send_timers_up = 0;
+		}
+	}
+	callout_stop(&tmr->timer);
+	return (0);
+}
+
+#ifdef SCTP_USE_ADLER32
+static uint32_t
+update_adler32(uint32_t adler, uint8_t *buf, int32_t len)
+{
+	u_int32_t s1 = adler & 0xffff;
+	u_int32_t s2 = (adler >> 16) & 0xffff;
+	int n;
+
+	for (n = 0; n < len; n++, buf++) {
+		/* s1 = (s1 + buf[n]) % BASE */
+		/* first we add */
+		s1 = (s1 + *buf);
+		/*
+		 * now if we need to, we do a mod by subtracting. It seems
+		 * a bit faster since I really will only ever do one subtract
+		 * at the MOST, since buf[n] is a max of 255.
+		 */
+		if (s1 >= SCTP_ADLER32_BASE) {
+			s1 -= SCTP_ADLER32_BASE;
+		}
+		/* s2 = (s2 + s1) % BASE */
+		/* first we add */
+		s2 = (s2 + s1);
+		/*
+		 * again, it is more efficent (it seems) to subtract since
+		 * the most s2 will ever be is (BASE-1 + BASE-1) in the worse
+		 * case. This would then be (2 * BASE) - 2, which will still
+		 * only do one subtract. On Intel this is much better to do
+		 * this way and avoid the divide. Have not -pg'd on sparc.
+		 */
+		if (s2 >= SCTP_ADLER32_BASE) {
+			s2 -= SCTP_ADLER32_BASE;
+		}
+	}
+	/* Return the adler32 of the bytes buf[0..len-1] */
+	return ((s2 << 16) + s1);
+}
+
+#endif
+
+
+u_int32_t
+sctp_calculate_len(struct mbuf *m)
+{
+	u_int32_t tlen=0;
+	struct mbuf *at;
+	at = m;
+	while (at) {
+		tlen += at->m_len;
+		at = at->m_next;
+	}
+	return (tlen);
+}
+
+#if defined(SCTP_WITH_NO_CSUM)
+
+uint32_t
+sctp_calculate_sum(struct mbuf *m, int32_t *pktlen, uint32_t offset)
+{
+	/*
+	 * given a mbuf chain with a packetheader offset by 'offset'
+	 * pointing at a sctphdr (with csum set to 0) go through
+	 * the chain of m_next's and calculate the SCTP checksum.
+	 * This is currently Adler32 but will change to CRC32x
+	 * soon. Also has a side bonus calculate the total length
+	 * of the mbuf chain.
+	 * Note: if offset is greater than the total mbuf length,
+	 * checksum=1, pktlen=0 is returned (ie. no real error code)
+	 */
+	if (pktlen == NULL)
+		return (0);
+	*pktlen = sctp_calculate_len(m);
+	return (0);
+}
+
+#elif defined(SCTP_USE_INCHKSUM)
+
+#include <machine/in_cksum.h>
+
+uint32_t
+sctp_calculate_sum(struct mbuf *m, int32_t *pktlen, uint32_t offset)
+{
+	/*
+	 * given a mbuf chain with a packetheader offset by 'offset'
+	 * pointing at a sctphdr (with csum set to 0) go through
+	 * the chain of m_next's and calculate the SCTP checksum.
+	 * This is currently Adler32 but will change to CRC32x
+	 * soon. Also has a side bonus calculate the total length
+	 * of the mbuf chain.
+	 * Note: if offset is greater than the total mbuf length,
+	 * checksum=1, pktlen=0 is returned (ie. no real error code)
+	 */
+	int32_t tlen=0;
+	struct mbuf *at;
+	uint32_t the_sum, retsum;
+
+	at = m;
+	while (at) {
+		tlen += at->m_len;
+		at = at->m_next;
+	}
+	the_sum = (uint32_t)(in_cksum_skip(m, tlen, offset));
+	if (pktlen != NULL)
+		*pktlen = (tlen-offset);
+	retsum = htons(the_sum);
+	return (the_sum);
+}
+
+#else
+
+uint32_t
+sctp_calculate_sum(struct mbuf *m, int32_t *pktlen, uint32_t offset)
+{
+	/*
+	 * given a mbuf chain with a packetheader offset by 'offset'
+	 * pointing at a sctphdr (with csum set to 0) go through
+	 * the chain of m_next's and calculate the SCTP checksum.
+	 * This is currently Adler32 but will change to CRC32x
+	 * soon. Also has a side bonus calculate the total length
+	 * of the mbuf chain.
+	 * Note: if offset is greater than the total mbuf length,
+	 * checksum=1, pktlen=0 is returned (ie. no real error code)
+	 */
+	int32_t tlen=0;
+#ifdef SCTP_USE_ADLER32
+	uint32_t base = 1L;
+#else
+	uint32_t base = 0xffffffff;
+#endif /* SCTP_USE_ADLER32 */
+	struct mbuf *at;
+	at = m;
+	/* find the correct mbuf and offset into mbuf */
+	while ((at != NULL) && (offset > (uint32_t)at->m_len)) {
+		offset -= at->m_len;	/* update remaining offset left */
+		at = at->m_next;
+	}
+
+	while (at != NULL) {
+#ifdef SCTP_USE_ADLER32
+		base = update_adler32(base, at->m_data + offset,
+		    at->m_len - offset);
+#else
+		base = update_crc32(base, at->m_data + offset,
+		    at->m_len - offset);
+#endif /* SCTP_USE_ADLER32 */
+		tlen += at->m_len - offset;
+		/* we only offset once into the first mbuf */
+		if (offset) {
+			offset = 0;
+		}
+		at = at->m_next;
+	}
+	if (pktlen != NULL) {
+		*pktlen = tlen;
+	}
+#ifdef SCTP_USE_ADLER32
+	/* Adler32 */
+	base = htonl(base);
+#else
+	/* CRC-32c */
+	base = sctp_csum_finalize(base);
+#endif
+	return (base);
+}
+
+
+#endif
+
+void
+sctp_mtu_size_reset(struct sctp_inpcb *inp,
+		    struct sctp_association *asoc, u_long mtu)
+{
+	/*
+	 * Reset the P-MTU size on this association, this involves changing
+	 * the asoc MTU, going through ANY chunk+overhead larger than mtu
+	 * to allow the DF flag to be cleared.
+	 */
+	struct sctp_tmit_chunk *chk;
+	struct sctp_stream_out *strm;
+	unsigned int eff_mtu, ovh;
+	asoc->smallest_mtu = mtu;
+	if (inp->sctp_flags & SCTP_PCB_FLAGS_BOUND_V6) {
+		ovh = SCTP_MIN_OVERHEAD;
+	} else {
+		ovh = SCTP_MIN_V4_OVERHEAD;
+	}
+	eff_mtu = mtu - ovh;
+	/* Now mark any chunks that need to let IP fragment */
+	TAILQ_FOREACH(strm, &asoc->out_wheel, next_spoke) {
+		TAILQ_FOREACH(chk, &strm->outqueue, sctp_next) {
+			if (chk->send_size > eff_mtu) {
+				chk->flags &= SCTP_DONT_FRAGMENT;
+				chk->flags |= CHUNK_FLAGS_FRAGMENT_OK;
+			}
+		}
+	}
+	TAILQ_FOREACH(chk, &asoc->send_queue, sctp_next) {
+		if (chk->send_size > eff_mtu) {
+			chk->flags &= SCTP_DONT_FRAGMENT;
+			chk->flags |= CHUNK_FLAGS_FRAGMENT_OK;
+		}
+	}
+	TAILQ_FOREACH(chk, &asoc->sent_queue, sctp_next) {
+		if (chk->send_size > eff_mtu) {
+			chk->flags &= SCTP_DONT_FRAGMENT;
+			chk->flags |= CHUNK_FLAGS_FRAGMENT_OK;
+		}
+	}
+}
+
+
+/*
+ * given an association and starting time of the current RTT period
+ * return RTO in number of usecs
+ * net should point to the current network
+ */
+u_int32_t
+sctp_calculate_rto(struct sctp_tcb *stcb,
+		   struct sctp_association *asoc,
+		   struct sctp_nets *net,
+		   struct timeval *old)
+{
+	/*
+	 * given an association and the starting time of the current RTT
+	 * period (in value1/value2) return RTO in number of usecs.
+	 */
+	int calc_time = 0;
+	int o_calctime;
+	unsigned int new_rto = 0;
+	int first_measure = 0;
+	struct timeval now;
+
+	/************************/
+	/* 1. calculate new RTT */
+	/************************/
+	/* get the current time */
+	SCTP_GETTIME_TIMEVAL(&now);
+	/* compute the RTT value */
+	if ((u_long)now.tv_sec > (u_long)old->tv_sec) {
+		calc_time = ((u_long)now.tv_sec - (u_long)old->tv_sec) * 1000;
+		if ((u_long)now.tv_usec > (u_long)old->tv_usec) {
+			calc_time += (((u_long)now.tv_usec -
+				       (u_long)old->tv_usec)/1000);
+		} else if ((u_long)now.tv_usec < (u_long)old->tv_usec) {
+			/* Borrow 1,000ms from current calculation */
+			calc_time -= 1000;
+			/* Add in the slop over */
+			calc_time += ((int)now.tv_usec/1000);
+			/* Add in the pre-second ms's */
+			calc_time += (((int)1000000 - (int)old->tv_usec)/1000);
+		}
+	} else if ((u_long)now.tv_sec == (u_long)old->tv_sec) {
+		if ((u_long)now.tv_usec > (u_long)old->tv_usec) {
+			calc_time = ((u_long)now.tv_usec -
+				     (u_long)old->tv_usec)/1000;
+		} else if ((u_long)now.tv_usec < (u_long)old->tv_usec) {
+			/* impossible .. garbage in nothing out */
+			return (((net->lastsa >> 2) + net->lastsv) >> 1);
+		} else {
+			/* impossible .. garbage in nothing out */
+			return (((net->lastsa >> 2) + net->lastsv) >> 1);
+		}
+	} else {
+		/* Clock wrapped? */
+		return (((net->lastsa >> 2) + net->lastsv) >> 1);
+	}
+	/***************************/
+	/* 2. update RTTVAR & SRTT */
+	/***************************/
+#if 0
+	/*	if (net->lastsv || net->lastsa) {*/
+	/* per Section 5.3.1 C3 in SCTP */
+	/*		net->lastsv = (int) 	*//* RTTVAR */
+	/*			(((double)(1.0 - 0.25) * (double)net->lastsv) +
+				(double)(0.25 * (double)abs(net->lastsa - calc_time)));
+				net->lastsa = (int) */	/* SRTT */
+	/*(((double)(1.0 - 0.125) * (double)net->lastsa) +
+	  (double)(0.125 * (double)calc_time));
+	  } else {
+	*//* the first RTT calculation, per C2 Section 5.3.1 */
+	/*		net->lastsa = calc_time;	*//* SRTT */
+	/*		net->lastsv = calc_time / 2;	*//* RTTVAR */
+	/*	}*/
+	/* if RTTVAR goes to 0 you set to clock grainularity */
+	/*	if (net->lastsv == 0) {
+		net->lastsv = SCTP_CLOCK_GRANULARITY;
+		}
+		new_rto = net->lastsa + 4 * net->lastsv;
+	*/
+#endif
+	o_calctime = calc_time;
+	/* this is Van Jacobson's integer version */
+	if (net->RTO) {
+		calc_time -= (net->lastsa >> 3);
+		net->lastsa += calc_time;
+		if (calc_time < 0) {
+			calc_time = -calc_time;
+		}
+		calc_time -= (net->lastsv >> 2);
+		net->lastsv += calc_time;
+		if (net->lastsv == 0) {
+			net->lastsv = SCTP_CLOCK_GRANULARITY;
+		}
+	} else {
+		/* First RTO measurment */
+		net->lastsa = calc_time;
+		net->lastsv = calc_time >> 1;
+		first_measure = 1;
+	}
+	new_rto = ((net->lastsa >> 2) + net->lastsv) >> 1;
+	if ((new_rto > SCTP_SAT_NETWORK_MIN) &&
+	    (stcb->asoc.sat_network_lockout == 0)) {
+		stcb->asoc.sat_network = 1;
+	} else 	if ((!first_measure) && stcb->asoc.sat_network) {
+		stcb->asoc.sat_network = 0;
+		stcb->asoc.sat_network_lockout = 1;
+	}
+	/* bound it, per C6/C7 in Section 5.3.1 */
+	if (new_rto < stcb->asoc.minrto) {
+		new_rto = stcb->asoc.minrto;
+	}
+	if (new_rto > stcb->asoc.maxrto) {
+		new_rto = stcb->asoc.maxrto;
+	}
+	/* we are now returning the RTT Smoothed */
+	return ((u_int32_t)new_rto);
+}
+
+
+/*
+ * return a pointer to a contiguous piece of data from the given
+ * mbuf chain starting at 'off' for 'len' bytes.  If the desired
+ * piece spans more than one mbuf, a copy is made at 'ptr'.
+ * caller must ensure that the buffer size is >= 'len'
+ * returns NULL if there there isn't 'len' bytes in the chain.
+ */
+caddr_t
+sctp_m_getptr(struct mbuf *m, int off, int len, u_int8_t *in_ptr)
+{
+	uint32_t count;
+	uint8_t *ptr;
+	ptr = in_ptr;
+	if ((off < 0) || (len <= 0))
+		return (NULL);
+
+	/* find the desired start location */
+	while ((m != NULL) && (off > 0)) {
+		if (off < m->m_len)
+			break;
+		off -= m->m_len;
+		m = m->m_next;
+	}
+	if (m == NULL)
+		return (NULL);
+
+	/* is the current mbuf large enough (eg. contiguous)? */
+	if ((m->m_len - off) >= len) {
+		return (mtod(m, caddr_t) + off);
+	} else {
+		/* else, it spans more than one mbuf, so save a temp copy... */
+		while ((m != NULL) && (len > 0)) {
+			count = min(m->m_len - off, len);
+			bcopy(mtod(m, caddr_t) + off, ptr, count);
+			len -= count;
+			ptr += count;
+			off = 0;
+			m = m->m_next;
+		}
+		if ((m == NULL) && (len > 0))
+			return (NULL);
+		else
+			return ((caddr_t)in_ptr);
+	}
+}
+
+
+struct sctp_paramhdr *
+sctp_get_next_param(struct mbuf *m,
+		    int offset,
+		    struct sctp_paramhdr *pull,
+		    int pull_limit)
+{
+	/* This just provides a typed signature to Peter's Pull routine */
+	return ((struct sctp_paramhdr *)sctp_m_getptr(m, offset, pull_limit,
+    	    (u_int8_t *)pull));
+}
+
+
+int
+sctp_add_pad_tombuf(struct mbuf *m, int padlen)
+{
+	/*
+	 * add padlen bytes of 0 filled padding to the end of the mbuf.
+	 * If padlen is > 3 this routine will fail.
+	 */
+	u_int8_t *dp;
+	int i;
+	if (padlen > 3) {
+		return (ENOBUFS);
+	}
+	if (M_TRAILINGSPACE(m)) {
+		/*
+		 * The easy way.
+		 * We hope the majority of the time we hit here :)
+		 */
+		dp = (u_int8_t *)(mtod(m, caddr_t) + m->m_len);
+		m->m_len += padlen;
+	} else {
+		/* Hard way we must grow the mbuf */
+		struct mbuf *tmp;
+		MGET(tmp, MB_DONTWAIT, MT_DATA);
+		if (tmp == NULL) {
+			/* Out of space GAK! we are in big trouble. */
+			return (ENOSPC);
+		}
+		/* setup and insert in middle */
+		tmp->m_next = m->m_next;
+		tmp->m_len = padlen;
+		m->m_next = tmp;
+		dp = mtod(tmp, u_int8_t *);
+	}
+	/* zero out the pad */
+	for (i=  0; i < padlen; i++) {
+		*dp = 0;
+		dp++;
+	}
+	return (0);
+}
+
+int
+sctp_pad_lastmbuf(struct mbuf *m, int padval)
+{
+	/* find the last mbuf in chain and pad it */
+	struct mbuf *m_at;
+	m_at = m;
+	while (m_at) {
+		if (m_at->m_next == NULL) {
+			return (sctp_add_pad_tombuf(m_at, padval));
+		}
+		m_at = m_at->m_next;
+	}
+	return (EFAULT);
+}
+
+static void
+sctp_notify_assoc_change(u_int32_t event, struct sctp_tcb *stcb,
+    u_int32_t error)
+{
+	struct mbuf *m_notify;
+	struct sctp_assoc_change *sac;
+	struct sockaddr *to;
+	struct sockaddr_in6 sin6, lsa6;
+
+	/*
+	 * First if we are are going down dump everything we
+	 * can to the socket rcv queue.
+	 */
+	if ((event == SCTP_SHUTDOWN_COMP) || (event == SCTP_COMM_LOST)) {
+		sctp_deliver_data(stcb, &stcb->asoc, NULL, 0);
+	}
+
+	/*
+	 * For TCP model AND UDP connected sockets we will send
+	 * an error up when an ABORT comes in.
+	 */
+	if (((stcb->sctp_ep->sctp_flags & SCTP_PCB_FLAGS_TCPTYPE) ||
+	     (stcb->sctp_ep->sctp_flags & SCTP_PCB_FLAGS_IN_TCPPOOL)) &&
+	    (event == SCTP_COMM_LOST)) {
+		stcb->sctp_socket->so_error = ECONNRESET;
+		/* Wake ANY sleepers */
+		sowwakeup(stcb->sctp_socket);
+		sorwakeup(stcb->sctp_socket);
+	}
+#if 0
+	if ((event == SCTP_COMM_UP) &&
+	    (stcb->sctp_ep->sctp_flags & SCTP_PCB_FLAGS_TCPTYPE) &&
+ 	    (stcb->sctp_ep->sctp_flags & SCTP_PCB_FLAGS_CONNECTED)) {
+		 soisconnected(stcb->sctp_socket);
+	}
+#endif
+	if (!(stcb->sctp_ep->sctp_flags & SCTP_PCB_FLAGS_RECVASSOCEVNT)) {
+		/* event not enabled */
+		return;
+	}
+	MGETHDR(m_notify, MB_DONTWAIT, MT_DATA);
+	if (m_notify == NULL)
+		/* no space left */
+		return;
+	m_notify->m_len = 0;
+
+	sac = mtod(m_notify, struct sctp_assoc_change *);
+	sac->sac_type = SCTP_ASSOC_CHANGE;
+	sac->sac_flags = 0;
+	sac->sac_length = sizeof(struct sctp_assoc_change);
+	sac->sac_state = event;
+	sac->sac_error = error;
+	/* XXX verify these stream counts */
+	sac->sac_outbound_streams = stcb->asoc.streamoutcnt;
+	sac->sac_inbound_streams = stcb->asoc.streamincnt;
+	sac->sac_assoc_id = sctp_get_associd(stcb);
+
+	m_notify->m_flags |= M_EOR | M_NOTIFICATION;
+	m_notify->m_pkthdr.len = sizeof(struct sctp_assoc_change);
+	m_notify->m_pkthdr.rcvif = 0;
+	m_notify->m_len = sizeof(struct sctp_assoc_change);
+	m_notify->m_next = NULL;
+
+	/* append to socket */
+	to = (struct sockaddr *)&stcb->asoc.primary_destination->ro._l_addr;
+	if ((stcb->sctp_ep->sctp_flags & SCTP_PCB_FLAGS_NEEDS_MAPPED_V4) &&
+	    to->sa_family == AF_INET) {
+		struct sockaddr_in *sin;
+
+		sin = (struct sockaddr_in *)to;
+		bzero(&sin6, sizeof(sin6));
+		sin6.sin6_family = AF_INET6;
+		sin6.sin6_len = sizeof(struct sockaddr_in6);
+		sin6.sin6_addr.s6_addr16[2] = 0xffff;
+		bcopy(&sin->sin_addr, &sin6.sin6_addr.s6_addr16[3],
+		    sizeof(sin6.sin6_addr.s6_addr16[3]));
+		sin6.sin6_port = sin->sin_port;
+		to = (struct sockaddr *)&sin6;
+	}
+	/* check and strip embedded scope junk */
+	to = (struct sockaddr *)sctp_recover_scope((struct sockaddr_in6 *)to,
+						   &lsa6);
+	/*
+	 * We need to always notify comm changes.
+	 * if (sctp_sbspace(&stcb->sctp_socket->so_rcv) < m_notify->m_len) {
+	 * 	sctp_m_freem(m_notify);
+	 *	return;
+	 * }
+	*/
+	SCTP_TCB_UNLOCK(stcb);
+	SCTP_INP_WLOCK(stcb->sctp_ep);
+	SCTP_TCB_LOCK(stcb);
+	if (!sbappendaddr_nocheck(&stcb->sctp_socket->so_rcv,
+	    to, m_notify, NULL, stcb->asoc.my_vtag, stcb->sctp_ep)) {
+		/* not enough room */
+		sctp_m_freem(m_notify);
+		SCTP_INP_WUNLOCK(stcb->sctp_ep);
+		return;
+	}
+	if (((stcb->sctp_ep->sctp_flags & SCTP_PCB_FLAGS_IN_TCPPOOL) == 0) &&
+	   ((stcb->sctp_ep->sctp_flags & SCTP_PCB_FLAGS_CONNECTED) == 0)){
+		if (sctp_add_to_socket_q(stcb->sctp_ep, stcb)) {
+			stcb->asoc.my_rwnd_control_len += sizeof(struct mbuf);
+		}
+	} else {
+		stcb->asoc.my_rwnd_control_len += sizeof(struct mbuf);
+	}
+	SCTP_INP_WUNLOCK(stcb->sctp_ep);
+	/* Wake up any sleeper */
+	sctp_sorwakeup(stcb->sctp_ep, stcb->sctp_socket);
+	sctp_sowwakeup(stcb->sctp_ep, stcb->sctp_socket);
+}
+
+static void
+sctp_notify_peer_addr_change(struct sctp_tcb *stcb, uint32_t state,
+    struct sockaddr *sa, uint32_t error)
+{
+	struct mbuf *m_notify;
+	struct sctp_paddr_change *spc;
+	struct sockaddr *to;
+	struct sockaddr_in6 sin6, lsa6;
+
+	if (!(stcb->sctp_ep->sctp_flags & SCTP_PCB_FLAGS_RECVPADDREVNT))
+		/* event not enabled */
+		return;
+
+	MGETHDR(m_notify, MB_DONTWAIT, MT_DATA);
+	if (m_notify == NULL)
+		return;
+	m_notify->m_len = 0;
+
+	MCLGET(m_notify, MB_DONTWAIT);
+	if ((m_notify->m_flags & M_EXT) != M_EXT) {
+		sctp_m_freem(m_notify);
+		return;
+	}
+
+	spc = mtod(m_notify, struct sctp_paddr_change *);
+	spc->spc_type = SCTP_PEER_ADDR_CHANGE;
+	spc->spc_flags = 0;
+	spc->spc_length = sizeof(struct sctp_paddr_change);
+	if (sa->sa_family == AF_INET) {
+		memcpy(&spc->spc_aaddr, sa, sizeof(struct sockaddr_in));
+	} else {
+		memcpy(&spc->spc_aaddr, sa, sizeof(struct sockaddr_in6));
+	}
+	spc->spc_state = state;
+	spc->spc_error = error;
+	spc->spc_assoc_id = sctp_get_associd(stcb);
+
+	m_notify->m_flags |= M_EOR | M_NOTIFICATION;
+	m_notify->m_pkthdr.len = sizeof(struct sctp_paddr_change);
+	m_notify->m_pkthdr.rcvif = 0;
+	m_notify->m_len = sizeof(struct sctp_paddr_change);
+	m_notify->m_next = NULL;
+
+	to = (struct sockaddr *)(struct sockaddr *)
+	    &stcb->asoc.primary_destination->ro._l_addr;
+	if ((stcb->sctp_ep->sctp_flags & SCTP_PCB_FLAGS_NEEDS_MAPPED_V4) &&
+	    to->sa_family == AF_INET) {
+		struct sockaddr_in *sin;
+
+		sin = (struct sockaddr_in *)to;
+		bzero(&sin6, sizeof(sin6));
+		sin6.sin6_family = AF_INET6;
+		sin6.sin6_len = sizeof(struct sockaddr_in6);
+		sin6.sin6_addr.s6_addr16[2] = 0xffff;
+		bcopy(&sin->sin_addr, &sin6.sin6_addr.s6_addr16[3],
+		    sizeof(sin6.sin6_addr.s6_addr16[3]));
+		sin6.sin6_port = sin->sin_port;
+		to = (struct sockaddr *)&sin6;
+	}
+	/* check and strip embedded scope junk */
+	to = (struct sockaddr *)sctp_recover_scope((struct sockaddr_in6 *)to,
+	    &lsa6);
+
+	if (sctp_sbspace(&stcb->sctp_socket->so_rcv) < m_notify->m_len) {
+		sctp_m_freem(m_notify);
+		return;
+	}
+	/* append to socket */
+	SCTP_TCB_UNLOCK(stcb);
+	SCTP_INP_WLOCK(stcb->sctp_ep);
+	SCTP_TCB_LOCK(stcb);
+	if (!sbappendaddr_nocheck(&stcb->sctp_socket->so_rcv, to,
+	    m_notify, NULL, stcb->asoc.my_vtag, stcb->sctp_ep)) {
+		/* not enough room */
+		sctp_m_freem(m_notify);
+		SCTP_INP_WUNLOCK(stcb->sctp_ep);
+		return;
+	}
+	if (((stcb->sctp_ep->sctp_flags & SCTP_PCB_FLAGS_IN_TCPPOOL) == 0) &&
+	   ((stcb->sctp_ep->sctp_flags & SCTP_PCB_FLAGS_CONNECTED) == 0)){
+		if (sctp_add_to_socket_q(stcb->sctp_ep, stcb)) {
+			stcb->asoc.my_rwnd_control_len += sizeof(struct mbuf);
+		}
+	} else {
+		stcb->asoc.my_rwnd_control_len += sizeof(struct mbuf);
+	}
+	SCTP_INP_WUNLOCK(stcb->sctp_ep);
+	sctp_sorwakeup(stcb->sctp_ep, stcb->sctp_socket);
+}
+
+
+static void
+sctp_notify_send_failed(struct sctp_tcb *stcb, u_int32_t error,
+			struct sctp_tmit_chunk *chk)
+{
+	struct mbuf *m_notify;
+	struct sctp_send_failed *ssf;
+	struct sockaddr_in6 sin6, lsa6;
+	struct sockaddr *to;
+	int length;
+
+	if (!(stcb->sctp_ep->sctp_flags & SCTP_PCB_FLAGS_RECVSENDFAILEVNT))
+		/* event not enabled */
+		return;
+
+	length = sizeof(struct sctp_send_failed) + chk->send_size;
+	MGETHDR(m_notify, MB_DONTWAIT, MT_DATA);
+	if (m_notify == NULL)
+		/* no space left */
+		return;
+	m_notify->m_len = 0;
+	ssf = mtod(m_notify, struct sctp_send_failed *);
+	ssf->ssf_type = SCTP_SEND_FAILED;
+	if (error == SCTP_NOTIFY_DATAGRAM_UNSENT)
+		ssf->ssf_flags = SCTP_DATA_UNSENT;
+	else
+		ssf->ssf_flags = SCTP_DATA_SENT;
+	ssf->ssf_length = length;
+	ssf->ssf_error = error;
+	/* not exactly what the user sent in, but should be close :) */
+	ssf->ssf_info.sinfo_stream = chk->rec.data.stream_number;
+	ssf->ssf_info.sinfo_ssn = chk->rec.data.stream_seq;
+	ssf->ssf_info.sinfo_flags = chk->rec.data.rcv_flags;
+	ssf->ssf_info.sinfo_ppid = chk->rec.data.payloadtype;
+	ssf->ssf_info.sinfo_context = chk->rec.data.context;
+	ssf->ssf_info.sinfo_assoc_id = sctp_get_associd(stcb);
+	ssf->ssf_assoc_id = sctp_get_associd(stcb);
+	m_notify->m_next = chk->data;
+	if (m_notify->m_next == NULL)
+		m_notify->m_flags |= M_EOR | M_NOTIFICATION;
+	else {
+		struct mbuf *m;
+		m_notify->m_flags |= M_NOTIFICATION;
+		m = m_notify;
+		while (m->m_next != NULL)
+			m = m->m_next;
+		m->m_flags |= M_EOR;
+	}
+	m_notify->m_pkthdr.len = length;
+	m_notify->m_pkthdr.rcvif = 0;
+	m_notify->m_len = sizeof(struct sctp_send_failed);
+
+	/* Steal off the mbuf */
+	chk->data = NULL;
+	to = (struct sockaddr *)(struct sockaddr *)&stcb->asoc.primary_destination->ro._l_addr;
+	if ((stcb->sctp_ep->sctp_flags & SCTP_PCB_FLAGS_NEEDS_MAPPED_V4) &&
+	    to->sa_family == AF_INET) {
+		struct sockaddr_in *sin;
+
+		sin = (struct sockaddr_in *)to;
+		bzero(&sin6, sizeof(sin6));
+		sin6.sin6_family = AF_INET6;
+		sin6.sin6_len = sizeof(struct sockaddr_in6);
+		sin6.sin6_addr.s6_addr16[2] = 0xffff;
+		bcopy(&sin->sin_addr, &sin6.sin6_addr.s6_addr16[3],
+		    sizeof(sin6.sin6_addr.s6_addr16[3]));
+		sin6.sin6_port = sin->sin_port;
+		to = (struct sockaddr *)&sin6;
+	}
+	/* check and strip embedded scope junk */
+	to = (struct sockaddr *)sctp_recover_scope((struct sockaddr_in6 *)to,
+						   &lsa6);
+
+	if (sctp_sbspace(&stcb->sctp_socket->so_rcv) < m_notify->m_len) {
+		sctp_m_freem(m_notify);
+		return;
+	}
+
+	/* append to socket */
+	SCTP_TCB_UNLOCK(stcb);
+	SCTP_INP_WLOCK(stcb->sctp_ep);
+	SCTP_TCB_LOCK(stcb);
+	if (!sbappendaddr_nocheck(&stcb->sctp_socket->so_rcv, to,
+	    m_notify, NULL, stcb->asoc.my_vtag, stcb->sctp_ep)) {
+		/* not enough room */
+		sctp_m_freem(m_notify);
+		SCTP_INP_WUNLOCK(stcb->sctp_ep);
+		return;
+	}
+	if (((stcb->sctp_ep->sctp_flags & SCTP_PCB_FLAGS_IN_TCPPOOL) == 0) &&
+	   ((stcb->sctp_ep->sctp_flags & SCTP_PCB_FLAGS_CONNECTED) == 0)){
+		if (sctp_add_to_socket_q(stcb->sctp_ep, stcb)) {
+			stcb->asoc.my_rwnd_control_len += sizeof(struct mbuf);
+		}
+	} else {
+		stcb->asoc.my_rwnd_control_len += sizeof(struct mbuf);
+	}
+	SCTP_INP_WUNLOCK(stcb->sctp_ep);
+	sctp_sorwakeup(stcb->sctp_ep, stcb->sctp_socket);
+}
+
+static void
+sctp_notify_adaption_layer(struct sctp_tcb *stcb,
+			   u_int32_t error)
+{
+	struct mbuf *m_notify;
+	struct sctp_adaption_event *sai;
+	struct sockaddr_in6 sin6, lsa6;
+	struct sockaddr *to;
+
+	if (!(stcb->sctp_ep->sctp_flags & SCTP_PCB_FLAGS_ADAPTIONEVNT))
+		/* event not enabled */
+		return;
+
+	MGETHDR(m_notify, MB_DONTWAIT, MT_DATA);
+	if (m_notify == NULL)
+		/* no space left */
+		return;
+	m_notify->m_len = 0;
+	sai = mtod(m_notify, struct sctp_adaption_event *);
+	sai->sai_type = SCTP_ADAPTION_INDICATION;
+	sai->sai_flags = 0;
+	sai->sai_length = sizeof(struct sctp_adaption_event);
+	sai->sai_adaption_ind = error;
+	sai->sai_assoc_id = sctp_get_associd(stcb);
+
+	m_notify->m_flags |= M_EOR | M_NOTIFICATION;
+	m_notify->m_pkthdr.len = sizeof(struct sctp_adaption_event);
+	m_notify->m_pkthdr.rcvif = 0;
+	m_notify->m_len = sizeof(struct sctp_adaption_event);
+	m_notify->m_next = NULL;
+
+	to = (struct sockaddr *)(struct sockaddr *)&stcb->asoc.primary_destination->ro._l_addr;
+	if ((stcb->sctp_ep->sctp_flags & SCTP_PCB_FLAGS_NEEDS_MAPPED_V4) &&
+	    (to->sa_family == AF_INET)) {
+		struct sockaddr_in *sin;
+
+		sin = (struct sockaddr_in *)to;
+		bzero(&sin6, sizeof(sin6));
+		sin6.sin6_family = AF_INET6;
+		sin6.sin6_len = sizeof(struct sockaddr_in6);
+		sin6.sin6_addr.s6_addr16[2] = 0xffff;
+		bcopy(&sin->sin_addr, &sin6.sin6_addr.s6_addr16[3],
+		    sizeof(sin6.sin6_addr.s6_addr16[3]));
+		sin6.sin6_port = sin->sin_port;
+		to = (struct sockaddr *)&sin6;
+	}
+	/* check and strip embedded scope junk */
+	to = (struct sockaddr *)sctp_recover_scope((struct sockaddr_in6 *)to,
+						   &lsa6);
+	if (sctp_sbspace(&stcb->sctp_socket->so_rcv) < m_notify->m_len) {
+		sctp_m_freem(m_notify);
+		return;
+	}
+	/* append to socket */
+	SCTP_TCB_UNLOCK(stcb);
+	SCTP_INP_WLOCK(stcb->sctp_ep);
+	SCTP_TCB_LOCK(stcb);
+	if (!sbappendaddr_nocheck(&stcb->sctp_socket->so_rcv, to,
+	    m_notify, NULL, stcb->asoc.my_vtag, stcb->sctp_ep)) {
+		/* not enough room */
+		sctp_m_freem(m_notify);
+		SCTP_INP_WUNLOCK(stcb->sctp_ep);
+		return;
+	}
+	if (((stcb->sctp_ep->sctp_flags & SCTP_PCB_FLAGS_IN_TCPPOOL) == 0) &&
+	   ((stcb->sctp_ep->sctp_flags & SCTP_PCB_FLAGS_CONNECTED) == 0)){
+		if (sctp_add_to_socket_q(stcb->sctp_ep, stcb)) {
+			stcb->asoc.my_rwnd_control_len += sizeof(struct mbuf);
+		}
+	} else {
+		stcb->asoc.my_rwnd_control_len += sizeof(struct mbuf);
+	}
+	SCTP_INP_WUNLOCK(stcb->sctp_ep);
+	sctp_sorwakeup(stcb->sctp_ep, stcb->sctp_socket);
+}
+
+static void
+sctp_notify_partial_delivery_indication(struct sctp_tcb *stcb,
+					u_int32_t error)
+{
+	struct mbuf *m_notify;
+	struct sctp_pdapi_event *pdapi;
+	struct sockaddr_in6 sin6, lsa6;
+	struct sockaddr *to;
+
+	if (!(stcb->sctp_ep->sctp_flags & SCTP_PCB_FLAGS_PDAPIEVNT))
+		/* event not enabled */
+		return;
+
+	MGETHDR(m_notify, MB_DONTWAIT, MT_DATA);
+	if (m_notify == NULL)
+		/* no space left */
+		return;
+	m_notify->m_len = 0;
+	pdapi = mtod(m_notify, struct sctp_pdapi_event *);
+	pdapi->pdapi_type = SCTP_PARTIAL_DELIVERY_EVENT;
+	pdapi->pdapi_flags = 0;
+	pdapi->pdapi_length = sizeof(struct sctp_pdapi_event);
+	pdapi->pdapi_indication = error;
+	pdapi->pdapi_assoc_id = sctp_get_associd(stcb);
+
+	m_notify->m_flags |= M_EOR | M_NOTIFICATION;
+	m_notify->m_pkthdr.len = sizeof(struct sctp_pdapi_event);
+	m_notify->m_pkthdr.rcvif = 0;
+	m_notify->m_len = sizeof(struct sctp_pdapi_event);
+	m_notify->m_next = NULL;
+
+	to = (struct sockaddr *)(struct sockaddr *)&stcb->asoc.primary_destination->ro._l_addr;
+	if ((stcb->sctp_ep->sctp_flags & SCTP_PCB_FLAGS_NEEDS_MAPPED_V4) &&
+	    (to->sa_family == AF_INET)) {
+		struct sockaddr_in *sin;
+
+		sin = (struct sockaddr_in *)to;
+		bzero(&sin6, sizeof(sin6));
+		sin6.sin6_family = AF_INET6;
+		sin6.sin6_len = sizeof(struct sockaddr_in6);
+		sin6.sin6_addr.s6_addr16[2] = 0xffff;
+		bcopy(&sin->sin_addr, &sin6.sin6_addr.s6_addr16[3],
+		    sizeof(sin6.sin6_addr.s6_addr16[3]));
+		sin6.sin6_port = sin->sin_port;
+		to = (struct sockaddr *)&sin6;
+	}
+	/* check and strip embedded scope junk */
+	to = (struct sockaddr *)sctp_recover_scope((struct sockaddr_in6 *)to,
+						   &lsa6);
+	if (sctp_sbspace(&stcb->sctp_socket->so_rcv) < m_notify->m_len) {
+		sctp_m_freem(m_notify);
+		return;
+	}
+	/* append to socket */
+	SCTP_TCB_UNLOCK(stcb);
+	SCTP_INP_WLOCK(stcb->sctp_ep);
+	SCTP_TCB_LOCK(stcb);
+	if (!sbappendaddr_nocheck(&stcb->sctp_socket->so_rcv, to,
+	    m_notify, NULL, stcb->asoc.my_vtag, stcb->sctp_ep)) {
+		/* not enough room */
+		sctp_m_freem(m_notify);
+		SCTP_INP_WUNLOCK(stcb->sctp_ep);
+		return;
+	}
+	if (((stcb->sctp_ep->sctp_flags & SCTP_PCB_FLAGS_IN_TCPPOOL) == 0) &&
+	   ((stcb->sctp_ep->sctp_flags & SCTP_PCB_FLAGS_CONNECTED) == 0)){
+		if (sctp_add_to_socket_q(stcb->sctp_ep, stcb)) {
+			stcb->asoc.my_rwnd_control_len += sizeof(struct mbuf);
+		}
+	} else {
+		stcb->asoc.my_rwnd_control_len += sizeof(struct mbuf);
+	}
+	SCTP_INP_WUNLOCK(stcb->sctp_ep);
+	sctp_sorwakeup(stcb->sctp_ep, stcb->sctp_socket);
+}
+
+static void
+sctp_notify_shutdown_event(struct sctp_tcb *stcb)
+{
+	struct mbuf *m_notify;
+	struct sctp_shutdown_event *sse;
+	struct sockaddr_in6 sin6, lsa6;
+	struct sockaddr *to;
+
+	/*
+	 * For TCP model AND UDP connected sockets we will send
+	 * an error up when an SHUTDOWN completes
+	 */
+	if ((stcb->sctp_ep->sctp_flags & SCTP_PCB_FLAGS_TCPTYPE) ||
+	    (stcb->sctp_ep->sctp_flags & SCTP_PCB_FLAGS_IN_TCPPOOL)) {
+		/* mark socket closed for read/write and wakeup! */
+		socantrcvmore(stcb->sctp_socket);
+		socantsendmore(stcb->sctp_socket);
+	}
+
+	if (!(stcb->sctp_ep->sctp_flags & SCTP_PCB_FLAGS_RECVSHUTDOWNEVNT))
+		/* event not enabled */
+		return;
+
+	MGETHDR(m_notify, MB_DONTWAIT, MT_DATA);
+	if (m_notify == NULL)
+		/* no space left */
+		return;
+	m_notify->m_len = 0;
+	sse = mtod(m_notify, struct sctp_shutdown_event *);
+	sse->sse_type = SCTP_SHUTDOWN_EVENT;
+	sse->sse_flags = 0;
+	sse->sse_length = sizeof(struct sctp_shutdown_event);
+	sse->sse_assoc_id = sctp_get_associd(stcb);
+
+	m_notify->m_flags |= M_EOR | M_NOTIFICATION;
+	m_notify->m_pkthdr.len = sizeof(struct sctp_shutdown_event);
+	m_notify->m_pkthdr.rcvif = 0;
+	m_notify->m_len = sizeof(struct sctp_shutdown_event);
+	m_notify->m_next = NULL;
+
+	to = (struct sockaddr *)(struct sockaddr *)&stcb->asoc.primary_destination->ro._l_addr;
+	if ((stcb->sctp_ep->sctp_flags & SCTP_PCB_FLAGS_NEEDS_MAPPED_V4) &&
+	    to->sa_family == AF_INET) {
+		struct sockaddr_in *sin;
+
+		sin = (struct sockaddr_in *)to;
+		bzero(&sin6, sizeof(sin6));
+		sin6.sin6_family = AF_INET6;
+		sin6.sin6_len = sizeof(struct sockaddr_in6);
+		sin6.sin6_addr.s6_addr16[2] = 0xffff;
+		bcopy(&sin->sin_addr, &sin6.sin6_addr.s6_addr16[3],
+		    sizeof(sin6.sin6_addr.s6_addr16[3]));
+		sin6.sin6_port = sin->sin_port;
+		to = (struct sockaddr *)&sin6;
+	}
+	/* check and strip embedded scope junk */
+	to = (struct sockaddr *)sctp_recover_scope((struct sockaddr_in6 *)to,
+	    &lsa6);
+	if (sctp_sbspace(&stcb->sctp_socket->so_rcv) < m_notify->m_len) {
+		sctp_m_freem(m_notify);
+		return;
+	}
+	/* append to socket */
+	SCTP_TCB_UNLOCK(stcb);
+	SCTP_INP_WLOCK(stcb->sctp_ep);
+	SCTP_TCB_LOCK(stcb);
+	if (!sbappendaddr_nocheck(&stcb->sctp_socket->so_rcv, to,
+	    m_notify, NULL, stcb->asoc.my_vtag, stcb->sctp_ep)) {
+		/* not enough room */
+		sctp_m_freem(m_notify);
+		SCTP_INP_WUNLOCK(stcb->sctp_ep);
+		return;
+	}
+	if (((stcb->sctp_ep->sctp_flags & SCTP_PCB_FLAGS_IN_TCPPOOL) == 0) &&
+	   ((stcb->sctp_ep->sctp_flags & SCTP_PCB_FLAGS_CONNECTED) == 0)){
+		if (sctp_add_to_socket_q(stcb->sctp_ep, stcb)) {
+			stcb->asoc.my_rwnd_control_len += sizeof(struct mbuf);
+		}
+	} else {
+		stcb->asoc.my_rwnd_control_len += sizeof(struct mbuf);
+	}
+	SCTP_INP_WUNLOCK(stcb->sctp_ep);
+	sctp_sorwakeup(stcb->sctp_ep, stcb->sctp_socket);
+}
+
+static void
+sctp_notify_stream_reset(struct sctp_tcb *stcb,
+    int number_entries, uint16_t *list, int flag)
+{
+	struct mbuf *m_notify;
+	struct sctp_stream_reset_event *strreset;
+	struct sockaddr_in6 sin6, lsa6;
+	struct sockaddr *to;
+	int len;
+
+	if (!(stcb->sctp_ep->sctp_flags & SCTP_PCB_FLAGS_STREAM_RESETEVNT))
+		/* event not enabled */
+		return;
+
+	MGETHDR(m_notify, MB_DONTWAIT, MT_DATA);
+	if (m_notify == NULL)
+		/* no space left */
+		return;
+	m_notify->m_len = 0;
+	len = sizeof(struct sctp_stream_reset_event) + (number_entries * sizeof(uint16_t));
+	if (len > M_TRAILINGSPACE(m_notify)) {
+		MCLGET(m_notify, MB_WAIT);
+	}
+	if (m_notify == NULL)
+		/* no clusters */
+		return;
+
+	if (len > M_TRAILINGSPACE(m_notify)) {
+		/* never enough room */
+		m_freem(m_notify);
+		return;
+	}
+	strreset = mtod(m_notify, struct sctp_stream_reset_event *);
+	strreset->strreset_type = SCTP_STREAM_RESET_EVENT;
+	if (number_entries == 0) {
+		strreset->strreset_flags = flag | SCTP_STRRESET_ALL_STREAMS;
+	} else {
+		strreset->strreset_flags = flag | SCTP_STRRESET_STREAM_LIST;
+	}
+	strreset->strreset_length = len;
+	strreset->strreset_assoc_id = sctp_get_associd(stcb);
+	if (number_entries) {
+		int i;
+		for (i=0; i<number_entries; i++) {
+			strreset->strreset_list[i] = list[i];
+		}
+	}
+	m_notify->m_flags |= M_EOR | M_NOTIFICATION;
+	m_notify->m_pkthdr.len = len;
+	m_notify->m_pkthdr.rcvif = 0;
+	m_notify->m_len = len;
+	m_notify->m_next = NULL;
+	if (sctp_sbspace(&stcb->sctp_socket->so_rcv) < m_notify->m_len) {
+		/* no space */
+		sctp_m_freem(m_notify);
+		return;
+	}
+	to = (struct sockaddr *)(struct sockaddr *)&stcb->asoc.primary_destination->ro._l_addr;
+	if ((stcb->sctp_ep->sctp_flags & SCTP_PCB_FLAGS_NEEDS_MAPPED_V4) &&
+	    to->sa_family == AF_INET) {
+		struct sockaddr_in *sin;
+
+		sin = (struct sockaddr_in *)to;
+		bzero(&sin6, sizeof(sin6));
+		sin6.sin6_family = AF_INET6;
+		sin6.sin6_len = sizeof(struct sockaddr_in6);
+		sin6.sin6_addr.s6_addr16[2] = 0xffff;
+		bcopy(&sin->sin_addr, &sin6.sin6_addr.s6_addr16[3],
+		    sizeof(sin6.sin6_addr.s6_addr16[3]));
+		sin6.sin6_port = sin->sin_port;
+		to = (struct sockaddr *)&sin6;
+	}
+	/* check and strip embedded scope junk */
+	to = (struct sockaddr *)sctp_recover_scope((struct sockaddr_in6 *)to,
+	    &lsa6);
+	/* append to socket */
+	SCTP_TCB_UNLOCK(stcb);
+	SCTP_INP_WLOCK(stcb->sctp_ep);
+	SCTP_TCB_LOCK(stcb);
+	if (!sbappendaddr_nocheck(&stcb->sctp_socket->so_rcv, to,
+	    m_notify, NULL, stcb->asoc.my_vtag, stcb->sctp_ep)) {
+		/* not enough room */
+		sctp_m_freem(m_notify);
+		SCTP_INP_WUNLOCK(stcb->sctp_ep);
+		return;
+	}
+	if (((stcb->sctp_ep->sctp_flags & SCTP_PCB_FLAGS_IN_TCPPOOL) == 0) &&
+	   ((stcb->sctp_ep->sctp_flags & SCTP_PCB_FLAGS_CONNECTED) == 0)){
+		if (sctp_add_to_socket_q(stcb->sctp_ep, stcb)) {
+			stcb->asoc.my_rwnd_control_len += sizeof(struct mbuf);
+		}
+	} else {
+		stcb->asoc.my_rwnd_control_len += sizeof(struct mbuf);
+	}
+	SCTP_INP_WUNLOCK(stcb->sctp_ep);
+	sctp_sorwakeup(stcb->sctp_ep, stcb->sctp_socket);
+}
+
+
+void
+sctp_ulp_notify(u_int32_t notification, struct sctp_tcb *stcb,
+		u_int32_t error, void *data)
+{
+	if (stcb->sctp_ep->sctp_flags & SCTP_PCB_FLAGS_SOCKET_GONE) {
+		/* No notifications up when we are in a no socket state */
+		return;
+	}
+	if (stcb->asoc.state & SCTP_STATE_CLOSED_SOCKET) {
+		/* Can't send up to a closed socket any notifications */
+		return;
+	}
+	switch (notification) {
+	case SCTP_NOTIFY_ASSOC_UP:
+		sctp_notify_assoc_change(SCTP_COMM_UP, stcb, error);
+		break;
+	case SCTP_NOTIFY_ASSOC_DOWN:
+		sctp_notify_assoc_change(SCTP_SHUTDOWN_COMP, stcb, error);
+		break;
+	case SCTP_NOTIFY_INTERFACE_DOWN:
+	{
+		struct sctp_nets *net;
+		net = (struct sctp_nets *)data;
+		sctp_notify_peer_addr_change(stcb, SCTP_ADDR_UNREACHABLE,
+		    (struct sockaddr *)&net->ro._l_addr, error);
+		break;
+	}
+	case SCTP_NOTIFY_INTERFACE_UP:
+	{
+		struct sctp_nets *net;
+		net = (struct sctp_nets *)data;
+		sctp_notify_peer_addr_change(stcb, SCTP_ADDR_AVAILABLE,
+		    (struct sockaddr *)&net->ro._l_addr, error);
+		break;
+	}
+	case SCTP_NOTIFY_INTERFACE_CONFIRMED:
+	{
+		struct sctp_nets *net;
+		net = (struct sctp_nets *)data;
+		sctp_notify_peer_addr_change(stcb, SCTP_ADDR_CONFIRMED,
+		    (struct sockaddr *)&net->ro._l_addr, error);
+		break;
+	}
+	case SCTP_NOTIFY_DG_FAIL:
+		sctp_notify_send_failed(stcb, error,
+		    (struct sctp_tmit_chunk *)data);
+		break;
+	case SCTP_NOTIFY_ADAPTION_INDICATION:
+		/* Here the error is the adaption indication */
+		sctp_notify_adaption_layer(stcb, error);
+		break;
+	case SCTP_NOTIFY_PARTIAL_DELVIERY_INDICATION:
+		sctp_notify_partial_delivery_indication(stcb, error);
+		break;
+	case SCTP_NOTIFY_STRDATA_ERR:
+		break;
+	case SCTP_NOTIFY_ASSOC_ABORTED:
+		sctp_notify_assoc_change(SCTP_COMM_LOST, stcb, error);
+		break;
+	case SCTP_NOTIFY_PEER_OPENED_STREAM:
+		break;
+	case SCTP_NOTIFY_STREAM_OPENED_OK:
+		break;
+	case SCTP_NOTIFY_ASSOC_RESTART:
+		sctp_notify_assoc_change(SCTP_RESTART, stcb, error);
+		break;
+	case SCTP_NOTIFY_HB_RESP:
+		break;
+	case SCTP_NOTIFY_STR_RESET_SEND:
+		sctp_notify_stream_reset(stcb, error, ((uint16_t *)data), SCTP_STRRESET_OUTBOUND_STR);
+		break;
+	case SCTP_NOTIFY_STR_RESET_RECV:
+		sctp_notify_stream_reset(stcb, error, ((uint16_t *)data), SCTP_STRRESET_INBOUND_STR);
+		break;
+	case SCTP_NOTIFY_ASCONF_ADD_IP:
+		sctp_notify_peer_addr_change(stcb, SCTP_ADDR_ADDED, data,
+		    error);
+		break;
+	case SCTP_NOTIFY_ASCONF_DELETE_IP:
+		sctp_notify_peer_addr_change(stcb, SCTP_ADDR_REMOVED, data,
+		    error);
+		break;
+	case SCTP_NOTIFY_ASCONF_SET_PRIMARY:
+		sctp_notify_peer_addr_change(stcb, SCTP_ADDR_MADE_PRIM, data,
+		    error);
+		break;
+	case SCTP_NOTIFY_ASCONF_SUCCESS:
+		break;
+	case SCTP_NOTIFY_ASCONF_FAILED:
+		break;
+	case SCTP_NOTIFY_PEER_SHUTDOWN:
+		sctp_notify_shutdown_event(stcb);
+		break;
+	default:
+#ifdef SCTP_DEBUG
+		if (sctp_debug_on & SCTP_DEBUG_UTIL1) {
+			printf("NOTIFY: unknown notification %xh (%u)\n",
+			    notification, notification);
+		}
+#endif /* SCTP_DEBUG */
+		break;
+	} /* end switch */
+}
+
+void
+sctp_report_all_outbound(struct sctp_tcb *stcb)
+{
+	struct sctp_association *asoc;
+	struct sctp_stream_out *outs;
+	struct sctp_tmit_chunk *chk;
+
+	asoc = &stcb->asoc;
+
+	if (stcb->sctp_ep->sctp_flags & SCTP_PCB_FLAGS_SOCKET_GONE) {
+		return;
+	}
+	/* now through all the gunk freeing chunks */
+	TAILQ_FOREACH(outs, &asoc->out_wheel, next_spoke) {
+		/* now clean up any chunks here */
+		chk = TAILQ_FIRST(&outs->outqueue);
+		while (chk) {
+			stcb->asoc.stream_queue_cnt--;
+			TAILQ_REMOVE(&outs->outqueue, chk, sctp_next);
+			sctp_ulp_notify(SCTP_NOTIFY_DG_FAIL, stcb,
+			    SCTP_NOTIFY_DATAGRAM_UNSENT, chk);
+			if (chk->data) {
+				sctp_m_freem(chk->data);
+				chk->data = NULL;
+			}
+			if (chk->whoTo)
+				sctp_free_remote_addr(chk->whoTo);
+			chk->whoTo = NULL;
+			chk->asoc = NULL;
+			/* Free the chunk */
+			SCTP_ZONE_FREE(sctppcbinfo.ipi_zone_chunk, chk);
+			sctppcbinfo.ipi_count_chunk--;
+			if ((int)sctppcbinfo.ipi_count_chunk < 0) {
+				panic("Chunk count is negative");
+			}
+			sctppcbinfo.ipi_gencnt_chunk++;
+			chk = TAILQ_FIRST(&outs->outqueue);
+		}
+	}
+	/* pending send queue SHOULD be empty */
+	if (!TAILQ_EMPTY(&asoc->send_queue)) {
+		chk = TAILQ_FIRST(&asoc->send_queue);
+		while (chk) {
+			TAILQ_REMOVE(&asoc->send_queue, chk, sctp_next);
+			sctp_ulp_notify(SCTP_NOTIFY_DG_FAIL, stcb, SCTP_NOTIFY_DATAGRAM_UNSENT, chk);
+			if (chk->data) {
+				sctp_m_freem(chk->data);
+				chk->data = NULL;
+			}
+			if (chk->whoTo)
+				sctp_free_remote_addr(chk->whoTo);
+			chk->whoTo = NULL;
+			SCTP_ZONE_FREE(sctppcbinfo.ipi_zone_chunk, chk);
+			sctppcbinfo.ipi_count_chunk--;
+			if ((int)sctppcbinfo.ipi_count_chunk < 0) {
+				panic("Chunk count is negative");
+			}
+			sctppcbinfo.ipi_gencnt_chunk++;
+			chk = TAILQ_FIRST(&asoc->send_queue);
+		}
+	}
+	/* sent queue SHOULD be empty */
+	if (!TAILQ_EMPTY(&asoc->sent_queue)) {
+		chk = TAILQ_FIRST(&asoc->sent_queue);
+		while (chk) {
+			TAILQ_REMOVE(&asoc->sent_queue, chk, sctp_next);
+			sctp_ulp_notify(SCTP_NOTIFY_DG_FAIL, stcb,
+			    SCTP_NOTIFY_DATAGRAM_SENT, chk);
+			if (chk->data) {
+				sctp_m_freem(chk->data);
+				chk->data = NULL;
+			}
+			if (chk->whoTo)
+				sctp_free_remote_addr(chk->whoTo);
+			chk->whoTo = NULL;
+			SCTP_ZONE_FREE(sctppcbinfo.ipi_zone_chunk, chk);
+			sctppcbinfo.ipi_count_chunk--;
+			if ((int)sctppcbinfo.ipi_count_chunk < 0) {
+				panic("Chunk count is negative");
+			}
+			sctppcbinfo.ipi_gencnt_chunk++;
+			chk = TAILQ_FIRST(&asoc->sent_queue);
+		}
+	}
+}
+
+void
+sctp_abort_notification(struct sctp_tcb *stcb, int error)
+{
+
+	if (stcb->sctp_ep->sctp_flags & SCTP_PCB_FLAGS_SOCKET_GONE) {
+		return;
+	}
+	/* Tell them we lost the asoc */
+	sctp_report_all_outbound(stcb);
+	sctp_ulp_notify(SCTP_NOTIFY_ASSOC_ABORTED, stcb, error, NULL);
+}
+
+void
+sctp_abort_association(struct sctp_inpcb *inp, struct sctp_tcb *stcb,
+    struct mbuf *m, int iphlen, struct sctphdr *sh, struct mbuf *op_err)
+{
+	u_int32_t vtag;
+
+	vtag = 0;
+	if (stcb != NULL) {
+		/* We have a TCB to abort, send notification too */
+		vtag = stcb->asoc.peer_vtag;
+		sctp_abort_notification(stcb, 0);
+	}
+	sctp_send_abort(m, iphlen, sh, vtag, op_err);
+	if (stcb != NULL) {
+		/* Ok, now lets free it */
+		sctp_free_assoc(inp, stcb);
+	} else {
+		if (inp->sctp_flags & SCTP_PCB_FLAGS_SOCKET_GONE) {
+			if (LIST_FIRST(&inp->sctp_asoc_list) == NULL) {
+				sctp_inpcb_free(inp, 1);
+			}
+		}
+	}
+}
+
+void
+sctp_abort_an_association(struct sctp_inpcb *inp, struct sctp_tcb *stcb,
+    int error, struct mbuf *op_err)
+{
+	u_int32_t vtag;
+
+	if (stcb == NULL) {
+		/* Got to have a TCB */
+		if (inp->sctp_flags & SCTP_PCB_FLAGS_SOCKET_GONE) {
+			if (LIST_FIRST(&inp->sctp_asoc_list) == NULL) {
+				sctp_inpcb_free(inp, 1);
+			}
+		}
+		return;
+	}
+	vtag = stcb->asoc.peer_vtag;
+	/* notify the ulp */
+	if ((inp->sctp_flags & SCTP_PCB_FLAGS_SOCKET_GONE) == 0)
+		sctp_abort_notification(stcb, error);
+	/* notify the peer */
+	sctp_send_abort_tcb(stcb, op_err);
+	/* now free the asoc */
+	sctp_free_assoc(inp, stcb);
+}
+
+void
+sctp_handle_ootb(struct mbuf *m, int iphlen, int offset, struct sctphdr *sh,
+    struct sctp_inpcb *inp, struct mbuf *op_err)
+{
+	struct sctp_chunkhdr *ch, chunk_buf;
+	unsigned int chk_length;
+
+	/* Generate a TO address for future reference */
+	if (inp && (inp->sctp_flags & SCTP_PCB_FLAGS_SOCKET_GONE)) {
+		if (LIST_FIRST(&inp->sctp_asoc_list) == NULL) {
+			sctp_inpcb_free(inp, 1);
+		}
+	}
+	ch = (struct sctp_chunkhdr *)sctp_m_getptr(m, offset,
+	    sizeof(*ch), (u_int8_t *)&chunk_buf);
+	while (ch != NULL) {
+		chk_length = ntohs(ch->chunk_length);
+		if (chk_length < sizeof(*ch)) {
+			/* break to abort land */
+			break;
+		}
+		switch (ch->chunk_type) {
+		case SCTP_PACKET_DROPPED:
+			/* we don't respond to pkt-dropped */
+			return;
+		case SCTP_ABORT_ASSOCIATION:
+			/* we don't respond with an ABORT to an ABORT */
+			return;
+		case SCTP_SHUTDOWN_COMPLETE:
+			/*
+			 * we ignore it since we are not waiting for it
+			 * and peer is gone
+			 */
+			return;
+		case SCTP_SHUTDOWN_ACK:
+			sctp_send_shutdown_complete2(m, iphlen, sh);
+			return;
+		default:
+			break;
+		}
+		offset += SCTP_SIZE32(chk_length);
+		ch = (struct sctp_chunkhdr *)sctp_m_getptr(m, offset,
+		    sizeof(*ch), (u_int8_t *)&chunk_buf);
+	}
+	sctp_send_abort(m, iphlen, sh, 0, op_err);
+}
+
+/*
+ * check the inbound datagram to make sure there is not an abort
+ * inside it, if there is return 1, else return 0.
+ */
+int
+sctp_is_there_an_abort_here(struct mbuf *m, int iphlen, int *vtagfill)
+{
+	struct sctp_chunkhdr *ch;
+	struct sctp_init_chunk *init_chk, chunk_buf;
+	int offset;
+	unsigned int chk_length;
+
+	offset = iphlen + sizeof(struct sctphdr);
+	ch = (struct sctp_chunkhdr *)sctp_m_getptr(m, offset, sizeof(*ch),
+	    (u_int8_t *)&chunk_buf);
+	while (ch != NULL) {
+		chk_length = ntohs(ch->chunk_length);
+		if (chk_length < sizeof(*ch)) {
+			/* packet is probably corrupt */
+			break;
+		}
+		/* we seem to be ok, is it an abort? */
+		if (ch->chunk_type == SCTP_ABORT_ASSOCIATION) {
+			/* yep, tell them */
+			return (1);
+		}
+		if (ch->chunk_type == SCTP_INITIATION) {
+			/* need to update the Vtag */
+			init_chk = (struct sctp_init_chunk *)sctp_m_getptr(m,
+			    offset, sizeof(*init_chk), (u_int8_t *)&chunk_buf);
+			if (init_chk != NULL) {
+				*vtagfill = ntohl(init_chk->init.initiate_tag);
+			}
+		}
+		/* Nope, move to the next chunk */
+		offset += SCTP_SIZE32(chk_length);
+		ch = (struct sctp_chunkhdr *)sctp_m_getptr(m, offset,
+		    sizeof(*ch), (u_int8_t *)&chunk_buf);
+	}
+	return (0);
+}
+
+/*
+ * currently (2/02), ifa_addr embeds scope_id's and don't
+ * have sin6_scope_id set (i.e. it's 0)
+ * so, create this function to compare link local scopes
+ */
+uint32_t
+sctp_is_same_scope(struct sockaddr_in6 *addr1, struct sockaddr_in6 *addr2)
+{
+	struct sockaddr_in6 a, b;
+
+	/* save copies */
+	a = *addr1;
+	b = *addr2;
+
+	if (a.sin6_scope_id == 0)
+		if (in6_recoverscope(&a, &a.sin6_addr, NULL)) {
+			/* can't get scope, so can't match */
+			return (0);
+		}
+	if (b.sin6_scope_id == 0)
+		if (in6_recoverscope(&b, &b.sin6_addr, NULL)) {
+			/* can't get scope, so can't match */
+			return (0);
+		}
+	if (a.sin6_scope_id != b.sin6_scope_id)
+		return (0);
+
+	return (1);
+}
+
+/*
+ * returns a sockaddr_in6 with embedded scope recovered and removed
+ */
+struct sockaddr_in6 *
+sctp_recover_scope(struct sockaddr_in6 *addr, struct sockaddr_in6 *store)
+{
+
+	/* check and strip embedded scope junk */
+	if (addr->sin6_family == AF_INET6) {
+		if (IN6_IS_SCOPE_LINKLOCAL(&addr->sin6_addr)) {
+			if (addr->sin6_scope_id == 0) {
+				*store = *addr;
+				if (!in6_recoverscope(store, &store->sin6_addr,
+				    NULL)) {
+					/* use the recovered scope */
+					addr = store;
+				}
+				/* else, return the original "to" addr */
+			}
+		}
+	}
+	return (addr);
+}
+
+/*
+ * are the two addresses the same?  currently a "scopeless" check
+ * returns: 1 if same, 0 if not
+ */
+int
+sctp_cmpaddr(struct sockaddr *sa1, struct sockaddr *sa2)
+{
+
+	/* must be valid */
+	if (sa1 == NULL || sa2 == NULL)
+		return (0);
+
+	/* must be the same family */
+	if (sa1->sa_family != sa2->sa_family)
+		return (0);
+
+	if (sa1->sa_family == AF_INET6) {
+		/* IPv6 addresses */
+		struct sockaddr_in6 *sin6_1, *sin6_2;
+
+		sin6_1 = (struct sockaddr_in6 *)sa1;
+		sin6_2 = (struct sockaddr_in6 *)sa2;
+		return (SCTP6_ARE_ADDR_EQUAL(&sin6_1->sin6_addr,
+		    &sin6_2->sin6_addr));
+	} else if (sa1->sa_family == AF_INET) {
+		/* IPv4 addresses */
+		struct sockaddr_in *sin_1, *sin_2;
+
+		sin_1 = (struct sockaddr_in *)sa1;
+		sin_2 = (struct sockaddr_in *)sa2;
+		return (sin_1->sin_addr.s_addr == sin_2->sin_addr.s_addr);
+	} else {
+		/* we don't do these... */
+		return (0);
+	}
+}
+
+void
+sctp_print_address(struct sockaddr *sa)
+{
+
+	if (sa->sa_family == AF_INET6) {
+		struct sockaddr_in6 *sin6;
+		sin6 = (struct sockaddr_in6 *)sa;
+		printf("IPv6 address: %s:%d scope:%u\n",
+		    ip6_sprintf(&sin6->sin6_addr), ntohs(sin6->sin6_port),
+		    sin6->sin6_scope_id);
+	} else if (sa->sa_family == AF_INET) {
+		struct sockaddr_in *sin;
+		sin = (struct sockaddr_in *)sa;
+		printf("IPv4 address: %s:%d\n", inet_ntoa(sin->sin_addr),
+		    ntohs(sin->sin_port));
+	} else {
+		printf("?\n");
+	}
+}
+
+void
+sctp_print_address_pkt(struct ip *iph, struct sctphdr *sh)
+{
+	if (iph->ip_v == IPVERSION) {
+		struct sockaddr_in lsa, fsa;
+
+		bzero(&lsa, sizeof(lsa));
+		lsa.sin_len = sizeof(lsa);
+		lsa.sin_family = AF_INET;
+		lsa.sin_addr = iph->ip_src;
+		lsa.sin_port = sh->src_port;
+		bzero(&fsa, sizeof(fsa));
+		fsa.sin_len = sizeof(fsa);
+		fsa.sin_family = AF_INET;
+		fsa.sin_addr = iph->ip_dst;
+		fsa.sin_port = sh->dest_port;
+		printf("src: ");
+		sctp_print_address((struct sockaddr *)&lsa);
+		printf("dest: ");
+		sctp_print_address((struct sockaddr *)&fsa);
+	} else if (iph->ip_v == (IPV6_VERSION >> 4)) {
+		struct ip6_hdr *ip6;
+		struct sockaddr_in6 lsa6, fsa6;
+
+		ip6 = (struct ip6_hdr *)iph;
+		bzero(&lsa6, sizeof(lsa6));
+		lsa6.sin6_len = sizeof(lsa6);
+		lsa6.sin6_family = AF_INET6;
+		lsa6.sin6_addr = ip6->ip6_src;
+		lsa6.sin6_port = sh->src_port;
+		bzero(&fsa6, sizeof(fsa6));
+		fsa6.sin6_len = sizeof(fsa6);
+		fsa6.sin6_family = AF_INET6;
+		fsa6.sin6_addr = ip6->ip6_dst;
+		fsa6.sin6_port = sh->dest_port;
+		printf("src: ");
+		sctp_print_address((struct sockaddr *)&lsa6);
+		printf("dest: ");
+		sctp_print_address((struct sockaddr *)&fsa6);
+	}
+}
+
+#if defined(__FreeBSD__) || defined(__APPLE__)
+
+/* cloned from uipc_socket.c */
+
+#define SCTP_SBLINKRECORD(sb, m0) do {					\
+	if ((sb)->sb_lastrecord != NULL)				\
+		(sb)->sb_lastrecord->m_nextpkt = (m0);			\
+	else								\
+		(sb)->sb_mb = (m0);					\
+	(sb)->sb_lastrecord = (m0);					\
+} while (/*CONSTCOND*/0)
+#endif
+
+
+int
+sbappendaddr_nocheck(sb, asa, m0, control, tag, inp)
+	struct sockbuf *sb;
+	struct sockaddr *asa;
+	struct mbuf *m0, *control;
+	u_int32_t tag;
+	struct sctp_inpcb *inp;
+{
+#ifdef __NetBSD__
+	struct mbuf *m, *n;
+
+	if (m0 && (m0->m_flags & M_PKTHDR) == 0)
+		panic("sbappendaddr_nocheck");
+
+	m0->m_pkthdr.csum_data = (int)tag;
+
+	for (n = control; n; n = n->m_next) {
+		if (n->m_next == 0)	/* keep pointer to last control buf */
+			break;
+	}
+	if (((inp->sctp_flags & SCTP_PCB_FLAGS_TCPTYPE) == 0) ||
+	    ((inp->sctp_flags & SCTP_PCB_FLAGS_IN_TCPPOOL)== 0)) {
+		MGETHDR(m, MB_DONTWAIT, MT_SONAME);
+		if (m == 0)
+			return (0);
+		m->m_len = 0;
+		if (asa->sa_len > MHLEN) {
+			MEXTMALLOC(m, asa->sa_len, M_NOWAIT);
+			if ((m->m_flags & M_EXT) == 0) {
+				m_free(m);
+				return (0);
+			}
+		}
+		m->m_len = asa->sa_len;
+		memcpy(mtod(m, caddr_t), (caddr_t)asa, asa->sa_len);
+	} else {
+		m = NULL;
+	}
+	if (n) {
+		n->m_next = m0;		/* concatenate data to control */
+	}else {
+		control = m0;
+	}
+	if (m)
+		m->m_next = control;
+	else
+		m = control;
+	m->m_pkthdr.csum_data = tag;
+
+	for (n = m; n; n = n->m_next)
+		sballoc(sb, n);
+	if ((n = sb->sb_mb) != NULL) {
+		if ((n->m_nextpkt != inp->sb_last_mpkt) && (n->m_nextpkt == NULL)) {
+			inp->sb_last_mpkt = NULL;
+		}
+		if (inp->sb_last_mpkt)
+			inp->sb_last_mpkt->m_nextpkt = m;
+ 		else {
+			while (n->m_nextpkt) {
+				n = n->m_nextpkt;
+			}
+			n->m_nextpkt = m;
+		}
+		inp->sb_last_mpkt = m;
+	} else {
+		inp->sb_last_mpkt = sb->sb_mb = m;
+		inp->sctp_vtag_first = tag;
+	}
+	return (1);
+#endif
+#if defined(__FreeBSD__) || defined(__APPLE__) || defined(__DragonFly__)
+	struct mbuf *m, *n, *nlast;
+	int cnt=0;
+
+	if (m0 && (m0->m_flags & M_PKTHDR) == 0)
+		panic("sbappendaddr_nocheck");
+
+	for (n = control; n; n = n->m_next) {
+		if (n->m_next == 0)	/* get pointer to last control buf */
+			break;
+	}
+	if (((inp->sctp_flags & SCTP_PCB_FLAGS_TCPTYPE) == 0) ||
+	    ((inp->sctp_flags & SCTP_PCB_FLAGS_IN_TCPPOOL)== 0)) {
+		if (asa->sa_len > MHLEN)
+			return (0);
+try_again:
+		MGETHDR(m, MB_DONTWAIT, MT_SONAME);
+		if (m == 0)
+			return (0);
+		m->m_len = 0;
+		/* safety */
+		if (m == m0) {
+			printf("Duplicate mbuf allocated %p in and mget returned %p?\n",
+			       m0, m);
+			if (cnt) {
+				panic("more than once");
+			}
+			cnt++;
+			goto try_again;
+		}
+		m->m_len = asa->sa_len;
+		bcopy((caddr_t)asa, mtod(m, caddr_t), asa->sa_len);
+	}
+	else {
+		m = NULL;
+	}
+	if (n)
+		n->m_next = m0;		/* concatenate data to control */
+	else
+		control = m0;
+	if (m)
+		m->m_next = control;
+	else
+		m = control;
+	m->m_pkthdr.csum_data = (int)tag;
+
+	SOCKBUF_LOCK(sb);
+	for (n = m; n; n = n->m_next)
+		sballoc(sb, n);
+	nlast = n;
+	if (sb->sb_mb == NULL) {
+		inp->sctp_vtag_first = tag;
+	}
+
+#if defined(__FREEBSD__)
+	if (sb->sb_mb == NULL)
+		inp->sctp_vtag_first = tag;
+	SCTP_SBLINKRECORD(sb, m);
+	sb->sb_lastrecord = nlast;
+#else
+	if ((n = sb->sb_mb) != NULL) {
+		if ((n->m_nextpkt != inp->sb_last_mpkt) && (n->m_nextpkt == NULL)) {
+			inp->sb_last_mpkt = NULL;
+		}
+		if (inp->sb_last_mpkt)
+			inp->sb_last_mpkt->m_nextpkt = m;
+ 		else {
+			while (n->m_nextpkt) {
+				n = n->m_nextpkt;
+			}
+			n->m_nextpkt = m;
+		}
+		inp->sb_last_mpkt = m;
+	} else {
+		inp->sb_last_mpkt = sb->sb_mb = m;
+		inp->sctp_vtag_first = tag;
+	}
+#endif
+	SOCKBUF_UNLOCK(sb);
+	return (1);
+#endif
+#ifdef __OpenBSD__
+	struct mbuf *m, *n;
+
+	if (m0 && (m0->m_flags & M_PKTHDR) == 0)
+		panic("sbappendaddr_nocheck");
+	m0->m_pkthdr.csum = (int)tag;
+	for (n = control; n; n = n->m_next) {
+		if (n->m_next == 0)	/* keep pointer to last control buf */
+			break;
+	}
+	if (((inp->sctp_flags & SCTP_PCB_FLAGS_TCPTYPE) == 0) ||
+	    ((inp->sctp_flags & SCTP_PCB_FLAGS_IN_TCPPOOL)== 0)) {
+		if (asa->sa_len > MHLEN)
+			return (0);
+		MGETHDR(m, MB_DONTWAIT, MT_SONAME);
+		if (m == 0)
+			return (0);
+		m->m_len = asa->sa_len;
+		bcopy((caddr_t)asa, mtod(m, caddr_t), asa->sa_len);
+	} else {
+		m = NULL;
+	}
+	if (n)
+		n->m_next = m0;		/* concatenate data to control */
+	else
+		control = m0;
+
+	m->m_pkthdr.csum = (int)tag;
+	m->m_next = control;
+	for (n = m; n; n = n->m_next)
+		sballoc(sb, n);
+	if ((n = sb->sb_mb) != NULL) {
+		if ((n->m_nextpkt != inp->sb_last_mpkt) && (n->m_nextpkt == NULL)) {
+			inp->sb_last_mpkt = NULL;
+		}
+		if (inp->sb_last_mpkt)
+			inp->sb_last_mpkt->m_nextpkt = m;
+ 		else {
+			while (n->m_nextpkt) {
+				n = n->m_nextpkt;
+			}
+			n->m_nextpkt = m;
+		}
+		inp->sb_last_mpkt = m;
+	} else {
+		inp->sb_last_mpkt = sb->sb_mb = m;
+		inp->sctp_vtag_first = tag;
+	}
+	return (1);
+#endif
+}
+
+/*************HOLD THIS COMMENT FOR PATCH FILE OF
+ *************ALTERNATE ROUTING CODE
+ */
+
+/*************HOLD THIS COMMENT FOR END OF PATCH FILE OF
+ *************ALTERNATE ROUTING CODE
+ */
+
+struct mbuf *
+sctp_generate_invmanparam(int err)
+{
+	/* Return a MBUF with a invalid mandatory parameter */
+	struct mbuf *m;
+
+	MGET(m, MB_DONTWAIT, MT_DATA);
+	if (m) {
+		struct sctp_paramhdr *ph;
+		m->m_len = sizeof(struct sctp_paramhdr);
+		ph = mtod(m, struct sctp_paramhdr *);
+		ph->param_length = htons(sizeof(struct sctp_paramhdr));
+		ph->param_type = htons(err);
+	}
+	return (m);
+}
+
+static int
+sctp_should_be_moved(struct mbuf *this, struct sctp_association *asoc)
+{
+	struct mbuf *m;
+	/*
+	 * given a mbuf chain, look through it finding
+	 * the M_PKTHDR and return 1 if it belongs to
+	 * the association given. We tell this by
+	 * a kludge where we stuff the my_vtag of the asoc
+	 * into the m->m_pkthdr.csum_data/csum field.
+	 */
+	m = this;
+	while (m) {
+		if (m->m_flags & M_PKTHDR) {
+			/* check it */
+#if defined(__OpenBSD__)
+			if ((u_int32_t)m->m_pkthdr.csum == asoc->my_vtag)
+#else
+			if ((u_int32_t)m->m_pkthdr.csum_data == asoc->my_vtag)
+#endif
+			{
+				/* Yep */
+				return (1);
+			}
+		}
+		m = m->m_next;
+	}
+	return (0);
+}
+
+u_int32_t
+sctp_get_first_vtag_from_sb(struct socket *so)
+{
+	struct mbuf *this, *at;
+	u_int32_t retval;
+
+	retval = 0;
+	if (so->so_rcv.sb_mb) {
+		/* grubbing time */
+		this = so->so_rcv.sb_mb;
+		while (this) {
+			at = this;
+			/* get to the m_pkthdr */
+			while (at) {
+				if (at->m_flags & M_PKTHDR)
+					break;
+				else {
+					at = at->m_next;
+				}
+			}
+			/* now do we have a m_pkthdr */
+			if (at && (at->m_flags & M_PKTHDR)) {
+				/* check it */
+#if defined(__OpenBSD__)
+				if ((u_int32_t)at->m_pkthdr.csum != 0)
+#else
+				if ((u_int32_t)at->m_pkthdr.csum_data != 0)
+#endif
+				{
+					/* its the one */
+#if defined(__OpenBSD__)
+					retval = (u_int32_t)at->m_pkthdr.csum;
+#else
+					retval =
+					    (u_int32_t)at->m_pkthdr.csum_data;
+#endif
+					break;
+				}
+			}
+			this = this->m_nextpkt;
+		}
+
+	}
+	return (retval);
+
+}
+void
+sctp_grub_through_socket_buffer(struct sctp_inpcb *inp, struct socket *old,
+    struct socket *new, struct sctp_tcb *stcb)
+{
+	struct mbuf **put, **take, *next, *this;
+	struct sockbuf *old_sb, *new_sb;
+	struct sctp_association *asoc;
+	int moved_top = 0;
+
+	asoc = &stcb->asoc;
+	old_sb = &old->so_rcv;
+	new_sb = &new->so_rcv;
+	if (old_sb->sb_mb == NULL) {
+		/* Nothing to move */
+		return;
+	}
+	SOCKBUF_LOCK(old_sb);
+	SOCKBUF_LOCK(new_sb);
+
+	if (inp->sctp_vtag_first == asoc->my_vtag) {
+		/* First one must be moved */
+		struct mbuf *mm;
+		for (mm = old_sb->sb_mb; mm; mm = mm->m_next) {
+			/*
+			 * Go down the chain and fix
+			 * the space allocation of the
+			 * two sockets.
+			 */
+			sbfree(old_sb, mm);
+			sballoc(new_sb, mm);
+		}
+		new_sb->sb_mb = old_sb->sb_mb;
+		old_sb->sb_mb = new_sb->sb_mb->m_nextpkt;
+		new_sb->sb_mb->m_nextpkt = NULL;
+		put = &new_sb->sb_mb->m_nextpkt;
+		moved_top = 1;
+	} else {
+		put = &new_sb->sb_mb;
+	}
+
+	take = &old_sb->sb_mb;
+	next = old_sb->sb_mb;
+	while (next) {
+		this = next;
+		/* postion for next one */
+		next = this->m_nextpkt;
+		/* check the tag of this packet */
+		if (sctp_should_be_moved(this, asoc)) {
+			/* yes this needs to be moved */
+			struct mbuf *mm;
+			*take = this->m_nextpkt;
+			this->m_nextpkt = NULL;
+			*put = this;
+			for (mm = this; mm; mm = mm->m_next) {
+				/*
+				 * Go down the chain and fix
+				 * the space allocation of the
+				 * two sockets.
+				 */
+				sbfree(old_sb, mm);
+				sballoc(new_sb, mm);
+			}
+			put = &this->m_nextpkt;
+
+		} else {
+			/* no advance our take point. */
+			take = &this->m_nextpkt;
+		}
+	}
+	if (moved_top) {
+		/*
+		 * Ok so now we must re-postion vtag_first to
+		 * match the new first one since we moved the
+		 * mbuf at the top.
+		 */
+		inp->sctp_vtag_first = sctp_get_first_vtag_from_sb(old);
+	}
+	SOCKBUF_UNLOCK(old_sb);
+	SOCKBUF_UNLOCK(new_sb);
+}
+
+void
+sctp_free_bufspace(struct sctp_tcb *stcb, struct sctp_association *asoc,
+    struct sctp_tmit_chunk *tp1)
+{
+	if (tp1->data == NULL) {
+		return;
+	}
+#ifdef SCTP_MBCNT_LOGGING
+	sctp_log_mbcnt(SCTP_LOG_MBCNT_DECREASE,
+		       asoc->total_output_queue_size,
+		       tp1->book_size,
+		       asoc->total_output_mbuf_queue_size,
+		       tp1->mbcnt);
+#endif
+	if (asoc->total_output_queue_size >= tp1->book_size) {
+		asoc->total_output_queue_size -= tp1->book_size;
+	} else {
+		asoc->total_output_queue_size = 0;
+	}
+
+	/* Now free the mbuf */
+	if (asoc->total_output_mbuf_queue_size >= tp1->mbcnt) {
+		asoc->total_output_mbuf_queue_size -= tp1->mbcnt;
+	} else {
+		asoc->total_output_mbuf_queue_size = 0;
+	}
+	if ((stcb->sctp_ep->sctp_flags & SCTP_PCB_FLAGS_TCPTYPE) ||
+	    (stcb->sctp_ep->sctp_flags & SCTP_PCB_FLAGS_IN_TCPPOOL)) {
+		if (stcb->sctp_socket->so_snd.sb_cc >= tp1->book_size) {
+			stcb->sctp_socket->so_snd.sb_cc -= tp1->book_size;
+		} else {
+			stcb->sctp_socket->so_snd.sb_cc = 0;
+
+		}
+		if (stcb->sctp_socket->so_snd.sb_mbcnt >= tp1->mbcnt) {
+			stcb->sctp_socket->so_snd.sb_mbcnt -= tp1->mbcnt;
+		} else {
+			stcb->sctp_socket->so_snd.sb_mbcnt = 0;
+		}
+	}
+}
+
+int
+sctp_release_pr_sctp_chunk(struct sctp_tcb *stcb, struct sctp_tmit_chunk *tp1,
+    int reason, struct sctpchunk_listhead *queue)
+{
+	int ret_sz = 0;
+	int notdone;
+	uint8_t foundeom = 0;
+
+	do {
+		ret_sz += tp1->book_size;
+		tp1->sent = SCTP_FORWARD_TSN_SKIP;
+		if (tp1->data) {
+			sctp_free_bufspace(stcb, &stcb->asoc, tp1);
+			sctp_ulp_notify(SCTP_NOTIFY_DG_FAIL, stcb, reason, tp1);
+			sctp_m_freem(tp1->data);
+			tp1->data = NULL;
+			sctp_sowwakeup(stcb->sctp_ep, stcb->sctp_socket);
+		}
+		if (tp1->flags & SCTP_PR_SCTP_BUFFER) {
+			stcb->asoc.sent_queue_cnt_removeable--;
+		}
+		if (queue == &stcb->asoc.send_queue) {
+			TAILQ_REMOVE(&stcb->asoc.send_queue, tp1, sctp_next);
+			/* on to the sent queue */
+			TAILQ_INSERT_TAIL(&stcb->asoc.sent_queue, tp1,
+			    sctp_next);
+			stcb->asoc.sent_queue_cnt++;
+		}
+		if ((tp1->rec.data.rcv_flags & SCTP_DATA_NOT_FRAG) ==
+		    SCTP_DATA_NOT_FRAG) {
+			/* not frag'ed we ae done   */
+			notdone = 0;
+			foundeom = 1;
+		} else if (tp1->rec.data.rcv_flags & SCTP_DATA_LAST_FRAG) {
+			/* end of frag, we are done */
+			notdone = 0;
+			foundeom = 1;
+		} else {
+			/* Its a begin or middle piece, we must mark all of it */
+			notdone = 1;
+			tp1 = TAILQ_NEXT(tp1, sctp_next);
+		}
+	} while (tp1 && notdone);
+	if ((foundeom == 0) && (queue == &stcb->asoc.sent_queue)) {
+		/*
+		 * The multi-part message was scattered
+		 * across the send and sent queue.
+		 */
+		tp1 = TAILQ_FIRST(&stcb->asoc.send_queue);
+		/*
+		 * recurse throught the send_queue too, starting at the
+		 * beginning.
+		 */
+		if (tp1) {
+			ret_sz += sctp_release_pr_sctp_chunk(stcb, tp1, reason,
+			    &stcb->asoc.send_queue);
+		} else {
+			printf("hmm, nothing on the send queue and no EOM?\n");
+		}
+	}
+	return (ret_sz);
+}
+
+/*
+ * checks to see if the given address, sa, is one that is currently
+ * known by the kernel
+ * note: can't distinguish the same address on multiple interfaces and
+ *       doesn't handle multiple addresses with different zone/scope id's
+ * note: ifa_ifwithaddr() compares the entire sockaddr struct
+ */
+struct ifaddr *
+sctp_find_ifa_by_addr(struct sockaddr *sa)
+{
+	struct ifnet *ifn;
+	struct ifaddr *ifa;
+
+	/* go through all our known interfaces */
+	TAILQ_FOREACH(ifn, &ifnet, if_list) {
+		/* go through each interface addresses */
+		TAILQ_FOREACH(ifa, &ifn->if_addrlist, ifa_list) {
+			/* correct family? */
+			if (ifa->ifa_addr->sa_family != sa->sa_family)
+				continue;
+
+#ifdef INET6
+			if (ifa->ifa_addr->sa_family == AF_INET6) {
+				/* IPv6 address */
+				struct sockaddr_in6 *sin1, *sin2, sin6_tmp;
+				sin1 = (struct sockaddr_in6 *)ifa->ifa_addr;
+				if (IN6_IS_SCOPE_LINKLOCAL(&sin1->sin6_addr)) {
+					/* create a copy and clear scope */
+					memcpy(&sin6_tmp, sin1,
+					    sizeof(struct sockaddr_in6));
+					sin1 = &sin6_tmp;
+					in6_clearscope(&sin1->sin6_addr);
+				}
+				sin2 = (struct sockaddr_in6 *)sa;
+				if (memcmp(&sin1->sin6_addr, &sin2->sin6_addr,
+					   sizeof(struct in6_addr)) == 0) {
+					/* found it */
+					return (ifa);
+				}
+			} else
+#endif
+			if (ifa->ifa_addr->sa_family == AF_INET) {
+				/* IPv4 address */
+				struct sockaddr_in *sin1, *sin2;
+				sin1 = (struct sockaddr_in *)ifa->ifa_addr;
+				sin2 = (struct sockaddr_in *)sa;
+				if (sin1->sin_addr.s_addr ==
+				    sin2->sin_addr.s_addr) {
+					/* found it */
+					return (ifa);
+				}
+			}
+			/* else, not AF_INET or AF_INET6, so skip */
+		} /* end foreach ifa */
+	} /* end foreach ifn */
+	/* not found! */
+	return (NULL);
+}
+
+
+#ifdef __APPLE__
+/*
+ * here we hack in a fix for Apple's m_copym for the case where the first mbuf
+ * in the chain is a M_PKTHDR and the length is zero
+ */
+static void
+sctp_pkthdr_fix(struct mbuf *m)
+{
+	struct mbuf *m_nxt;
+
+	if ((m->m_flags & M_PKTHDR) == 0) {
+		/* not a PKTHDR */
+		return;
+	}
+
+	if (m->m_len != 0) {
+		/* not a zero length PKTHDR mbuf */
+		return;
+	}
+
+	/* let's move in a word into the first mbuf... yes, ugly! */
+	m_nxt = m->m_next;
+	if (m_nxt == NULL) {
+		/* umm... not a very useful mbuf chain... */
+		return;
+	}
+	if ((size_t)m_nxt->m_len > sizeof(long)) {
+		/* move over a long */
+		bcopy(mtod(m_nxt, caddr_t), mtod(m, caddr_t), sizeof(long));
+		/* update mbuf data pointers and lengths */
+		m->m_len += sizeof(long);
+		m_nxt->m_data += sizeof(long);
+		m_nxt->m_len -= sizeof(long);
+	}
+}
+
+inline struct mbuf *
+sctp_m_copym(struct mbuf *m, int off, int len, int wait)
+{
+	sctp_pkthdr_fix(m);
+	return (m_copym(m, off, len, wait));
+}
+#endif /* __APPLE__ */
Index: sys/netinet/sctputil.h
===================================================================
RCS file: sys/netinet/sctputil.h
diff -N sys/netinet/sctputil.h
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ sys/netinet/sctputil.h	18 Jun 2005 16:05:50 -0000
@@ -0,0 +1,288 @@
+/*	$KAME: sctputil.h,v 1.14 2004/08/17 04:06:21 itojun Exp $	*/
+
+#ifndef __sctputil_h__
+#define __sctputil_h__
+
+/*
+ * Copyright (C) 2002, 2003, 2004 Cisco Systems Inc,
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the project nor the names of its contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#if defined(_KERNEL) || (defined(__APPLE__) && defined(KERNEL))
+
+#ifdef SCTP_MBUF_DEBUG
+#define sctp_m_freem(m) do { \
+    printf("m_freem(%p) m->nxtpkt:%p at %s[%d]\n", \
+	   (m), (m)->m_next, __FILE__, __LINE__); \
+    m_freem(m); \
+} while (0);
+#else
+#define sctp_m_freem m_freem
+#endif
+
+#ifdef __APPLE__
+struct mbuf *sctp_m_copym(struct mbuf *m, int off, int len, int wait);
+#else
+#define sctp_m_copym	m_copym
+#endif /* __APPLE__ */
+
+/*
+ * Zone(pool) allocation routines: MUST be defined for each OS
+ * zone = zone/pool pointer
+ * name = string name of the zone/pool
+ * size = size of each zone/pool element
+ * number = number of elements in zone/pool
+ */
+#if defined(__FreeBSD__) || defined(__DragonFly__)
+#if __FreeBSD_version >= 500000
+#include <vm/uma.h>
+#else
+#include <vm/vm_zone.h>
+#endif
+#elif defined(__NetBSD__) || defined(__OpenBSD__)
+#include <sys/pool.h>
+#endif
+
+/* SCTP_ZONE_INIT: initialize the zone */
+#if defined(__FreeBSD__) || defined(__DragonFly__)
+#if __FreeBSD_version >= 500000
+#define UMA_ZFLAG_FULL	0x0020
+#define SCTP_ZONE_INIT(zone, name, size, number) { \
+	zone = uma_zcreate(name, size, NULL, NULL, NULL, NULL, UMA_ALIGN_PTR,\
+		UMA_ZFLAG_FULL); \
+	uma_zone_set_max(zone, number); \
+}
+#else
+#define SCTP_ZONE_INIT(zone, name, size, number) \
+	zone = zinit(name, size, number, ZONE_INTERRUPT, 0);
+#endif
+#elif defined(__APPLE__)
+#define SCTP_ZONE_INIT(zone, name, size, number) \
+	zone = (void *)zinit(size, number * size, number, name);
+#elif defined(__OpenBSD__) || defined(__NetBSD__)
+#define SCTP_ZONE_INIT(zone, name, size, number) \
+	pool_init(&(zone), size, 0, 0, 0, name, NULL);
+#else
+	/* don't know this OS! */
+	force_comile_error;
+#endif
+
+/* SCTP_ZONE_GET: allocate element from the zone */
+#if defined(__FreeBSD__)
+#if __FreeBSD_version >= 500000
+#define SCTP_ZONE_GET(zone) \
+	uma_zalloc(zone, M_NOWAIT);
+#else
+#define SCTP_ZONE_GET(zone) \
+	zalloci(zone);
+#endif
+#elif defined(__DragonFly__)
+#define SCTP_ZONE_GET(zone) \
+		zalloc(zone);
+#elif defined(__APPLE__)
+#define SCTP_ZONE_GET(zone) \
+	zalloc(zone);
+#elif defined(__NetBSD__) || defined(__OpenBSD__)
+#define SCTP_ZONE_GET(zone) \
+	pool_get(&zone, PR_NOWAIT);
+#else
+	/* don't know this OS! */
+	force_comile_error;
+#endif
+
+/* SCTP_ZONE_FREE: free element from the zone */
+#if defined(__FreeBSD__)
+#if __FreeBSD_version >= 500000
+#define SCTP_ZONE_FREE(zone, element) \
+	uma_zfree(zone, element);
+#else
+#define SCTP_ZONE_FREE(zone, element) \
+	zfreei(zone, element);
+#endif
+#elif defined(__DragonFly__)
+#define SCTP_ZONE_FREE(zone, element) \
+	zfree(zone, element);
+#elif defined(__APPLE__)
+#define SCTP_ZONE_FREE(zone, element) \
+	zfree(zone, element);
+#elif defined(__NetBSD__) || defined(__OpenBSD__)
+#define SCTP_ZONE_FREE(zone, element) \
+	pool_put(&zone, element);
+#else
+	/* don't know this OS! */
+	force_comile_error;
+#endif
+
+#define sctp_get_associd(stcb) ((sctp_assoc_t)stcb->asoc.my_vtag)
+
+/*
+ * Function prototypes
+ */
+struct ifaddr *sctp_find_ifa_by_addr(struct sockaddr *sa);
+
+u_int32_t sctp_select_initial_TSN(struct sctp_pcb *);
+
+u_int32_t sctp_select_a_tag(struct sctp_inpcb *);
+
+int sctp_init_asoc(struct sctp_inpcb *, struct sctp_association *, int, uint32_t);
+
+void sctp_fill_random_store(struct sctp_pcb *);
+
+int sctp_timer_start(int, struct sctp_inpcb *, struct sctp_tcb *,
+	struct sctp_nets *);
+
+int sctp_timer_stop(int, struct sctp_inpcb *, struct sctp_tcb *,
+	struct sctp_nets *);
+
+u_int32_t sctp_calculate_sum(struct mbuf *, int32_t *, u_int32_t);
+
+void sctp_mtu_size_reset(struct sctp_inpcb *, struct sctp_association *,
+	u_long);
+
+int find_next_best_mtu(int);
+
+u_int32_t sctp_calculate_rto(struct sctp_tcb *, struct sctp_association *,
+	struct sctp_nets *, struct timeval *);
+
+u_int32_t sctp_calculate_len(struct mbuf *);
+
+caddr_t sctp_m_getptr(struct mbuf *, int, int, u_int8_t *);
+
+struct sctp_paramhdr *sctp_get_next_param(struct mbuf *, int,
+	struct sctp_paramhdr *, int);
+
+int sctp_add_pad_tombuf(struct mbuf *, int);
+
+int sctp_pad_lastmbuf(struct mbuf *, int);
+
+void sctp_ulp_notify(u_int32_t, struct sctp_tcb *, u_int32_t, void *);
+
+void sctp_report_all_outbound(struct sctp_tcb *);
+
+int sctp_expand_mapping_array(struct sctp_association *);
+
+void sctp_abort_notification(struct sctp_tcb *, int);
+
+/* We abort responding to an IP packet for some reason */
+void sctp_abort_association(struct sctp_inpcb *, struct sctp_tcb *,
+    struct mbuf *, int, struct sctphdr *, struct mbuf *);
+
+/* We choose to abort via user input */
+void sctp_abort_an_association(struct sctp_inpcb *, struct sctp_tcb *, int,
+	struct mbuf *);
+
+void sctp_handle_ootb(struct mbuf *, int, int, struct sctphdr *,
+    struct sctp_inpcb *, struct mbuf *);
+
+int sctp_is_there_an_abort_here(struct mbuf *, int, int *);
+uint32_t sctp_is_same_scope(struct sockaddr_in6 *, struct sockaddr_in6 *);
+struct sockaddr_in6 *sctp_recover_scope(struct sockaddr_in6 *,
+	struct sockaddr_in6 *);
+
+int sctp_cmpaddr(struct sockaddr *, struct sockaddr *);
+
+void sctp_print_address(struct sockaddr *);
+void sctp_print_address_pkt(struct ip *, struct sctphdr *);
+
+int sbappendaddr_nocheck __P((struct sockbuf *, struct sockaddr *,
+	struct mbuf *, struct mbuf *, u_int32_t, struct sctp_inpcb *));
+
+
+int sctp_release_pr_sctp_chunk(struct sctp_tcb *, struct sctp_tmit_chunk *,
+	int, struct sctpchunk_listhead *);
+
+struct mbuf *sctp_generate_invmanparam(int);
+
+/*
+ * this is an evil layer violation that I think is a hack.. but I stand
+ * alone on the tsvwg in this thought... everyone else considers it part
+ * of the sockets layer (along with all of the peeloff code :<)
+ */
+u_int32_t sctp_get_first_vtag_from_sb(struct socket *);
+
+
+void sctp_grub_through_socket_buffer(struct sctp_inpcb *, struct socket *,
+				     struct socket *, struct sctp_tcb *);
+
+void sctp_free_bufspace(struct sctp_tcb *, struct sctp_association *,
+	struct sctp_tmit_chunk *);
+
+#ifdef SCTP_STAT_LOGGING
+void sctp_log_strm_del_alt(u_int32_t, u_int16_t, int);
+
+void sctp_log_strm_del(struct sctp_tmit_chunk *, struct sctp_tmit_chunk *, int);
+void sctp_log_cwnd(struct sctp_nets *, int, uint8_t);
+void sctp_log_maxburst(struct sctp_nets *, int, int, uint8_t);
+void sctp_log_block(uint8_t, struct socket *, struct sctp_association *);
+void sctp_log_rwnd(uint8_t, u_int32_t, u_int32_t, u_int32_t );
+void sctp_log_mbcnt(uint8_t, u_int32_t, u_int32_t, u_int32_t, u_int32_t);
+void sctp_log_rwnd_set(uint8_t, u_int32_t, u_int32_t, u_int32_t, u_int32_t);
+int sctp_fill_stat_log(struct mbuf *);
+void sctp_log_fr(uint32_t, uint32_t, uint32_t, int);
+void sctp_log_map(uint32_t, uint32_t, uint32_t, int);
+
+void sctp_clr_stat_log(void);
+
+#endif
+
+#ifdef SCTP_AUDITING_ENABLED
+void sctp_auditing(int, struct sctp_inpcb *, struct sctp_tcb *,
+	struct sctp_nets *);
+void sctp_audit_log(u_int8_t, u_int8_t);
+
+#endif
+
+#if defined(SCTP_BASE_FREEBSD) || defined(__DragonFly__)
+/* Note: these are in <sys/time.h>, but not in kernel space */
+#define	timerclear(tvp)		(tvp)->tv_sec = (tvp)->tv_usec = 0
+#define	timerisset(tvp)		((tvp)->tv_sec || (tvp)->tv_usec)
+#define	timercmp(tvp, uvp, cmp)						\
+	(((tvp)->tv_sec == (uvp)->tv_sec) ?				\
+	    ((tvp)->tv_usec cmp (uvp)->tv_usec) :			\
+	    ((tvp)->tv_sec cmp (uvp)->tv_sec))
+#define	timeradd(tvp, uvp, vvp)						\
+	do {								\
+		(vvp)->tv_sec = (tvp)->tv_sec + (uvp)->tv_sec;		\
+		(vvp)->tv_usec = (tvp)->tv_usec + (uvp)->tv_usec;	\
+		if ((vvp)->tv_usec >= 1000000) {			\
+			(vvp)->tv_sec++;				\
+			(vvp)->tv_usec -= 1000000;			\
+		}							\
+	} while (/* CONSTCOND */ 0)
+#define	timersub(tvp, uvp, vvp)						\
+	do {								\
+		(vvp)->tv_sec = (tvp)->tv_sec - (uvp)->tv_sec;		\
+		(vvp)->tv_usec = (tvp)->tv_usec - (uvp)->tv_usec;	\
+		if ((vvp)->tv_usec < 0) {				\
+			(vvp)->tv_sec--;				\
+			(vvp)->tv_usec += 1000000;			\
+		}							\
+	} while (/* CONSTCOND */ 0)
+#endif /* SCTP_BASE_FREEBSD */
+
+#endif /* _KERNEL */
+#endif
Index: sys/netinet6/sctp6_usrreq.c
===================================================================
RCS file: sys/netinet6/sctp6_usrreq.c
diff -N sys/netinet6/sctp6_usrreq.c
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ sys/netinet6/sctp6_usrreq.c	18 Jun 2005 16:05:50 -0000
@@ -0,0 +1,1780 @@
+/*	$KAME: sctp6_usrreq.c,v 1.35 2004/08/17 06:28:03 t-momose Exp $	*/
+
+/*
+ * Copyright (c) 2001, 2002, 2003, 2004 Cisco Systems, Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *      This product includes software developed by Cisco Systems, Inc.
+ * 4. Neither the name of the project nor the names of its contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY CISCO SYSTEMS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL CISCO SYSTEMS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+#if !(defined(__OpenBSD__) || defined(__APPLE__))
+#include "opt_inet.h"
+#endif
+#if defined(__FreeBSD__) || defined(__DragonFly__)
+#include "opt_inet6.h"
+#include "opt_inet.h"
+#endif
+#ifdef __NetBSD__
+#include "opt_inet.h"
+#endif
+#if !(defined(__OpenBSD__) || defined(__APPLE__))
+#include "opt_ipsec.h"
+#endif
+#ifdef __APPLE__
+#include <sctp.h>
+#elif !defined(__OpenBSD__)
+#include "opt_sctp.h"
+#endif
+
+#include <sys/param.h>
+#include <sys/kernel.h>
+#include <sys/mbuf.h>
+#include <sys/domain.h>
+#include <sys/protosw.h>
+#include <sys/socket.h>
+#include <sys/malloc.h>
+#include <sys/socketvar.h>
+#include <sys/sysctl.h>
+#include <sys/errno.h>
+#include <sys/stat.h>
+#include <sys/systm.h>
+#include <sys/syslog.h>
+#include <sys/proc.h>
+#include <sys/thread2.h>
+#include <net/if.h>
+#include <net/route.h>
+#include <net/if_types.h>
+#include <netinet/in.h>
+#include <netinet/in_systm.h>
+#include <netinet/ip.h>
+#include <netinet/in_pcb.h>
+#include <netinet/in_var.h>
+#include <netinet/ip_var.h>
+#include <netinet/sctp_pcb.h>
+#include <netinet/sctp_header.h>
+#include <netinet/sctp_var.h>
+#include <netinet/sctputil.h>
+#include <netinet/sctp_output.h>
+#include <netinet/sctp_input.h>
+#include <netinet/sctp_asconf.h>
+#include <netinet6/ip6_var.h>
+#include <netinet/ip6.h>
+#if !defined(__OpenBSD__)
+#include <netinet6/in6_pcb.h>
+#endif
+#include <netinet/icmp6.h>
+#include <netinet6/sctp6_var.h>
+#include <netinet6/ip6protosw.h>
+#include <netinet6/nd6.h>
+
+#ifdef IPSEC
+#ifndef __OpenBSD__
+#include <netinet6/ipsec.h>
+#else
+#undef IPSEC
+#endif
+#endif /*IPSEC*/
+
+#if defined(NFAITH) && NFAITH > 0
+#include <net/if_faith.h>
+#endif
+
+#include <net/net_osdep.h>
+
+extern struct protosw inetsw[];
+
+#if defined(HAVE_NRL_INPCB) || defined(__FreeBSD__) || defined(__DragonFly__)
+#ifndef in6pcb
+#define in6pcb		inpcb
+#endif
+#ifndef sotoin6pcb
+#define sotoin6pcb      sotoinpcb
+#endif
+#endif
+
+#ifdef SCTP_DEBUG
+extern u_int32_t sctp_debug_on;
+#endif
+
+static	int sctp6_detach __P((struct socket *so));
+
+#if !(defined(__FreeBSD__) || defined(__APPLE__) || defined(__DragonFly__))
+extern void in6_sin_2_v4mapsin6 (struct sockaddr_in *sin,
+				 struct sockaddr_in6 *sin6);
+extern void in6_sin6_2_sin (struct sockaddr_in *,
+			    struct sockaddr_in6 *sin6);
+extern void in6_sin6_2_sin_in_sock(struct sockaddr *nam);
+
+/*
+ * Convert sockaddr_in6 to sockaddr_in.  Original sockaddr_in6 must be
+ * v4 mapped addr or v4 compat addr
+ */
+void
+in6_sin6_2_sin(struct sockaddr_in *sin, struct sockaddr_in6 *sin6)
+{
+	bzero(sin, sizeof(*sin));
+	sin->sin_len = sizeof(struct sockaddr_in);
+	sin->sin_family = AF_INET;
+	sin->sin_port = sin6->sin6_port;
+	sin->sin_addr.s_addr = sin6->sin6_addr.s6_addr32[3];
+}
+
+/* Convert sockaddr_in to sockaddr_in6 in v4 mapped addr format. */
+void
+in6_sin_2_v4mapsin6(struct sockaddr_in *sin, struct sockaddr_in6 *sin6)
+{
+	bzero(sin6, sizeof(*sin6));
+	sin6->sin6_len = sizeof(struct sockaddr_in6);
+	sin6->sin6_family = AF_INET6;
+	sin6->sin6_port = sin->sin_port;
+	sin6->sin6_addr.s6_addr32[0] = 0;
+	sin6->sin6_addr.s6_addr32[1] = 0;
+	sin6->sin6_addr.s6_addr32[2] = IPV6_ADDR_INT32_SMP;
+	sin6->sin6_addr.s6_addr32[3] = sin->sin_addr.s_addr;
+}
+
+/* Convert sockaddr_in6 into sockaddr_in. */
+void
+in6_sin6_2_sin_in_sock(struct sockaddr *nam)
+{
+	struct sockaddr_in *sin_p;
+	struct sockaddr_in6 sin6;
+
+	/* save original sockaddr_in6 addr and convert it to sockaddr_in  */
+	sin6 = *(struct sockaddr_in6 *)nam;
+	sin_p = (struct sockaddr_in *)nam;
+	in6_sin6_2_sin(sin_p, &sin6);
+}
+
+#endif /* !(__FreeBSD__ || __APPLE__) */
+
+extern int sctp_no_csum_on_loopback;
+
+int
+#if defined(__APPLE__)
+sctp6_input(mp, offp)
+#else
+sctp6_input(mp, offp, proto)
+#endif
+     struct mbuf **mp;
+     int *offp;
+#ifndef __APPLE__
+     int proto;
+#endif
+{
+	struct mbuf *m = *mp;
+	struct ip6_hdr *ip6;
+	struct sctphdr *sh;
+	struct sctp_inpcb *in6p = NULL;
+	struct sctp_nets *net;
+	int refcount_up = 0;
+	u_int32_t check, calc_check;
+	struct inpcb *in6p_ip;
+	struct sctp_chunkhdr *ch;
+	struct mbuf *opts = NULL;
+	int length, mlen, offset, iphlen;
+	u_int8_t ecn_bits;
+	struct sctp_tcb *stcb = NULL;
+	int off = *offp;
+
+	ip6 = mtod(m, struct ip6_hdr *);
+#ifndef PULLDOWN_TEST
+	/* If PULLDOWN_TEST off, must be in a single mbuf. */
+	IP6_EXTHDR_CHECK(m, off, (int)(sizeof(*sh) + sizeof(*ch)), IPPROTO_DONE);
+	sh = (struct sctphdr *)((caddr_t)ip6 + off);
+	ch = (struct sctp_chunkhdr *)((caddr_t)sh + sizeof(*sh));
+#else
+	/* Ensure that (sctphdr + sctp_chunkhdr) in a row. */
+	IP6_EXTHDR_GET(sh, struct sctphdr *, m, off, sizeof(*sh) + sizeof(*ch));
+	if (sh == NULL) {
+		sctp_pegs[SCTP_HDR_DROPS]++;
+		return IPPROTO_DONE;
+	}
+	ch = (struct sctp_chunkhdr *)((caddr_t)sh + sizeof(struct sctphdr));
+#endif
+
+	iphlen = off;
+	offset = iphlen + sizeof(*sh) + sizeof(*ch);
+
+#if defined(NFAITH) && NFAITH > 0
+#if defined(__FreeBSD_cc_version) && __FreeBSD_cc_version <= 430000
+#if defined(NFAITH) && 0 < NFAITH
+	if (faithprefix(&ip6h->ip6_dst)) {
+		/* XXX send icmp6 host/port unreach? */
+		goto bad;
+	}
+#endif
+#else
+
+#ifdef __FreeBSD__
+	if (faithprefix_p != NULL && (*faithprefix_p)(&ip6->ip6_dst)) {
+		/* XXX send icmp6 host/port unreach? */
+		goto bad;
+	}
+#else
+	if (faithprefix(&ip6->ip6_dst))
+		goto bad;
+#endif
+#endif /* __FreeBSD_cc_version */
+
+#endif /* NFAITH defined and > 0 */
+	sctp_pegs[SCTP_INPKTS]++;
+#ifdef SCTP_DEBUG
+	if (sctp_debug_on & SCTP_DEBUG_INPUT1) {
+		printf("V6 input gets a packet iphlen:%d pktlen:%d\n", iphlen, m->m_pkthdr.len);
+	}
+#endif
+ 	if (IN6_IS_ADDR_MULTICAST(&ip6->ip6_dst)) {
+		/* No multi-cast support in SCTP */
+		sctp_pegs[SCTP_IN_MCAST]++;
+		goto bad;
+	}
+	/* destination port of 0 is illegal, based on RFC2960. */
+	if (sh->dest_port == 0)
+		goto bad;
+	if ((sctp_no_csum_on_loopback == 0) ||
+	   (m->m_pkthdr.rcvif == NULL) ||
+	   (m->m_pkthdr.rcvif->if_type != IFT_LOOP)) {
+		/* we do NOT validate things from the loopback if the
+		 * sysctl is set to 1.
+		 */
+		check = sh->checksum;		/* save incoming checksum */
+		if ((check == 0) && (sctp_no_csum_on_loopback)) {
+			/* special hook for where we got a local address
+			 * somehow routed across a non IFT_LOOP type interface
+			 */
+			if (IN6_ARE_ADDR_EQUAL(&ip6->ip6_src, &ip6->ip6_dst))
+				goto sctp_skip_csum;
+		}
+		sh->checksum = 0;		/* prepare for calc */
+		calc_check = sctp_calculate_sum(m, &mlen, iphlen);
+		if (calc_check != check) {
+#ifdef SCTP_DEBUG
+			if (sctp_debug_on & SCTP_DEBUG_INPUT1) {
+				printf("Bad CSUM on SCTP packet calc_check:%x check:%x  m:%x mlen:%d iphlen:%d\n",
+				       calc_check, check, (u_int)m,
+				       mlen, iphlen);
+			}
+#endif
+			stcb = sctp_findassociation_addr(m, iphlen, offset - sizeof(*ch),
+							 sh, ch, &in6p, &net);
+			/* in6p's ref-count increased && stcb locked */
+			if ((in6p) && (stcb)) {
+				sctp_send_packet_dropped(stcb, net, m, iphlen, 1);
+				sctp_chunk_output((struct sctp_inpcb *)in6p, stcb, 2);
+			}  else if ((in6p != NULL) && (stcb == NULL)) {
+				refcount_up = 1;
+			}
+			sctp_pegs[SCTP_BAD_CSUM]++;
+			goto bad;
+		}
+		sh->checksum = calc_check;
+	} else {
+sctp_skip_csum:
+		mlen = m->m_pkthdr.len;
+	}
+	net = NULL;
+	/*
+	 * Locate pcb and tcb for datagram
+	 * sctp_findassociation_addr() wants IP/SCTP/first chunk header...
+	 */
+#ifdef SCTP_DEBUG
+	if (sctp_debug_on & SCTP_DEBUG_INPUT1) {
+		printf("V6 Find the association\n");
+	}
+#endif
+	stcb = sctp_findassociation_addr(m, iphlen, offset - sizeof(*ch),
+	    sh, ch, &in6p, &net);
+	/* in6p's ref-count increased */
+	if (in6p == NULL) {
+		struct sctp_init_chunk *init_chk, chunk_buf;
+
+		sctp_pegs[SCTP_NOPORTS]++;
+		if (ch->chunk_type == SCTP_INITIATION) {
+			/* we do a trick here to get the INIT tag,
+			 * dig in and get the tag from the INIT and
+			 * put it in the common header.
+			 */
+			init_chk = (struct sctp_init_chunk *)sctp_m_getptr(m,
+			    iphlen + sizeof(*sh), sizeof(*init_chk),
+			    (u_int8_t *)&chunk_buf);
+			sh->v_tag = init_chk->init.initiate_tag;
+		}
+		sctp_send_abort(m, iphlen, sh, 0, NULL);
+		goto bad;
+	} else if (stcb == NULL) {
+		refcount_up = 1;
+	}
+	in6p_ip = (struct inpcb *)in6p;
+#ifdef IPSEC
+	/*
+	 * Check AH/ESP integrity.
+	 */
+#ifdef __OpenBSD__
+	{
+		struct inpcb *i_inp;
+		struct m_tag *mtag;
+		struct tdb_ident *tdbi;
+		struct tdb *tdb;
+		int error;
+
+		/* Find most recent IPsec tag */
+		i_inp = (struct inpcb *)in6p;
+		mtag = m_tag_find(m, PACKET_TAG_IPSEC_IN_DONE, NULL);
+		crit_enter();
+		if (mtag != NULL) {
+			tdbi = (struct tdb_ident *)(mtag + 1);
+			tdb = gettdb(tdbi->spi, &tdbi->dst, tdbi->proto);
+		} else
+			tdb = NULL;
+
+		ipsp_spd_lookup(m, af, iphlen, &error, IPSP_DIRECTION_IN,
+		    tdb, i_inp);
+		if (error) {
+			crit_exit();
+			goto bad;
+		}
+
+		/* Latch SA */
+		if (i_inp->inp_tdb_in != tdb) {
+			if (tdb) {
+				tdb_add_inp(tdb, i_inp, 1);
+				if (i_inp->inp_ipo == NULL) {
+					i_inp->inp_ipo = ipsec_add_policy(i_inp,
+					    af, IPSP_DIRECTION_OUT);
+					if (i_inp->inp_ipo == NULL) {
+						crit_exit();
+						goto bad;
+					}
+				}
+				if (i_inp->inp_ipo->ipo_dstid == NULL &&
+				    tdb->tdb_srcid != NULL) {
+					i_inp->inp_ipo->ipo_dstid =
+					    tdb->tdb_srcid;
+					tdb->tdb_srcid->ref_count++;
+				}
+				if (i_inp->inp_ipsec_remotecred == NULL &&
+				    tdb->tdb_remote_cred != NULL) {
+					i_inp->inp_ipsec_remotecred =
+					    tdb->tdb_remote_cred;
+					tdb->tdb_remote_cred->ref_count++;
+				}
+				if (i_inp->inp_ipsec_remoteauth == NULL &&
+				    tdb->tdb_remote_auth != NULL) {
+					i_inp->inp_ipsec_remoteauth =
+					    tdb->tdb_remote_auth;
+					tdb->tdb_remote_auth->ref_count++;
+				}
+			} else { /* Just reset */
+				TAILQ_REMOVE(&i_inp->inp_tdb_in->tdb_inp_in,
+				    i_inp, binp_tdb_in_next);
+				i_inp->inp_tdb_in = NULL;
+			}
+		}
+		crit_exit();
+	}
+#else
+	if (ipsec6_in_reject_so(m, in6p->sctp_socket)) {
+/* XXX */
+#ifndef __APPLE__
+		/* FIX ME: need to find right stat for __APPLE__ */
+		ipsec6stat.in_polvio++;
+#endif
+		goto bad;
+	}
+#endif
+#endif /*IPSEC*/
+
+	/*
+	 * Construct sockaddr format source address.
+	 * Stuff source address and datagram in user buffer.
+	 */
+	if ((in6p->ip_inp.inp.inp_flags & INP_CONTROLOPTS)
+#ifndef __OpenBSD__
+	    || (in6p->sctp_socket->so_options & SO_TIMESTAMP)
+#endif
+	    ) {
+#if defined(__FreeBSD__) || defined(__APPLE__) || defined(__DragonFly__)
+#if (defined(SCTP_BASE_FREEBSD) && __FreeBSD_version < 501113) || \
+    defined(__APPLE__) || defined(__DragonFly__)
+		ip6_savecontrol(in6p_ip, &opts, ip6, m);
+#elif __FreeBSD_version >= 440000 || (defined(SCTP_BASE_FREEBSD) && __FreeBSD_version >= 501113)
+		ip6_savecontrol(in6p_ip, m, &opts);
+#else
+		ip6_savecontrol(in6p_ip, m, &opts, NULL);
+#endif
+#else
+		ip6_savecontrol((struct in6pcb *)in6p_ip, m, &opts);
+#endif
+	}
+
+	/*
+	 * CONTROL chunk processing
+	 */
+	length = ntohs(ip6->ip6_plen) + iphlen;
+	offset -= sizeof(*ch);
+	ecn_bits = ((ntohl(ip6->ip6_flow) >> 20) & 0x000000ff);
+	crit_enter();
+	(void)sctp_common_input_processing(&m, iphlen, offset, length, sh, ch,
+	    in6p, stcb, net, ecn_bits);
+	/* inp's ref-count reduced && stcb unlocked */
+	crit_exit();
+	/* XXX this stuff below gets moved to appropriate parts later... */
+	if (m)
+		m_freem(m);
+	if (opts)
+		m_freem(opts);
+
+	if ((in6p) && refcount_up){
+		/* reduce ref-count */
+		SCTP_INP_WLOCK(in6p);
+		SCTP_INP_DECR_REF(in6p);
+		SCTP_INP_WUNLOCK(in6p);
+	}
+
+	return IPPROTO_DONE;
+
+bad:
+	if (stcb)
+		SCTP_TCB_UNLOCK(stcb);
+
+	if ((in6p) && refcount_up){
+		/* reduce ref-count */
+		SCTP_INP_WLOCK(in6p);
+		SCTP_INP_DECR_REF(in6p);
+		SCTP_INP_WUNLOCK(in6p);
+	}
+	if (m)
+		m_freem(m);
+	if (opts)
+		m_freem(opts);
+	return IPPROTO_DONE;
+}
+
+
+static void
+sctp6_notify_mbuf(struct sctp_inpcb *inp,
+		  struct icmp6_hdr *icmp6,
+		  struct sctphdr *sh,
+		  struct sctp_tcb *stcb,
+		  struct sctp_nets *net)
+{
+	unsigned int nxtsz;
+
+	if ((inp == NULL) || (stcb == NULL) || (net == NULL) ||
+	    (icmp6 == NULL) || (sh == NULL)) {
+		goto out;
+	}
+
+	/* First do we even look at it? */
+	if (ntohl(sh->v_tag) != (stcb->asoc.peer_vtag))
+		goto out;
+
+	if (icmp6->icmp6_type != ICMP6_PACKET_TOO_BIG) {
+		/* not PACKET TO BIG */
+		goto out;
+	}
+	/*
+	 * ok we need to look closely. We could even get smarter and
+	 * look at anyone that we sent to in case we get a different
+	 * ICMP that tells us there is no way to reach a host, but for
+	 * this impl, all we care about is MTU discovery.
+	 */
+	nxtsz = ntohl(icmp6->icmp6_mtu);
+	/* Stop any PMTU timer */
+	sctp_timer_stop(SCTP_TIMER_TYPE_PATHMTURAISE, inp, stcb, NULL);
+
+	/* Adjust destination size limit */
+	if (net->mtu > nxtsz) {
+		net->mtu = nxtsz;
+	}
+	/* now what about the ep? */
+	if (stcb->asoc.smallest_mtu > nxtsz) {
+		struct sctp_tmit_chunk *chk;
+		struct sctp_stream_out *strm;
+		/* Adjust that too */
+		stcb->asoc.smallest_mtu = nxtsz;
+		/* now off to subtract IP_DF flag if needed */
+
+		TAILQ_FOREACH(chk, &stcb->asoc.send_queue, sctp_next) {
+			if ((chk->send_size+IP_HDR_SIZE) > nxtsz) {
+				chk->flags |= CHUNK_FLAGS_FRAGMENT_OK;
+			}
+		}
+		TAILQ_FOREACH(chk, &stcb->asoc.sent_queue, sctp_next) {
+			if ((chk->send_size+IP_HDR_SIZE) > nxtsz) {
+				/*
+				 * For this guy we also mark for immediate
+				 * resend since we sent to big of chunk
+				 */
+				chk->flags |= CHUNK_FLAGS_FRAGMENT_OK;
+				if (chk->sent != SCTP_DATAGRAM_RESEND)
+					stcb->asoc.sent_queue_retran_cnt++;
+				chk->sent = SCTP_DATAGRAM_RESEND;
+				chk->rec.data.doing_fast_retransmit = 0;
+
+				chk->sent = SCTP_DATAGRAM_RESEND;
+				/* Clear any time so NO RTT is being done */
+				chk->sent_rcv_time.tv_sec = 0;
+				chk->sent_rcv_time.tv_usec = 0;
+				stcb->asoc.total_flight -= chk->send_size;
+				net->flight_size -= chk->send_size;
+			}
+		}
+		TAILQ_FOREACH(strm, &stcb->asoc.out_wheel, next_spoke) {
+			TAILQ_FOREACH(chk, &strm->outqueue, sctp_next) {
+				if ((chk->send_size+IP_HDR_SIZE) > nxtsz) {
+					chk->flags |= CHUNK_FLAGS_FRAGMENT_OK;
+				}
+			}
+		}
+	}
+	sctp_timer_start(SCTP_TIMER_TYPE_PATHMTURAISE, inp, stcb, NULL);
+out:
+	if (inp) {
+		/* reduce inp's ref-count */
+		SCTP_INP_WLOCK(inp);
+		SCTP_INP_DECR_REF(inp);
+		SCTP_INP_WUNLOCK(inp);
+	}
+	if (stcb)
+		SCTP_TCB_UNLOCK(stcb);
+}
+
+
+void
+sctp6_ctlinput(cmd, pktdst, d)
+     int cmd;
+     struct sockaddr *pktdst;
+     void *d;
+{
+	struct sctphdr sh;
+	struct ip6ctlparam *ip6cp = NULL;
+	int cm;
+
+	if (pktdst->sa_family != AF_INET6 ||
+	    pktdst->sa_len != sizeof(struct sockaddr_in6))
+		return;
+
+	if ((unsigned)cmd >= PRC_NCMDS)
+		return;
+	if (PRC_IS_REDIRECT(cmd)) {
+		d = NULL;
+	} else if (inet6ctlerrmap[cmd] == 0) {
+		return;
+	}
+
+	/* if the parameter is from icmp6, decode it. */
+	if (d != NULL) {
+		ip6cp = (struct ip6ctlparam *)d;
+	} else {
+		ip6cp = (struct ip6ctlparam *)NULL;
+	}
+
+	if (ip6cp) {
+		/*
+		 * XXX: We assume that when IPV6 is non NULL,
+		 * M and OFF are valid.
+		 */
+		/* check if we can safely examine src and dst ports */
+		struct sctp_inpcb *inp;
+		struct sctp_tcb *stcb;
+		struct sctp_nets *net;
+		struct sockaddr_in6 final;
+
+		if (ip6cp->ip6c_m == NULL ||
+		    (size_t)ip6cp->ip6c_m->m_pkthdr.len < (ip6cp->ip6c_off + sizeof(sh)))
+			return;
+
+		bzero(&sh, sizeof(sh));
+		bzero(&final, sizeof(final));
+		inp = NULL;
+		net = NULL;
+		m_copydata(ip6cp->ip6c_m, ip6cp->ip6c_off, sizeof(sh),
+		    (caddr_t)&sh);
+		ip6cp->ip6c_src->sin6_port = sh.src_port;
+		final.sin6_len = sizeof(final);
+		final.sin6_family = AF_INET6;
+#if defined(__FreeBSD__) && __FreeBSD_cc_version < 440000
+		final.sin6_addr = *ip6cp->ip6c_finaldst;
+#else
+		final.sin6_addr = ((struct sockaddr_in6 *)pktdst)->sin6_addr;
+#endif /* __FreeBSD_cc_version */
+		final.sin6_port = sh.dest_port;
+		crit_enter();
+		stcb = sctp_findassociation_addr_sa((struct sockaddr *)ip6cp->ip6c_src,
+						    (struct sockaddr *)&final,
+						    &inp, &net, 1);
+		/* inp's ref-count increased && stcb locked */
+		if (stcb != NULL && inp && (inp->sctp_socket != NULL)) {
+			if (cmd == PRC_MSGSIZE) {
+				sctp6_notify_mbuf(inp,
+						  ip6cp->ip6c_icmp6,
+						  &sh,
+						  stcb,
+						  net);
+				/* inp's ref-count reduced && stcb unlocked */
+			} else {
+				if (cmd == PRC_HOSTDEAD) {
+					cm = EHOSTUNREACH;
+				} else {
+					cm = inet6ctlerrmap[cmd];
+				}
+				sctp_notify(inp, cm, &sh,
+					    (struct sockaddr *)&final,
+					    stcb, net);
+				/* inp's ref-count reduced && stcb unlocked */
+			}
+		} else {
+			if (PRC_IS_REDIRECT(cmd) && inp) {
+#ifdef __OpenBSD__
+				in_rtchange((struct inpcb *)inp,
+					    inetctlerrmap[cmd]);
+#else
+				in6_rtchange((struct in6pcb *)inp,
+					     inet6ctlerrmap[cmd]);
+#endif
+			}
+			if (inp) {
+				/* reduce inp's ref-count */
+				SCTP_INP_WLOCK(inp);
+				SCTP_INP_DECR_REF(inp);
+				SCTP_INP_WUNLOCK(inp);
+			}
+			if (stcb)
+				SCTP_TCB_UNLOCK(stcb);
+		}
+		crit_exit();
+	}
+}
+
+/*
+ * this routine can probably be collasped into the one in sctp_userreq.c
+ * since they do the same thing and now we lookup with a sockaddr
+ */
+#ifdef __FreeBSD__
+static int
+sctp6_getcred(SYSCTL_HANDLER_ARGS)
+{
+	struct sockaddr_in6 addrs[2];
+	struct sctp_inpcb *inp;
+	struct sctp_nets *net;
+	struct sctp_tcb *stcb;
+	int error;
+
+#if defined(__FreeBSD__) && __FreeBSD_version >= 500000
+	error = suser(req->td);
+#else
+	error = suser(req->p);
+#endif
+	if (error)
+		return (error);
+
+	if (req->newlen != sizeof(addrs))
+		return (EINVAL);
+	if (req->oldlen != sizeof(struct ucred))
+		return (EINVAL);
+	error = SYSCTL_IN(req, addrs, sizeof(addrs));
+	if (error)
+		return (error);
+	crit_enter();
+
+        stcb = sctp_findassociation_addr_sa(sin6tosa(&addrs[0]),
+                                           sin6tosa(&addrs[1]),
+                                           &inp, &net, 1);
+	if (stcb == NULL || inp == NULL || inp->sctp_socket == NULL) {
+		error = ENOENT;
+		if (inp) {
+			SCTP_INP_WLOCK(inp);
+			SCTP_INP_DECR_REF(inp);
+			SCTP_INP_WUNLOCK(inp);
+		}
+		goto out;
+	}
+	error = SYSCTL_OUT(req, inp->sctp_socket->so_cred,
+			   sizeof(struct ucred));
+
+	SCTP_TCB_UNLOCK (stcb);
+out:
+	crit_exit();
+	return (error);
+}
+
+SYSCTL_PROC(_net_inet6_sctp6, OID_AUTO, getcred, CTLTYPE_OPAQUE|CTLFLAG_RW,
+	    0, 0,
+	    sctp6_getcred, "S,ucred", "Get the ucred of a SCTP6 connection");
+
+#endif
+
+/* This is the same as the sctp_abort() could be made common */
+static int
+sctp6_abort(struct socket *so)
+{
+	struct sctp_inpcb *inp;
+
+	inp = (struct sctp_inpcb *)so->so_pcb;
+	if (inp == 0)
+		return EINVAL;	/* ??? possible? panic instead? */
+	soisdisconnected(so);
+	crit_enter();
+	sctp_inpcb_free(inp, 1);
+	crit_exit();
+	return 0;
+}
+
+static int
+#if defined(__FreeBSD__) && __FreeBSD_version >= 500000
+sctp6_attach(struct socket *so, int proto, struct thread *p)
+#elif defined(__DragonFly__)
+sctp6_attach(struct socket *so, int proto, struct pru_attach_info *ai)
+#else
+sctp6_attach(struct socket *so, int proto, struct proc *p)
+#endif
+{
+	struct in6pcb *inp6;
+	int error;
+	struct sctp_inpcb *inp;
+
+	inp = (struct sctp_inpcb *)so->so_pcb;
+	if (inp != NULL)
+		return EINVAL;
+
+	if (so->so_snd.sb_hiwat == 0 || so->so_rcv.sb_hiwat == 0) {
+		error = soreserve(so, sctp_sendspace, sctp_recvspace, NULL);
+		if (error)
+			return error;
+	}
+	crit_enter();
+	error = sctp_inpcb_alloc(so);
+	crit_exit();
+	if (error)
+		return error;
+	inp = (struct sctp_inpcb *)so->so_pcb;
+	inp->sctp_flags |= SCTP_PCB_FLAGS_BOUND_V6;	/* I'm v6! */
+	inp6 = (struct in6pcb *)inp;
+
+#if defined(__FreeBSD__) || defined(__APPLE__) || defined(__DragonFly__)
+	inp6->inp_vflag |= INP_IPV6;
+#else
+#if defined(__OpenBSD__)
+	inp->ip_inp.inp.inp_flags |= INP_IPV6;
+#else
+	inp->inp_vflag |=  INP_IPV6;
+#endif
+#endif
+#if defined(__NetBSD__)
+	if (ip6_v6only) {
+		inp6->in6p_flags |= IN6P_IPV6_V6ONLY;
+	}
+	so->so_send = sctp_sosend;
+#endif
+	inp6->in6p_hops = -1;	        /* use kernel default */
+	inp6->in6p_cksum = -1;	/* just to be sure */
+#ifdef INET
+	/*
+	 * XXX: ugly!!
+	 * IPv4 TTL initialization is necessary for an IPv6 socket as well,
+	 * because the socket may be bound to an IPv6 wildcard address,
+	 * which may match an IPv4-mapped IPv6 address.
+	 */
+#if defined(__FreeBSD__) || defined(__APPLE__) || defined(__DragonFly__)
+	inp6->inp_ip_ttl = ip_defttl;
+#else
+	inp->inp_ip_ttl = ip_defttl;
+#endif
+#endif
+	/*
+	 * Hmm what about the IPSEC stuff that is missing here but
+	 * in sctp_attach()?
+	 */
+	return 0;
+}
+
+static int
+#if (defined(__FreeBSD__) && __FreeBSD_version >= 500000) || defined(__DragonFly__)
+sctp6_bind(struct socket *so, struct sockaddr *addr, struct thread *p)
+{
+#else
+#if defined(__FreeBSD__) || defined(__APPLE__)
+sctp6_bind(struct socket *so, struct sockaddr *addr, struct proc *p)
+{
+#else
+sctp6_bind(struct socket *so, struct mbuf *nam, struct proc *p)
+{
+	struct sockaddr *addr = nam ?  mtod(nam, struct sockaddr *) : NULL;
+#endif
+#endif
+	struct sctp_inpcb *inp;
+	struct in6pcb *inp6;
+	int error;
+
+	inp = (struct sctp_inpcb *)so->so_pcb;
+	if (inp == 0)
+		return EINVAL;
+
+	inp6 = (struct in6pcb *)inp;
+#if defined(__FreeBSD__) || defined(__APPLE__) || defined(__DragonFly__)
+	inp6->inp_vflag &= ~INP_IPV4;
+	inp6->inp_vflag |= INP_IPV6;
+#else
+#if defined(__OpenBSD__)
+	inp->ip_inp.inp.inp_flags &= ~INP_IPV4;
+	inp->ip_inp.inp.inp_flags |= INP_IPV6;
+#else
+	inp->inp_vflag &= ~INP_IPV4;
+	inp->inp_vflag |= INP_IPV6;
+#endif
+#endif
+	if (addr != NULL &&
+#if defined(__OpenBSD__)
+	     (0) /* we always do dual bind */
+#elif defined (__NetBSD__)
+	     (inp6->in6p_flags & IN6P_IPV6_V6ONLY)
+#else
+	     (inp6->inp_flags & IN6P_IPV6_V6ONLY)
+#endif
+	     == 0) {
+		if (addr->sa_family == AF_INET) {
+			/* binding v4 addr to v6 socket, so reset flags */
+#if defined(__FreeBSD__) || defined(__APPLE__) || defined(__DragonFly__)
+			inp6->inp_vflag |= INP_IPV4;
+			inp6->inp_vflag &= ~INP_IPV6;
+#else
+#if defined(__OpenBSD__)
+			inp->ip_inp.inp.inp_flags |= INP_IPV4;
+			inp->ip_inp.inp.inp_flags &= ~INP_IPV6;
+#else
+			inp->inp_vflag |= INP_IPV4;
+			inp->inp_vflag &= ~INP_IPV6;
+#endif
+#endif
+		} else {
+			struct sockaddr_in6 *sin6_p;
+			sin6_p = (struct sockaddr_in6 *)addr;
+
+			if (IN6_IS_ADDR_UNSPECIFIED(&sin6_p->sin6_addr)) {
+#if defined(__FreeBSD__) || defined(__APPLE__) || defined(__DragonFly__)
+			  inp6->inp_vflag |= INP_IPV4;
+#else
+#if defined(__OpenBSD__)
+			  inp->ip_inp.inp.inp_flags |= INP_IPV4;
+#else
+			  inp->inp_vflag |= INP_IPV4;
+#endif
+#endif
+			}
+			else if (IN6_IS_ADDR_V4MAPPED(&sin6_p->sin6_addr)) {
+				struct sockaddr_in sin;
+				in6_sin6_2_sin(&sin, sin6_p);
+#if defined(__FreeBSD__) || defined(__APPLE__) || defined(__DragonFly__)
+				inp6->inp_vflag |= INP_IPV4;
+				inp6->inp_vflag &= ~INP_IPV6;
+#else
+#if defined(__OpenBSD__)
+				inp->ip_inp.inp.inp_flags |= INP_IPV4;
+				inp->ip_inp.inp.inp_flags &= ~INP_IPV6;
+
+#else
+				inp->inp_vflag |= INP_IPV4;
+				inp->inp_vflag &= ~INP_IPV6;
+#endif
+#endif
+				crit_enter();
+				error = sctp_inpcb_bind(so, (struct sockaddr *)&sin, p);
+				crit_exit();
+				return error;
+			}
+		}
+	} else if (addr != NULL) {
+		/* IPV6_V6ONLY socket */
+		if (addr->sa_family == AF_INET) {
+			/* can't bind v4 addr to v6 only socket! */
+			return EINVAL;
+		} else {
+			struct sockaddr_in6 *sin6_p;
+			sin6_p = (struct sockaddr_in6 *)addr;
+
+			if (IN6_IS_ADDR_V4MAPPED(&sin6_p->sin6_addr))
+				/* can't bind v4-mapped addrs either! */
+				/* NOTE: we don't support SIIT */
+				return EINVAL;
+		}
+	}
+	crit_enter();
+	error = sctp_inpcb_bind(so, addr, p);
+	crit_exit();
+	return error;
+}
+
+/*This could be made common with sctp_detach() since they are identical */
+static int
+sctp6_detach(struct socket *so)
+{
+	struct sctp_inpcb *inp;
+
+	inp = (struct sctp_inpcb *)so->so_pcb;
+	if (inp == 0)
+		return EINVAL;
+	crit_enter();
+	if (((so->so_options & SO_LINGER) && (so->so_linger == 0)) ||
+	    (so->so_rcv.sb_cc > 0))
+		sctp_inpcb_free(inp, 1);
+	else
+		sctp_inpcb_free(inp, 0);
+	crit_exit();
+	return 0;
+}
+
+static int
+sctp6_disconnect(struct socket *so)
+{
+	struct sctp_inpcb *inp;
+
+	crit_enter();
+	inp = (struct sctp_inpcb *)so->so_pcb;
+	if (inp == NULL) {
+		crit_exit();
+		return (ENOTCONN);
+	}
+	if (inp->sctp_flags & SCTP_PCB_FLAGS_TCPTYPE) {
+		if (LIST_EMPTY(&inp->sctp_asoc_list)) {
+			/* No connection */
+			crit_exit();
+			return (ENOTCONN);
+		} else {
+			int some_on_streamwheel = 0;
+			struct sctp_association *asoc;
+			struct sctp_tcb *stcb;
+
+			stcb = LIST_FIRST(&inp->sctp_asoc_list);
+			if (stcb == NULL) {
+				crit_exit();
+				return (EINVAL);
+			}
+			asoc = &stcb->asoc;
+			if (!TAILQ_EMPTY(&asoc->out_wheel)) {
+				/* Check to see if some data queued */
+				struct sctp_stream_out *outs;
+				TAILQ_FOREACH(outs, &asoc->out_wheel,
+					      next_spoke) {
+					if (!TAILQ_EMPTY(&outs->outqueue)) {
+						some_on_streamwheel = 1;
+						break;
+					}
+				}
+			}
+
+			if (TAILQ_EMPTY(&asoc->send_queue) &&
+			    TAILQ_EMPTY(&asoc->sent_queue) &&
+			    (some_on_streamwheel == 0)) {
+				/* nothing queued to send, so I'm done... */
+				if ((SCTP_GET_STATE(asoc) !=
+				     SCTP_STATE_SHUTDOWN_SENT) &&
+				    (SCTP_GET_STATE(asoc) !=
+				     SCTP_STATE_SHUTDOWN_ACK_SENT)) {
+					/* only send SHUTDOWN the first time */
+#ifdef SCTP_DEBUG
+					if (sctp_debug_on & SCTP_DEBUG_OUTPUT4) {
+						printf("%s:%d sends a shutdown\n",
+						       __FILE__,
+						       __LINE__
+							);
+					}
+#endif
+					sctp_send_shutdown(stcb, stcb->asoc.primary_destination);
+					sctp_chunk_output(stcb->sctp_ep, stcb, 1);
+					asoc->state = SCTP_STATE_SHUTDOWN_SENT;
+					sctp_timer_start(SCTP_TIMER_TYPE_SHUTDOWN,
+							 stcb->sctp_ep, stcb,
+							 asoc->primary_destination);
+					sctp_timer_start(SCTP_TIMER_TYPE_SHUTDOWNGUARD,
+							 stcb->sctp_ep, stcb,
+							 asoc->primary_destination);
+				}
+			} else {
+				/*
+				 * we still got (or just got) data to send,
+				 * so set SHUTDOWN_PENDING
+				 */
+				/*
+				 * XXX sockets draft says that MSG_EOF should
+				 * be sent with no data.  currently, we will
+				 * allow user data to be sent first and move
+				 * to SHUTDOWN-PENDING
+				 */
+				asoc->state |= SCTP_STATE_SHUTDOWN_PENDING;
+			}
+			crit_exit();
+			return (0);
+		}
+	} else {
+		/* UDP model does not support this */
+		crit_exit();
+		return EOPNOTSUPP;
+	}
+}
+
+int
+#if (defined(__FreeBSD__) && __FreeBSD_version >= 500000) || defined(__DragonFly__)
+sctp_send(struct socket *so, int flags, struct mbuf *m, struct sockaddr *addr,
+	  struct mbuf *control, struct thread *p);
+#else
+sctp_send(struct socket *so, int flags, struct mbuf *m, struct sockaddr *addr,
+	  struct mbuf *control, struct proc *p);
+#endif
+
+
+static int
+#if (defined(__FreeBSD__) && __FreeBSD_version >= 500000) || defined(__DragonFly__)
+sctp6_send(struct socket *so, int flags, struct mbuf *m, struct sockaddr *addr,
+	   struct mbuf *control, struct thread *p)
+{
+#else
+#if defined(__FreeBSD__) || defined(__APPLE__)
+sctp6_send(struct socket *so, int flags, struct mbuf *m, struct sockaddr *addr,
+	   struct mbuf *control, struct proc *p)
+{
+#else
+sctp6_send(struct socket *so, int flags, struct mbuf *m, struct mbuf *nam,
+	   struct mbuf *control, struct proc *p)
+{
+	struct sockaddr *addr = nam ? mtod(nam, struct sockaddr *) : NULL;
+#endif
+#endif
+	struct sctp_inpcb *inp;
+	struct inpcb *in_inp;
+	struct in6pcb *inp6;
+#ifdef INET
+	struct sockaddr_in6 *sin6;
+#endif /* INET */
+	/* No SPL needed since sctp_output does this */
+
+	inp = (struct sctp_inpcb *)so->so_pcb;
+	if (inp == NULL) {
+	        if (control) {
+			m_freem(control);
+			control = NULL;
+		}
+		m_freem(m);
+		return EINVAL;
+	}
+	in_inp = (struct inpcb *)inp;
+	inp6 = (struct in6pcb *)inp;
+	/* For the TCP model we may get a NULL addr, if we
+	 * are a connected socket thats ok.
+	 */
+	if ((inp->sctp_flags & SCTP_PCB_FLAGS_CONNECTED) &&
+	    (addr == NULL)) {
+	        goto connected_type;
+	}
+	if (addr == NULL) {
+		m_freem(m);
+		if (control) {
+			m_freem(control);
+			control = NULL;
+		}
+		return (EDESTADDRREQ);
+	}
+
+#ifdef INET
+	sin6 = (struct sockaddr_in6 *)addr;
+	if (
+
+#if defined(__OpenBSD__)
+	     (0) /* we always do dual bind */
+#elif defined (__NetBSD__)
+	     (inp6->in6p_flags & IN6P_IPV6_V6ONLY)
+#else
+	     (inp6->inp_flags & IN6P_IPV6_V6ONLY)
+#endif
+	    ) {
+		/*
+		 * if IPV6_V6ONLY flag, we discard datagrams
+		 * destined to a v4 addr or v4-mapped addr
+		 */
+		if (addr->sa_family == AF_INET) {
+			return EINVAL;
+		}
+		if (IN6_IS_ADDR_V4MAPPED(&sin6->sin6_addr)) {
+			return EINVAL;
+		}
+	}
+
+	if (IN6_IS_ADDR_V4MAPPED(&sin6->sin6_addr)) {
+		if (!ip6_v6only) {
+			struct sockaddr_in sin;
+			/* convert v4-mapped into v4 addr and send */
+			in6_sin6_2_sin(&sin, sin6);
+			return sctp_send(so, flags,  m, (struct sockaddr *)&sin,
+					   control, p);
+		} else {
+			/* mapped addresses aren't enabled */
+			return EINVAL;
+		}
+	}
+#endif /* INET */
+ connected_type:
+	/* now what about control */
+	if (control) {
+		if (inp->control) {
+			printf("huh? control set?\n");
+			m_freem(inp->control);
+			inp->control = NULL;
+		}
+		inp->control = control;
+	}
+	/* add it in possibly */
+	if ((inp->pkt) &&
+	    (inp->pkt->m_flags & M_PKTHDR)) {
+		struct mbuf *x;
+		int c_len;
+
+		c_len = 0;
+		/* How big is it */
+		for (x=m;x;x = x->m_next) {
+			c_len += x->m_len;
+		}
+		inp->pkt->m_pkthdr.len += c_len;
+	}
+	/* Place the data */
+	if (inp->pkt) {
+		inp->pkt_last->m_next = m;
+		inp->pkt_last = m;
+	} else {
+		inp->pkt_last = inp->pkt = m;
+	}
+	if (
+#if defined(__FreeBSD__) || defined(__APPLE__) || defined(__DragonFly__)
+	    /* FreeBSD and MacOSX uses a flag passed */
+	    ((flags & PRUS_MORETOCOME) == 0)
+#elif defined(__NetBSD__)
+	    /* NetBSD uses the so_state field */
+	    ((so->so_state & SS_MORETOCOME) == 0)
+#else
+	    1   /* Open BSD does not have any "more to come" indication */
+#endif
+	    ) {
+		/*
+		 * note with the current version this code will only be
+		 * used by OpenBSD, NetBSD and FreeBSD have methods for
+		 * re-defining sosend() to use sctp_sosend().  One can
+		 * optionaly switch back to this code (by changing back
+		 * the defininitions but this is not advisable.
+		 */
+		int ret;
+		ret = sctp_output(inp, inp->pkt , addr, inp->control, p, flags);
+		inp->pkt = NULL;
+		inp->control = NULL;
+		return (ret);
+	} else {
+		return (0);
+	}
+}
+
+static int
+#if (defined(__FreeBSD__) && __FreeBSD_version >= 500000) || defined(__DragonFly__)
+sctp6_connect(struct socket *so, struct sockaddr *addr, struct thread *p)
+{
+#else
+#if defined(__FreeBSD__) || defined(__APPLE__)
+sctp6_connect(struct socket *so, struct sockaddr *addr, struct proc *p)
+{
+#else
+sctp6_connect(struct socket *so, struct mbuf *nam, struct proc *p)
+{
+	struct sockaddr *addr = mtod(nam, struct sockaddr *);
+#endif
+#endif
+	int error = 0;
+	struct sctp_inpcb *inp;
+	struct in6pcb *inp6;
+	struct sctp_tcb *stcb;
+#ifdef INET
+	struct sockaddr_in6 *sin6;
+	struct sockaddr_storage ss;
+#endif /* INET */
+
+	crit_enter();
+	inp6 = (struct in6pcb *)so->so_pcb;
+	inp = (struct sctp_inpcb *)so->so_pcb;
+	if (inp == 0) {
+		crit_exit();
+		return (ECONNRESET);	/* I made the same as TCP since
+					 * we are not setup? */
+	}
+	SCTP_ASOC_CREATE_LOCK(inp);
+	SCTP_INP_RLOCK(inp);
+	if ((inp->sctp_flags & SCTP_PCB_FLAGS_UNBOUND) ==
+	    SCTP_PCB_FLAGS_UNBOUND) {
+		/* Bind a ephemeral port */
+		SCTP_INP_RUNLOCK(inp);
+		error = sctp6_bind(so, NULL, p);
+		if (error) {
+			crit_exit();
+			SCTP_ASOC_CREATE_UNLOCK(inp);
+
+			return (error);
+		}
+		SCTP_INP_RLOCK(inp);
+	}
+
+	if ((inp->sctp_flags & SCTP_PCB_FLAGS_TCPTYPE) &&
+	    (inp->sctp_flags & SCTP_PCB_FLAGS_CONNECTED)) {
+		/* We are already connected AND the TCP model */
+		crit_exit();
+		SCTP_INP_RUNLOCK(inp);
+		SCTP_ASOC_CREATE_UNLOCK(inp);
+		return (EADDRINUSE);
+	}
+
+#ifdef INET
+	sin6 = (struct sockaddr_in6 *)addr;
+	if (
+#if defined(__OpenBSD__)
+	     (0) /* we always do dual bind */
+#elif defined (__NetBSD__)
+	     (inp6->in6p_flags & IN6P_IPV6_V6ONLY)
+#else
+	     (inp6->inp_flags & IN6P_IPV6_V6ONLY)
+#endif
+	    ) {
+		/*
+		 * if IPV6_V6ONLY flag, ignore connections
+		 * destined to a v4 addr or v4-mapped addr
+		 */
+		if (addr->sa_family == AF_INET) {
+			crit_exit();
+			SCTP_INP_RUNLOCK(inp);
+			SCTP_ASOC_CREATE_UNLOCK(inp);
+			return EINVAL;
+		}
+		if (IN6_IS_ADDR_V4MAPPED(&sin6->sin6_addr)) {
+			crit_exit();
+			SCTP_INP_RUNLOCK(inp);
+			SCTP_ASOC_CREATE_UNLOCK(inp);
+			return EINVAL;
+		}
+	}
+
+	if (IN6_IS_ADDR_V4MAPPED(&sin6->sin6_addr)) {
+		if (!ip6_v6only) {
+			/* convert v4-mapped into v4 addr */
+			in6_sin6_2_sin((struct sockaddr_in *)&ss, sin6);
+			addr = (struct sockaddr *)&ss;
+		} else {
+			/* mapped addresses aren't enabled */
+			crit_exit();
+			SCTP_INP_RUNLOCK(inp);
+			SCTP_ASOC_CREATE_UNLOCK(inp);
+			return EINVAL;
+		}
+	} else
+#endif /* INET */
+		addr = addr;	/* for true v6 address case */
+
+	/* Now do we connect? */
+	if (inp->sctp_flags & SCTP_PCB_FLAGS_CONNECTED) {
+		stcb = LIST_FIRST(&inp->sctp_asoc_list);
+		if (stcb)
+			SCTP_TCB_UNLOCK (stcb);
+		SCTP_INP_RUNLOCK(inp);
+	}else {
+		SCTP_INP_RUNLOCK(inp);
+		SCTP_INP_WLOCK(inp);
+		SCTP_INP_INCR_REF(inp);
+		SCTP_INP_WUNLOCK(inp);
+		stcb = sctp_findassociation_ep_addr(&inp, addr, NULL, NULL, NULL);
+		if (stcb == NULL) {
+			SCTP_INP_WLOCK(inp);
+			SCTP_INP_DECR_REF(inp);
+			SCTP_INP_WUNLOCK(inp);
+		}
+	}
+
+	if (stcb != NULL) {
+		/* Already have or am bring up an association */
+		SCTP_ASOC_CREATE_UNLOCK(inp);
+		SCTP_TCB_UNLOCK (stcb);
+		crit_exit();
+		return (EALREADY);
+	}
+	/* We are GOOD to go */
+	stcb = sctp_aloc_assoc(inp, addr, 1, &error, 0);
+	SCTP_ASOC_CREATE_UNLOCK(inp);
+	if (stcb == NULL) {
+		/* Gak! no memory */
+		crit_exit();
+		return (error);
+	}
+	if (stcb->sctp_ep->sctp_flags & SCTP_PCB_FLAGS_TCPTYPE) {
+		stcb->sctp_ep->sctp_flags |= SCTP_PCB_FLAGS_CONNECTED;
+		/* Set the connected flag so we can queue data */
+		soisconnecting(so);
+	}
+	stcb->asoc.state = SCTP_STATE_COOKIE_WAIT;
+	SCTP_GETTIME_TIMEVAL(&stcb->asoc.time_entered);
+	sctp_send_initiate(inp, stcb);
+	SCTP_TCB_UNLOCK (stcb);
+	crit_exit();
+	return error;
+}
+
+static int
+#if defined(__FreeBSD__) || defined(__APPLE__) || defined(__DragonFly__)
+sctp6_getaddr(struct socket *so, struct sockaddr **addr)
+{
+	struct sockaddr_in6 *sin6;
+#else
+sctp6_getaddr(struct socket *so, struct mbuf *nam)
+{
+	struct sockaddr_in6 *sin6 = mtod(nam, struct sockaddr_in6 *);
+#endif
+	struct sctp_inpcb *inp;
+	/*
+	 * Do the malloc first in case it blocks.
+	 */
+#if defined(__FreeBSD__) || defined(__APPLE__) || defined(__DragonFly__)
+	MALLOC(sin6, struct sockaddr_in6 *, sizeof *sin6, M_SONAME,
+	       M_WAITOK | M_ZERO);
+#else
+	nam->m_len = sizeof(*sin6);
+#endif
+	bzero(sin6, sizeof(*sin6));
+	sin6->sin6_family = AF_INET6;
+	sin6->sin6_len = sizeof(*sin6);
+
+	inp = (struct sctp_inpcb *)so->so_pcb;
+	if (!inp) {
+#if defined(__FreeBSD__) || defined(__APPLE__) || defined(__DragonFly__)
+		FREE(sin6, M_SONAME);
+#endif
+		return ECONNRESET;
+	}
+
+	sin6->sin6_port = inp->sctp_lport;
+	if (inp->sctp_flags & SCTP_PCB_FLAGS_BOUNDALL) {
+		/* For the bound all case you get back 0 */
+		if (inp->sctp_flags & SCTP_PCB_FLAGS_CONNECTED) {
+			struct sctp_tcb *stcb;
+			struct sockaddr_in6 *sin_a6;
+			struct sctp_nets *net;
+			int fnd;
+
+			stcb = LIST_FIRST(&inp->sctp_asoc_list);
+			if (stcb == NULL) {
+				goto notConn6;
+			}
+			fnd = 0;
+			sin_a6 = NULL;
+			TAILQ_FOREACH(net, &stcb->asoc.nets, sctp_next) {
+				sin_a6 = (struct sockaddr_in6 *)&net->ro._l_addr;
+				if (sin_a6->sin6_family == AF_INET6) {
+					fnd = 1;
+					break;
+				}
+			}
+			if ((!fnd) || (sin_a6 == NULL)) {
+				/* punt */
+				goto notConn6;
+			}
+			sin6->sin6_addr = sctp_ipv6_source_address_selection(
+			    inp, stcb, (struct route *)&net->ro, net, 0);
+
+		} else {
+			/* For the bound all case you get back 0 */
+		notConn6:
+			memset(&sin6->sin6_addr, 0, sizeof(sin6->sin6_addr));
+		}
+	} else {
+		/* Take the first IPv6 address in the list */
+		struct sctp_laddr *laddr;
+		int fnd = 0;
+		LIST_FOREACH(laddr, &inp->sctp_addr_list, sctp_nxt_addr) {
+			if (laddr->ifa->ifa_addr->sa_family == AF_INET6) {
+				struct sockaddr_in6 *sin_a;
+				sin_a = (struct sockaddr_in6 *)laddr->ifa->ifa_addr;
+				sin6->sin6_addr = sin_a->sin6_addr;
+				fnd = 1;
+				break;
+			}
+		}
+		if (!fnd) {
+#if defined(__FreeBSD__) || defined(__APPLE__) || defined(__DragonFly__)
+			FREE(sin6, M_SONAME);
+#endif
+			return ENOENT;
+		}
+	}
+	/* Scoping things for v6 */
+	if (IN6_IS_SCOPE_LINKLOCAL(&sin6->sin6_addr))
+		/* skip ifp check below */
+		in6_recoverscope(sin6, &sin6->sin6_addr, NULL);
+	else
+		sin6->sin6_scope_id = 0;	/*XXX*/
+#if defined(__FreeBSD__) || defined(__APPLE__) || defined(__DragonFly__)
+	(*addr) = (struct sockaddr *)sin6;
+#endif
+	return (0);
+}
+
+static int
+#if defined(__FreeBSD__) || defined(__APPLE__) || defined(__DragonFly__)
+sctp6_peeraddr(struct socket *so, struct sockaddr **addr)
+{
+	struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *)*addr;
+#else
+sctp6_peeraddr(struct socket *so, struct mbuf *nam)
+{
+	struct sockaddr_in6 *sin6 = mtod(nam, struct sockaddr_in6 *);
+#endif
+	int fnd;
+	struct sockaddr_in6 *sin_a6;
+	struct sctp_inpcb *inp;
+	struct sctp_tcb *stcb;
+	struct sctp_nets *net;
+	/*
+	 * Do the malloc first in case it blocks.
+	 */
+	inp = (struct sctp_inpcb *)so->so_pcb;
+	if ((inp->sctp_flags & SCTP_PCB_FLAGS_CONNECTED) == 0) {
+		/* UDP type and listeners will drop out here */
+		return (ENOTCONN);
+	}
+#if defined(__FreeBSD__) || defined(__APPLE__) || defined(__DragonFly__)
+	MALLOC(sin6, struct sockaddr_in6 *, sizeof *sin6, M_SONAME,
+	       M_WAITOK | M_ZERO);
+#else
+	nam->m_len = sizeof(*sin6);
+#endif
+	bzero(sin6, sizeof(*sin6));
+	sin6->sin6_family = AF_INET6;
+	sin6->sin6_len = sizeof(*sin6);
+
+	/* We must recapture incase we blocked */
+	inp = (struct sctp_inpcb *)so->so_pcb;
+	if (!inp) {
+#if defined(__FreeBSD__) || defined(__APPLE__) || defined(__DragonFly__)
+		FREE(sin6, M_SONAME);
+#endif
+		return ECONNRESET;
+	}
+	stcb = LIST_FIRST(&inp->sctp_asoc_list);
+	if (stcb == NULL) {
+#if defined(__FreeBSD__) || defined(__APPLE__) || defined(__DragonFly__)
+		FREE(sin6, M_SONAME);
+#endif
+		return ECONNRESET;
+	}
+	fnd = 0;
+	TAILQ_FOREACH(net, &stcb->asoc.nets, sctp_next) {
+		sin_a6 = (struct sockaddr_in6 *)&net->ro._l_addr;
+		if (sin_a6->sin6_family == AF_INET6) {
+			fnd = 1;
+			sin6->sin6_port = stcb->rport;
+			sin6->sin6_addr = sin_a6->sin6_addr;
+			break;
+		}
+	}
+	if (!fnd) {
+		/* No IPv4 address */
+#if defined(__FreeBSD__) || defined(__APPLE__) || defined(__DragonFly__)
+		FREE(sin6, M_SONAME);
+#endif
+		return ENOENT;
+	}
+	in6_recoverscope(sin6, &sin6->sin6_addr, NULL);
+#if defined(__FreeBSD__) || defined(__APPLE__) || defined(__DragonFly__)
+	*addr = (struct sockaddr *)sin6;
+#endif
+	return (0);
+}
+
+static int
+#if defined(__FreeBSD__) || defined(__APPLE__) || defined(__DragonFly__)
+sctp6_in6getaddr(struct socket *so, struct sockaddr **nam)
+{
+	struct sockaddr *addr;
+#else
+sctp6_in6getaddr(struct socket *so, struct mbuf *nam)
+{
+	struct sockaddr *addr = mtod(nam, struct sockaddr *);
+#endif
+	struct in6pcb *inp6 = sotoin6pcb(so);
+	int error;
+
+	if (inp6 == NULL)
+		return EINVAL;
+
+	crit_enter();
+	/* allow v6 addresses precedence */
+	error = sctp6_getaddr(so, nam);
+	if (error) {
+		/* try v4 next if v6 failed */
+		error = sctp_ingetaddr(so, nam);
+		if (error) {
+			crit_exit();
+			return (error);
+		}
+#if defined(__FreeBSD__) || defined(__APPLE__) || defined(__DragonFly__)
+		addr = *nam;
+#endif
+		/* if I'm V6ONLY, convert it to v4-mapped */
+		if (
+#if defined(__OpenBSD__)
+	     (0) /* we always do dual bind */
+#elif defined (__NetBSD__)
+	     (inp6->in6p_flags & IN6P_IPV6_V6ONLY)
+#else
+	     (inp6->inp_flags & IN6P_IPV6_V6ONLY)
+#endif
+		    ) {
+			struct sockaddr_in6 sin6;
+			in6_sin_2_v4mapsin6((struct sockaddr_in *)addr, &sin6);
+			memcpy(addr, &sin6, sizeof(struct sockaddr_in6));
+#if !(defined(__FreeBSD__) || defined(__APPLE__) || defined(__DragonFly__))
+			nam->m_len = sizeof(sin6);
+#endif
+#if !(defined(__FreeBSD__) || defined(__APPLE__) || defined(__DragonFly__))
+		} else {
+			nam->m_len = sizeof(struct sockaddr_in);
+#endif
+		}
+#if !(defined(__FreeBSD__) || defined(__APPLE__) || defined(__DragonFly__))
+	} else {
+		nam->m_len = sizeof(struct sockaddr_in6);
+#endif
+	}
+	crit_exit();
+	return (error);
+}
+
+
+static int
+#if defined(__FreeBSD__) || defined(__APPLE__) || defined(__DragonFly__)
+sctp6_getpeeraddr(struct socket *so, struct sockaddr **nam)
+{
+	struct sockaddr *addr = *nam;
+#else
+sctp6_getpeeraddr(struct socket *so, struct mbuf *nam)
+{
+	struct sockaddr *addr = mtod(nam, struct sockaddr *);
+#endif
+	struct in6pcb *inp6 = sotoin6pcb(so);
+	int error;
+
+	if (inp6 == NULL)
+		return EINVAL;
+
+	crit_enter();
+	/* allow v6 addresses precedence */
+	error = sctp6_peeraddr(so, nam);
+	if (error) {
+		/* try v4 next if v6 failed */
+		error = sctp_peeraddr(so, nam);
+		if (error) {
+			crit_exit();
+			return (error);
+		}
+		/* if I'm V6ONLY, convert it to v4-mapped */
+		if (
+#if defined(__OpenBSD__)
+	     (0) /* we always do dual bind */
+#elif defined (__NetBSD__)
+	     (inp6->in6p_flags & IN6P_IPV6_V6ONLY)
+#else
+	     (inp6->inp_flags & IN6P_IPV6_V6ONLY)
+#endif
+		    ) {
+			struct sockaddr_in6 sin6;
+			in6_sin_2_v4mapsin6((struct sockaddr_in *)addr, &sin6);
+			memcpy(addr, &sin6, sizeof(struct sockaddr_in6));
+#if !(defined(__FreeBSD__) || defined(__APPLE__) || defined(__DragonFly__))
+			nam->m_len = sizeof(sin6);
+#endif
+#if !(defined(__FreeBSD__) || defined(__APPLE__) || defined(__DragonFly__))
+		} else {
+			nam->m_len = sizeof(struct sockaddr_in);
+#endif
+		}
+#if !(defined(__FreeBSD__) || defined(__APPLE__) || defined(__DragonFly__))
+	} else {
+		nam->m_len = sizeof(struct sockaddr_in6);
+#endif
+	}
+	crit_exit();
+	return error;
+}
+
+#if defined(__FreeBSD__) || defined(__APPLE__) || defined(__DragonFly__)
+struct pr_usrreqs sctp6_usrreqs = {
+	sctp6_abort, sctp_accept, sctp6_attach, sctp6_bind,
+	sctp6_connect, pru_connect2_notsupp, in6_control,
+	sctp6_detach, sctp6_disconnect, sctp_listen, sctp6_getpeeraddr,
+	sctp_usr_recvd, pru_rcvoob_notsupp, sctp6_send, pru_sense_null,
+	sctp_shutdown, sctp6_in6getaddr, sctp_sosend, soreceive, sopoll
+};
+
+#else
+
+int
+sctp6_usrreq(so, req, m, nam, control, p)
+     struct socket *so;
+     int req;
+     struct mbuf *m, *nam, *control;
+     struct proc *p;
+{
+	int s;
+	int error = 0;
+	int family;
+
+#if defined(__OpenBSD__)
+	p = curproc;
+#endif
+	s = splsoftnet();
+	family = so->so_proto->pr_domain->dom_family;
+
+	if (req == PRU_CONTROL) {
+		switch (family) {
+		case PF_INET:
+			error = in_control(so, (long)m, (caddr_t)nam,
+			    (struct ifnet *)control
+#if defined(__NetBSD__)
+			     , p
+#endif
+			     );
+#ifdef INET6
+		case PF_INET6:
+			error = in6_control(so, (long)m, (caddr_t)nam,
+			    (struct ifnet *)control, p);
+#endif
+		default:
+			error = EAFNOSUPPORT;
+		}
+		splx(s);
+		return (error);
+	}
+#ifdef __NetBSD__
+	if (req == PRU_PURGEIF) {
+		struct ifnet *ifn;
+		struct ifaddr *ifa;
+		ifn = (struct ifnet *)control;
+		TAILQ_FOREACH(ifa, &ifn->if_addrlist, ifa_list) {
+			if (ifa->ifa_addr->sa_family == family) {
+				sctp_delete_ip_address(ifa);
+			}
+		}
+		switch (family) {
+		case PF_INET:
+			in_purgeif (ifn);
+			break;
+#ifdef INET6
+		case PF_INET6:
+			in6_purgeif (ifn);
+			break;
+#endif
+		default:
+			splx(s);
+			return (EAFNOSUPPORT);
+		}
+		splx(s);
+		return (0);
+	}
+#endif
+	switch (req) {
+	case PRU_ATTACH:
+		error = sctp6_attach(so, family, p);
+		break;
+	case PRU_DETACH:
+		error = sctp6_detach(so);
+		break;
+	case PRU_BIND:
+		if (nam == NULL)
+			return (EINVAL);
+		error = sctp6_bind(so, nam, p);
+		break;
+	case PRU_LISTEN:
+		error = sctp_listen(so, p);
+		break;
+	case PRU_CONNECT:
+		if (nam == NULL)
+			return (EINVAL);
+		error = sctp6_connect(so, nam, p);
+		break;
+	case PRU_DISCONNECT:
+		error = sctp6_disconnect(so);
+		break;
+	case PRU_ACCEPT:
+		if (nam == NULL)
+			return (EINVAL);
+		error = sctp_accept(so, nam);
+		break;
+	case PRU_SHUTDOWN:
+		error = sctp_shutdown(so);
+		break;
+
+	case PRU_RCVD:
+		/*
+		 * For OpenBSD and NetBSD, this is real ugly. The (mbuf *)
+		 * nam that is passed (by soreceive()) is the int flags
+		 * cast as a (mbuf *) yuck!
+		 */
+ 		error = sctp_usr_recvd(so, (int)((long)nam));
+		break;
+
+	case PRU_SEND:
+		/* Flags are ignored */
+		error = sctp6_send(so, 0, m, nam, control, p);
+		break;
+	case PRU_ABORT:
+		error = sctp6_abort(so);
+		break;
+
+	case PRU_SENSE:
+		error = 0;
+		break;
+	case PRU_RCVOOB:
+		error = EAFNOSUPPORT;
+		break;
+	case PRU_SENDOOB:
+		error = EAFNOSUPPORT;
+		break;
+	case PRU_PEERADDR:
+		error = sctp6_getpeeraddr(so, nam);
+		break;
+	case PRU_SOCKADDR:
+		error = sctp6_in6getaddr(so, nam);
+		break;
+	case PRU_SLOWTIMO:
+		error = 0;
+		break;
+	default:
+		break;
+	}
+	splx(s);
+	return (error);
+}
+#endif
Index: sys/netinet6/sctp6_var.h
===================================================================
RCS file: sys/netinet6/sctp6_var.h
diff -N sys/netinet6/sctp6_var.h
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ sys/netinet6/sctp6_var.h	18 Jun 2005 16:05:50 -0000
@@ -0,0 +1,64 @@
+/*	$KAME: sctp6_var.h,v 1.6 2003/11/25 06:40:55 ono Exp $	*/
+
+/*
+ * Copyright (c) 2001, 2002, 2004 Cisco Systems, Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *      This product includes software developed by Cisco Systems, Inc.
+ * 4. Neither the name of the project nor the names of its contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY CISCO SYSTEMS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL CISCO SYSTEMS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+#ifndef _NETINET6_SCTP6_VAR_H_
+#define _NETINET6_SCTP6_VAR_H_
+
+#if defined(_KERNEL) || (defined(__APPLE__) && defined(KERNEL))
+
+#if defined(__FreeBSD__) || (__APPLE__) || defined(__DragonFly__)
+SYSCTL_DECL(_net_inet6_sctp6);
+extern struct pr_usrreqs sctp6_usrreqs;
+int	sctp6_ctloutput __P((struct socket *, struct sockopt *));
+#else
+int	sctp6_ctloutput __P((int, struct socket *, int, int, struct mbuf **));
+#if defined(__NetBSD__) || defined(__OpenBSD__)
+int	sctp6_usrreq __P((struct socket *, int, struct mbuf *, struct mbuf *,
+			   struct mbuf *, struct proc *));
+#else
+int	sctp6_usrreq __P((struct socket *, int, struct mbuf *, struct mbuf *,
+			   struct mbuf *));
+#endif /* __NetBSD__ || __OpenBSD__ */
+#endif /* __FreeBSD__ || __APPLE__ || __DragonFly__ */
+
+#if defined(__APPLE__)
+int	sctp6_input __P((struct mbuf **, int *));
+#else
+int	sctp6_input __P((struct mbuf **, int *, int));
+#endif
+int	sctp6_output __P((struct sctp_inpcb *, struct mbuf *, struct sockaddr *,
+	struct mbuf *, struct proc *));
+void	sctp6_ctlinput __P((int, struct sockaddr *, void *));
+
+#endif /* _KERNEL */
+#endif
Index: sys/sys/mbuf.h
===================================================================
RCS file: /home/dcvs/src/sys/sys/mbuf.h,v
retrieving revision 1.31
diff -u -p -r1.31 mbuf.h
--- sys/sys/mbuf.h	17 Jun 2005 19:12:18 -0000	1.31
+++ sys/sys/mbuf.h	18 Jun 2005 16:05:50 -0000
@@ -184,6 +184,7 @@ struct mbuf {
 #define	M_CLCACHE	0x2000	/* mbuf allocated from the cluster cache */
 #define M_EXT_CLUSTER	0x4000	/* standard cluster else special */
 #define	M_PHCACHE	0x8000	/* mbuf allocated from the pkt header cache */
+#define M_NOTIFICATION	0x10000	/* notification event */
 
 /*
  * Flags copied when copying m_pkthdr.
Index: sys/sys/protosw.h
===================================================================
RCS file: /home/dcvs/src/sys/sys/protosw.h,v
retrieving revision 1.14
diff -u -p -r1.14 protosw.h
--- sys/sys/protosw.h	4 Mar 2005 02:21:49 -0000	1.14
+++ sys/sys/protosw.h	18 Jun 2005 16:05:50 -0000
@@ -117,8 +117,9 @@ struct protosw {
 #define	PR_CONNREQUIRED	0x04		/* connection required by protocol */
 #define	PR_WANTRCVD	0x08		/* want PRU_RCVD calls */
 #define	PR_RIGHTS	0x10		/* passes capabilities */
-#define PR_IMPLOPCL	0x20		/* implied open/close */
+#define	PR_IMPLOPCL	0x20		/* implied open/close */
 #define	PR_LASTHDR	0x40		/* enforce ipsec policy; last header */
+#define	PR_ADDR_OPT	0x80		/* allow addresses during delivery */
 
 /*
  * The arguments to usrreq are:
Index: sys/sys/socket.h
===================================================================
RCS file: /home/dcvs/src/sys/sys/socket.h,v
retrieving revision 1.10
diff -u -p -r1.10 socket.h
--- sys/sys/socket.h	26 Jan 2005 23:09:58 -0000	1.10
+++ sys/sys/socket.h	18 Jun 2005 16:05:50 -0000
@@ -342,6 +342,7 @@ struct msghdr {
 #define	MSG_WAITALL	0x40		/* wait for full request or error */
 #define	MSG_DONTWAIT	0x80		/* this message should be nonblocking */
 #define	MSG_EOF		0x100		/* data completes connection */
+#define	MSG_NOTIFICATION 0x200		/* notification message */
 
 /*
  * Header for ancillary data objects in msg_control buffer.
Index: sys/sys/syscall-args
===================================================================
RCS file: /home/dcvs/src/sys/sys/syscall-args,v
retrieving revision 1.12
diff -u -p -r1.12 syscall-args
--- sys/sys/syscall-args	8 Apr 2005 08:31:45 -0000	1.12
+++ sys/sys/syscall-args	18 Jun 2005 16:05:50 -0000
@@ -1,6 +1,6 @@
 # System call argument table.
 # DO NOT EDIT-- this file is automatically generated.
-# $DragonFly: src/sys/sys/syscall-args,v 1.12 2005/04/08 08:31:45 joerg Exp $
+# $DragonFly$
 
 # Created from DragonFly: src/sys/kern/syscalls.master,v 1.22 2005/04/08 08:31:06 joerg Exp 
 
@@ -238,6 +238,7 @@ int	getresuid	getresuid	getresuid_args	u
 int	getresgid	getresgid	getresgid_args	gid_t *	rgid	gid_t *	egid	gid_t *	sgid
 int	kqueue	kqueue	kqueue_args
 int	kevent	kevent	kevent_args	int	fd	const struct kevent *	changelist	int	nchanges	struct kevent *	eventlist	int	nevents	const struct timespec *	timeout
+int	sctp_peeloff	sctp_peeloff	sctp_peeloff_args	int	sd	caddr_t	name
 int	sendfile	sendfile	sendfile_args	int	fd	int	s	off_t	offset	size_t	nbytes	struct sf_hdtr *	hdtr	off_t *	sbytes	int	flags
 int	varsym_set	varsym_set	varsym_set_args	int	level	const char *	name	const char *	data
 int	varsym_get	varsym_get	varsym_get_args	int	mask	const char *	wild	char *	buf	int	bufsize
Index: sys/sys/syscall-hide.h
===================================================================
RCS file: /home/dcvs/src/sys/sys/syscall-hide.h,v
retrieving revision 1.29
diff -u -p -r1.29 syscall-hide.h
--- sys/sys/syscall-hide.h	8 Apr 2005 08:31:45 -0000	1.29
+++ sys/sys/syscall-hide.h	18 Jun 2005 16:05:50 -0000
@@ -2,7 +2,7 @@
  * System call hiders.
  *
  * DO NOT EDIT-- this file is automatically generated.
- * $DragonFly: src/sys/sys/syscall-hide.h,v 1.29 2005/04/08 08:31:45 joerg Exp $
+ * $DragonFly$
  * created from DragonFly: src/sys/kern/syscalls.master,v 1.22 2005/04/08 08:31:06 joerg Exp 
  */
 
@@ -276,6 +276,7 @@ HIDE_BSD(getresuid)
 HIDE_BSD(getresgid)
 HIDE_BSD(kqueue)
 HIDE_BSD(kevent)
+HIDE_BSD(sctp_peeloff)
 HIDE_BSD(sendfile)
 HIDE_BSD(varsym_set)
 HIDE_BSD(varsym_get)
Index: sys/sys/syscall.h
===================================================================
RCS file: /home/dcvs/src/sys/sys/syscall.h,v
retrieving revision 1.29
diff -u -p -r1.29 syscall.h
--- sys/sys/syscall.h	8 Apr 2005 08:31:45 -0000	1.29
+++ sys/sys/syscall.h	18 Jun 2005 16:05:50 -0000
@@ -2,7 +2,7 @@
  * System call numbers.
  *
  * DO NOT EDIT-- this file is automatically generated.
- * $DragonFly: src/sys/sys/syscall.h,v 1.29 2005/04/08 08:31:45 joerg Exp $
+ * $DragonFly$
  * created from DragonFly: src/sys/kern/syscalls.master,v 1.22 2005/04/08 08:31:06 joerg Exp 
  */
 
@@ -287,6 +287,7 @@
 #define	SYS_getresgid	361
 #define	SYS_kqueue	362
 #define	SYS_kevent	363
+#define	SYS_sctp_peeloff	364
 #define	SYS_sendfile	393
 #define	SYS_varsym_set	450
 #define	SYS_varsym_get	451
Index: sys/sys/syscall.mk
===================================================================
RCS file: /home/dcvs/src/sys/sys/syscall.mk,v
retrieving revision 1.29
diff -u -p -r1.29 syscall.mk
--- sys/sys/syscall.mk	8 Apr 2005 08:31:45 -0000	1.29
+++ sys/sys/syscall.mk	18 Jun 2005 16:05:50 -0000
@@ -1,6 +1,6 @@
 # DragonFly system call names.
 # DO NOT EDIT-- this file is automatically generated.
-# $DragonFly: src/sys/sys/syscall.mk,v 1.29 2005/04/08 08:31:45 joerg Exp $
+# $DragonFly$
 # created from DragonFly: src/sys/kern/syscalls.master,v 1.22 2005/04/08 08:31:06 joerg Exp 
 MIASM =  \
 	syscall.o \
@@ -237,6 +237,7 @@ MIASM =  \
 	getresgid.o \
 	kqueue.o \
 	kevent.o \
+	sctp_peeloff.o \
 	sendfile.o \
 	varsym_set.o \
 	varsym_get.o \
Index: sys/sys/sysproto.h
===================================================================
RCS file: /home/dcvs/src/sys/sys/sysproto.h,v
retrieving revision 1.29
diff -u -p -r1.29 sysproto.h
--- sys/sys/sysproto.h	8 Apr 2005 08:31:45 -0000	1.29
+++ sys/sys/sysproto.h	18 Jun 2005 16:05:50 -0000
@@ -2,7 +2,7 @@
  * System call prototypes.
  *
  * DO NOT EDIT-- this file is automatically generated.
- * $DragonFly: src/sys/sys/sysproto.h,v 1.29 2005/04/08 08:31:45 joerg Exp $
+ * $DragonFly$
  * created from DragonFly: src/sys/kern/syscalls.master,v 1.22 2005/04/08 08:31:06 joerg Exp 
  */
 
@@ -1960,6 +1960,14 @@ struct	kevent_args {
 	int	nevents;	char nevents_[PAD_(int)];
 	const struct timespec *	timeout;	char timeout_[PAD_(const struct timespec *)];
 };
+struct	sctp_peeloff_args {
+#ifdef _KERNEL
+	struct sysmsg sysmsg;
+#endif
+	union usrmsg usrmsg;
+	int	sd;	char sd_[PAD_(int)];
+	caddr_t	name;	char name_[PAD_(caddr_t)];
+};
 struct	sendfile_args {
 #ifdef _KERNEL
 	struct sysmsg sysmsg;
@@ -2436,6 +2444,7 @@ int	getresuid (struct getresuid_args *);
 int	getresgid (struct getresgid_args *);
 int	kqueue (struct kqueue_args *);
 int	kevent (struct kevent_args *);
+int	sctp_peeloff (struct sctp_peeloff_args *);
 int	sendfile (struct sendfile_args *);
 int	varsym_set (struct varsym_set_args *);
 int	varsym_get (struct varsym_get_args *);
Index: sys/sys/sysunion.h
===================================================================
RCS file: /home/dcvs/src/sys/sys/sysunion.h,v
retrieving revision 1.26
diff -u -p -r1.26 sysunion.h
--- sys/sys/sysunion.h	8 Apr 2005 08:31:45 -0000	1.26
+++ sys/sys/sysunion.h	18 Jun 2005 16:05:50 -0000
@@ -2,7 +2,7 @@
  * Union of syscall args for messaging.
  *
  * DO NOT EDIT-- this file is automatically generated.
- * $DragonFly: src/sys/sys/sysunion.h,v 1.26 2005/04/08 08:31:45 joerg Exp $
+ * $DragonFly$
  * created from DragonFly: src/sys/kern/syscalls.master,v 1.22 2005/04/08 08:31:06 joerg Exp 
  */
 
@@ -321,6 +321,7 @@ union sysunion {
 	struct	getresgid_args getresgid;
 	struct	kqueue_args kqueue;
 	struct	kevent_args kevent;
+	struct	sctp_peeloff_args sctp_peeloff;
 	struct	sendfile_args sendfile;
 	struct	varsym_set_args varsym_set;
 	struct	varsym_get_args varsym_get;
