--- Copyright (c) 2011, Centre for Advanced Internet Architectures
--- Swinburne University of Technology, Melbourne, Australia
--- (CRICOS number 00111D).
---
--- Stateless TCP for HTTP
--- 
--- This software was developed by David Hayes <dahayes@swin.edu.au> 
--- and Michael Welzl <michawe@ifi.uio.no> and tested by 
--- Mattia Rossi <mrossi@swin.edu.au>
--- 
--- 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, the "Centre for Advanced Internet Architectures"
---    and "Swinburne University of Technology" 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.
---
---
--- a/sys/conf/files	Tue Sep 14 20:41:06 2010 +0000
+++ b/sys/conf/files	Thu Mar 03 11:54:14 2011 +1100
@@ -2605,6 +2605,7 @@
 netinet/tcp_timer.c		optional inet
 netinet/tcp_timewait.c		optional inet
 netinet/tcp_usrreq.c		optional inet
+netinet/statelesstcp.c		optional inet
 netinet/udp_usrreq.c		optional inet
 netinet/libalias/alias.c	optional libalias inet | netgraph_nat inet
 netinet/libalias/alias_db.c	optional libalias inet | netgraph_nat inet
diff -r 0394c100d759 sys/netinet/statelesstcp.c
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/sys/netinet/statelesstcp.c	Thu Mar 03 11:54:14 2011 +1100
@@ -0,0 +1,993 @@
+/*-
+ * Copyright (c) 2010
+ * 	Swinburne University of Technology, Melbourne, Australia
+ * All rights reserved.
+ *
+ * This software was developed at the Centre for Advanced Internet
+ * Architectures, Swinburne University, by David Hayes
+ *
+ * Development is part of the CAIA Stateless TCP project,
+ * http://caia.swin.edu.au/ngen/statelesstcp
+ *
+ * This project has been made possible in part by a grant from 
+ * APNIC Pty Ltd and Nominet UK
+ *
+ * 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$");
+
+/*
+ * This work is motivated by Geoff Huston's proposal (
+ * http://www.potaroo.net/ispcol/2009- 11/stateless.html and
+ * http://www.potaroo.net/presentations/2010-01-29-stateless-short.pdf) to
+ * address the emerging need for DNS servers to handle high volumes of DNS
+ * queries over TCP once DNSSEC becomes widely deployed. Geoff proposed that if
+ * TCP's connection establishment steps were simplified, the process of setting
+ * up and tearing down TCP sessions would become significantly less
+ * computationally expensive.
+ *
+ * Statelesstcp.c and statelesstcp.h provide the necessary functionality to
+ * implement this idea. TCP sessions to the DNS listening port are redirected
+ * from tcp_input to statlesstcp_input. If it is not a zone transfer request,
+ * statelesstcp_input manages the TCP ACK, and forwards the information to
+ * udp_input, for the DNS server to receive on its UDP listening
+ * socket. Responses from the DNS server are redirected from udp_output to
+ * statelesstcp_output for sending to the DNS client.
+ *
+ */
+
+#include "opt_inet.h"
+#include "opt_inet6.h"
+
+#include <machine/stdarg.h>
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/domain.h>
+#include <sys/kernel.h>
+#include <sys/syslog.h>
+#include <sys/lock.h>
+#include <sys/mbuf.h>
+#include <sys/mutex.h>
+#include <sys/protosw.h>
+#include <sys/socket.h>
+#include <sys/socketvar.h>
+#include <sys/sysctl.h>
+
+#include <netinet/in.h>
+#include <netinet/in_systm.h>
+#include <netinet/ip.h>
+#include <netinet/in_pcb.h>
+#include <netinet/ip_var.h>
+#include <netinet/ip_options.h>
+#ifdef INET6
+#include <netinet/ip6.h>
+#include <netinet6/in6_pcb.h>
+#include <netinet6/ip6_var.h>
+#include <netinet6/tcp6_var.h>
+#endif
+#include <netinet/tcp_var.h>
+#include <netinet/tcpip.h>
+#include <netinet/udp.h>
+#include <netinet/udp_var.h>
+#include <machine/in_cksum.h>
+
+#include <security/mac/mac_framework.h>
+
+#include <netinet/statelesstcp.h>
+
+/* Michael for HTTP
+static int firstTime = 1;
+*/
+
+/* These definitions are taken from sys/netinet/tcp_syncache.c */
+#define	SCH_LOCK(sch)		mtx_lock(&(sch)->sch_mtx)
+#define	SCH_UNLOCK(sch)		mtx_unlock(&(sch)->sch_mtx)
+#define	SCH_LOCK_ASSERT(sch)	mtx_assert(&(sch)->sch_mtx, MA_OWNED)
+
+/* These definitions are taken from sys/netinet/tcp_input.c */
+#define	TI_UNLOCKED	1
+#define	TI_RLOCKED	2
+#define	TI_WLOCKED	3
+
+SYSCTL_NODE(_net_inet_tcp, OID_AUTO, stateless, CTLFLAG_RW, 0, "Stateless TCP");
+
+VNET_DEFINE(int, tcp_stateless) = 0;
+SYSCTL_VNET_INT(_net_inet_tcp_stateless,  OID_AUTO, active,
+			CTLFLAG_RW, &VNET_NAME(tcp_stateless), 0,
+			"Flag to enable stateless TCP operation for DNS");
+
+VNET_DEFINE(int, tcp_stateless_axfr_detect) = 0;
+SYSCTL_VNET_INT(_net_inet_tcp_stateless,  OID_AUTO, axfr_detect,
+			CTLFLAG_RW, &VNET_NAME(tcp_stateless_axfr_detect), 0,
+			"Flag to enable stateless TCP detection of axfr query (zone transfer)");
+
+VNET_DEFINE(int, tcp_stateless_port) = 80;		// was 53 for DNS
+SYSCTL_VNET_INT(_net_inet_tcp_stateless,  OID_AUTO, port,
+			CTLFLAG_RW, &VNET_NAME(tcp_stateless_port), 0,
+			"Port monitored for stateless tcp if active");
+
+VNET_DEFINE(int, tcp_stateless_loglevel) = 1;
+SYSCTL_VNET_INT(_net_inet_tcp_stateless,  OID_AUTO, loglevel,
+				CTLFLAG_RW, &VNET_NAME(tcp_stateless_loglevel), 0,
+				 "Stateless TCP logging level (0-3)");
+
+VNET_DEFINE(int, tcp_stateless_timeout) = 3000;
+SYSCTL_VNET_INT(_net_inet_tcp_stateless,  OID_AUTO, timeout,
+				CTLFLAG_RW, &VNET_NAME(tcp_stateless_timeout), 0,
+				 "Stateless TCP lookup table timeout (ticks)");
+
+
+int udp6_input(struct mbuf **mp, int *offp, int proto);
+char *ip6_sprintf(char *ip6buf, const struct in6_addr *addr);
+
+
+/* Definition of different log levels. Level set through sysctl */
+#define	SLT_LOG_ZERO	0	
+#define SLT_LOG_LOW     1
+#define	SLT_LOG_DETAIL  2
+#define	SLT_LOG_DEBUG   3
+/*
+ * Log only if the current log level >= the specified log level
+ */
+#define	SLT_LOG(level, action)	if (V_tcp_stateless_loglevel >= level) { action; }
+/*max u_int16 - 1 */
+#define CACHE_RTX_VAL  0xfffe
+
+u_int16_t stateless_mss(struct syncache *sc, struct inpcb *inp, int *mtuflags);
+
+struct syncache * syncache_lookup(struct in_conninfo *inc,
+								  struct syncache_head **schp);
+
+void syncache_drop(struct syncache *sc, struct syncache_head *sch);
+
+void stateless_striptcphdr(struct mbuf *m, int ipoff, int tcpoff);
+
+void statelesstcpip_fillheaders(int isipv6, void *srcaddr, void *dstaddr,
+								u_int16_t srcport, u_int16_t dstport,
+								struct syncache *sc, void *ip_ptr, void *tcp_ptr);
+
+int stateless_send_ack(struct in_conninfo *inc, struct tcphdr *rth,
+					   struct tcpopt *rto, int dlen, int free_sincache);
+
+static void StatelessTcpLog(const char *format, ...);
+
+
+/*
+ * Stateless version adapted from tcp_mss_update in tcp_input.c
+ * returns the mss of the route.
+ */
+u_int16_t stateless_mss(struct syncache *sc, struct inpcb *inp, int *mtuflags)
+{
+	u_int16_t mss;
+	u_long maxmtu;
+	struct in_conninfo *inc = &sc->sc_inc;
+	struct hc_metrics_lite metrics;
+#ifdef INET6
+	int isipv6 = ((inp->inp_vflag & INP_IPV6) != 0) ? 1 : 0;
+	size_t min_protoh = isipv6 ?
+			    sizeof (struct ip6_hdr) + sizeof (struct tcphdr) :
+			    sizeof (struct tcpiphdr);
+#else
+	const size_t min_protoh = sizeof(struct tcpiphdr);
+#endif
+
+	/* Initialize. */
+#ifdef INET6
+	if (isipv6)
+		maxmtu = tcp_maxmtu6(inc, mtuflags);
+	else
+#endif
+		maxmtu = tcp_maxmtu(inc, mtuflags);
+
+
+	/* What have we got? */
+	switch (sc->sc_peer_mss) {
+		case 0:
+			/*
+			 * No MSS on the SYN
+			 * use tcp_mssdflt
+			 * using sc->sc_peer_mss to store state
+			 */
+			mss =  V_tcp_mssdflt;
+			break;
+		default:
+			/*
+			 * Prevent DoS attack with too small MSS. Round up
+			 * to at least minmss.
+			 */
+			mss = max(sc->sc_peer_mss, V_tcp_minmss);
+	}
+
+	/*
+	 * rmx information is now retrieved from tcp_hostcache.
+	 */
+	tcp_hc_get(inc, &metrics);
+
+	/*
+	 * If there's a discovered mtu int tcp hostcache, use it
+	 * else, use the link mtu.
+	 */
+	if (metrics.rmx_mtu)
+		mss = min(metrics.rmx_mtu, maxmtu) - min_protoh;
+	else {
+#ifdef INET6
+		if (isipv6) {
+			mss = maxmtu - min_protoh;
+			if (!V_path_mtu_discovery &&
+			    !in6_localaddr(&inc->inc6_faddr))
+				mss = min(mss, V_tcp_v6mssdflt);
+		} else
+#endif
+		{
+			mss = maxmtu - min_protoh;
+			if (!V_path_mtu_discovery &&
+			    !in_localaddr(inc->inc_faddr))
+				mss = min(mss, V_tcp_mssdflt);
+		}
+		/*
+		 * XXX - The above conditional (mss = maxmtu - min_protoh)
+		 * probably violates the TCP spec.
+		 * The problem is that, since we don't know the
+		 * other end's MSS, we are supposed to use a conservative
+		 * default.  But, if we do that, then MTU discovery will
+		 * never actually take place, because the conservative
+		 * default is much less than the MTUs typically seen
+		 * on the Internet today.  For the moment, we'll sweep
+		 * this under the carpet.
+		 *
+		 * The conservative default might not actually be a problem
+		 * if the only case this occurs is when sending an initial
+		 * SYN with options and data to a host we've never talked
+		 * to before.  Then, they will reply with an MSS value which
+		 * will get recorded and the new parameters should get
+		 * recomputed.  For Further Study.
+		 */
+	}
+	mss = min(mss, sc->sc_peer_mss);
+
+	/*
+	 * Sanity check: make sure that maxopd will be large
+	 * enough to allow some data on segments even if the
+	 * all the option space is used (40bytes).
+	 */
+	return (max(mss, 64));
+}
+
+/*
+ * Truncate header to IP+UDP size insead of IP+TCP size
+ * returns offset to the start of the UDP header
+ */
+void
+stateless_striptcphdr(struct mbuf *m, int ipoff, int tcpoff)
+{
+	int copylen, difflen;
+	void *copy_src, *copy_dst;
+/*
+ * The DNS PDU for TCP is different to the UDP PDU in that it has an
+ * additional field, the length field. We need to strip this off when
+ * redirecting to UDP.
+ */
+#define DNS_LENGTH_FIELD 0    /* Michael for HTTP */ 
+
+	SLT_LOG(SLT_LOG_DEBUG,
+			StatelessTcpLog("striptcphdr: orig_m_len=%d, orig_m_pkthdr.len=%d\n",
+							m->m_len,m->m_pkthdr.len));
+	
+	copy_dst = mtod(m, char *) + ipoff + sizeof(struct udphdr);
+	copy_src = mtod(m, char *) + ipoff + tcpoff + DNS_LENGTH_FIELD;
+
+	difflen = tcpoff + DNS_LENGTH_FIELD - sizeof(struct udphdr);
+	copylen = m->m_len - ipoff - tcpoff - DNS_LENGTH_FIELD;
+
+	SLT_LOG(SLT_LOG_DEBUG,
+			StatelessTcpLog("striptcphdr: copylen=%d, difflen=%d\n",
+							copylen,difflen));
+
+	bcopy(copy_src, copy_dst, (unsigned)copylen);
+	m->m_len -= difflen;
+	if (m->m_flags & M_PKTHDR)
+		m->m_pkthdr.len -= difflen;
+}
+
+/*
+ * Fill in the IP and TCP headers for an outgoing packet
+ * based values on 
+ */
+void
+statelesstcpip_fillheaders(int isipv6, void *srcaddr, void *dstaddr,
+						   u_int16_t srcport, u_int16_t dstport,
+						   struct syncache *sc,
+						   void *ip_ptr, void *tcp_ptr)
+{
+	struct tcphdr *th = (struct tcphdr *)tcp_ptr;
+
+#ifdef INET6
+	if (isipv6) {
+		struct ip6_hdr *ip6;
+		ip6 = (struct ip6_hdr *)ip_ptr;
+		ip6->ip6_flow &= ~IPV6_FLOWLABEL_MASK;
+		ip6->ip6_flow = sc->sc_flowlabel;
+		ip6->ip6_vfc = (ip6->ip6_vfc & ~IPV6_VERSION_MASK) |
+			(IPV6_VERSION & IPV6_VERSION_MASK);
+		ip6->ip6_nxt = IPPROTO_TCP;
+		ip6->ip6_plen = htons(sizeof(struct tcphdr));
+		ip6->ip6_src = *((struct in6_addr *) srcaddr);
+		ip6->ip6_dst = *((struct in6_addr *) dstaddr);
+	} else
+#endif
+	{
+		struct ip *ip;
+		ip = (struct ip *)ip_ptr;
+		ip->ip_v = IPVERSION;
+		ip->ip_hl = sizeof(struct ip) >> 2;
+		ip->ip_tos = sc->sc_ip_tos;
+		ip->ip_len = 0;
+		ip->ip_id = 0;
+		ip->ip_off = 0;
+		ip->ip_ttl = sc->sc_ip_ttl;
+		ip->ip_sum = 0;
+		ip->ip_p = IPPROTO_TCP;
+		ip->ip_src = *((struct in_addr *) srcaddr);
+		ip->ip_dst = *((struct in_addr *) dstaddr);
+		if (V_path_mtu_discovery && ((sc->sc_flags & SCF_UNREACH) == 0))
+			ip->ip_off |= IP_DF;
+	}
+	th->th_sport = srcport;
+	th->th_dport = dstport;
+	th->th_seq = htonl(sc->sc_iss);
+	th->th_ack = htonl(sc->sc_irs);
+	th->th_x2 = 0;
+	th->th_off = sizeof(struct tcphdr) >> 2;
+	th->th_flags = 0;
+	th->th_win = htons(sc->sc_wnd);
+	th->th_urp = 0;
+	th->th_sum = 0;
+}
+
+/*
+ * Send an empty TCP acknowledgement packet
+ */
+int
+stateless_send_ack(struct in_conninfo *inc, struct tcphdr *rth,
+				   struct tcpopt *rto, int dlen, int free_syncache)
+{
+	struct mbuf *m;
+	int isipv6=0;
+	unsigned optlen, iphlen, thdrlen;
+	u_char opt[TCP_MAXOLEN];
+	struct syncache *sc;
+	struct syncache_head *sch;
+#ifdef INET6
+	struct ip6_hdr *ip6=NULL;
+#endif
+	struct ip *ip=NULL;
+	struct ipovly *ipov;
+	struct tcpopt to;
+	struct tcphdr *th;
+	char addrbuf[50];
+	int error=0;
+
+	SLT_LOG(SLT_LOG_DETAIL,
+			StatelessTcpLog("ack: acking dlen=%d\n",dlen));
+
+#ifdef INET6
+	isipv6 = inc->inc_flags & INC_ISIPV6;
+	if (isipv6) {
+		iphlen = sizeof (struct ip6_hdr);
+	} else
+#endif
+		iphlen = sizeof (struct ip);
+
+	thdrlen = iphlen + sizeof(struct tcphdr);
+
+	MGETHDR(m, M_DONTWAIT, MT_DATA);
+	if (m == NULL) {
+		error = ENOBUFS;
+		return (error);
+	}
+	m->m_data += max_linkhdr;
+	m->m_len = thdrlen;
+	m->m_pkthdr.len = thdrlen;
+	m->m_pkthdr.rcvif = NULL;
+
+	/* get syncache info for this address */
+	sc = syncache_lookup(inc, &sch);	/* returns locked sch */
+	SCH_LOCK_ASSERT(sch);
+	if (sc == NULL) {
+		SLT_LOG(SLT_LOG_DEBUG,
+				StatelessTcpLog("send ack: no syncache entry\n"));
+		SCH_UNLOCK(sch);
+		return(SLT_SYNCACHE_ERROR);
+	}
+	/* set table timout. rxmits is set to limit + 1 so a TO removes entry from cache */
+	sc->sc_rxttime = ticks + V_tcp_stateless_timeout;
+	/* Michael for HTTP: No, DO keep a timer - but not for SYN/ACK; use it to send a FIN at the end */
+//	sc->sc_rxmits = CACHE_RTX_VAL;
+	sc->sc_rxmits = 3;
+	sc->sc_flags |= SCF_STATELESS_FIN;
+
+	sc->sc_irs = rth->th_seq + dlen;
+	/* ACK for SYN  */
+	// Michael, David: LAST FIX!
+	if (((sc->sc_flags & SCF_STATELESS_SYNINC) == 0)) { // ||
+//		(rth->th_flags & TH_FIN)) {
+		sc->sc_iss++;
+		sc->sc_flags |= SCF_STATELESS_SYNINC;
+	}
+	sc->sc_tsreflect = rto->to_tsval;
+	if (!free_syncache)
+		SCH_UNLOCK(sch);
+	/*
+	 * Compute options for segment.
+	 */
+	optlen = 0;
+	if ((sc->sc_flags & SCF_NOOPT) == 0) {
+		to.to_flags = 0;
+		if (sc->sc_flags & SCF_TIMESTAMP) {
+			to.to_tsval = ticks + sc->sc_tsoff;
+			to.to_tsecr = sc->sc_tsreflect;
+			to.to_flags |= TOF_TS;
+		}
+
+#ifdef TCP_SIGNATURE
+		/* TCP-MD5 (RFC2385). */
+		if (sc->sc_flags & SCF_SIGNATURE)
+			to.to_flags |= TOF_SIGNATURE;
+#endif /* TCP_SIGNATURE */
+
+		/* Processing the options. */
+		optlen = tcp_addoptions(&to, opt);
+	}
+	m->m_len += optlen;
+	m->m_pkthdr.len += optlen;
+	thdrlen += optlen;
+
+#ifdef INET6
+	if (isipv6) {
+		ip6 = mtod(m, struct ip6_hdr *);
+		th = (struct tcphdr *)(ip6 + 1);
+		statelesstcpip_fillheaders(isipv6, &(inc->inc6_laddr),
+								   &(inc->inc6_faddr),
+								   rth->th_dport, rth->th_sport,
+								   sc,ip6, th);
+		ip6->ip6_plen = htons(sizeof(struct tcphdr) + optlen);
+	} else
+#endif /* INET6 */
+	{
+		ip = mtod(m, struct ip *);
+		ipov = (struct ipovly *)ip;
+		th = (struct tcphdr *)(ip + 1);
+		statelesstcpip_fillheaders(isipv6, &(inc->inc_laddr),
+								   &(inc->inc_faddr),
+								   rth->th_dport, rth->th_sport,
+								   sc,ip, th);
+		ip->ip_len = m->m_pkthdr.len;
+	}
+
+	/*Michael: moved up here from below the if (free_syncache), to either OR a FIN in the flags or not */
+	th->th_flags = TH_ACK;
+
+	/* We have received a FIN */
+	if (free_syncache) {
+		SLT_LOG(SLT_LOG_DEBUG,
+				StatelessTcpLog("send_ack: free syncache\n"));
+		syncache_drop(sc,sch);
+		SCH_UNLOCK(sch);
+		/* Michael for HTTP */
+		th->th_flags |= TH_FIN;
+	}		
+
+	
+	if (optlen) {
+		bcopy(opt, th + 1, optlen);
+		th->th_off = (sizeof (struct tcphdr) + optlen) >> 2;
+	}
+	th->th_win = htons(sc->sc_wnd);
+
+#ifdef INET6
+	if (isipv6) {
+		th->th_sum = in6_cksum(m, IPPROTO_TCP, iphlen,
+							   thdrlen - iphlen);
+		ip6->ip6_hlim = in6_selecthlim(NULL, NULL);
+		SLT_LOG(SLT_LOG_DETAIL,
+				StatelessTcpLog("ack: pkt: faddr=%s,fport=%d\n",
+								ip6_sprintf(addrbuf,&(ip6->ip6_dst)),
+								ntohs(th->th_dport)));
+		SLT_LOG(SLT_LOG_DETAIL,
+				StatelessTcpLog("ack: pkt: laddr=%s,lport=%d\n",
+								ip6_sprintf(addrbuf,&(ip6->ip6_src)),
+								ntohs(th->th_sport)));
+		SLT_LOG(SLT_LOG_DEBUG,
+				StatelessTcpLog("ack: pkt: mbufpkthdrlen=%d,iplen=%d\n",
+								m->m_pkthdr.len,
+								ntohs(ip6->ip6_plen)));
+		error = ip6_output(m, NULL, NULL, 0, NULL, NULL, NULL);
+	} else
+#endif /* INET6 */
+	{
+		th->th_sum = in_pseudo(ip->ip_src.s_addr, ip->ip_dst.s_addr,
+		    htons(thdrlen - iphlen + IPPROTO_TCP));
+		m->m_pkthdr.csum_flags = CSUM_TCP;
+		m->m_pkthdr.csum_data = offsetof(struct tcphdr, th_sum);
+		SLT_LOG(SLT_LOG_DETAIL,
+				StatelessTcpLog("ack: pkt: faddr=%s,fport=%d\n",
+								inet_ntoa_r(ip->ip_dst,addrbuf),
+								ntohs(th->th_dport)));
+		SLT_LOG(SLT_LOG_DETAIL,
+				StatelessTcpLog("ack: pkt: laddr=%s,lport=%d\n",
+								inet_ntoa_r(ip->ip_src,addrbuf),
+								ntohs(th->th_sport)));
+		SLT_LOG(SLT_LOG_DEBUG,
+				StatelessTcpLog("ack: pkt: mbufpkthdrlen=%d,iplen=%d\n",
+								m->m_pkthdr.len,ip->ip_len));
+		error = ip_output(m, sc->sc_ipopts , NULL, 0, NULL, NULL);
+	}
+	return(error);
+}
+
+/*
+ * Handles DNS packets on a TCP listening port in a stateless manner Packets
+ * reaching here are part of an incoming connection that has progressed past the
+ * 3 way handshake.
+ * 
+ */
+int statelesstcp_input( struct mbuf *m, struct in_conninfo *inc,
+						struct tcphdr *th, struct tcpopt *to,
+						int ipoff, int dlen, int ti_locked)
+{
+	struct udphdr *uh;
+	int tcpoff =  th->th_off << 2;
+	int startdns;
+	int pulluplength=0;
+#ifdef INET6
+	int isipv6 = inc->inc_flags & INC_ISIPV6;
+	struct ip6_hdr *ip6;
+#endif /* INET6 */
+	struct mbuf *m_last;
+	int pktlen;
+	struct ip *ip;
+	pktlen = m_length(m,&m_last);
+
+#ifdef INET6
+	ip6 = mtod(m, struct ip6_hdr *);
+#endif
+	ip = mtod(m, struct ip *);
+
+	/*
+	 * if the packet contains data (and not a zone transfer),
+	 * spoof as UDP, redirect to UDP_input and
+     *    send ack
+	 * if packet has a FIN flag set, send back an ACK
+	 */
+	if (dlen <= 0 && th->th_flags & TH_FIN) {
+		/* release lock since we have no use for it now */
+		if (ti_locked == TI_RLOCKED)
+			INP_INFO_RUNLOCK(&V_tcbinfo);
+		else if (ti_locked == TI_WLOCKED)
+			INP_INFO_WUNLOCK(&V_tcbinfo);
+		else
+			panic("%s: ti_locked %d in stateless tcp",
+				  __func__, ti_locked);
+
+		SLT_LOG(SLT_LOG_DETAIL,
+				StatelessTcpLog("input: acking FIN\n"));
+		/*
+		 * Send ACK with dlen=1 so ACK number is increased
+		 * Free syncache entry
+		 */
+		stateless_send_ack(inc, th, to, 1, 1);
+
+		if (m != NULL) /* release mbuf */
+			m_freem(m);
+	} else {
+		if (dlen > 0) { /* data in packet */
+			if (V_tcp_stateless_axfr_detect) {
+				/* Peek to see if this is a zone transfer */
+				/*
+				 * When a zone transfer (AXFR = 252) request is sent, it must be
+				 * the only question in the request (RFC 5936) The largest
+				 * amount we need to pull up is: TCPIP headers + 14B DNS header
+				 * + max 64B Name + 2B Question type.
+				 */
+				startdns = ipoff + tcpoff;
+				pulluplength = startdns + 78;
+				
+				if (pulluplength > pktlen)
+					pulluplength = pktlen;
+				if (m->m_len < pulluplength) {
+					if ((m = m_pullup(m, pulluplength))	== NULL) {
+						SLT_LOG(SLT_LOG_LOW,
+								StatelessTcpLog("input: ERROR can't pull up DNS header\n"));
+						return(SLT_DNS_ERROR); /* can't pull up DNS header so return and exit */
+					}
+				}
+				int dnslen = ntohs(*((uint16_t *) (mtod(m, char *) + startdns)));
+				int queryname_offset = startdns + 14;
+				#define MAX_DNAME 64
+				int max_searchlen = min(MAX_DNAME,dnslen);
+				char* ptr =  (mtod(m, char *) + queryname_offset);
+				int cnt=0;	   
+				while(cnt++ < max_searchlen) {
+					if (*ptr++ == 0)
+						break;
+				}
+				if (cnt <= max_searchlen) {
+					/* find querytype */
+					if (ntohs(*((uint16_t *) ptr)) == AXFR) {
+						SLT_LOG(SLT_LOG_DETAIL,
+								StatelessTcpLog("input: Zone transfer (AXFR) revert to full state TCP\n"));
+						return(SLT_FULLSTATE_TCP); /* revert to full state TCP */
+					}
+					SLT_LOG(SLT_LOG_DEBUG,
+							StatelessTcpLog("input: query type 0x%x\n",
+											ntohs(*((uint16_t *)ptr))));
+				}
+			}	
+			 /* release lock since we have no use for it now */
+			if (ti_locked == TI_RLOCKED)
+				INP_INFO_RUNLOCK(&V_tcbinfo);
+			else if (ti_locked == TI_WLOCKED)
+				INP_INFO_WUNLOCK(&V_tcbinfo);
+			else
+				panic("%s: ti_locked %d in stateless tcp",
+				  __func__, ti_locked);
+
+			/* send ACK before changing header */
+			stateless_send_ack(inc, th, to, dlen, 0);
+		
+			/* Fix IP header */
+			dlen -= DNS_LENGTH_FIELD;
+#ifdef INET6
+			if (isipv6) {
+				ip6->ip6_nxt = IPPROTO_UDP;
+				ip6->ip6_plen = htons(sizeof(struct udphdr) + (u_short)dlen);
+			} else
+#endif /* INET6 */
+			{
+				ip->ip_p = IPPROTO_UDP;
+				/*
+				 * ip_len in host byte order (ip_input)
+				 * also, udp_input expects it to not include the 
+				 * length of the ip header (see ip_input.c).
+				 */
+				ip->ip_len =  sizeof(struct udphdr) + (u_short)dlen;
+			}
+			/*
+			 * Replace TCP header with UDP header
+			 */
+			uh = (struct udphdr *) th; /* we are overwriting the tcp header */
+			/* leaving port numbers the same */
+			/* set length and checksum */
+			uh->uh_ulen = htons((u_short)dlen + sizeof(struct udphdr));
+			/* set checksum to zero, we aready checked the TCP one */
+			uh->uh_sum = 0; 
+			/* adjust mbuf to remove excess TCP header */
+			stateless_striptcphdr(m, ipoff, tcpoff);
+			
+			SLT_LOG(SLT_LOG_DETAIL,
+					StatelessTcpLog("input: data in fport=%d, dlen=%d\n",
+									ntohs(uh->uh_sport),dlen));
+
+			/* pass to UDP */
+#ifdef INET6
+			if (isipv6)
+				udp6_input(&m, &ipoff, SLT_PROTO);
+			else
+#endif /* INET6 */
+				udp_input(m, ipoff);
+		} else {
+			/* all other packets are dropped */	
+			SLT_LOG(SLT_LOG_DETAIL,
+					StatelessTcpLog("input: dlen <= 0 and not FIN, so dropping packet\n"));
+			return(SLT_STATELESS_DROP);
+		}
+	}
+	return(SLT_STATELESS_TCP);
+	
+}
+
+int statelesstcp_output(struct inpcb *inp, void *_sin, struct mbuf *m)
+{
+	unsigned iphlen, optlen, thdrlen, datalen = 0,totaldatalen;
+#ifdef INET6
+	struct ip6_hdr *ip6=NULL;
+	struct sockaddr_in6 *sin6;
+#endif
+	struct ip *ip=NULL;
+	struct sockaddr_in *sin;
+	struct ipovly *ipov;
+	struct tcpopt to;
+	struct tcphdr *th;
+	struct syncache *sc;
+	struct syncache_head *sch;
+	struct in_conninfo inc_cpy;
+	u_char opt[TCP_MAXOLEN];
+	struct in_conninfo *inc;
+	char addrbuf[50];
+	int isipv6 = 0;
+	int error = 0;
+	isipv6 = ((inp->inp_vflag & INP_IPV6) != 0) ? 1 : 0;
+	if (!isipv6)
+		 INP_RLOCK(inp);
+
+	bcopy(&(inp->inp_inc),&inc_cpy,sizeof(struct in_conninfo));
+
+#ifdef INET6
+	if (isipv6) {
+		inc_cpy.inc_flags |= INC_ISIPV6; /* required by syncache_lookup */
+		sin6 =  (struct sockaddr_in6 *) _sin;
+		inc_cpy.inc_fport = sin6->sin6_port;
+		inc_cpy.inc6_faddr = sin6->sin6_addr; 
+		SLT_LOG(SLT_LOG_DETAIL,
+				StatelessTcpLog("output: faddr=%s,fport=%d\n",
+								ip6_sprintf(addrbuf,&inc_cpy.inc6_faddr),
+								ntohs(inc_cpy.inc_fport)));
+		SLT_LOG(SLT_LOG_DETAIL,
+				StatelessTcpLog("output: laddr=%s,lport=%d\n",
+								ip6_sprintf(addrbuf,&inc_cpy.inc6_laddr),
+								ntohs(inc_cpy.inc_lport)));
+	} else
+#endif
+	{
+		sin =  (struct sockaddr_in *) _sin;
+		inc_cpy.inc_fport = sin->sin_port;
+		inc_cpy.inc_faddr = sin->sin_addr; 
+		SLT_LOG(SLT_LOG_DETAIL,
+				StatelessTcpLog("output: faddr=%s, fport=%d\n",
+								inet_ntoa_r(inc_cpy.inc_faddr,addrbuf),
+								ntohs(inc_cpy.inc_fport)));
+		SLT_LOG(SLT_LOG_DETAIL,
+				StatelessTcpLog("output: laddr=%s, lport=%d\n",
+								inet_ntoa_r(inc_cpy.inc_laddr,addrbuf),
+								ntohs(inc_cpy.inc_lport)));
+	}
+	sc = syncache_lookup(&inc_cpy, &sch);	/* returns locked sch */
+	if (sc == NULL) {
+		SCH_UNLOCK(sch);
+		SLT_LOG(SLT_LOG_DEBUG,
+				StatelessTcpLog("output: no syncache entry\n"));
+		if (!isipv6)
+			INP_RUNLOCK(inp);
+		return(SLT_NO_SYNCACHE_ENTRY);
+	}
+	inc = &(inc_cpy);
+
+	totaldatalen = m->m_pkthdr.len;
+#ifdef INET6
+	if (isipv6) {
+		iphlen = sizeof (struct ip6_hdr);
+	} else
+#endif
+		iphlen = sizeof (struct ip);
+
+	thdrlen = iphlen + sizeof(struct tcphdr);
+
+
+	/*
+	 * Compute options for segment.
+	 */
+	optlen = 0;
+	if ((sc->sc_flags & SCF_NOOPT) == 0) {
+		to.to_flags = 0;
+		if (sc->sc_flags & SCF_TIMESTAMP) {
+			to.to_tsval = ticks + sc->sc_tsoff;
+			to.to_tsecr = sc->sc_tsreflect;
+			to.to_flags |= TOF_TS;
+		}
+#ifdef TCP_SIGNATURE
+
+		/* TCP-MD5 (RFC2385). */
+		if (sc->sc_flags & SCF_SIGNATURE)
+			to.to_flags |= TOF_SIGNATURE;
+#endif /* TCP_SIGNATURE */
+		/* Processing the options. */
+		optlen = tcp_addoptions(&to, opt);
+	}
+	thdrlen += optlen;
+
+	int mtuflags;
+	u_int16_t mss;
+	mss = stateless_mss(sc, inp, &mtuflags);
+	SLT_LOG(SLT_LOG_DEBUG,
+			StatelessTcpLog("output: mss=%d\n",mss));
+
+/* Michael for HTTP
+	M_PREPEND(m, DNS_LENGTH_FIELD, M_DONTWAIT);
+	if (m == NULL) {
+		SLT_LOG(SLT_LOG_LOW,
+				StatelessTcpLog("output: ERROR - can't prepend DNS length field (no mbufs)\n"));
+		error = ENOBUFS;
+		goto release;
+	}
+
+	uint16_t *pdnslength;
+	pdnslength = mtod(m, uint16_t *);
+	*pdnslength = htons((uint16_t) (totaldatalen));
+	totaldatalen += DNS_LENGTH_FIELD;
+*/
+
+	long dataleft = (long) totaldatalen;
+	unsigned maxdata =  mss - thdrlen;
+	unsigned moff = 0;
+	int copylen;	
+	while (dataleft > 0 ) {
+		struct mbuf *mnew = NULL;
+		if (totaldatalen  > maxdata) {
+			if (dataleft > maxdata) {
+				MGETHDR(mnew, M_DONTWAIT, MT_DATA);
+				if (mnew == NULL) {
+					SLT_LOG(SLT_LOG_LOW,
+							StatelessTcpLog("output: ERROR - can't get mbuf\n"));
+					error = ENOBUFS;
+					goto release;
+				}
+				copylen = (dataleft < maxdata) ? dataleft : maxdata;
+				mnew->m_next = m_copym(m, moff, copylen, M_DONTWAIT);
+				if (mnew->m_next == NULL) {	/* copy failed */
+					m_free(mnew);
+					error = ENOBUFS;
+					goto release;
+				}
+				moff += copylen;
+				dataleft -= copylen;
+				datalen = copylen;
+			} else {
+				/* Use original mbuf for the last packet */
+				m_adj(m, totaldatalen - dataleft);
+				mnew = m;
+				datalen = dataleft;
+				dataleft = 0;
+			}
+		} else {
+			mnew = m;
+			datalen = dataleft;
+			dataleft = 0;
+		}
+			
+		/*
+		 * Calculate data length and get a mbuf for TCP, IP, and possible
+		 * link-layer headers.  Immediate slide the data pointer back forward
+		 * since we won't use that space at this layer.
+		 */
+		M_PREPEND(mnew, thdrlen + max_linkhdr, M_DONTWAIT);
+		if (mnew == NULL) {
+			SLT_LOG(SLT_LOG_LOW,
+					StatelessTcpLog("output: ERROR - can't prepend TCP headers (no mbufs)\n"));
+			error = ENOBUFS;
+			goto release;
+		}
+		mnew->m_data += max_linkhdr;
+		mnew->m_len -= max_linkhdr;
+		mnew->m_pkthdr.len -= max_linkhdr;
+		mnew->m_pkthdr.rcvif = NULL;
+		
+#ifdef MAC
+		mac_inpcb_create_mbuf(inp, mnew);
+#endif
+
+#ifdef INET6
+		if (isipv6) {
+			ip6 = mtod(mnew, struct ip6_hdr *);
+			th = (struct tcphdr *)(ip6 + 1);
+			statelesstcpip_fillheaders(isipv6, &(inc->inc6_laddr),
+									   &(inc->inc6_faddr),
+									   inc->inc_lport, inc->inc_fport,
+									   sc,ip6, th);
+			ip6->ip6_plen = htons(sizeof(struct tcphdr) + optlen + datalen);
+		} else
+#endif /* INET6 */
+		{
+			ip = mtod(mnew, struct ip *);
+			ipov = (struct ipovly *)ip;
+			th = (struct tcphdr *)(ip + 1);
+			statelesstcpip_fillheaders(isipv6, &(inc->inc_laddr),
+									   &(inc->inc_faddr),
+									   inc->inc_lport, inc->inc_fport,
+									   sc,ip, th);
+			ip->ip_len = thdrlen + datalen;
+		}
+		sc->sc_iss += datalen;
+		if (optlen) {
+			bcopy(opt, th + 1, optlen);
+			th->th_off = (sizeof (struct tcphdr) + optlen) >> 2;
+		}
+		th->th_flags = TH_ACK | TH_PUSH;
+
+/* Michael for HTTP: remove this if you do not want to send a FIN right away
+		if (dataleft <= 0) {
+			th->th_flags |= TH_FIN; // stuff below: Michael for HTTP
+			// if((firstTime % 2) == 0)
+			//	th->th_flags |= TH_RST;
+			// firstTime++;
+		}
+ */ 
+
+		th->th_win = htons(sc->sc_wnd);
+		mnew->m_pkthdr.len = thdrlen + datalen; /* in6_cksum() needs this */
+#ifdef INET6
+		if (isipv6) {
+			th->th_sum = in6_cksum(mnew, IPPROTO_TCP, iphlen,
+								   sizeof(struct tcphdr) + optlen + datalen);
+			ip6->ip6_hlim = in6_selecthlim(NULL, NULL);
+			SLT_LOG(SLT_LOG_DEBUG,
+					StatelessTcpLog("output: pkt: faddr=%s,fport=%d\n",
+									ip6_sprintf(addrbuf,&(ip6->ip6_dst)),
+									ntohs(th->th_dport)));
+			SLT_LOG(SLT_LOG_DEBUG,
+					StatelessTcpLog("output: pkt: laddr=%s,lport=%d\n",
+									ip6_sprintf(addrbuf,&(ip6->ip6_src)),
+									ntohs(th->th_sport)));
+			SLT_LOG(SLT_LOG_DEBUG,
+					StatelessTcpLog("output: pkt: mbufpkthdrlen=%d,iplen=%d\n",
+									mnew->m_pkthdr.len,
+									ntohs(ip6->ip6_plen)));
+			error = ip6_output(mnew, NULL, NULL, 0, NULL, NULL, NULL);
+		} else
+#endif /* INET6 */
+		{
+			th->th_sum = in_pseudo(ip->ip_src.s_addr, ip->ip_dst.s_addr,
+								   htons(sizeof(struct tcphdr) + optlen
+										 + IPPROTO_TCP + datalen));
+			mnew->m_pkthdr.csum_flags = CSUM_TCP;
+			mnew->m_pkthdr.csum_data = offsetof(struct tcphdr, th_sum);
+			SLT_LOG(SLT_LOG_DEBUG,
+					StatelessTcpLog("output: pkt: faddr=%s,fport=%d\n",
+									inet_ntoa_r(ip->ip_dst,addrbuf),
+									ntohs(th->th_dport)));
+			SLT_LOG(SLT_LOG_DEBUG,
+					StatelessTcpLog("output: pkt: laddr=%s,lport=%d\n",
+									inet_ntoa_r(ip->ip_src,addrbuf),
+									ntohs(th->th_sport)));
+			SLT_LOG(SLT_LOG_DEBUG,
+					StatelessTcpLog("output: pkt: mbufpkthdrlen=%d,iplen=%d\n",
+									mnew->m_pkthdr.len,ip->ip_len));
+			error = ip_output(mnew, sc->sc_ipopts , NULL, 0, NULL, NULL);
+		}
+	}
+
+	if (!isipv6)
+		INP_RUNLOCK(inp);
+	SCH_UNLOCK(sch);
+	SLT_LOG(SLT_LOG_DEBUG,
+			StatelessTcpLog("output: ip_output ret=%d, datalen=%d\n",
+							error,datalen));
+	return(error);
+
+release:
+	if (!isipv6)
+		INP_RUNLOCK(inp);
+	SCH_UNLOCK(sch);
+	m_freem(m);
+	return (error);
+}
+
+static void  StatelessTcpLog(const char *format, ...)
+{
+  char buffer[100];
+  va_list ap;
+  va_start(ap, format);
+  vsnprintf(buffer, 100, format, ap);
+  va_end(ap);
+  log(LOG_INFO,
+      "stateless tcp: %s", buffer);
+}
diff -r 0394c100d759 sys/netinet/statelesstcp.h
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/sys/netinet/statelesstcp.h	Thu Mar 03 11:54:14 2011 +1100
@@ -0,0 +1,77 @@
+/*-
+ * Copyright (c) 2010
+ * 	Swinburne University of Technology, Melbourne, Australia
+ * All rights reserved.
+ *
+ * This software was developed at the Centre for Advanced Internet
+ * Architectures, Swinburne University, by David Hayes
+ *
+ * Development is part of the CAIA Stateless TCP project,
+ * http://caia.swin.edu.au/ngen/statelesstcp
+ *
+ * This project has been made possible in part by a grant from 
+ * APNIC Pty Ltd and Nominet UK
+ *
+ * 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$");
+
+#ifndef _NETINET_TCP_STATELESS_H_
+#define _NETINET_TCP_STATELESS_H_
+
+#include <netinet/tcp.h>
+#include <netinet/tcp_var.h>
+#include <netinet/tcp_syncache.h>
+
+#define SLT_FULLSTATE_TCP 1
+#define SLT_STATELESS_TCP 2
+#define SLT_STATELESS_DROP 3
+#define SLT_SYNCACHE_ERROR 4
+#define SLT_NO_SYNCACHE_ENTRY -1000 /* someting not used by errno.h */
+#define SLT_DNS_ERROR 5
+#define AXFR 252
+
+/* this is for udp6_input, though it is unclear what int proto is used for,
+ * that is aprt from what I'm using it for */
+#define SLT_PROTO -1000 
+VNET_DECLARE(int, tcp_stateless);
+#define V_tcp_stateless VNET(tcp_stateless)
+
+VNET_DECLARE(int, tcp_stateless_port);
+#define V_tcp_stateless_port VNET(tcp_stateless_port)
+
+VNET_DECLARE(int, tcp_stateless_axfr_detect);
+#define V_tcp_stateless_axfr_detect VNET(tcp_stateless_axfr_detect)
+
+VNET_DECLARE(int, tcp_stateless_loglevel);
+#define V_tcp_stateless_loglevel VNET(tcp_stateless_loglevel)
+
+VNET_DECLARE(int, tcp_stateless_timeout);
+#define V_tcp_stateless_timeout VNET(tcp_stateless_timeout)
+
+int statelesstcp_input( struct mbuf *m, struct in_conninfo *inc,
+						struct tcphdr *th, struct tcpopt *to,
+						int ipoff, int tlen, int ti_locked);
+int statelesstcp_output(struct inpcb *inp, 	void *sin, struct mbuf *m);
+
+#endif /* !__NETINET_TCP_STATELESS_H_ */
diff -r 0394c100d759 sys/netinet/tcp_input.c
--- a/sys/netinet/tcp_input.c	Tue Sep 14 20:41:06 2010 +0000
+++ b/sys/netinet/tcp_input.c	Thu Mar 03 11:54:14 2011 +1100
@@ -96,6 +96,8 @@
 
 #include <security/mac/mac_framework.h>
 
