svn commit: r297662 - head/sys/netinet

Randall Stewart rrs at FreeBSD.org
Thu Apr 7 09:10:36 UTC 2016


Author: rrs
Date: Thu Apr  7 09:10:34 2016
New Revision: 297662
URL: https://svnweb.freebsd.org/changeset/base/297662

Log:
  This is work done by Michael Tuexen and myself at the IETF. This
  adds the new I-Data (Interleaved Data) message. This allows a user
  to be able to have complete freedom from Head Of Line blocking that
  was previously there due to the in-ability to send multiple large
  messages without the TSN's being in sequence. The code as been
  tested with Michaels various packet drill scripts as well as
  inter-networking between the IETF's location in Argentina and Germany.

Modified:
  head/sys/netinet/sctp.h
  head/sys/netinet/sctp_constants.h
  head/sys/netinet/sctp_dtrace_define.h
  head/sys/netinet/sctp_header.h
  head/sys/netinet/sctp_indata.c
  head/sys/netinet/sctp_indata.h
  head/sys/netinet/sctp_input.c
  head/sys/netinet/sctp_os_bsd.h
  head/sys/netinet/sctp_output.c
  head/sys/netinet/sctp_pcb.c
  head/sys/netinet/sctp_pcb.h
  head/sys/netinet/sctp_structs.h
  head/sys/netinet/sctp_sysctl.h
  head/sys/netinet/sctp_uio.h
  head/sys/netinet/sctp_usrreq.c
  head/sys/netinet/sctp_var.h
  head/sys/netinet/sctputil.c
  head/sys/netinet/sctputil.h

Modified: head/sys/netinet/sctp.h
==============================================================================
--- head/sys/netinet/sctp.h	Thu Apr  7 08:32:37 2016	(r297661)
+++ head/sys/netinet/sctp.h	Thu Apr  7 09:10:34 2016	(r297662)
@@ -196,6 +196,9 @@ struct sctp_paramhdr {
 #define SCTP_SS_VALUE			0x00001204
 #define SCTP_CC_OPTION			0x00001205	/* Options for CC
 							 * modules */
+/* For I-DATA */
+#define SCTP_INTERLEAVING_SUPPORTED	0x00001206
+
 /* read only */
 #define SCTP_GET_SNDBUF_USE		0x00001101
 #define SCTP_GET_STAT_LOG		0x00001103
@@ -452,6 +455,7 @@ struct sctp_error_auth_invalid_hmac {
 /* EY nr_sack chunk id*/
 #define SCTP_NR_SELECTIVE_ACK	0x10
 /************0x40 series ***********/
+#define SCTP_IDATA		0x40
 /************0x80 series ***********/
 /* RFC5061 */
 #define	SCTP_ASCONF_ACK		0x80
@@ -467,7 +471,7 @@ struct sctp_error_auth_invalid_hmac {
 #define SCTP_FORWARD_CUM_TSN	0xc0
 /* RFC5061 */
 #define SCTP_ASCONF		0xc1
-
+#define SCTP_IFORWARD_CUM_TSN	0xc2
 
 /* ABORT and SHUTDOWN COMPLETE FLAG */
 #define SCTP_HAD_NO_TCB		0x01

Modified: head/sys/netinet/sctp_constants.h
==============================================================================
--- head/sys/netinet/sctp_constants.h	Thu Apr  7 08:32:37 2016	(r297661)
+++ head/sys/netinet/sctp_constants.h	Thu Apr  7 09:10:34 2016	(r297662)
@@ -386,8 +386,8 @@ __FBSDID("$FreeBSD$");
 /* 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)
+#define IS_SCTP_CONTROL(a) (((a)->chunk_type != SCTP_DATA) && ((a)->chunk_type != SCTP_IDATA))
+#define IS_SCTP_DATA(a) (((a)->chunk_type == SCTP_DATA) || ((a)->chunk_type == SCTP_IDATA))
 
 
 /* SCTP parameter types */
@@ -886,12 +886,19 @@ __FBSDID("$FreeBSD$");
 
 /* modular comparison */
 /* See RFC 1982 for details. */
-#define SCTP_SSN_GT(a, b) (((a < b) && ((uint16_t)(b - a) > (1U<<15))) || \
-                           ((a > b) && ((uint16_t)(a - b) < (1U<<15))))
-#define SCTP_SSN_GE(a, b) (SCTP_SSN_GT(a, b) || (a == b))
-#define SCTP_TSN_GT(a, b) (((a < b) && ((uint32_t)(b - a) > (1U<<31))) || \
-                           ((a > b) && ((uint32_t)(a - b) < (1U<<31))))
-#define SCTP_TSN_GE(a, b) (SCTP_TSN_GT(a, b) || (a == b))
+#define SCTP_UINT16_GT(a, b) (((a < b) && ((uint16_t)(b - a) > (1U<<15))) || \
+                              ((a > b) && ((uint16_t)(a - b) < (1U<<15))))
+#define SCTP_UINT16_GE(a, b) (SCTP_UINT16_GT(a, b) || (a == b))
+#define SCTP_UINT32_GT(a, b) (((a < b) && ((uint32_t)(b - a) > (1U<<31))) || \
+                              ((a > b) && ((uint32_t)(a - b) < (1U<<31))))
+#define SCTP_UINT32_GE(a, b) (SCTP_UINT32_GT(a, b) || (a == b))
+
+#define SCTP_SSN_GT(a, b) SCTP_UINT16_GT(a, b)
+#define SCTP_SSN_GE(a, b) SCTP_UINT16_GE(a, b)
+#define SCTP_TSN_GT(a, b) SCTP_UINT32_GT(a, b)
+#define SCTP_TSN_GE(a, b) SCTP_UINT32_GE(a, b)
+#define SCTP_MSGID_GT(o, a, b) ((o == 1) ? SCTP_UINT16_GT((uint16_t)a, (uint16_t)b) : SCTP_UINT32_GT(a, b))
+#define SCTP_MSGID_GE(o, a, b) ((o == 1) ? SCTP_UINT16_GE((uint16_t)a, (uint16_t)b) : SCTP_UINT32_GE(a, b))
 
 /* Mapping array manipulation routines */
 #define SCTP_IS_TSN_PRESENT(arry, gap) ((arry[(gap >> 3)] >> (gap & 0x07)) & 0x01)

