--- Copyright (c) 2010, Lawrence Stewart <lstewart@freebsd.org>
--- 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. The names of the authors, "Swinburne University of Technology" and the
---    "Centre for Advanced Internet Architectures" may not be used to endorse
---    or promote products derived from this software without specific
---    prior written permission.
---
--- THIS SOFTWARE IS PROVIDED BY THE AUTHORS 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 AUTHORS 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.
---
--- CAIA Khelp Framework Patch v0.1.0
---
--- This patch was created against revision 203910 of the FreeBSD 9-CURRENT
--- Subversion source tree by running the following command and cleaning up the
--- output to remove irrelevant parts:
---
--- svn diff http://svn.freebsd.org/base/head/sys@203910 \
--- http://svn.freebsd.org/base/projects/tcp_cc_head/sys@203947
---
--- To obtain the correct revision of the FreeBSD source tree that this patch
--- applies to, and store it in the local directory "/path/to/src", run:
---
--- svn co -r 203910 http://svn.freebsd.org/base/head </path/to/src>
---
--- Make sure the base system you are installing onto is already running
--- FreeBSD 9.x before continuing.
---
--- Issuing the following commands will result in a running Khelp framework
--- capable system:
---
--- cd /path/to/src/sys
--- patch -p0 < /path/to/caia_khelp_framework_v0.1.0_9.x.r203910.patch
--- cd /path/to/src/
--- make buildworld buildkernel installkernel installworld
--- mergemaster -iF -m /path/to/src
--- reboot
---
--- The Khelp framework patch was first released in 2010 by Lawrence Stewart
--- whilst studying at Swinburne University's Centre for Advanced Internet
--- Architectures, Melbourne, Australia. The work is released as part
--- of the NewTCP research project. More details are available at:
---     http://caia.swin.edu.au/urp/newtcp/
---
--- Lawrence Stewart is currently the sole maintainer. All contact regarding
--- this patch should be directed to him via email: lastewart@swin.edu.au
---
Index: conf/files
===================================================================
--- conf/files	(.../head/sys)	(revision 203910)
+++ conf/files	(.../projects/tcp_cc_head/sys)	(revision 203947)
@@ -2465,6 +2465,8 @@
 netinet/accf_data.c		optional accept_filter_data inet
 netinet/accf_dns.c		optional accept_filter_dns inet
 netinet/accf_http.c		optional accept_filter_http inet
+netinet/helper.c		optional inet
+netinet/hhooks.c		optional inet
 netinet/if_atm.c		optional atm
 netinet/if_ether.c		optional inet ether
 netinet/igmp.c			optional inet
Index: netinet/tcp_input.c
===================================================================
--- netinet/tcp_input.c	(.../head/sys)	(revision 203910)
+++ netinet/tcp_input.c	(.../projects/tcp_cc_head/sys)	(revision 203947)
@@ -56,11 +56,12 @@
 #include <vm/uma.h>
 
 #include <net/if.h>
 #include <net/route.h>
 #include <net/vnet.h>
 
 #define TCPSTATES		/* for logging */
 
+#include <netinet/hhooks.h>
 #include <netinet/in.h>
 #include <netinet/in_pcb.h>
 #include <netinet/in_systm.h>
@@ -1067,7 +1175,7 @@
 	int rstreason, todrop, win;
 	u_long tiwin;
 	struct tcpopt to;