+#include <netinet/statelesstcp.h>
+
 static const int tcprexmtthresh = 3;
 
 VNET_DEFINE(struct tcpstat, tcpstat);
@@ -769,6 +771,23 @@
 			 */
 			tcp_dooptions(&to, optp, optlen, 0);
 			/*
+			 * Process ack as stateless tcp,
+			 * but if data indicates it should be full state create it
+			 */
+			if (V_tcp_stateless && (ntohs(th->th_dport) == V_tcp_stateless_port)) {
+				switch(statelesstcp_input(m,&inc,th,&to,off0,tlen,ti_locked)) {
+				case SLT_FULLSTATE_TCP:
+					break; /* continue in normal TCP */
+				case SLT_STATELESS_TCP:
+					INP_WUNLOCK(inp);
+					return; /* finished */
+				case SLT_STATELESS_DROP:
+					goto dropunlock; /* drop packet */
+				default:
+					goto dropunlock; /* drop packet */
+				}
+			}
+			/*
 			 * NB: syncache_expand() doesn't unlock
 			 * inp and tcpinfo locks.
 			 */
@@ -1011,6 +1030,7 @@
 			tcp_trace(TA_INPUT, ostate, tp,
 			    (void *)tcp_saveipgen, &tcp_savetcp, 0);
 #endif