Modified: head/sys/netinet/sctp_dtrace_define.h
==============================================================================
--- head/sys/netinet/sctp_dtrace_define.h	Thu Apr  7 08:32:37 2016	(r297661)
+++ head/sys/netinet/sctp_dtrace_define.h	Thu Apr  7 09:10:34 2016	(r297662)
@@ -45,131 +45,131 @@ SDT_PROVIDER_DEFINE(sctp);
 /********************************************************/
 /* Initial */
 SDT_PROBE_DEFINE5(sctp, cwnd, net, init,
-    "uint32_t",		/* The Vtag for this end */
-    "uint32_t",		/*
-			 * The port number of the local side << 16 | port number
-			 * of remote in network byte order.
-			 */
-    "uintptr_t",	/* The pointer to the struct sctp_nets * changing */
-    "int",		/* The old value of the cwnd */
-    "int");		/* The new value of the cwnd */
+    "uint32_t",			/* The Vtag for this end */
+    "uint32_t",			/* The port number of the local side << 16 |
+				 * port number of remote in network byte
+				 * order. */
+    "uintptr_t",		/* The pointer to the struct sctp_nets *
+				 * changing */
+    "int",			/* The old value of the cwnd */
+    "int");			/* The new value of the cwnd */
 
 /* ACK-INCREASE */
 SDT_PROBE_DEFINE5(sctp, cwnd, net, ack,
-    "uint32_t",		/* The Vtag for this end */
-    "uint32_t",		/*
-			 * The port number of the local side << 16 | port number
-			 * of remote in network byte order.
-			 */
-    "uintptr_t",	/* The pointer to the struct sctp_nets * changing */
-    "int",		/* The old value of the cwnd */
-    "int");		/* The new value of the cwnd */
+    "uint32_t",			/* The Vtag for this end */
+    "uint32_t",			/* The port number of the local side << 16 |
+				 * port number of remote in network byte
+				 * order. */
+    "uintptr_t",		/* The pointer to the struct sctp_nets *
+				 * changing */
+    "int",			/* The old value of the cwnd */
+    "int");			/* The new value of the cwnd */
 
 /* ACK-INCREASE */
 SDT_PROBE_DEFINE5(sctp, cwnd, net, rttvar,
-    "uint64_t",		/* The Vtag << 32 | localport << 16 | remoteport */
-    "uint64_t",		/* obw | nbw */
-    "uint64_t",		/* bwrtt | newrtt */
-    "uint64_t",		/* flight */
-    "uint64_t");	/* (cwnd << 32) | point << 16 | retval(0/1) */
+    "uint64_t",			/* The Vtag << 32 | localport << 16 |
+				 * remoteport */
+    "uint64_t",			/* obw | nbw */
+    "uint64_t",			/* bwrtt | newrtt */
+    "uint64_t",			/* flight */
+    "uint64_t");		/* (cwnd << 32) | point << 16 | retval(0/1) */
 
 SDT_PROBE_DEFINE5(sctp, cwnd, net, rttstep,
-    "uint64_t",		/* The Vtag << 32 | localport << 16 | remoteport */
-    "uint64_t",		/* obw | nbw */
-    "uint64_t",		/* bwrtt | newrtt */
-    "uint64_t",		/* flight */
-    "uint64_t");	/* (cwnd << 32) | point << 16 | retval(0/1) */
+    "uint64_t",			/* The Vtag << 32 | localport << 16 |
+				 * remoteport */
+    "uint64_t",			/* obw | nbw */
+    "uint64_t",			/* bwrtt | newrtt */
+    "uint64_t",			/* flight */
+    "uint64_t");		/* (cwnd << 32) | point << 16 | retval(0/1) */
 
 /* FastRetransmit-DECREASE */
 SDT_PROBE_DEFINE5(sctp, cwnd, net, fr,
-    "uint32_t",		/* The Vtag for this end */
-    "uint32_t",		/*
-			 * The port number of the local side << 16 | port number
-			 * of remote in network byte order.
-			 */
-    "uintptr_t",	/* The pointer to the struct sctp_nets * changing */
-    "int",		/* The old value of the cwnd */
-    "int");		/* The new value of the cwnd */
+    "uint32_t",			/* The Vtag for this end */
+    "uint32_t",			/* The port number of the local side << 16 |
+				 * port number of remote in network byte
+				 * order. */
+    "uintptr_t",		/* The pointer to the struct sctp_nets *
+				 * changing */
+    "int",			/* The old value of the cwnd */
+    "int");			/* The new value of the cwnd */
 
 /* TimeOut-DECREASE */
 SDT_PROBE_DEFINE5(sctp, cwnd, net, to,
-    "uint32_t",		/* The Vtag for this end */
-    "uint32_t",		/*
-			 * The port number of the local side << 16 | port number
-			 * of remote in network byte order.
-			 */
-    "uintptr_t",	/* The pointer to the struct sctp_nets * changing */
-    "int",		/* The old value of the cwnd */
-    "int");		/* The new value of the cwnd */
+    "uint32_t",			/* The Vtag for this end */
+    "uint32_t",			/* The port number of the local side << 16 |
+				 * port number of remote in network byte
+				 * order. */
+    "uintptr_t",		/* The pointer to the struct sctp_nets *
+				 * changing */
+    "int",			/* The old value of the cwnd */
+    "int");			/* The new value of the cwnd */
 
 /* BurstLimit-DECREASE */
 SDT_PROBE_DEFINE5(sctp, cwnd, net, bl,
-    "uint32_t",		/* The Vtag for this end */
-    "uint32_t",		/*
-			 * The port number of the local side << 16 | port number
-			 * of remote in network byte order.
-			 */
-    "uintptr_t",	/* The pointer to the struct sctp_nets * changing */
-    "int",		/* The old value of the cwnd */
-    "int");		/* The new value of the cwnd */
+    "uint32_t",			/* The Vtag for this end */
+    "uint32_t",			/* The port number of the local side << 16 |
+				 * port number of remote in network byte
+				 * order. */
+    "uintptr_t",		/* The pointer to the struct sctp_nets *
+				 * changing */
+    "int",			/* The old value of the cwnd */
+    "int");			/* The new value of the cwnd */
 
 /* ECN-DECREASE */
 SDT_PROBE_DEFINE5(sctp, cwnd, net, ecn,
-    "uint32_t",		/* The Vtag for this end */
-    "uint32_t",		/*
-			 * The port number of the local side << 16 | port number
-			 * of remote in network byte order.
-			 */
-    "uintptr_t",	/* The pointer to the struct sctp_nets * changing */
-    "int",		/* The old value of the cwnd */
-    "int");		/* The new value of the cwnd */
+    "uint32_t",			/* The Vtag for this end */
+    "uint32_t",			/* The port number of the local side << 16 |
+				 * port number of remote in network byte
+				 * order. */
+    "uintptr_t",		/* The pointer to the struct sctp_nets *
+				 * changing */
+    "int",			/* The old value of the cwnd */
+    "int");			/* The new value of the cwnd */
 
 /* PacketDrop-DECREASE */
 SDT_PROBE_DEFINE5(sctp, cwnd, net, pd,
-    "uint32_t",		/* The Vtag for this end */
-    "uint32_t",		/*
-			 * The port number of the local side << 16 | port number
-			 * of remote in network byte order.
-			 */
-    "uintptr_t",	/* The pointer to the struct sctp_nets * changing */
-    "int",		/* The old value of the cwnd */
-    "int");		/* The new value of the cwnd */
+    "uint32_t",			/* The Vtag for this end */
+    "uint32_t",			/* The port number of the local side << 16 |
+				 * port number of remote in network byte
+				 * order. */
+    "uintptr_t",		/* The pointer to the struct sctp_nets *
+				 * changing */
+    "int",			/* The old value of the cwnd */
+    "int");			/* The new value of the cwnd */
 
 /********************************************************/
 /* Rwnd probe - tracks changes in the receiver window for an assoc */
 /********************************************************/
 SDT_PROBE_DEFINE4(sctp, rwnd, assoc, val,
-    "uint32_t",		/* The Vtag for this end */
-    "uint32_t",		/*
-			 * The port number of the local side << 16 | port number
-			 * of remote in network byte order.
-			 */
-    "int",		/* The up/down amount */
-    "int");		/* The new value of the cwnd */
+    "uint32_t",			/* The Vtag for this end */
+    "uint32_t",			/* The port number of the local side << 16 |
+				 * port number of remote in network byte
+				 * order. */
+    "int",			/* The up/down amount */
+    "int");			/* The new value of the cwnd */
 
 /********************************************************/
 /* flight probe - tracks changes in the flight size on a net or assoc */
 /********************************************************/
 SDT_PROBE_DEFINE5(sctp, flightsize, net, val,
-    "uint32_t",		/* The Vtag for this end */
-    "uint32_t",		/*
-			 * The port number of the local side << 16 | port number
-			 * of remote in network byte order.
-			 */
-    "uintptr_t",        /* The pointer to the struct sctp_nets * changing */
-    "int",		/* The up/down amount */
-    "int");		/* The new value of the cwnd */
+    "uint32_t",			/* The Vtag for this end */
+    "uint32_t",			/* The port number of the local side << 16 |
+				 * port number of remote in network byte
+				 * order. */
+    "uintptr_t",		/* The pointer to the struct sctp_nets *
+				 * changing */
+    "int",			/* The up/down amount */
+    "int");			/* The new value of the cwnd */
 
 /********************************************************/
 /* The total flight version */
 /********************************************************/
 SDT_PROBE_DEFINE4(sctp, flightsize, assoc, val,
-    "uint32_t",		/* The Vtag for this end */
-    "uint32_t",		/*
-			 * The port number of the local side << 16 | port number
-			 * of remote in network byte order.
-			 */
-    "int",		/* The up/down amount */
-    "int");		/* The new value of the cwnd */
+    "uint32_t",			/* The Vtag for this end */
+    "uint32_t",			/* The port number of the local side << 16 |
+				 * port number of remote in network byte
+				 * order. */
+    "int",			/* The up/down amount */
+    "int");			/* The new value of the cwnd */
 
 #endif