-
+	struct tcp_hhook_data hhook_data;
 #ifdef TCPDEBUG
 	/*
 	 * The size of tcp_saveipgen must be the size of the max ip header,
@@ -2014,10 +2119,21 @@
 			TCPSTAT_INC(tcps_rcvacktoomuch);
 			goto dropafterack;
 		}
+		hhook_data.new_sacked_bytes = 0;
 		if ((tp->t_flags & TF_SACK_PERMIT) &&
 		    ((to.to_flags & TOF_SACK) ||
-		     !TAILQ_EMPTY(&tp->snd_holes)))
+		     !TAILQ_EMPTY(&tp->snd_holes))) {
 			tcp_sack_doack(tp, &to, th->th_ack);
+			/* XXXDH: should only be one if a productive SACK */
+			hhook_data.new_sacked_bytes = 1;
+		}
+
+		hhook_data.tp = tp;
+		hhook_data.th = th;
+		hhook_data.to = &to;
+		run_hhooks(HHOOK_TYPE_TCP, HHOOK_TCP_ESTABLISHED_IN,
+		    &hhook_data, tp->hdbs);
+
 		if (SEQ_LEQ(th->th_ack, tp->snd_una)) {
 			if (tlen == 0 && tiwin == tp->snd_wnd) {
 				TCPSTAT_INC(tcps_rcvdupack);
Index: netinet/tcp_subr.c
===================================================================
--- netinet/tcp_subr.c	(.../head/sys)	(revision 203910)
+++ netinet/tcp_subr.c	(.../projects/tcp_cc_head/sys)	(revision 203947)
@@ -62,6 +64,8 @@
 #include <net/if.h>
 #include <net/vnet.h>
 
+#include <netinet/helper.h>
+#include <netinet/hhooks.h>
 #include <netinet/in.h>
 #include <netinet/in_systm.h>
 #include <netinet/ip.h>
@@ -290,6 +294,7 @@
 struct tcpcb_mem {
 	struct	tcpcb		tcb;
 	struct	tcp_timer	tt;
+	struct	helper_dblocks	hdbs;
 };
 
 static VNET_DEFINE(uma_zone_t, tcpcb_zone);
@@ -374,6 +379,13 @@
 
 	V_tcp_inflight_rttthresh = TCPTV_INFLIGHT_RTTTHRESH;
 
+	if (register_hhook_head(HHOOK_TYPE_TCP, HHOOK_TCP_ESTABLISHED_IN,
+	    HHOOK_NOWAIT) != 0)
+		printf("%s: WARNING: unable to register helper hook\n", __func__);
+	if (register_hhook_head(HHOOK_TYPE_TCP, HHOOK_TCP_ESTABLISHED_OUT,
+	    HHOOK_NOWAIT) != 0)
+		printf("%s: WARNING: unable to register helper hook\n", __func__);
+
 	TUNABLE_INT_FETCH("net.inet.tcp.sack.enable", &V_tcp_do_sack);
 
 	INP_INFO_LOCK_INIT(&V_tcbinfo, "tcp");
@@ -749,6 +763,14 @@
 	if (tm == NULL)
 		return (NULL);
 	tp = &tm->tcb;
+
+	tp->hdbs = &tm->hdbs;
+	tp->hdbs->class = HELPER_CLASS_TCP;
+	if (init_helper_dblocks(tp->hdbs)) {
+		uma_zfree(V_tcpcb_zone, tm);
+		return (NULL);
+	}
+
 #ifdef VIMAGE
 	tp->t_vnet = inp->inp_vnet;
 #endif
@@ -912,8 +948,9 @@
 	}
 	/* Disconnect offload device, if any. */
 	tcp_offload_detach(tp);
-		
 	tcp_free_sackholes(tp);
+	destroy_helper_dblocks(tp->hdbs);
+
 	inp->inp_ppcb = NULL;
 	tp->t_inpcb = NULL;
 	uma_zfree(V_tcpcb_zone, tp);
Index: netinet/tcp_var.h
===================================================================
--- netinet/tcp_var.h	(.../head/sys)	(revision 203910)
+++ netinet/tcp_var.h	(.../projects/tcp_cc_head/sys)	(revision 203947)
@@ -74,7 +76,7 @@
 struct sackhint {
 	struct sackhole	*nexthole;
 	int		sack_bytes_rexmit;
-
+        tcp_seq		last_sack_ack; /* Last sack block acked with current pkt - used for enhanced RTT calculations*/
 	int		ispare;		/* explicit pad for 64bit alignment */
 	uint64_t	_pad[2];	/* 1 sacked_bytes, 1 TBD */
 };
@@ -199,10 +201,10 @@
 	struct toe_usrreqs *t_tu;	/* offload operations vector */
 	void	*t_toe;			/* TOE pcb pointer */
 	int	t_bytes_acked;		/* # bytes acked during current RTT */
-
 	int	t_ispare;		/* explicit pad for 64bit alignment */
 	void	*t_pspare2[6];		/* 2 CC / 4 TBD */
 	uint64_t _pad[12];		/* 7 UTO, 5 TBD (1-2 CC/RTT?) */
+	struct helper_dblocks	*hdbs;
 };
 
 /*
@@ -239,7 +243,23 @@
 #define ENTER_FASTRECOVERY(tp)	tp->t_flags |= TF_FASTRECOVERY
 #define EXIT_FASTRECOVERY(tp)	tp->t_flags &= ~TF_FASTRECOVERY
 
 /*
+ * TCP specific helper hook point identifiers.
+ */
+#define	HHOOK_TCP_ESTABLISHED_IN	1
+#define	HHOOK_TCP_ESTABLISHED_OUT	2
+
+struct tcp_hhook_data {
+	struct tcpcb *tp;
+	struct tcphdr *th;
+	struct tcpopt *to;
+	long len;
+	int tso;
+	tcp_seq  curack;
+	int new_sacked_bytes;
+};
+
+/*
  * Flags for the t_oobflags field.
  */
 #define	TCPOOB_HAVEDATA	0x01