+
 		tcp_dooptions(&to, optp, optlen, TO_SYN);
 		syncache_add(&inc, &to, th, inp, &so, m);
 		/*
@@ -1020,7 +1040,11 @@
 		INP_INFO_UNLOCK_ASSERT(&V_tcbinfo);
 		return;
 	}
-
+	/*
+	 * Stateless TCP note:
+	 * Only gets here if packet is not arriving in the listening socket,
+	 * or a problem above.
+	 */
 	/*
 	 * Segment belongs to a connection in SYN_SENT, ESTABLISHED or later
 	 * state.  tcp_do_segment() always consumes the mbuf chain, unlocks
diff -r 0394c100d759 sys/netinet/tcp_syncache.c
--- a/sys/netinet/tcp_syncache.c	Tue Sep 14 20:41:06 2010 +0000
+++ b/sys/netinet/tcp_syncache.c	Thu Mar 03 11:54:14 2011 +1100
@@ -115,7 +115,8 @@
 #define TOEPCB_ISSET(sc) ((sc)->sc_toepcb != NULL)
 #endif
 
-static void	 syncache_drop(struct syncache *, struct syncache_head *);
+/* Changed from static for stateless tcp to use */
+void	 syncache_drop(struct syncache *, struct syncache_head *);
 static void	 syncache_free(struct syncache *);
 static void	 syncache_insert(struct syncache *, struct syncache_head *);
 struct syncache *syncache_lookup(struct in_conninfo *, struct syncache_head **);