Modified: head/sys/netinet/sctp_header.h
==============================================================================
--- head/sys/netinet/sctp_header.h	Thu Apr  7 08:32:37 2016	(r297661)
+++ head/sys/netinet/sctp_header.h	Thu Apr  7 09:10:34 2016	(r297662)
@@ -152,6 +152,23 @@ struct sctp_data_chunk {
 	struct sctp_data dp;
 }               SCTP_PACKED;
 
+struct sctp_idata {
+	uint32_t tsn;
+	uint16_t stream_id;
+	uint16_t reserved;	/* Where does the SSN go? */
+	uint32_t msg_id;
+	union {
+		uint32_t protocol_id;
+		uint32_t fsn;	/* Fragment Sequence Number */
+	};
+	/* user data follows */
+}          SCTP_PACKED;
+
+struct sctp_idata_chunk {
+	struct sctp_chunkhdr ch;
+	struct sctp_idata dp;
+}                SCTP_PACKED;
+
 /*
  * Structures for the control chunks
  */
@@ -378,6 +395,12 @@ struct sctp_strseq {
 	uint16_t sequence;
 }           SCTP_PACKED;
 
+struct sctp_strseq_mid {
+	uint16_t stream;
+	uint16_t reserved;
+	uint32_t msg_id;
+};
+
 struct sctp_forward_tsn_msg {
 	struct sctphdr sh;
 	struct sctp_forward_tsn_chunk msg;

Modified: head/sys/netinet/sctp_indata.c
==============================================================================
--- head/sys/netinet/sctp_indata.c	Thu Apr  7 08:32:37 2016	(r297661)
+++ head/sys/netinet/sctp_indata.c	Thu Apr  7 09:10:34 2016	(r297662)
@@ -34,18 +34,22 @@
 __FBSDID("$FreeBSD$");
 
 #include <netinet/sctp_os.h>
+#include <sys/proc.h>
 #include <netinet/sctp_var.h>
 #include <netinet/sctp_sysctl.h>
-#include <netinet/sctp_pcb.h>
 #include <netinet/sctp_header.h>
+#include <netinet/sctp_pcb.h>
 #include <netinet/sctputil.h>
 #include <netinet/sctp_output.h>
-#include <netinet/sctp_input.h>
-#include <netinet/sctp_indata.h>
 #include <netinet/sctp_uio.h>
+#include <netinet/sctp_auth.h>
 #include <netinet/sctp_timer.h>
-
-
+#include <netinet/sctp_asconf.h>
+#include <netinet/sctp_indata.h>
+#include <netinet/sctp_bsd_addr.h>
+#include <netinet/sctp_input.h>
+#include <netinet/sctp_crc32.h>
+#include <netinet/sctp_lock_bsd.h>
 /*
  * 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
@@ -55,6 +59,13 @@ __FBSDID("$FreeBSD$");
  * This will cause sctp_service_queues() to get called on the top entry in
  * the list.
  */
+static void
+sctp_add_chk_to_control(struct sctp_queued_to_read *control,
+    struct sctp_stream_in *strm,
+    struct sctp_tcb *stcb,
+    struct sctp_association *asoc,
+    struct sctp_tmit_chunk *chk);
+
 
 void
 sctp_set_rwnd(struct sctp_tcb *stcb, struct sctp_association *asoc)
@@ -74,9 +85,9 @@ sctp_calc_rwnd(struct sctp_tcb *stcb, st
 	 * sctp_soreceive then we will fix this so that ONLY this
 	 * associations data is taken into account.
 	 */
-	if (stcb->sctp_socket == NULL)
+	if (stcb->sctp_socket == NULL) {
 		return (calc);
-
+	}
 	if (stcb->asoc.sb_cc == 0 &&
 	    asoc->size_on_reasm_queue == 0 &&
 	    asoc->size_on_all_streams == 0) {
@@ -86,7 +97,6 @@ sctp_calc_rwnd(struct sctp_tcb *stcb, st
 	}
 	/* get actual space */
 	calc = (uint32_t) sctp_sbspace(&stcb->asoc, &stcb->sctp_socket->so_rcv);
-
 	/*
 	 * take out what has NOT been put on socket queue and we yet hold
 	 * for putting up.
@@ -95,7 +105,6 @@ sctp_calc_rwnd(struct sctp_tcb *stcb, st
 	    asoc->cnt_on_reasm_queue * MSIZE));
 	calc = sctp_sbspace_sub(calc, (uint32_t) (asoc->size_on_all_streams +
 	    asoc->cnt_on_all_streams * MSIZE));
-
 	if (calc == 0) {
 		/* out of space */
 		return (calc);
@@ -122,7 +131,7 @@ sctp_build_readq_entry(struct sctp_tcb *
     struct sctp_nets *net,
     uint32_t tsn, uint32_t ppid,
     uint32_t context, uint16_t stream_no,
-    uint16_t stream_seq, uint8_t flags,
+    uint32_t stream_seq, uint8_t flags,
     struct mbuf *dm)
 {
 	struct sctp_queued_to_read *read_queue_e = NULL;
@@ -131,73 +140,26 @@ sctp_build_readq_entry(struct sctp_tcb *
 	if (read_queue_e == NULL) {
 		goto failed_build;
 	}
+	memset(read_queue_e, 0, sizeof(struct sctp_queued_to_read));
 	read_queue_e->sinfo_stream = stream_no;
 	read_queue_e->sinfo_ssn = stream_seq;
 	read_queue_e->sinfo_flags = (flags << 8);
 	read_queue_e->sinfo_ppid = ppid;
 	read_queue_e->sinfo_context = context;
-	read_queue_e->sinfo_timetolive = 0;
 	read_queue_e->sinfo_tsn = tsn;
 	read_queue_e->sinfo_cumtsn = tsn;
 	read_queue_e->sinfo_assoc_id = sctp_get_associd(stcb);
+	read_queue_e->top_fsn = read_queue_e->fsn_included = 0xffffffff;
+	TAILQ_INIT(&read_queue_e->reasm);
 	read_queue_e->whoFrom = net;
-	read_queue_e->length = 0;
 	atomic_add_int(&net->ref_count, 1);
 	read_queue_e->data = dm;
-	read_queue_e->spec_flags = 0;
-	read_queue_e->tail_mbuf = NULL;
-	read_queue_e->aux_data = NULL;
-	read_queue_e->stcb = stcb;
-	read_queue_e->port_from = stcb->rport;
-	read_queue_e->do_not_ref_stcb = 0;
-	read_queue_e->end_added = 0;
-	read_queue_e->some_taken = 0;
-	read_queue_e->pdapi_aborted = 0;
-failed_build:
-	return (read_queue_e);
-}
-
-
-/*
- * Build out our readq entry based on the incoming packet.
- */
-static struct sctp_queued_to_read *
-sctp_build_readq_entry_chk(struct sctp_tcb *stcb,
-    struct sctp_tmit_chunk *chk)
-{
-	struct sctp_queued_to_read *read_queue_e = NULL;
-
-	sctp_alloc_a_readq(stcb, read_queue_e);
-	if (read_queue_e == NULL) {
-		goto failed_build;
-	}
-	read_queue_e->sinfo_stream = chk->rec.data.stream_number;
-	read_queue_e->sinfo_ssn = chk->rec.data.stream_seq;
-	read_queue_e->sinfo_flags = (chk->rec.data.rcv_flags << 8);
-	read_queue_e->sinfo_ppid = chk->rec.data.payloadtype;
-	read_queue_e->sinfo_context = stcb->asoc.context;
-	read_queue_e->sinfo_timetolive = 0;
-	read_queue_e->sinfo_tsn = chk->rec.data.TSN_seq;
-	read_queue_e->sinfo_cumtsn = chk->rec.data.TSN_seq;
-	read_queue_e->sinfo_assoc_id = sctp_get_associd(stcb);
-	read_queue_e->whoFrom = chk->whoTo;
-	read_queue_e->aux_data = NULL;
-	read_queue_e->length = 0;
-	atomic_add_int(&chk->whoTo->ref_count, 1);
-	read_queue_e->data = chk->data;
-	read_queue_e->tail_mbuf = NULL;
 	read_queue_e->stcb = stcb;
 	read_queue_e->port_from = stcb->rport;
-	read_queue_e->spec_flags = 0;
-	read_queue_e->do_not_ref_stcb = 0;
-	read_queue_e->end_added = 0;
-	read_queue_e->some_taken = 0;
-	read_queue_e->pdapi_aborted = 0;
 failed_build:
 	return (read_queue_e);
 }
 
-
 struct mbuf *
 sctp_build_ctl_nchunk(struct sctp_inpcb *inp, struct sctp_sndrcvinfo *sinfo)
 {
@@ -317,6 +279,7 @@ sctp_mark_non_revokable(struct sctp_asso
 {
 	uint32_t gap, i, cumackp1;
 	int fnd = 0;
+	int in_r = 0, in_nr = 0;
 
 	if (SCTP_BASE_SYSCTL(sctp_do_drain) == 0) {
 		return;
@@ -330,15 +293,20 @@ sctp_mark_non_revokable(struct sctp_asso
 		return;
 	}
 	SCTP_CALC_TSN_TO_GAP(gap, tsn, asoc->mapping_array_base_tsn);
-	if (!SCTP_IS_TSN_PRESENT(asoc->mapping_array, gap)) {
+	in_r = SCTP_IS_TSN_PRESENT(asoc->mapping_array, gap);
+	in_nr = SCTP_IS_TSN_PRESENT(asoc->nr_mapping_array, gap);
+	if ((in_r == 0) && (in_nr == 0)) {
+#ifdef INVARIANTS
+		panic("Things are really messed up now");
+#else
 		SCTP_PRINTF("gap:%x tsn:%x\n", gap, tsn);
 		sctp_print_mapping_array(asoc);
-#ifdef INVARIANTS
-		panic("Things are really messed up now!!");
 #endif
 	}
-	SCTP_SET_TSN_PRESENT(asoc->nr_mapping_array, gap);
-	SCTP_UNSET_TSN_PRESENT(asoc->mapping_array, gap);
+	if (in_nr == 0)
+		SCTP_SET_TSN_PRESENT(asoc->nr_mapping_array, gap);
+	if (in_r)
+		SCTP_UNSET_TSN_PRESENT(asoc->mapping_array, gap);
 	if (SCTP_TSN_GT(tsn, asoc->highest_tsn_inside_nr_map)) {
 		asoc->highest_tsn_inside_nr_map = tsn;
 	}
@@ -358,191 +326,157 @@ sctp_mark_non_revokable(struct sctp_asso
 	}
 }
 
-
-/*
- * 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)
+static int
+sctp_place_control_in_stream(struct sctp_stream_in *strm,
+    struct sctp_association *asoc,
+    struct sctp_queued_to_read *control)
 {
-	struct sctp_tmit_chunk *chk, *nchk;
-	uint16_t nxt_todel;
-	uint16_t stream_no;
-	int end = 0;
-	int cntDel;
-	struct sctp_queued_to_read *control, *ctl, *nctl;
-
-	if (stcb == NULL)
-		return;
+	struct sctp_queued_to_read *at;
+	struct sctp_readhead *q;
+	uint8_t bits, unordered;
 
-	cntDel = stream_no = 0;
-	if ((stcb->sctp_ep->sctp_flags & SCTP_PCB_FLAGS_SOCKET_GONE) ||
-	    (stcb->asoc.state & SCTP_STATE_ABOUT_TO_BE_FREED) ||
-	    (stcb->asoc.state & SCTP_STATE_CLOSED_SOCKET)) {
-		/* socket above is long gone or going.. */
-abandon:
-		asoc->fragmented_delivery_inprogress = 0;
-		TAILQ_FOREACH_SAFE(chk, &asoc->reasmqueue, sctp_next, nchk) {
-			TAILQ_REMOVE(&asoc->reasmqueue, chk, sctp_next);
-			asoc->size_on_reasm_queue -= chk->send_size;
-			sctp_ucount_decr(asoc->cnt_on_reasm_queue);
-			/*
-			 * Lose the data pointer, since its in the socket
-			 * buffer
-			 */
-			if (chk->data) {
-				sctp_m_freem(chk->data);
-				chk->data = NULL;
+	bits = (control->sinfo_flags >> 8);
+	unordered = bits & SCTP_DATA_UNORDERED;
+	if (unordered) {
+		q = &strm->uno_inqueue;
+		if (asoc->idata_supported == 0) {
+			if (!TAILQ_EMPTY(q)) {
+				/*
+				 * Only one stream can be here in old style
+				 * -- abort
+				 */
+				return (-1);
 			}
-			/* Now free the address and data */
-			sctp_free_a_chunk(stcb, chk, SCTP_SO_NOT_LOCKED);
-			/* sa_ignore FREED_MEMORY */
+			TAILQ_INSERT_TAIL(q, control, next_instrm);
+			control->on_strm_q = SCTP_ON_UNORDERED;
+			return (0);
 		}
-		return;
+	} else {
+		q = &strm->inqueue;
 	}
-	SCTP_TCB_LOCK_ASSERT(stcb);
-	TAILQ_FOREACH_SAFE(chk, &asoc->reasmqueue, sctp_next, nchk) {
-		if (chk->rec.data.TSN_seq != (asoc->tsn_last_delivered + 1)) {
-			/* Can't deliver more :< */
-			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
-			 */
-			return;
-		}
-		if (chk->rec.data.rcv_flags & SCTP_DATA_FIRST_FRAG) {
-
-			control = sctp_build_readq_entry_chk(stcb, chk);
-			if (control == NULL) {
-				/* out of memory? */
-				return;
-			}
-			/* save it off for our future deliveries */
-			stcb->asoc.control_pdapi = control;
-			if (chk->rec.data.rcv_flags & SCTP_DATA_LAST_FRAG)
-				end = 1;
-			else
-				end = 0;
-			sctp_mark_non_revokable(asoc, chk->rec.data.TSN_seq);
-			sctp_add_to_readq(stcb->sctp_ep,
-			    stcb, control, &stcb->sctp_socket->so_rcv, end,
-			    SCTP_READ_LOCK_NOT_HELD, SCTP_SO_NOT_LOCKED);
-			cntDel++;
+	if ((bits & SCTP_DATA_NOT_FRAG) == SCTP_DATA_NOT_FRAG) {
+		control->end_added = control->last_frag_seen = control->first_frag_seen = 1;
+	}
+	if (TAILQ_EMPTY(q)) {
+		/* Empty queue */
+		TAILQ_INSERT_HEAD(q, control, next_instrm);
+		if (unordered) {
+			control->on_strm_q = SCTP_ON_UNORDERED;
 		} else {
-			if (chk->rec.data.rcv_flags & SCTP_DATA_LAST_FRAG)
-				end = 1;
-			else
-				end = 0;
-			sctp_mark_non_revokable(asoc, chk->rec.data.TSN_seq);
-			if (sctp_append_to_readq(stcb->sctp_ep, stcb,
-			    stcb->asoc.control_pdapi,
-			    chk->data, end, chk->rec.data.TSN_seq,
-			    &stcb->sctp_socket->so_rcv)) {
+			control->on_strm_q = SCTP_ON_ORDERED;
+		}
+		return (0);
+	} else {
+		TAILQ_FOREACH(at, q, next_instrm) {
+			if (SCTP_TSN_GT(at->msg_id, control->msg_id)) {
 				/*
-				 * something is very wrong, either
-				 * control_pdapi is NULL, or the tail_mbuf
-				 * is corrupt, or there is a EOM already on
-				 * the mbuf chain.
+				 * one in queue is bigger than the new one,
+				 * insert before this one
 				 */
-				if (stcb->asoc.state & SCTP_STATE_ABOUT_TO_BE_FREED) {
-					goto abandon;
+				TAILQ_INSERT_BEFORE(at, control, next_instrm);
+				if (unordered) {
+					control->on_strm_q = SCTP_ON_UNORDERED;
 				} else {
-#ifdef INVARIANTS
-					if ((stcb->asoc.control_pdapi == NULL) || (stcb->asoc.control_pdapi->tail_mbuf == NULL)) {
-						panic("This should not happen control_pdapi NULL?");
+					control->on_strm_q = SCTP_ON_ORDERED;
+				}
+				break;
+			} else if (at->msg_id == control->msg_id) {
+				/*
+				 * Gak, He sent me a duplicate msg id
+				 * number?? return -1 to abort.
+				 */
+				return (-1);
+			} else {
+				if (TAILQ_NEXT(at, next_instrm) == NULL) {
+					/*
+					 * We are at the end, insert it
+					 * after this one
+					 */
+					if (SCTP_BASE_SYSCTL(sctp_logging_level) & SCTP_STR_LOGGING_ENABLE) {
+						sctp_log_strm_del(control, at,
+						    SCTP_STR_LOG_FROM_INSERT_TL);
 					}
-					/* if we did not panic, it was a EOM */
-					panic("Bad chunking ??");
-#else
-					if ((stcb->asoc.control_pdapi == NULL) || (stcb->asoc.control_pdapi->tail_mbuf == NULL)) {
-						SCTP_PRINTF("This should not happen control_pdapi NULL?\n");
+					TAILQ_INSERT_AFTER(q,
+					    at, control, next_instrm);
+					if (unordered) {
+						control->on_strm_q = SCTP_ON_UNORDERED;
+					} else {
+						control->on_strm_q = SCTP_ON_ORDERED;
 					}
-					SCTP_PRINTF("Bad chunking ??\n");
-					SCTP_PRINTF("Dumping re-assembly queue this will probably hose the association\n");
-
-#endif
-					goto abandon;
+					break;
 				}
 			}
-			cntDel++;
-		}
-		/* 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++;
-			}
-			if ((chk->rec.data.rcv_flags & SCTP_DATA_FIRST_FRAG) == 0) {
-				SCTP_STAT_INCR_COUNTER64(sctps_reasmusrmsgs);
-			}
-		} else if (chk->rec.data.rcv_flags & SCTP_DATA_FIRST_FRAG) {
-			/*
-			 * turn the flag back on since we just  delivered
-			 * yet another one.
-			 */
-			asoc->fragmented_delivery_inprogress = 1;
 		}
-		asoc->tsn_of_pdapi_last_delivered = chk->rec.data.TSN_seq;
-		asoc->last_flags_delivered = chk->rec.data.rcv_flags;
-		asoc->last_strm_seq_delivered = chk->rec.data.stream_seq;
-		asoc->last_strm_no_delivered = chk->rec.data.stream_number;
+	}
+	return (0);
+}
 
-		asoc->tsn_last_delivered = chk->rec.data.TSN_seq;
-		asoc->size_on_reasm_queue -= chk->send_size;
-		sctp_ucount_decr(asoc->cnt_on_reasm_queue);
-		/* free up the chk */
-		chk->data = NULL;
-		sctp_free_a_chunk(stcb, chk, SCTP_SO_NOT_LOCKED);
+static void
+sctp_abort_in_reasm(struct sctp_tcb *stcb,
+    struct sctp_stream_in *strm,
+    struct sctp_queued_to_read *control,
+    struct sctp_tmit_chunk *chk,
+    int *abort_flag, int opspot)
+{
+	char msg[SCTP_DIAG_INFO_LEN];
+	struct mbuf *oper;
 
-		if (asoc->fragmented_delivery_inprogress == 0) {
-			/*
-			 * Now lets see if we can deliver the next one on
-			 * the stream
-			 */
-			struct sctp_stream_in *strm;
+	if (stcb->asoc.idata_supported) {
+		snprintf(msg, sizeof(msg),
+		    "Reass %x,CF:%x,TSN=%8.8x,SID=%4.4x,FSN=%8.8x,MID:%8.8x",
+		    opspot,
+		    control->fsn_included,
+		    chk->rec.data.TSN_seq,
+		    chk->rec.data.stream_number,
+		    chk->rec.data.fsn_num, chk->rec.data.stream_seq);
+	} else {
+		snprintf(msg, sizeof(msg),
+		    "Reass %x, CI:%x,TSN=%8.8x,SID=%4.4x,FSN=%4.4x, SSN:%4.4x",
+		    opspot,
+		    control->fsn_included,
+		    chk->rec.data.TSN_seq,
+		    chk->rec.data.stream_number,
+		    chk->rec.data.fsn_num,
+		    (uint16_t) chk->rec.data.stream_seq);
+	}
+	oper = sctp_generate_cause(SCTP_CAUSE_PROTOCOL_VIOLATION, msg);
+	sctp_m_freem(chk->data);
+	chk->data = NULL;
+	sctp_free_a_chunk(stcb, chk, SCTP_SO_NOT_LOCKED);
+	stcb->sctp_ep->last_abort_code = SCTP_FROM_SCTP_INDATA + SCTP_LOC_1;
+	sctp_abort_an_association(stcb->sctp_ep, stcb, oper, SCTP_SO_NOT_LOCKED);
+	*abort_flag = 1;
+}
 
-			strm = &asoc->strmin[stream_no];
-			nxt_todel = strm->last_sequence_delivered + 1;
-			TAILQ_FOREACH_SAFE(ctl, &strm->inqueue, next, nctl) {
-				/* Deliver more if we can. */
-				if (nxt_todel == ctl->sinfo_ssn) {
-					TAILQ_REMOVE(&strm->inqueue, ctl, next);
-					asoc->size_on_all_streams -= ctl->length;
-					sctp_ucount_decr(asoc->cnt_on_all_streams);
-					strm->last_sequence_delivered++;
-					sctp_mark_non_revokable(asoc, ctl->sinfo_tsn);
-					sctp_add_to_readq(stcb->sctp_ep, stcb,
-					    ctl,
-					    &stcb->sctp_socket->so_rcv, 1,
-					    SCTP_READ_LOCK_NOT_HELD, SCTP_SO_NOT_LOCKED);
-				} else {
-					break;
-				}
-				nxt_todel = strm->last_sequence_delivered + 1;
-			}
-			break;
-		}
+static void
+clean_up_control(struct sctp_tcb *stcb, struct sctp_queued_to_read *control)
+{
+	/*
+	 * The control could not be placed and must be cleaned.
+	 */
+	struct sctp_tmit_chunk *chk, *nchk;
+
+	TAILQ_FOREACH_SAFE(chk, &control->reasm, sctp_next, nchk) {
+		TAILQ_REMOVE(&control->reasm, chk, sctp_next);
+		if (chk->data)
+			sctp_m_freem(chk->data);
+		chk->data = NULL;
+		sctp_free_a_chunk(stcb, chk, SCTP_SO_NOT_LOCKED);
 	}
+	sctp_free_a_readq(stcb, control);
 }
 
 /*
  * 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??
+ * append to the so_buf, keep doing so until we are out of order as
+ * long as the control's entered are non-fragmented.
  */
 static void
-sctp_queue_data_to_stream(struct sctp_tcb *stcb, struct sctp_association *asoc,
-    struct sctp_queued_to_read *control, int *abort_flag)
+sctp_queue_data_to_stream(struct sctp_tcb *stcb,
+    struct sctp_stream_in *strm,
+    struct sctp_association *asoc,
+    struct sctp_queued_to_read *control, int *abort_flag, int *need_reasm)
 {
 	/*
 	 * FIX-ME maybe? What happens when the ssn wraps? If we are getting
@@ -562,27 +496,16 @@ sctp_queue_data_to_stream(struct sctp_tc
 	 * SSN alone. Maybe a hybred approach is the answer
 	 * 
 	 */
-	struct sctp_stream_in *strm;
 	struct sctp_queued_to_read *at;
 	int queue_needed;
-	uint16_t nxt_todel;
+	uint32_t nxt_todel;
 	struct mbuf *op_err;
 	char msg[SCTP_DIAG_INFO_LEN];
 
-	queue_needed = 1;
-	asoc->size_on_all_streams += control->length;
-	sctp_ucount_incr(asoc->cnt_on_all_streams);
-	strm = &asoc->strmin[control->sinfo_stream];
-	nxt_todel = strm->last_sequence_delivered + 1;
 	if (SCTP_BASE_SYSCTL(sctp_logging_level) & SCTP_STR_LOGGING_ENABLE) {
 		sctp_log_strm_del(control, NULL, SCTP_STR_LOG_FROM_INTO_STRD);
 	}
-	SCTPDBG(SCTP_DEBUG_INDATA1,
-	    "queue to stream called for sid:%u ssn:%u tsn:%u lastdel:%u nxt:%u\n",
-	    (uint32_t) control->sinfo_stream, (uint32_t) control->sinfo_ssn,
-	    (uint32_t) control->sinfo_tsn,
-	    (uint32_t) strm->last_sequence_delivered, (uint32_t) nxt_todel);
-	if (SCTP_SSN_GE(strm->last_sequence_delivered, control->sinfo_ssn)) {
+	if (SCTP_MSGID_GT((!asoc->idata_supported), strm->last_sequence_delivered, control->sinfo_ssn)) {
 		/* The incoming sseq is behind where we last delivered? */
 		SCTPDBG(SCTP_DEBUG_INDATA1, "Duplicate S-SEQ:%d delivered:%d from peer, Abort association\n",
 		    control->sinfo_ssn, strm->last_sequence_delivered);
@@ -591,32 +514,39 @@ protocol_error:
 		 * throw it in the stream so it gets cleaned up in
 		 * association destruction
 		 */
-		TAILQ_INSERT_HEAD(&strm->inqueue, control, next);
+		TAILQ_INSERT_HEAD(&strm->inqueue, control, next_instrm);
 		snprintf(msg, sizeof(msg), "Delivered SSN=%4.4x, got TSN=%8.8x, SID=%4.4x, SSN=%4.4x",
 		    strm->last_sequence_delivered, control->sinfo_tsn,
 		    control->sinfo_stream, control->sinfo_ssn);
 		op_err = sctp_generate_cause(SCTP_CAUSE_PROTOCOL_VIOLATION, msg);
-		stcb->sctp_ep->last_abort_code = SCTP_FROM_SCTP_INDATA + SCTP_LOC_1;
+		stcb->sctp_ep->last_abort_code = SCTP_FROM_SCTP_INDATA + SCTP_LOC_2;
 		sctp_abort_an_association(stcb->sctp_ep, stcb, op_err, SCTP_SO_NOT_LOCKED);
 		*abort_flag = 1;
 		return;
 
 	}
+	if ((SCTP_TSN_GE(asoc->cumulative_tsn, control->sinfo_tsn)) && (asoc->idata_supported == 0)) {
+		goto protocol_error;
+	}
+	queue_needed = 1;
+	asoc->size_on_all_streams += control->length;
+	sctp_ucount_incr(asoc->cnt_on_all_streams);
+	nxt_todel = strm->last_sequence_delivered + 1;
+	if (nxt_todel == control->sinfo_ssn) {
 #if defined(__APPLE__) || defined(SCTP_SO_LOCK_TESTING)
-	struct socket *so;
+		struct socket *so;
 
-	so = SCTP_INP_SO(stcb->sctp_ep);
-	atomic_add_int(&stcb->asoc.refcnt, 1);
-	SCTP_TCB_UNLOCK(stcb);
-	SCTP_SOCKET_LOCK(so, 1);
-	SCTP_TCB_LOCK(stcb);
-	atomic_subtract_int(&stcb->asoc.refcnt, 1);
-	if (stcb->sctp_ep->sctp_flags & SCTP_PCB_FLAGS_SOCKET_GONE) {
-		SCTP_SOCKET_UNLOCK(so, 1);
-		return;
-	}
+		so = SCTP_INP_SO(stcb->sctp_ep);
+		atomic_add_int(&stcb->asoc.refcnt, 1);
+		SCTP_TCB_UNLOCK(stcb);
+		SCTP_SOCKET_LOCK(so, 1);
+		SCTP_TCB_LOCK(stcb);
+		atomic_subtract_int(&stcb->asoc.refcnt, 1);
+		if (stcb->sctp_ep->sctp_flags & SCTP_PCB_FLAGS_SOCKET_GONE) {
+			SCTP_SOCKET_UNLOCK(so, 1);
+			return;
+		}
 #endif
-	if (nxt_todel == control->sinfo_ssn) {
 		/* can be delivered right away? */
 		if (SCTP_BASE_SYSCTL(sctp_logging_level) & SCTP_STR_LOGGING_ENABLE) {
 			sctp_log_strm_del(control, NULL, SCTP_STR_LOG_FROM_IMMED_DEL);
@@ -626,19 +556,25 @@ protocol_error:
 		asoc->size_on_all_streams -= control->length;
 		sctp_ucount_decr(asoc->cnt_on_all_streams);
 		strm->last_sequence_delivered++;
-
 		sctp_mark_non_revokable(asoc, control->sinfo_tsn);
 		sctp_add_to_readq(stcb->sctp_ep, stcb,
 		    control,
 		    &stcb->sctp_socket->so_rcv, 1,
-		    SCTP_READ_LOCK_NOT_HELD, SCTP_SO_LOCKED);
-		TAILQ_FOREACH_SAFE(control, &strm->inqueue, next, at) {
+		    SCTP_READ_LOCK_NOT_HELD, SCTP_SO_NOT_LOCKED);
+		TAILQ_FOREACH_SAFE(control, &strm->inqueue, next_instrm, at) {
 			/* all delivered */
 			nxt_todel = strm->last_sequence_delivered + 1;
-			if (nxt_todel == control->sinfo_ssn) {
-				TAILQ_REMOVE(&strm->inqueue, control, next);
+			if ((nxt_todel == control->sinfo_ssn) &&
+			    (((control->sinfo_flags >> 8) & SCTP_DATA_NOT_FRAG) == SCTP_DATA_NOT_FRAG)) {
 				asoc->size_on_all_streams -= control->length;
 				sctp_ucount_decr(asoc->cnt_on_all_streams);
+				if (control->on_strm_q == SCTP_ON_ORDERED) {
+					TAILQ_REMOVE(&strm->inqueue, control, next_instrm);
+				} else {
+					panic("Huh control:%p is on_strm_q:%d",
+					    control, control->on_strm_q);
+				}
+				control->on_strm_q = 0;
 				strm->last_sequence_delivered++;
 				/*
 				 * We ignore the return of deliver_data here
@@ -655,655 +591,1000 @@ protocol_error:
 				    control,
 				    &stcb->sctp_socket->so_rcv, 1,
 				    SCTP_READ_LOCK_NOT_HELD,
-				    SCTP_SO_LOCKED);
+				    SCTP_SO_NOT_LOCKED);
 				continue;
+			} else if (nxt_todel == control->sinfo_ssn) {
+				*need_reasm = 1;
 			}
 			break;
 		}
+#if defined(__APPLE__) || defined(SCTP_SO_LOCK_TESTING)
+		SCTP_SOCKET_UNLOCK(so, 1);
+#endif
 	}
 	if (queue_needed) {
 		/*
 		 * Ok, we did not deliver this guy, find the correct place
 		 * to put it on the queue.
 		 */
-		if (SCTP_TSN_GE(asoc->cumulative_tsn, control->sinfo_tsn)) {
-#if defined(__APPLE__) || defined(SCTP_SO_LOCK_TESTING)
-			SCTP_SOCKET_UNLOCK(so, 1);
-#endif
-			goto protocol_error;
-		}
-		if (TAILQ_EMPTY(&strm->inqueue)) {
-			/* Empty queue */
-			if (SCTP_BASE_SYSCTL(sctp_logging_level) & SCTP_STR_LOGGING_ENABLE) {
-				sctp_log_strm_del(control, NULL, SCTP_STR_LOG_FROM_INSERT_HD);
-			}
-			TAILQ_INSERT_HEAD(&strm->inqueue, control, next);
-		} else {
-			TAILQ_FOREACH(at, &strm->inqueue, next) {
-				if (SCTP_SSN_GT(at->sinfo_ssn, control->sinfo_ssn)) {
-					/*
-					 * one in queue is bigger than the
-					 * new one, insert before this one
-					 */
-					if (SCTP_BASE_SYSCTL(sctp_logging_level) & SCTP_STR_LOGGING_ENABLE) {
-						sctp_log_strm_del(control, at,
-						    SCTP_STR_LOG_FROM_INSERT_MD);
-					}
-					TAILQ_INSERT_BEFORE(at, control, next);
-					break;

*** DIFF OUTPUT TRUNCATED AT 1000 LINES ***


More information about the svn-src-head mailing list