Index: netinet/tcp_sack.c
===================================================================
--- netinet/tcp_sack.c	(.../head/sys)	(revision 203910)
+++ netinet/tcp_sack.c	(.../projects/tcp_cc_head/sys)	(revision 203947)
@@ -426,6 +426,7 @@
 	 * are received.
 	 */
 	sblkp = &sack_blocks[num_sack_blks - 1];	/* Last SACK block */
+	tp->sackhint.last_sack_ack = sblkp->end;
 	if (SEQ_LT(tp->snd_fack, sblkp->start)) {
 		/*
 		 * The highest SACK block is beyond fack.  Append new SACK
Index: netinet/tcp_output.c
===================================================================
--- netinet/tcp_output.c	(.../head/sys)	(revision 203910)
+++ netinet/tcp_output.c	(.../projects/tcp_cc_head/sys)	(revision 203947)
@@ -53,6 +53,7 @@
 #include <net/route.h>
 #include <net/vnet.h>
 
+#include <netinet/hhooks.h>
 #include <netinet/in.h>
 #include <netinet/in_systm.h>
 #include <netinet/ip.h>
@@ -152,6 +162,7 @@
 	struct sackhole *p;
 	int tso = 0;
 	struct tcpopt to;
+	struct tcp_hhook_data hhook_data;
 #if 0
 	int maxburst = TCP_MAXBURST;
 #endif
@@ -1121,6 +1114,15 @@
 			tp->snd_max = tp->snd_nxt + len;
 	}
 
+	hhook_data.th = th;
+	hhook_data.tp = tp;
+	hhook_data.to = &to;
+	hhook_data.len = len;
+	hhook_data.tso = tso;
+	run_hhooks(HHOOK_TYPE_TCP, HHOOK_TCP_ESTABLISHED_OUT, &hhook_data,
+	    tp->hdbs);
+
+
 #ifdef TCPDEBUG
 	/*
 	 * Trace.
Index: netinet/helper.c
===================================================================
--- netinet/helper.c	(.../head/sys)	(revision 0)
+++ netinet/helper.c	(.../projects/tcp_cc_head/sys)	(revision 203947)
@@ -0,0 +1,277 @@
+/*-
+ * Copyright (c) 2010 Lawrence Stewart <lstewart@freebsd.org>
+ * All rights reserved.
+ *
+ * This software was developed at the Centre for Advanced Internet
+ * Architectures, Swinburne University, by Lawrence Stewart,
+ * made possible in part by a grant from the FreeBSD Foundation.
+ *
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 AUTHOR 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/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <sys/kernel.h>
+#include <sys/lock.h>
+#include <sys/malloc.h>
+#include <sys/module.h>
+#include <sys/queue.h>
+#include <sys/refcount.h>
+#include <sys/rwlock.h>
+#include <sys/systm.h>
+
+#include <vm/uma.h>
+
+#include <netinet/helper.h>
+#include <netinet/helper_module.h>
+
+static struct rwlock helper_list_lock;
+RW_SYSINIT(helperlistlock, &helper_list_lock, "helper list lock");
+
+static STAILQ_HEAD(helper_head, helper) helpers = STAILQ_HEAD_INITIALIZER(helpers);
+
+static int num_dblocks = 0;
+
+/* Monotonically increasing ID assigned to helpers on registration. */
+static int32_t helper_id = 0;
+
+static struct helper * get_helper(int32_t id);
+
+/*
+ * Public KPI functions.
+ */
+int
+init_helper_dblocks(struct helper_dblocks *hdbs)
+{
+	struct helper *h;
+	struct helper_dblock *dblock;
+	int i = 0, error = 0;
+
+	KASSERT(hdbs != NULL, ("struct helper_dblocks not initialised!"));
+
+	HELPER_LIST_RLOCK();
+
+	if (num_dblocks == 0) {
+		HELPER_LIST_RUNLOCK();
+		return (0);
+	}
+
+	/* XXXLAS: Should only allocate for helpers of the appropriate class. */
+	hdbs->blocks = malloc(num_dblocks * sizeof(struct helper_dblock), M_HELPER,
+	    M_NOWAIT | M_ZERO);
+
+	if (hdbs->blocks != NULL) {
+		/*printf("Malloced ptr %p for %d data blocks\n", hdbs->blocks,
+		    num_dblocks);*/
+		STAILQ_FOREACH(h, &helpers, h_next) {
+			if (h->h_flags & HELPER_NEEDS_DBLOCK) {
+				dblock = hdbs->blocks+i;
+				/*printf("Current dblock ptr: %p\n", dblock);*/
+				dblock->hd_block = uma_zalloc(h->h_zone,
+				    M_NOWAIT);
+				/*
+				if (dblock[i]->block == NULL) {
+					XXX: Free all previous dblocks.
+					error = ENOMEM
+					break;
+				}
+				*/
+				dblock->hd_id = h->h_id;
+				/*printf("dblock[%d]: id=%d, block=%p\n", i,
+				    dblock->hd_id, dblock->hd_block);*/
+				i++;
+				refcount_acquire(&h->h_refcount);
+			}
+		}
+		hdbs->nblocks = i;
+	} else
+		error = ENOMEM;
+
+	HELPER_LIST_RUNLOCK();
+	return (error);
+}
+
+int
+destroy_helper_dblocks(struct helper_dblocks *hdbs)
+{
+	struct helper *h;
+	int32_t nblocks = hdbs->nblocks;
+
+	HELPER_LIST_WLOCK();
+
+	for (nblocks--; nblocks >= 0; nblocks--) {
+		if ((h = get_helper(hdbs->blocks[nblocks].hd_id)) != NULL) {
+			refcount_release(&h->h_refcount);
+			/*printf("destroy() freeing hdbs->blocks[%d] with ptr %p\n",
+			    nblocks, hdbs->blocks[nblocks].hd_block);*/
+			uma_zfree(h->h_zone, hdbs->blocks[nblocks].hd_block);
+		}
+	}
+
+	HELPER_LIST_WUNLOCK();
+	free(hdbs->blocks, M_HELPER);
+	return (0);
+}
+
+int
+register_helper(struct helper *h)
+{
+	HELPER_LIST_WLOCK();
+	if (h->h_flags | HELPER_NEEDS_DBLOCK)
+		num_dblocks++;
+
+	refcount_init(&h->h_refcount, 0);
+	h->h_id = helper_id++;
+	STAILQ_INSERT_TAIL(&helpers, h, h_next);
+	HELPER_LIST_WUNLOCK();
+	printf("Registered \"%s\" helper (mem %p)\n", h->h_name, h);
+	return (0);
+}
+
+int
+deregister_helper(struct helper *h)
+{
+	int error = 0;
+
+	/*
+	HHOOK_WLOCK
+	Remove this helper's hooks
+	HHOOK_WUNLOCK
+	*/
+
+	HELPER_LIST_WLOCK();
+	if (h->h_refcount > 0)
+		error = EBUSY;
+	
+	if (!error) {
+		STAILQ_REMOVE(&helpers, h, helper, h_next);
+		if (h->h_flags | HELPER_NEEDS_DBLOCK)
+			num_dblocks--;
+		printf("Deregistered \"%s\" helper (mem %p)\n", h->h_name, h);
+	}
+	HELPER_LIST_WUNLOCK();
+	return (error);
+}
+
+int32_t
+get_helper_id(char *hname)
+{
+	struct helper *h;
+	int32_t id = -1;
+
+	HELPER_LIST_RLOCK();
+	STAILQ_FOREACH(h, &helpers, h_next) {
+		if (strncmp(h->h_name, hname, HELPER_NAME_MAXLEN) == 0) {
+			id = h->h_id;
+			break;
+		}
+	}
+	HELPER_LIST_RUNLOCK();
+	return (id);
+}
+
+void *
+get_helper_dblock(struct helper_dblocks *hdbs, int32_t id)
+{
+	uint32_t nblocks = hdbs->nblocks;
+
+	for (nblocks--; nblocks >= 0; nblocks--) {
+		if (hdbs->blocks[nblocks].hd_id == id)
+			return (hdbs->blocks[nblocks].hd_block);
+	}
+	return (NULL);
+}
+
+/*
+ * Private KPI functions.
+ */
+static struct helper *
+get_helper(int32_t id)
+{
+	struct helper *h;
+
+	HELPER_LIST_LOCK_ASSERT();
+
+	STAILQ_FOREACH(h, &helpers, h_next) {
+		if (h->h_id == id)
+			return (h);
+	}
+	return (NULL);
+}
+
+/*
+ * Handles kld related events. Returns 0 on success, non-zero on failure.
+ */
+int
+helper_modevent(module_t mod, int event_type, void *data)
+{
+	int error = 0;
+	struct helper_modevent_data *hmd = (struct helper_modevent_data *)data;
+
+	switch(event_type) {
+		case MOD_LOAD:
+			if (hmd->helper->h_flags & HELPER_NEEDS_DBLOCK) {
+				if (hmd->uma_zsize <= 0) {
+					printf("Use DECLARE_HELPER_UMA() instead!\n");
+					error = EDOOFUS;
+					break;
+				}
+				hmd->helper->h_zone =
+				    uma_zcreate(hmd->name, hmd->uma_zsize,
+				    hmd->umactor, hmd->umadtor, NULL, NULL, 0,
+				    0);
+				if (hmd->helper->h_zone == NULL) {
+					error = ENOMEM;
+					break;
+				}
+			}
+			strlcpy(hmd->helper->h_name, hmd->name,
+			    HELPER_NAME_MAXLEN);
+			if (hmd->helper->mod_init != NULL)
+				error = hmd->helper->mod_init();
+			if (!error)
+				error = register_helper(hmd->helper);
+			break;
+
+		case MOD_QUIESCE:
+			error = deregister_helper(hmd->helper);
+			if (!error) {
+				uma_zdestroy(hmd->helper->h_zone);
+				if (hmd->helper->mod_destroy != NULL)
+					hmd->helper->mod_destroy();
+			} else
+				printf("Helper's refcount != 0, can't unload\n");
+			break;
+
+		case MOD_SHUTDOWN:
+		case MOD_UNLOAD:
+			break;
+
+		default:
+			error = EINVAL;
+			break;
+	}
+
+	return (error);
+}

Index: netinet/helper.h
===================================================================
--- netinet/helper.h	(.../head/sys)	(revision 0)
+++ netinet/helper.h	(.../projects/tcp_cc_head/sys)	(revision 203947)
@@ -0,0 +1,80 @@
+/*-
+ * Copyright (c) 2010 Lawrence Stewart <lstewart@freebsd.org>
+ * All rights reserved.
+ *
+ * This software was developed at the Centre for Advanced Internet
+ * Architectures, Swinburne University, by Lawrence Stewart,
+ * made possible in part by a grant from the FreeBSD Foundation.
+ *
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 AUTHOR 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.
+ *
+ * $FreeBSD$
+ */
+
+#ifndef	_NETINET_HELPER_H_
+#define	_NETINET_HELPER_H_
+
+
+struct helper_dblock {
+	int32_t		hd_id;
+	void		*hd_block;
+};
+
+struct helper_dblocks {
+	struct helper_dblock	*blocks;
+	int32_t			nblocks;
+	uint32_t		class;
+};
+
+struct helper {
+	int (*mod_init) (void);
+	int (*mod_destroy) (void);
+	uma_zone_t	h_zone;
+#define HELPER_NAME_MAXLEN 16
+	char		h_name[HELPER_NAME_MAXLEN];
+	uint16_t	h_flags;
+	uint32_t	h_class;
+	int32_t		h_id;
+	volatile uint32_t	h_refcount;
+	STAILQ_ENTRY(helper) h_next;
+};
+
+/* Helper flags */
+#define HELPER_NEEDS_DBLOCK	0x0001
+
+/* Helper classes */
+#define HELPER_CLASS_TCP	0x00000001
+
+int	init_helper_dblocks(struct helper_dblocks *hdbs);
+int	destroy_helper_dblocks(struct helper_dblocks *hdbs);
+int	register_helper(struct helper *h);
+int	deregister_helper(struct helper *h);
+int32_t	get_helper_id(char *hname);
+void *	get_helper_dblock(struct helper_dblocks *hdbs, int32_t id);
+
+#define	HELPER_LIST_WLOCK() rw_wlock(&helper_list_lock)
+#define	HELPER_LIST_WUNLOCK() rw_wunlock(&helper_list_lock)
+#define	HELPER_LIST_RLOCK() rw_rlock(&helper_list_lock)
+#define	HELPER_LIST_RUNLOCK() rw_runlock(&helper_list_lock)
+#define	HELPER_LIST_LOCK_ASSERT() rw_assert(&helper_list_lock, RA_LOCKED)
+
+#endif /* _NETINET_HELPER_H_ */

Index: netinet/hhooks.c
===================================================================
--- netinet/hhooks.c	(.../head/sys)	(revision 0)
+++ netinet/hhooks.c	(.../projects/tcp_cc_head/sys)	(revision 203947)
@@ -0,0 +1,301 @@
+/*-
+ * Copyright (c) 2010 Lawrence Stewart <lstewart@freebsd.org>
+ * All rights reserved.
+ *
+ * This software was developed at the Centre for Advanced Internet
+ * Architectures, Swinburne University, by Lawrence Stewart,
+ * made possible in part by a grant from the FreeBSD Foundation.
+ *
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 AUTHOR 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/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <sys/kernel.h>
+#include <sys/lock.h>
+#include <sys/malloc.h>
+#include <sys/queue.h>
+#include <sys/rmlock.h>
+#include <sys/systm.h>
+
+#include <vm/uma.h>
+
+#include <net/vnet.h>
+
+#include <netinet/helper.h>
+#include <netinet/hhooks.h>
+
+#define	RLOCK_HHOOK_HEAD	0x01
+#define	WLOCK_HHOOK_HEAD	0x02
+
+MALLOC_DECLARE(M_HHOOK);
+MALLOC_DEFINE(M_HHOOK, "helper hook related memory", "Blah");
+
+struct hhook {
+	hhook_func_t h_func;
+	void	*h_udata;
+	struct helper *h_helper;
+        STAILQ_ENTRY(hhook) h_next;
+};
+
+typedef	STAILQ_HEAD(hhook_list, hhook) hhook_list_t;
+
+struct hhook_head {
+	int	hh_type;
+	int	hh_id;
+	int	hh_nhooks;
+	hhook_list_t	hh_hooks;
+	struct rmlock	hh_lock;
+	LIST_ENTRY(hhook_head) hh_next;
+};
+
+LIST_HEAD(hhookheadhead, hhook_head);
+VNET_DEFINE(struct hhookheadhead, hhook_head_list);
+#define	V_hhook_head_list	VNET(hhook_head_list)
+
+static struct mtx hhook_head_list_lock;
+MTX_SYSINIT(hhookheadlistlock, &hhook_head_list_lock, "hhook_head list lock",
+    MTX_DEF);
+
+static struct	hhook_head *	get_hhook_head(int hhook_type, int hhook_id,
+    struct rm_priotracker* rmpt, int flags);
+
+
+/*
+ * Public KPI functions
+ */
+int
+register_hhook_head(int hhook_type, int hhook_id, int flags)
+{
+	struct hhook_head *hh;
+
+	HHOOK_HEAD_LIST_LOCK();
+	hh = get_hhook_head(hhook_type, hhook_id, NULL, 0);
+
+	if (hh != NULL)
+		return (EEXIST);
+
+	hh = malloc(sizeof(struct hhook_head), M_HHOOK,
+	    M_ZERO | ((flags & HHOOK_WAITOK) ? M_WAITOK : M_NOWAIT));
+
+	if (hh == NULL)
+		return (ENOMEM);
+
+	printf("About to register hhook_head %p with type: %d and id: %d\n", hh,
+	hhook_type, hhook_id);
+
+	hh->hh_type = hhook_type;
+	hh->hh_id = hhook_id;
+	hh->hh_nhooks = 0;
+	STAILQ_INIT(&hh->hh_hooks);
+	HHOOK_HEAD_LOCK_INIT(hh);
+
+	LIST_INSERT_HEAD(&V_hhook_head_list, hh, hh_next);
+	HHOOK_HEAD_LIST_UNLOCK();
+	return (0);
+}
+
+int
+deregister_hhook_head(int hhook_type, int hhook_id)
+{
+	struct hhook_head *hh;
+	struct hhook *tmp, *tmp2;
+	int error = 0;
+
+	HHOOK_HEAD_LIST_LOCK();
+	hh = get_hhook_head(hhook_type, hhook_id, NULL, WLOCK_HHOOK_HEAD);
+
+	if (hh == NULL)
+		error = ENOENT;
+	else {
+		LIST_REMOVE(hh, hh_next);
+
+		STAILQ_FOREACH_SAFE(tmp, &hh->hh_hooks, h_next, tmp2) {
+			free(tmp, M_HHOOK);
+		}
+
+		HHOOK_HEAD_WUNLOCK(hh);
+		HHOOK_HEAD_LOCK_DESTROY(hh);
+		free(hh, M_HHOOK);
+	}
+
+	HHOOK_HEAD_LIST_UNLOCK();
+	return (error);
+}
+
+int
+register_hhook(int hhook_type, int hhook_id, struct helper *helper,
+    hhook_func_t hook, void *udata, int flags)
+{
+	struct hhook *h, *tmp;
+	struct hhook_head *hh;
+	int error = 0;
+
+	h = malloc(sizeof(struct hhook), M_HHOOK,
+	    M_ZERO | ((flags & HHOOK_WAITOK) ? M_WAITOK : M_NOWAIT));
+
+	if (h == NULL)
+		return (ENOMEM);
+
+	h->h_helper = helper;
+	h->h_func = hook;
+	h->h_udata = udata;
+
+	hh = get_hhook_head(hhook_type, hhook_id, NULL, WLOCK_HHOOK_HEAD);
+
+	if (hh == NULL) {
+		free(h, M_HHOOK);
+		return (ENOENT);
+	}
+
+	STAILQ_FOREACH(tmp, &hh->hh_hooks, h_next) {
+		if (tmp->h_func == hook && tmp->h_udata == udata) {
+			error = EEXIST;
+			break;
+		}
+	}
+
+	if (!error) {
+		STAILQ_INSERT_TAIL(&hh->hh_hooks, h, h_next);
+		hh->hh_nhooks++;
+	}
+	else
+		free(h, M_HHOOK);
+
+	HHOOK_HEAD_WUNLOCK(hh);
+
+	return (error);
+}
+
+int
+deregister_hhook(int hhook_type, int hhook_id, hhook_func_t hook, void *udata,
+    int flags)
+{
+	struct hhook *tmp;
+	struct hhook_head *hh;
+
+	hh = get_hhook_head(hhook_type, hhook_id, NULL, WLOCK_HHOOK_HEAD);
+
+	if (hh == NULL)
+		return (ENOENT);
+
+	STAILQ_FOREACH(tmp, &hh->hh_hooks, h_next) {
+		if (tmp->h_func == hook && tmp->h_udata == udata) {
+			STAILQ_REMOVE(&hh->hh_hooks, tmp, hhook, h_next);
+			free(tmp, M_HHOOK);
+			hh->hh_nhooks--;
+			break;
+		}
+	}
+
+	HHOOK_HEAD_WUNLOCK(hh);
+	return (0);
+}
+
+void
+run_hhooks(int hhook_type, int hhook_id, void *ctx_data,
+    struct helper_dblocks *hdbs)
+{
+	struct hhook_head *hh;
+	struct hhook *tmp;
+	struct rm_priotracker rmpt;
+	int i = 0;
+	void *dblock = NULL;
+	uint32_t nblocks = hdbs->nblocks;
+
+	hh = get_hhook_head(hhook_type, hhook_id, &rmpt, RLOCK_HHOOK_HEAD);
+
+	if (hh == NULL)
+		return;
+
+	STAILQ_FOREACH(tmp, &hh->hh_hooks, h_next) {
+		//printf("Running hook %p for helper %d\n", tmp,
+		//tmp->h_helper->id);
+		if (tmp->h_helper->h_flags & HELPER_NEEDS_DBLOCK) {
+			if (nblocks == 0
+			    || i >= nblocks
+			    || tmp->h_helper->h_id != hdbs->blocks[i].hd_id)
+				continue;
+			dblock = hdbs->blocks[i].hd_block;
+			i++;
+		}
+		tmp->h_func(tmp->h_udata, ctx_data, dblock, hdbs);
+		dblock = NULL;
+	}
+
+	HHOOK_HEAD_RUNLOCK(hh, &rmpt);
+}
+
+
+/*
+ * Private KPI functions
+ */
+static struct hhook_head *
+get_hhook_head(int hhook_type, int hhook_id, struct rm_priotracker *rmpt,
+    int flags)
+{
+	struct hhook_head *tmp, *ret = NULL;
+
+	/*KASSERT(HHOOK_HEAD_LIST_LOCK_ASSERT(), ("hhook_head_list_lock not
+	 * locked"));*/
+
+	LIST_FOREACH(tmp, &V_hhook_head_list, hh_next) {
+		if (tmp->hh_type == hhook_type && tmp->hh_id == hhook_id) {
+			ret = tmp;
+			if (flags & RLOCK_HHOOK_HEAD)
+				HHOOK_HEAD_RLOCK(ret, rmpt);
+			else if (flags & WLOCK_HHOOK_HEAD)
+				HHOOK_HEAD_WLOCK(ret);
+			break;
+		}
+	}
+
+	return (ret);
+}
+
+static int
+vnet_hhook_init(const void *unused)
+{
+
+	LIST_INIT(&V_hhook_head_list);
+	return (0);
+}
+
+static int
+vnet_hhook_uninit(const void *unused)
+{
+
+	return (0);
+}
+
+#define	HHOOK_SYSINIT_ORDER	SI_SUB_PROTO_BEGIN
+#define	HHOOK_MODEVENT_ORDER	(SI_ORDER_FIRST) 
+#define	HHOOK_VNET_ORDER	(HHOOK_MODEVENT_ORDER + 2) 
+
+VNET_SYSINIT(vnet_hhook_init, HHOOK_SYSINIT_ORDER, HHOOK_VNET_ORDER,
+    vnet_hhook_init, NULL);
+ 
+VNET_SYSUNINIT(vnet_hhook_uninit, HHOOK_SYSINIT_ORDER, HHOOK_VNET_ORDER,
+    vnet_hhook_uninit, NULL);
+

Index: netinet/hhooks.h
===================================================================
--- netinet/hhooks.h	(.../head/sys)	(revision 0)
+++ netinet/hhooks.h	(.../projects/tcp_cc_head/sys)	(revision 203947)
@@ -0,0 +1,69 @@
+/*-
+ * Copyright (c) 2010 Lawrence Stewart <lstewart@freebsd.org>
+ * All rights reserved.
+ *
+ * This software was developed at the Centre for Advanced Internet
+ * Architectures, Swinburne University, by Lawrence Stewart,
+ * made possible in part by a grant from the FreeBSD Foundation.
+ *
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 AUTHOR 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.
+ *
+ * $FreeBSD$
+ */
+
+#ifndef _NETINET_HHOOKS_H_
+#define _NETINET_HHOOKS_H_
+
+#define	HHOOK_WAITOK	0x01
+#define	HHOOK_NOWAIT	0x02
+
+#define	HHOOK_TYPE_TCP		1
+
+struct helper;
+struct helper_dblocks;
+struct hhook_head;
+
+typedef void (*hhook_func_t)(void *udata, void *ctx_data, void *helper_dblock,
+    struct helper_dblocks *hdbs);
+
+int	register_hhook_head(int hhook_type, int hhook_id, int flags);
+int	deregister_hhook_head(int hhook_type, int hhook_id);
+int	register_hhook(int hhook_type, int hhook_id, struct helper *helper,
+    hhook_func_t hook, void *udata, int flags);
+int	deregister_hhook(int hhook_type, int hhook_id, hhook_func_t hook,
+    void *udata, int flags);
+void	run_hhooks(int hhook_type, int hhook_id, void *ctx_data,
+    struct helper_dblocks *hdbs);
+
+#define	HHOOK_HEAD_LIST_LOCK() mtx_lock(&hhook_head_list_lock)
+#define	HHOOK_HEAD_LIST_UNLOCK() mtx_unlock(&hhook_head_list_lock)
+#define	HHOOK_HEAD_LIST_LOCK_ASSERT() mtx_assert(&hhook_head_list_lock, MA_OWNED)
+
+#define	HHOOK_HEAD_LOCK_INIT(hh) rm_init(&(hh)->hh_lock, "hhook_head rm lock")
+#define	HHOOK_HEAD_LOCK_DESTROY(hh) rm_destroy(&(hh)->hh_lock)
+#define	HHOOK_HEAD_WLOCK(hh) rm_wlock(&(hh)->hh_lock)
+#define	HHOOK_HEAD_WUNLOCK(hh) rm_wunlock(&(hh)->hh_lock)
+#define	HHOOK_HEAD_RLOCK(hh,rmpt) rm_rlock(&(hh)->hh_lock, (rmpt))
+#define	HHOOK_HEAD_RUNLOCK(hh,rmpt) rm_runlock(&(hh)->hh_lock, (rmpt))
+
+#endif /* _NETINET_HHOOKS_H_ */
+

Index: netinet/helper_module.h
===================================================================
--- netinet/helper_module.h	(.../head/sys)	(revision 0)
+++ netinet/helper_module.h	(.../projects/tcp_cc_head/sys)	(revision 203947)
@@ -0,0 +1,81 @@
+/*-
+ * Copyright (c) 2010 Lawrence Stewart <lstewart@freebsd.org>
+ * All rights reserved.
+ *
+ * This software was developed at the Centre for Advanced Internet
+ * Architectures, Swinburne University, by Lawrence Stewart,
+ * made possible in part by a grant from the FreeBSD Foundation.
+ *
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 AUTHOR 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.
+ *
+ * $FreeBSD$
+ */
+
+#ifndef _NETINET_HELPER_MODULE_H_
+#define _NETINET_HELPER_MODULE_H_
+
+struct helper_modevent_data {
+	char name[HELPER_NAME_MAXLEN];
+	struct helper *helper;
+	int uma_zsize;
+	uma_ctor umactor;
+	uma_dtor umadtor;
+};
+
+#define	DECLARE_HELPER(hname, hdata, version)				\
+	static struct helper_modevent_data hmd_##hname = {		\
+		.name = #hname,						\
+		.helper = hdata						\
+	};								\
+	static moduledata_t h_##hname = {				\
+		.name = #hname,						\
+		.evhand = helper_modevent,				\
+		.priv = &hmd_##hname					\
+	};								\
+	DECLARE_MODULE(hname, h_##hname, SI_SUB_PROTO_IFATTACHDOMAIN, \
+	    SI_ORDER_ANY);						\
+	MODULE_VERSION(hname, version)
+
+#define	DECLARE_HELPER_UMA(hname, hdata, version, size, ctor, dtor)	\
+	static struct helper_modevent_data hmd_##hname = {		\
+		.name = #hname,						\
+		.helper = hdata,					\
+		.uma_zsize = size,					\
+		.umactor = ctor,					\
+		.umadtor = dtor						\
+	};								\
+	static moduledata_t h_##hname = {				\
+		.name = #hname,						\
+		.evhand = helper_modevent,				\
+		.priv = &hmd_##hname					\
+	};								\
+	DECLARE_MODULE(hname, h_##hname, SI_SUB_PROTO_IFATTACHDOMAIN, \
+	    SI_ORDER_ANY);						\
+	MODULE_VERSION(hname, version)
+
+int	helper_modevent(module_t mod, int type, void *data);
+
+MALLOC_DECLARE(M_HELPER);
+MALLOC_DEFINE(M_HELPER, "helper data", "Blah");
+
+
+#endif /* _NETINET_HELPER_MODULE_H_ */