@@ -345,7 +346,8 @@
  * Remove and free entry from syncache bucket row.
  * Expects locked syncache head.
  */
-static void
+/* Changed from static for stateless tcp to use */
+void
 syncache_drop(struct syncache *sc, struct syncache_head *sch)
 {
 
@@ -368,8 +370,11 @@
 static void
 syncache_timeout(struct syncache *sc, struct syncache_head *sch, int docallout)
 {
-	sc->sc_rxttime = ticks +
-		TCPTV_RTOBASE * (tcp_backoff[sc->sc_rxmits]);
+	/* Michael for HTTP */
+	if (!(sc->sc_flags & SCF_STATELESS_FIN))
+		sc->sc_rxttime = ticks +
+			TCPTV_RTOBASE * (tcp_backoff[sc->sc_rxmits]);
+
 	sc->sc_rxmits++;
 	if (TSTMP_LT(sc->sc_rxttime, sch->sch_nextc)) {
 		sch->sch_nextc = sc->sc_rxttime;
@@ -1381,10 +1386,21 @@
 	th->th_dport = sc->sc_inc.inc_fport;
 
 	th->th_seq = htonl(sc->sc_iss);
-	th->th_ack = htonl(sc->sc_irs + 1);
+	/* Michael for HTTP */
+	if (sc->sc_flags & SCF_STATELESS_FIN)
+		th->th_ack = 0;
+	else
+		th->th_ack = htonl(sc->sc_irs + 1);
+
 	th->th_off = sizeof(struct tcphdr) >> 2;
 	th->th_x2 = 0;
-	th->th_flags = TH_SYN|TH_ACK;
+
+	/* Michael for HTTP */
+	if (sc->sc_flags & SCF_STATELESS_FIN)
+		th->th_flags = TH_FIN|TH_RST;
+	else
+		th->th_flags = TH_SYN|TH_ACK;
+		
 	th->th_win = htons(sc->sc_wnd);
 	th->th_urp = 0;
 
diff -r 0394c100d759 sys/netinet/tcp_syncache.h
--- a/sys/netinet/tcp_syncache.h	Tue Sep 14 20:41:06 2010 +0000
+++ b/sys/netinet/tcp_syncache.h	Thu Mar 03 11:54:14 2011 +1100
@@ -94,6 +94,13 @@
 #define SCF_SIGNATURE	0x20			/* send MD5 digests */
 #define SCF_SACK	0x80			/* send SACK option */
 #define SCF_ECN		0x100			/* send ECN setup packet */
+/*
+ * Used by stateless TCP to note that the
+ * sequence number has been incremented for the sent SYN
+ */
+#define SCF_STATELESS_SYNINC 0x8000
+/* Michael for HTTP: send a FIN instead of a SYN on the SYN/ACK when the timer expires (and increase the acked seqno) */
+#define SCF_STATELESS_FIN 0x4000
 
 #define	SYNCOOKIE_SECRET_SIZE	8	/* dwords */
 #define	SYNCOOKIE_LIFETIME	16	/* seconds */
diff -r 0394c100d759 sys/netinet/udp_usrreq.c
--- a/sys/netinet/udp_usrreq.c	Tue Sep 14 20:41:06 2010 +0000
+++ b/sys/netinet/udp_usrreq.c	Thu Mar 03 11:54:14 2011 +1100
@@ -89,6 +89,8 @@
 
 #include <security/mac/mac_framework.h>
 
+#include <netinet/statelesstcp.h>
+
 /*
  * UDP protocol implementation.
  * Per RFC 768, August, 1980.
@@ -146,6 +148,7 @@
 static void	udp_detach(struct socket *so);
 static int	udp_output(struct inpcb *, struct mbuf *, struct sockaddr *,
 		    struct mbuf *, struct thread *);
+
 #ifdef IPSEC
 #ifdef IPSEC_NAT_T
 #define	UF_ESPINUDP_ALL	(UF_ESPINUDP_NON_IKE|UF_ESPINUDP)
@@ -614,6 +617,7 @@
 
 		(*up->u_tun_func)(m, iphlen, inp);
 	}
+
 	INP_RUNLOCK(inp);
 	return;
 
@@ -951,6 +955,18 @@
 	int unlock_udbinfo;
 
 	/*
+	 * Check to see whether this is a stateless TCP packet
+	 * and redirect it to stateless tcp if it is.
+	 */
+	if (V_tcp_stateless && (ntohs(inp->inp_lport) == V_tcp_stateless_port)) {
+		error = statelesstcp_output(inp, (void *) addr, m);
+		if (error == SLT_NO_SYNCACHE_ENTRY)
+			error = 0; /* not a stateless TCP DNS query */
+		else
+			return(error);
+	}
+
+	/*
 	 * udp_output() may need to temporarily bind or connect the current
 	 * inpcb.  As such, we don't know up front whether we will need the
 	 * pcbinfo lock or not.  Do any work to decide what is needed up
diff -r 0394c100d759 sys/netinet6/udp6_usrreq.c
--- a/sys/netinet6/udp6_usrreq.c	Tue Sep 14 20:41:06 2010 +0000
+++ b/sys/netinet6/udp6_usrreq.c	Thu Mar 03 11:54:14 2011 +1100
@@ -115,6 +115,8 @@
 
 #include <security/mac/mac_framework.h>
 
+#include <netinet/statelesstcp.h>
+
 /*
  * UDP protocol implementation.
  * Per RFC 768, August, 1980.
@@ -196,7 +198,6 @@
 	if (!uh)
 		return (IPPROTO_DONE);
 #endif
-
 	UDPSTAT_INC(udps_ipackets);
 
 	/*
@@ -216,11 +217,12 @@
 	/*
 	 * Checksum extended UDP header and data.
 	 */
-	if (uh->uh_sum == 0) {
+	/* Note: checksums are not recalculated in stateless tcp */
+	if (uh->uh_sum == 0 && proto != SLT_PROTO) {
 		UDPSTAT_INC(udps_nosum);
 		goto badunlocked;
 	}
-	if (in6_cksum(m, IPPROTO_UDP, off, ulen) != 0) {
+	if (in6_cksum(m, IPPROTO_UDP, off, ulen) != 0 && proto != SLT_PROTO) {
 		UDPSTAT_INC(udps_badsum);
 		goto badunlocked;
 	}
@@ -658,6 +660,7 @@
 		if (inp->inp_lport == 0 &&
 		    (error = in6_pcbsetport(laddr, inp, td->td_ucred)) != 0)
 			goto release;
+
 	} else {
 		if (IN6_IS_ADDR_UNSPECIFIED(&inp->in6p_faddr)) {
 			error = ENOTCONN;
@@ -684,6 +687,22 @@
 		fport = inp->inp_fport;
 	}
 
+	/*
+	 * Check to see whether this is a stateless TCP packet
+	 * and redirect it to stateless tcp if it is.
+	 * NB needs to be here so that we have the right addresses
+	 */
+	if (V_tcp_stateless && (ntohs(inp->inp_lport) == V_tcp_stateless_port)) {
+		if (sin6) {
+			inp->in6p_laddr=*laddr;
+			error = statelesstcp_output(inp, (void *) sin6, m);
+			if (error == SLT_NO_SYNCACHE_ENTRY)
+				error = 0; /* not a stateless TCP DNS query */
+			else
+				return(error);
+		}
+	}
+
 	if (af == AF_INET)
 		hlen = sizeof(struct ip);
 
