--- //depot/vendor/freebsd/src/sys/kern/uipc_domain.c	2006/04/21 10:18:17
+++ //depot/user/rwatson/sockref/src/sys/kern/uipc_domain.c	2006/06/14 11:09:35
@@ -100,7 +100,8 @@
 	.pru_sosend =		pru_sosend_notsupp,
 	.pru_soreceive =	pru_soreceive_notsupp,
 	.pru_sopoll =		pru_sopoll_notsupp,
-	.pru_sosetlabel =	pru_sosetlabel_null
+	.pru_sosetlabel =	pru_sosetlabel_null,
+	.pru_close =		pru_close_notsupp,
 };
 
 static void
@@ -126,6 +127,7 @@
 	DEFAULT(pu->pru_soreceive, soreceive);
 	DEFAULT(pu->pru_sopoll, sopoll);
 	DEFAULT(pu->pru_sosetlabel, pru_sosetlabel_null);
+	DEFAULT(pu->pru_close, pru_close_notsupp);
 #undef DEFAULT
 	if (pr->pr_init)
 		(*pr->pr_init)();
--- //depot/vendor/freebsd/src/sys/kern/uipc_socket.c	2006/06/10 14:35:55
+++ //depot/user/rwatson/sockref/src/sys/kern/uipc_socket.c	2006/06/16 21:16:17
@@ -353,10 +353,7 @@
 	so->so_count = 1;
 	error = (*prp->pr_usrreqs->pru_attach)(so, proto, td);
 	if (error) {
-		ACCEPT_LOCK();
-		SOCK_LOCK(so);
-		so->so_state |= SS_NOFDREF;
-		sorele(so);
+		sodealloc(so);
 		return (error);
 	}
 	*aso = so;
@@ -583,6 +580,9 @@
 	 * socantsendmore_locked() drops the socket buffer mutex so that it
 	 * can safely perform wakeups.  Re-acquire the mutex before
 	 * continuing.
+	 *
+	 * XXXRW: Why do we do wakeups here?  At this point we should just
+	 * be tearing down.
 	 */
 	socantsendmore_locked(so);
 	SOCKBUF_LOCK(&so->so_snd);
@@ -592,6 +592,7 @@
 	sorflush(so);
 	knlist_destroy(&so->so_rcv.sb_sel.si_note);
 	knlist_destroy(&so->so_snd.sb_sel.si_note);
+	(*so->so_proto->pr_usrreqs->pru_detach)(so);
 	sodealloc(so);
 }
 
@@ -656,7 +657,7 @@
 	}
 
 drop:
-	(*so->so_proto->pr_usrreqs->pru_detach)(so);
+	(*so->so_proto->pr_usrreqs->pru_close)(so);
 	ACCEPT_LOCK();
 	SOCK_LOCK(so);
 	KASSERT((so->so_state & SS_NOFDREF) == 0, ("soclose: NOFDREF"));
@@ -666,9 +667,9 @@
 }
 
 /*
- * soabort() allows the socket code or protocol code to detach a socket that
- * has been in an incomplete or completed listen queue, but has not yet been
- * accepted.
+ * soabort() is used to abruptly tear down a connection, such as when a
+ * resource limit is reached (listen queue depth exceeded), or if a listen
+ * socket is closed while there are sockets waiting to be accepted.
  *
  * This interface is tricky, because it is called on an unreferenced socket,
  * and must be called only by a thread that has actually removed the socket
@@ -678,9 +679,6 @@
  * with any socket locks held.  Protocols do call it while holding their own
  * recursible protocol mutexes, but this is something that should be subject
  * to review in the future.
- *
- * XXXRW: Why do we maintain a distinction between pru_abort() and
- * pru_detach()?
  */
 void
 soabort(so)
--- //depot/vendor/freebsd/src/sys/kern/uipc_socket2.c	2006/06/17 22:50:57
+++ //depot/user/rwatson/sockref/src/sys/kern/uipc_socket2.c	2006/06/18 00:05:41
@@ -1271,6 +1271,12 @@
 
 }
 
+void
+pru_close_notsupp(struct socket *so)
+{
+
+}
+
 /*
  * Make a copy of a sockaddr in a malloced buffer of type M_SONAME.
  */
--- //depot/vendor/freebsd/src/sys/kern/uipc_usrreq.c	2006/06/16 22:16:23
+++ //depot/user/rwatson/sockref/src/sys/kern/uipc_usrreq.c	2006/06/16 22:21:44
@@ -149,8 +149,7 @@
 	KASSERT(unp != NULL, ("uipc_abort: unp == NULL"));
 	UNP_LOCK();
 	unp_drop(unp, ECONNABORTED);
-	unp_detach(unp);
-	UNP_UNLOCK_ASSERT();
+	UNP_UNLOCK();
 }
 
 static int
@@ -213,6 +212,21 @@
 	return (error);
 }
 
+/*
+ * XXXRW: Should also unbind?
+ */
+static void
+uipc_close(struct socket *so)
+{
+	struct unpcb *unp;
+
+	unp = sotounpcb(so);
+	KASSERT(unp != NULL, ("uipc_close: unp == NULL"));
+	UNP_LOCK();
+	unp_disconnect(unp);
+	UNP_UNLOCK();
+}
+
 int
 uipc_connect2(struct socket *so1, struct socket *so2)
 {
@@ -560,6 +574,7 @@
 	.pru_sosend =		sosend,
 	.pru_soreceive =	soreceive,
 	.pru_sopoll =		sopoll,
+	.pru_close =		uipc_close,
 };
 
 int
--- //depot/vendor/freebsd/src/sys/net/raw_usrreq.c	2006/06/02 10:03:02
+++ //depot/user/rwatson/sockref/src/sys/net/raw_usrreq.c	2006/06/14 22:56:14
@@ -146,7 +146,16 @@
 	KASSERT(rp != NULL, ("raw_uabort: rp == NULL"));
 	raw_disconnect(rp);
 	soisdisconnected(so);
-	raw_detach(rp);
+}
+
+static void
+raw_uclose(struct socket *so)
+{
+	struct rawcb *rp = sotorawcb(so);
+
+	KASSERT(rp != NULL, ("raw_uabort: rp == NULL"));
+	raw_disconnect(rp);
+	soisdisconnected(so);
 }
 
 /* pru_accept is EOPNOTSUPP */
@@ -295,4 +304,5 @@
 	.pru_send =		raw_usend,
 	.pru_shutdown =		raw_ushutdown,
 	.pru_sockaddr =		raw_usockaddr,
+	.pru_close =		raw_uclose,
 };
--- //depot/vendor/freebsd/src/sys/net/rtsock.c	2006/04/01 15:45:41
+++ //depot/user/rwatson/sockref/src/sys/net/rtsock.c	2006/06/14 22:56:14
@@ -144,6 +144,13 @@
 	raw_usrreqs.pru_abort(so);
 }
 
+static void
+rts_close(struct socket *so)
+{
+
+	raw_usrreqs.pru_close(so);
+}
+
 /* pru_accept is EOPNOTSUPP */
 
 static int
@@ -292,6 +299,7 @@
 	.pru_send =		rts_send,
 	.pru_shutdown =		rts_shutdown,
 	.pru_sockaddr =		rts_sockaddr,
+	.pru_close =		rts_close,
 };
 
 /*ARGSUSED*/
--- //depot/vendor/freebsd/src/sys/netatalk/ddp_usrreq.c	2006/04/01 16:55:45
+++ //depot/user/rwatson/sockref/src/sys/netatalk/ddp_usrreq.c	2006/06/14 22:56:14
@@ -202,6 +202,10 @@
 	return (error);
 }
 
+/*
+ * XXXRW: This is never called because we only invoke abort on stream
+ * protocols.
+ */
 static void
 ddp_abort(struct socket *so)
 {
@@ -210,10 +214,22 @@
 	ddp = sotoddpcb(so);
 	KASSERT(ddp != NULL, ("ddp_abort: ddp == NULL"));
 
-	DDP_LIST_XLOCK();
+	DDP_LOCK(ddp);
+	at_pcbdisconnect(ddp);
+	DDP_UNLOCK(ddp);
+}
+
+static void
+ddp_close(struct socket *so)
+{
+	struct ddpcb	*ddp;
+	
+	ddp = sotoddpcb(so);
+	KASSERT(ddp != NULL, ("ddp_close: ddp == NULL"));
+
 	DDP_LOCK(ddp);
-	at_pcbdetach(so, ddp);
-	DDP_LIST_XUNLOCK();
+	at_pcbdisconnect(ddp);
+	DDP_UNLOCK(ddp);
 }
 
 void 
@@ -276,4 +292,5 @@
 	.pru_send =		ddp_send,
 	.pru_shutdown =		ddp_shutdown,
 	.pru_sockaddr =		at_setsockaddr,
+	.pru_close =		ddp_close,
 };
--- //depot/vendor/freebsd/src/sys/netatm/atm_aal5.c	2006/04/01 15:45:41
+++ //depot/user/rwatson/sockref/src/sys/netatm/atm_aal5.c	2006/06/17 02:11:38
@@ -88,6 +88,7 @@
 			Atm_attributes *, void **);
 static void	atm_aal5_cpcs_data(void *, KBuffer *);
 static caddr_t	atm_aal5_getname(void *);
+static void	atm_aal5_close(struct socket *);
 
 
 /*
@@ -108,6 +109,7 @@
 	.pru_sense =		atm_aal5_sense,
 	.pru_shutdown =		atm_aal5_shutdown,
 	.pru_sockaddr =		atm_aal5_sockaddr,
+	.pru_close =		atm_aal5_close,
 };
 
 /*
@@ -565,8 +567,19 @@
 {
 	ATM_INTRO_NOERR("abort");
 
+	(void)atm_sock_disconnect(so);
 	so->so_error = ECONNABORTED;
-	atm_sock_detach(so);
+
+	ATM_OUTRO_NOERR();
+}
+
+static void
+atm_aal5_close(so)
+	struct socket	*so;
+{
+	ATM_INTRO_NOERR("close");
+
+	(void)atm_sock_disconnect(so);
 
 	ATM_OUTRO_NOERR();
 }
--- //depot/vendor/freebsd/src/sys/netatm/atm_usrreq.c	2006/04/01 15:45:41
+++ //depot/user/rwatson/sockref/src/sys/netatm/atm_usrreq.c	2006/06/17 02:11:38
@@ -79,6 +79,7 @@
 	.pru_sosend =		NULL,
 	.pru_soreceive =	NULL,
 	.pru_sopoll =		NULL,
+	.pru_close =		atm_proto_notsupp5,
 };
 
 
--- //depot/vendor/freebsd/src/sys/netgraph/bluetooth/include/ng_btsocket_hci_raw.h	2006/04/01 15:45:41
+++ //depot/user/rwatson/sockref/src/sys/netgraph/bluetooth/include/ng_btsocket_hci_raw.h	2006/06/14 22:56:14
@@ -67,6 +67,7 @@
 
 void ng_btsocket_hci_raw_init       (void);
 void ng_btsocket_hci_raw_abort      (struct socket *);
+void ng_btsocket_hci_raw_close      (struct socket *);
 int  ng_btsocket_hci_raw_attach     (struct socket *, int, struct thread *);
 int  ng_btsocket_hci_raw_bind       (struct socket *, struct sockaddr *, 
                                      struct thread *);
--- //depot/vendor/freebsd/src/sys/netgraph/bluetooth/include/ng_btsocket_l2cap.h	2006/04/01 15:45:41
+++ //depot/user/rwatson/sockref/src/sys/netgraph/bluetooth/include/ng_btsocket_l2cap.h	2006/06/14 22:56:14
@@ -93,6 +93,7 @@
 
 void ng_btsocket_l2cap_raw_init       (void);
 void ng_btsocket_l2cap_raw_abort      (struct socket *);
+void ng_btsocket_l2cap_raw_close      (struct socket *);
 int  ng_btsocket_l2cap_raw_attach     (struct socket *, int, struct thread *);
 int  ng_btsocket_l2cap_raw_bind       (struct socket *, struct sockaddr *,
                                        struct thread *);
@@ -184,6 +185,7 @@
 
 void ng_btsocket_l2cap_init       (void);
 void ng_btsocket_l2cap_abort      (struct socket *);
+void ng_btsocket_l2cap_close      (struct socket *);
 int  ng_btsocket_l2cap_accept     (struct socket *, struct sockaddr **);
 int  ng_btsocket_l2cap_attach     (struct socket *, int, struct thread *);
 int  ng_btsocket_l2cap_bind       (struct socket *, struct sockaddr *,
--- //depot/vendor/freebsd/src/sys/netgraph/bluetooth/include/ng_btsocket_rfcomm.h	2006/04/01 15:45:41
+++ //depot/user/rwatson/sockref/src/sys/netgraph/bluetooth/include/ng_btsocket_rfcomm.h	2006/06/14 22:56:14
@@ -315,6 +315,7 @@
 
 void ng_btsocket_rfcomm_init       (void);
 void ng_btsocket_rfcomm_abort      (struct socket *);
+void ng_btsocket_rfcomm_close      (struct socket *);
 int  ng_btsocket_rfcomm_accept     (struct socket *, struct sockaddr **);
 int  ng_btsocket_rfcomm_attach     (struct socket *, int, struct thread *);
 int  ng_btsocket_rfcomm_bind       (struct socket *, struct sockaddr *,
--- //depot/vendor/freebsd/src/sys/netgraph/bluetooth/socket/ng_btsocket.c	2005/11/09 13:31:29
+++ //depot/user/rwatson/sockref/src/sys/netgraph/bluetooth/socket/ng_btsocket.c	2006/06/14 22:56:14
@@ -74,6 +74,7 @@
 	.pru_send =		ng_btsocket_hci_raw_send,
 	.pru_shutdown =		NULL,
 	.pru_sockaddr =		ng_btsocket_hci_raw_sockaddr,
+	.pru_close =		ng_btsocket_hci_raw_close,
 };
 
 /*
@@ -92,6 +93,7 @@
 	.pru_send =		ng_btsocket_l2cap_raw_send,
 	.pru_shutdown =		NULL,
 	.pru_sockaddr =		ng_btsocket_l2cap_raw_sockaddr,
+	.pru_close =		ng_btsocket_l2cap_raw_close,
 };
 
 /*
@@ -112,6 +114,7 @@
 	.pru_send =		ng_btsocket_l2cap_send,
 	.pru_shutdown =		NULL,
 	.pru_sockaddr =		ng_btsocket_l2cap_sockaddr,
+	.pru_close =		ng_btsocket_l2cap_close,
 };
 
 /*
@@ -132,6 +135,7 @@
 	.pru_send =		ng_btsocket_rfcomm_send,
 	.pru_shutdown =		NULL,
 	.pru_sockaddr =		ng_btsocket_rfcomm_sockaddr,
+	.pru_close =		ng_btsocket_rfcomm_close,
 };
 
 /* 
--- //depot/vendor/freebsd/src/sys/netgraph/bluetooth/socket/ng_btsocket_hci_raw.c	2006/05/17 00:16:08
+++ //depot/user/rwatson/sockref/src/sys/netgraph/bluetooth/socket/ng_btsocket_hci_raw.c	2006/06/14 22:56:14
@@ -876,9 +876,13 @@
 void
 ng_btsocket_hci_raw_abort(struct socket *so)
 {
-	ng_btsocket_hci_raw_detach(so);
 } /* ng_btsocket_hci_raw_abort */
 
+void
+ng_btsocket_hci_raw_close(struct socket *so)
+{
+} /* ng_btsocket_hci_raw_close */
+
 /*
  * Create new raw HCI socket
  */
--- //depot/vendor/freebsd/src/sys/netgraph/bluetooth/socket/ng_btsocket_l2cap.c	2006/04/01 15:45:41
+++ //depot/user/rwatson/sockref/src/sys/netgraph/bluetooth/socket/ng_btsocket_l2cap.c	2006/06/14 22:56:14
@@ -1917,9 +1917,16 @@
 {
 	so->so_error = ECONNABORTED;
 
-	ng_btsocket_l2cap_detach(so);
+	(void)ng_btsocket_l2cap_disconnect(so);
 } /* ng_btsocket_l2cap_abort */
 
+void
+ng_btsocket_l2cap_close(struct socket *so)
+{
+
+	(void)ng_btsocket_l2cap_disconnect(so);
+} /* ng_btsocket_l2cap_close */
+
 /*
  * Accept connection on socket. Nothing to do here, socket must be connected
  * and ready, so just return peer address and be done with it.
--- //depot/vendor/freebsd/src/sys/netgraph/bluetooth/socket/ng_btsocket_l2cap_raw.c	2006/04/01 15:45:41
+++ //depot/user/rwatson/sockref/src/sys/netgraph/bluetooth/socket/ng_btsocket_l2cap_raw.c	2006/06/14 22:56:14
@@ -575,9 +575,17 @@
 void
 ng_btsocket_l2cap_raw_abort(struct socket *so)
 {
-	ng_btsocket_l2cap_raw_detach(so);
+
+	(void)ng_btsocket_l2cap_raw_disconnect(so);
 } /* ng_btsocket_l2cap_raw_abort */
 
+void
+ng_btsocket_l2cap_raw_close(struct socket *so)
+{
+
+	(void)ng_btsocket_l2cap_raw_disconnect(so);
+} /* ng_btsocket_l2cap_raw_close */
+
 /*
  * Create and attach new socket
  */
--- //depot/vendor/freebsd/src/sys/netgraph/bluetooth/socket/ng_btsocket_rfcomm.c	2006/04/01 15:45:41
+++ //depot/user/rwatson/sockref/src/sys/netgraph/bluetooth/socket/ng_btsocket_rfcomm.c	2006/06/14 22:56:14
@@ -346,10 +346,17 @@
 void
 ng_btsocket_rfcomm_abort(struct socket *so)
 {
+
 	so->so_error = ECONNABORTED;
+	(void)ng_btsocket_rfcomm_disconnect(so);
+} /* ng_btsocket_rfcomm_abort */
 
-	ng_btsocket_rfcomm_detach(so);
-} /* ng_btsocket_rfcomm_abort */
+void
+ng_btsocket_rfcomm_close(struct socket *so)
+{
+
+	(void)ng_btsocket_rfcomm_disconnect(so);
+} /* ng_btsocket_rfcomm_close */
 
 /*
  * Accept connection on socket. Nothing to do here, socket must be connected
--- //depot/vendor/freebsd/src/sys/netgraph/ng_socket.c	2006/06/13 21:38:14
+++ //depot/user/rwatson/sockref/src/sys/netgraph/ng_socket.c	2006/06/14 22:56:14
@@ -1087,6 +1087,8 @@
 }
 /*
  * Control and data socket type descriptors
+ *
+ * XXXRW: Perhaps _close should do something?
  */
 
 static struct pr_usrreqs ngc_usrreqs = {
@@ -1100,6 +1102,7 @@
 	.pru_send =		ngc_send,
 	.pru_shutdown =		NULL,
 	.pru_sockaddr =		ng_setsockaddr,
+	.pru_close =		NULL,
 };
 
 static struct pr_usrreqs ngd_usrreqs = {
@@ -1113,6 +1116,7 @@
 	.pru_send =		ngd_send,
 	.pru_shutdown =		NULL,
 	.pru_sockaddr =		ng_setsockaddr,
+	.pru_close =		NULL,
 };
 
 /*
--- //depot/vendor/freebsd/src/sys/netinet/ip_divert.c	2006/04/21 10:18:17
+++ //depot/user/rwatson/sockref/src/sys/netinet/ip_divert.c	2006/06/14 23:41:26
@@ -427,6 +427,24 @@
 }
 
 static void
+div_close(struct socket *so)
+{
+#if 0
+	/*
+	 * XXXRW: Tear-down to do here?
+	 */
+	struct inpcb *inp;
+
+	inp = sotoinpcb(so);
+	KASSERT(inp != NULL, ("div_close: inp == NULL"));
+	INP_INFO_WLOCK(&divcbinfo);
+	INP_LOCK(inp);
+	INP_UNLOCK(inp);
+	INP_INFO_WUNLOCK(&divcbinfo);
+#endif
+}
+
+static void
 div_detach(struct socket *so)
 {
 	struct inpcb *inp;
@@ -634,7 +652,8 @@
 	.pru_send =		div_send,
 	.pru_shutdown =		div_shutdown,
 	.pru_sockaddr =		div_sockaddr,
-	.pru_sosetlabel =	in_pcbsosetlabel
+	.pru_sosetlabel =	in_pcbsosetlabel,
+	.pru_close =		div_close,
 };
 
 struct protosw div_protosw = {
--- //depot/vendor/freebsd/src/sys/netinet/raw_ip.c	2006/05/21 19:30:34
+++ //depot/user/rwatson/sockref/src/sys/netinet/raw_ip.c	2006/06/14 23:41:26
@@ -615,46 +615,65 @@
 }
 
 static void
-rip_pcbdetach(struct socket *so, struct inpcb *inp)
+rip_detach(struct socket *so)
 {
+	struct inpcb *inp;
 
-	INP_INFO_WLOCK_ASSERT(&ripcbinfo);
-	INP_LOCK_ASSERT(inp);
-
-	if (so == ip_mrouter && ip_mrouter_done)
-		ip_mrouter_done();
-	if (ip_rsvp_force_done)
-		ip_rsvp_force_done(so);
-	if (so == ip_rsvpd)
-		ip_rsvp_done();
+	/* XXXRW: Should assert detachment. */
+	inp = sotoinpcb(so);
+	KASSERT(inp != NULL, ("rip_detach: inp == NULL"));
+	INP_INFO_WLOCK(&ripcbinfo);
+	INP_LOCK(inp);
 	in_pcbdetach(inp);
 	in_pcbfree(inp);
+	INP_INFO_WUNLOCK(&ripcbinfo);
 }
 
 static void
-rip_detach(struct socket *so)
+rip_abort(struct socket *so)
 {
 	struct inpcb *inp;
 
 	inp = sotoinpcb(so);
-	KASSERT(inp != NULL, ("rip_detach: inp == NULL"));
+	KASSERT(inp != NULL, ("rip_abort: inp == NULL"));
+
 	INP_INFO_WLOCK(&ripcbinfo);
 	INP_LOCK(inp);
-	rip_pcbdetach(so, inp);
+	if (so == ip_mrouter && ip_mrouter_done)
+		ip_mrouter_done();
+	if (ip_rsvp_force_done)
+		ip_rsvp_force_done(so);
+	if (so == ip_rsvpd)
+		ip_rsvp_done();
+	inp->inp_faddr.s_addr = INADDR_ANY;
+	SOCK_LOCK(so);
+	so->so_state &= ~SS_ISCONNECTED;
+	SOCK_UNLOCK(so);
+	INP_UNLOCK(inp);
 	INP_INFO_WUNLOCK(&ripcbinfo);
 }
 
 static void
-rip_abort(struct socket *so)
+rip_close(struct socket *so)
 {
 	struct inpcb *inp;
 
 	inp = sotoinpcb(so);
-	KASSERT(inp != NULL, ("rip_abort: inp == NULL"));
+	KASSERT(inp != NULL, ("rip_close: inp == NULL"));
+
 	INP_INFO_WLOCK(&ripcbinfo);
 	INP_LOCK(inp);
-	soisdisconnected(so);
-	rip_pcbdetach(so, inp);
+	if (so == ip_mrouter && ip_mrouter_done)
+		ip_mrouter_done();
+	if (ip_rsvp_force_done)
+		ip_rsvp_force_done(so);
+	if (so == ip_rsvpd)
+		ip_rsvp_done();
+	inp->inp_faddr.s_addr = INADDR_ANY;
+	SOCK_LOCK(so);
+	so->so_state &= ~SS_ISCONNECTED;
+	SOCK_UNLOCK(so);
+	INP_UNLOCK(inp);
 	INP_INFO_WUNLOCK(&ripcbinfo);
 }
 
@@ -902,5 +921,6 @@
 	.pru_send =		rip_send,
 	.pru_shutdown =		rip_shutdown,
 	.pru_sockaddr =		rip_sockaddr,
-	.pru_sosetlabel =	in_pcbsosetlabel
+	.pru_sosetlabel =	in_pcbsosetlabel,
+	.pru_close =		rip_close,
 };
--- //depot/vendor/freebsd/src/sys/netinet/tcp_subr.c	2006/04/25 11:43:02
+++ //depot/user/rwatson/sockref/src/sys/netinet/tcp_subr.c	2006/06/15 21:14:18
@@ -800,18 +800,7 @@
 		KASSERT(so->so_state & SS_PROTOREF,
 		    ("tcp_close: !SS_PROTOREF"));
 		inp->inp_vflag &= ~INP_SOCKREF;
-		tcp_discardcb(tp);
-#ifdef INET6
-		if (inp->inp_vflag & INP_IPV6PROTO) {
-			in6_pcbdetach(inp);
-			in6_pcbfree(inp);
-		} else {
-#endif
-			in_pcbdetach(inp);
-			in_pcbfree(inp);
-#ifdef INET6
-		}
-#endif
+		INP_UNLOCK(inp);
 		ACCEPT_LOCK();
 		SOCK_LOCK(so);
 		so->so_state &= ~SS_PROTOREF;
@@ -1777,12 +1766,6 @@
 		KASSERT(so->so_state & SS_PROTOREF,
 		    ("tcp_twstart: !SS_PROTOREF"));
 		inp->inp_vflag &= ~INP_SOCKREF;
-#ifdef INET6
-		if (inp->inp_vflag & INP_IPV6PROTO)
-			in6_pcbdetach(inp);
-		else
-#endif
-			in_pcbdetach(inp);
 		INP_UNLOCK(inp);
 		ACCEPT_LOCK();
 		SOCK_LOCK(so);
@@ -1835,12 +1818,11 @@
 	/*
 	 * At this point, we are in one of two situations:
 	 *
-	 * (1) We have no socket, just an inpcb<->twtcp pair.  Release it all
-	 * after validating.
+	 * (1) We have no socket, just an inpcb<->twtcp pair.  We can free
+	 *     all state.
 	 *
-	 * (2) We have a socket, which we may or may now own the reference
-	 * for.  If we own the reference, release all the state after
-	 * validating.  If not, leave it for the socket close to clean up.
+	 * (2) We have a socket -- if we own a reference, release it and
+	 *     notify the socket layer.
 	 */
 	inp = tw->tw_inpcb;
 	KASSERT((inp->inp_vflag & INP_TIMEWAIT), ("tcp_twclose: !timewait"));
@@ -1855,22 +1837,15 @@
 
 	so = inp->inp_socket;
 	if (so != NULL) {
+		/*
+		 * If there's a socket, handle two cases: first, we own a
+		 * strong reference, which we will now release, or we don't
+		 * in which case another reference exists (XXXRW: think
+		 * about this more), and we don't need to take action.
+		 */
 		if (inp->inp_vflag & INP_SOCKREF) {
-			/*
-			 * If a socket is present, and we own the only
-			 * reference, we need to tear down the socket and the
-			 * inpcb.
-			 */
 			inp->inp_vflag &= ~INP_SOCKREF;
-#ifdef INET6
-			if (inp->inp_vflag & INP_IPV6PROTO) {
-				in6_pcbdetach(inp);
-				in6_pcbfree(inp);
-			} else {
-				in_pcbdetach(inp);
-				in_pcbfree(inp);
-			}
-#endif
+			INP_UNLOCK(inp);
 			ACCEPT_LOCK();
 			SOCK_LOCK(so);
 			KASSERT(so->so_state & SS_PROTOREF,
--- //depot/vendor/freebsd/src/sys/netinet/tcp_usrreq.c	2006/06/04 09:36:00
+++ //depot/user/rwatson/sockref/src/sys/netinet/tcp_usrreq.c	2006/06/15 21:30:27
@@ -137,12 +137,10 @@
 }
 
 /*
- * tcp_detach() releases any protocol state that can be reasonably released
- * when a socket shutdown is requested, and is a shared code path for
- * tcp_usr_detach() and tcp_usr_abort(), the two socket close entry points.
- *
- * Accepts pcbinfo, inpcb locked, will unlock the inpcb (if needed) on
- * return.
+ * tcp_detach is called when the socket layer loses its final reference
+ * to the socket, be it a file descriptor reference, a reference from TCP,
+ * etc.  At this point, there is only one case in which we will keep around
+ * inpcb state: time wait.
  */
 static void
 tcp_detach(struct socket *so, struct inpcb *inp)
@@ -158,19 +156,24 @@
 	KASSERT(so->so_pcb == inp, ("tcp_detach: so_pcb != inp"));
 	KASSERT(inp->inp_socket == so, ("tcp_detach: inp_socket != so"));
 
+	tp = intotcpcb(inp);
+
 	if (inp->inp_vflag & INP_TIMEWAIT) {
+		/*
+		 * There are two cases to handle: one in which the time wait
+		 * state is being discarded (INP_DROPPED), and one in which
+		 * this connection will remain in timewait.  In the former,
+		 * it is time to discard all state (except tcptw, which has
+		 * already been discarded by the timewait close code, which
+		 * should be further up the call stack somewhere).  In the
+		 * latter case, we detach from the socket, but leave the pcb
+		 * present until timewait ends.
+		 *
+		 * XXXRW: Would it be cleaner to free the tcptw here?
+		 */
 		if (inp->inp_vflag & INP_DROPPED) {
-			/*
-			 * Connection was in time wait and has been dropped;
-			 * the calling path is either via tcp_twclose(), or
-			 * as a result of an eventual soclose() after
-			 * tcp_twclose() has been called.  In either case,
-			 * tcp_twclose() has detached the tcptw from the
-			 * inpcb, so we just detach and free the inpcb.
-			 *
-			 * XXXRW: Would it be cleaner to free the tcptw
-			 * here?
-			 */
+			KASSERT(tp == NULL, ("tcp_detach: INP_TIMEWAIT && "
+			    "INP_DROPPED && tp != NULL"));
 #ifdef INET6
 			if (isipv6) {
 				in6_pcbdetach(inp);
@@ -183,11 +186,6 @@
 			}
 #endif
 		} else {
-			/*
-			 * Connection is in time wait and has not yet been
-			 * dropped; allow the socket to be discarded, but
-			 * need to keep inpcb until end of time wait.
-			 */
 #ifdef INET6
 			if (isipv6)
 				in6_pcbdetach(inp);
@@ -198,20 +196,21 @@
 		}
 	} else {
 		/*
-		 * If not in timewait, there are two possible paths.  First,
-		 * the TCP connection is either embryonic or done, in which
-		 * case we tear down all state.  Second, it may still be
-		 * active, in which case we acquire a reference to the socket
-		 * and will free it later when TCP is done.
+		 * If the connection is not in timewait, we consider two
+		 * two conditions: one in which no further processing is
+		 * necessary (dropped || embryonic), and one in which TCP is
+		 * not yet done, but no longer requires the socket, so the
+		 * pcb will persist for the time being.
+		 *
+		 * XXXRW: Does the second case still occur?
 		 */
-		tp = intotcpcb(inp);
 		if (inp->inp_vflag & INP_DROPPED ||
 		    tp->t_state < TCPS_SYN_SENT) {
 			tcp_discardcb(tp);
 #ifdef INET6
 			if (isipv6) {
-				in_pcbdetach(inp);
-				in_pcbfree(inp);
+				in6_pcbdetach(inp);
+				in6_pcbfree(inp);
 			} else {
 #endif
 				in_pcbdetach(inp);
@@ -220,11 +219,12 @@
 			}
 #endif
 		} else {
-			SOCK_LOCK(so);
-			so->so_state |= SS_PROTOREF;
-			SOCK_UNLOCK(so);
-			inp->inp_vflag |= INP_SOCKREF;
-			INP_UNLOCK(inp);
+#ifdef INET6
+			if (isipv6)
+				in6_pcbdetach(inp);
+			else
+#endif
+				in_pcbdetach(inp);
 		}
 	}
 }
@@ -251,15 +251,6 @@
 	    ("tcp_usr_detach: inp_socket == NULL"));
 	TCPDEBUG1();
 
-	/*
-	 * First, if we still have full TCP state, and we're not dropped,
-	 * initiate a disconnect.
-	 */
-	if (!(inp->inp_vflag & INP_TIMEWAIT) &&
-	    !(inp->inp_vflag & INP_DROPPED)) {
-		tp = intotcpcb(inp);
-		tcp_disconnect(tp);
-	}
 	tcp_detach(so, inp);
 	tp = NULL;
 	TCPDEBUG2(PRU_DETACH);
@@ -928,16 +919,16 @@
 }
 
 /*
- * Abort the TCP.
- *
- * First, drop the connection.  Then collect state if possible.
+ * Abort the TCP.  Drop the connection abruptly.
  */
 static void
 tcp_usr_abort(struct socket *so)
 {
 	struct inpcb *inp;
 	struct tcpcb *tp;
+#if 0
 	TCPDEBUG0;
+#endif
 
 	inp = sotoinpcb(so);
 	KASSERT(inp != NULL, ("tcp_usr_abort: inp == NULL"));
@@ -946,20 +937,75 @@
 	INP_LOCK(inp);
 	KASSERT(inp->inp_socket != NULL,
 	    ("tcp_usr_abort: inp_socket == NULL"));
+#if 0
 	TCPDEBUG1();
+#endif
 
 	/*
-	 * First, if we still have full TCP state, and we're not dropped,
-	 * drop.
+	 * If we still have full TCP state, and we're not dropped, drop.
 	 */
 	if (!(inp->inp_vflag & INP_TIMEWAIT) &&
 	    !(inp->inp_vflag & INP_DROPPED)) {
 		tp = intotcpcb(inp);
 		tcp_drop(tp, ECONNABORTED);
 	}
-	tcp_detach(so, inp);
-	tp = NULL;
-	TCPDEBUG2(PRU_DETACH);
+	if (!(inp->inp_vflag & INP_DROPPED)) {
+		SOCK_LOCK(so);
+		so->so_state |= SS_PROTOREF;
+		SOCK_UNLOCK(so);
+		inp->inp_vflag |= INP_SOCKREF;
+	}
+#if 0
+	tp = intotcpcb(inp);
+	TCPDEBUG2(PRU_ABORT);
+#endif
+	INP_UNLOCK(inp);
+	INP_INFO_WUNLOCK(&tcbinfo);
+}
+
+/*
+ * TCP socket is closed.  Start friendly disconnect.
+ */
+static void
+tcp_usr_close(struct socket *so)
+{
+	struct inpcb *inp;
+	struct tcpcb *tp;
+#if 0
+	TCPDEBUG0;
+#endif
+
+	inp = sotoinpcb(so);
+	KASSERT(inp != NULL, ("tcp_usr_close: inp == NULL"));
+
+	INP_INFO_WLOCK(&tcbinfo);
+	INP_LOCK(inp);
+	KASSERT(inp->inp_socket != NULL,
+	    ("tcp_usr_close: inp_socket == NULL"));
+#if 0
+	TCPDEBUG1();
+#endif
+
+	/*
+	 * If we still have full TCP state, and we're not dropped, initiate
+	 * a disconnect.
+	 */
+	if (!(inp->inp_vflag & INP_TIMEWAIT) &&
+	    !(inp->inp_vflag & INP_DROPPED)) {
+		tp = intotcpcb(inp);
+		tcp_disconnect(tp);
+	}
+	if (!(inp->inp_vflag & INP_DROPPED)) {
+		SOCK_LOCK(so);
+		so->so_state |= SS_PROTOREF;
+		SOCK_UNLOCK(so);
+		inp->inp_vflag |= INP_SOCKREF;
+	}
+#if 0
+	tp = intotcpcb(inp);
+	TCPDEBUG2(PRU_CLOSE);
+#endif
+	INP_UNLOCK(inp);
 	INP_INFO_WUNLOCK(&tcbinfo);
 }
 
@@ -1021,7 +1067,8 @@
 	.pru_send =		tcp_usr_send,
 	.pru_shutdown =		tcp_usr_shutdown,
 	.pru_sockaddr =		tcp_sockaddr,
-	.pru_sosetlabel =	in_pcbsosetlabel
+	.pru_sosetlabel =	in_pcbsosetlabel,
+	.pru_close =		tcp_usr_close,
 };
 
 #ifdef INET6
@@ -1041,7 +1088,8 @@
 	.pru_send =		tcp_usr_send,
 	.pru_shutdown =		tcp_usr_shutdown,
 	.pru_sockaddr =		in6_mapped_sockaddr,
- 	.pru_sosetlabel =	in_pcbsosetlabel
+ 	.pru_sosetlabel =	in_pcbsosetlabel,
+	.pru_close =		tcp_usr_close,
 };
 #endif /* INET6 */
 
--- //depot/vendor/freebsd/src/sys/netinet/udp_usrreq.c	2006/06/03 19:30:35
+++ //depot/user/rwatson/sockref/src/sys/netinet/udp_usrreq.c	2006/06/14 23:41:26
@@ -948,9 +948,12 @@
 	KASSERT(inp != NULL, ("udp_abort: inp == NULL"));
 	INP_INFO_WLOCK(&udbinfo);
 	INP_LOCK(inp);
-	soisdisconnected(so);
-	in_pcbdetach(inp);
-	in_pcbfree(inp);
+	if (inp->inp_faddr.s_addr != INADDR_ANY) {
+		in_pcbdisconnect(inp);
+		inp->inp_laddr.s_addr = INADDR_ANY;
+		soisdisconnected(so);
+	}
+	INP_UNLOCK(inp);
 	INP_INFO_WUNLOCK(&udbinfo);
 }
 
@@ -997,6 +1000,24 @@
 	return error;
 }
 
+static void
+udp_close(struct socket *so)
+{
+	struct inpcb *inp;
+
+	inp = sotoinpcb(so);
+	KASSERT(inp != NULL, ("udp_close: inp == NULL"));
+	INP_INFO_WLOCK(&udbinfo);
+	INP_LOCK(inp);
+	if (inp->inp_faddr.s_addr != INADDR_ANY) {
+		in_pcbdisconnect(inp);
+		inp->inp_laddr.s_addr = INADDR_ANY;
+		soisdisconnected(so);
+	}
+	INP_UNLOCK(inp);
+	INP_INFO_WUNLOCK(&udbinfo);
+}
+
 static int
 udp_connect(struct socket *so, struct sockaddr *nam, struct thread *td)
 {
@@ -1029,6 +1050,7 @@
 {
 	struct inpcb *inp;
 
+	/* XXXRW: Should assert detach has taken place. */
 	inp = sotoinpcb(so);
 	KASSERT(inp != NULL, ("udp_detach: inp == NULL"));
 	INP_INFO_WLOCK(&udbinfo);
@@ -1121,5 +1143,6 @@
 	.pru_sosend =		sosend_dgram,
 	.pru_shutdown =		udp_shutdown,
 	.pru_sockaddr =		udp_sockaddr,
-	.pru_sosetlabel =	in_pcbsosetlabel
+	.pru_sosetlabel =	in_pcbsosetlabel,
+	.pru_close =		udp_close,
 };
--- //depot/vendor/freebsd/src/sys/netinet6/raw_ip6.c	2006/04/12 03:11:12
+++ //depot/user/rwatson/sockref/src/sys/netinet6/raw_ip6.c	2006/06/14 23:41:26
@@ -587,25 +587,58 @@
 
 	inp = sotoinpcb(so);
 	KASSERT(inp != NULL, ("rip6_detach: inp == NULL"));
-	/* xxx: RSVP */
-	if (so == ip6_mrouter)
-		ip6_mrouter_done();
+	INP_INFO_WLOCK(&ripcbinfo);
+	INP_LOCK(inp);
 	if (inp->in6p_icmp6filt) {
 		FREE(inp->in6p_icmp6filt, M_PCB);
 		inp->in6p_icmp6filt = NULL;
 	}
-	INP_INFO_WLOCK(&ripcbinfo);
-	INP_LOCK(inp);
 	in6_pcbdetach(inp);
 	in6_pcbfree(inp);
 	INP_INFO_WUNLOCK(&ripcbinfo);
 }
 
+/* XXXRW: This can't ever be called. */
 static void
 rip6_abort(struct socket *so)
 {
+	struct inpcb *inp;
+
+	inp = sotoinpcb(so);
+	KASSERT(inp != NULL, ("rip6_abort: inp == NULL"));
+
+	/* xxx: RSVP */
+	if (so == ip6_mrouter)
+		ip6_mrouter_done();
+#if 0
+	/* XXXRW: Some work to do here? */
+	INP_INFO_WLOCK(&ripcbinfo);
+	INP_LOCK(inp);
+	INP_UNLOCK(inp);
+	INP_INFO_WUNLOCK(&ripcbinfo);
+#endif
+	soisdisconnected(so);
+}
+
+static void
+rip6_close(struct socket *so)
+{
+	struct inpcb *inp;
+
+	inp = sotoinpcb(so);
+	KASSERT(inp != NULL, ("rip6_close: inp == NULL"));
+
+	/* xxx: RSVP */
+	if (so == ip6_mrouter)
+		ip6_mrouter_done();
+#if 0
+	/* XXXRW: Some work to do here? */
+	INP_INFO_WLOCK(&ripcbinfo);
+	INP_LOCK(inp);
+	INP_UNLOCK(inp);
+	INP_INFO_WUNLOCK(&ripcbinfo);
+#endif
 	soisdisconnected(so);
-	rip6_detach(so);
 }
 
 static int
@@ -795,4 +828,5 @@
 	.pru_send =		rip6_send,
 	.pru_shutdown =		rip6_shutdown,
 	.pru_sockaddr =		in6_setsockaddr,
+	.pru_close =		rip6_close,
 };
--- //depot/vendor/freebsd/src/sys/netinet6/udp6_usrreq.c	2006/05/20 13:30:39
+++ //depot/user/rwatson/sockref/src/sys/netinet6/udp6_usrreq.c	2006/06/14 23:41:26
@@ -480,11 +480,24 @@
 	inp = sotoinpcb(so);
 	KASSERT(inp != NULL, ("udp6_abort: inp == NULL"));
 
+#ifdef INET
+	if (inp->inp_vflag & INP_IPV4) {
+		struct pr_usrreqs *pru;
+
+		pru = inetsw[ip_protox[IPPROTO_UDP]].pr_usrreqs;
+		(*pru->pru_abort)(so);
+		return;
+	}
+#endif
+
 	INP_INFO_WLOCK(&udbinfo);
 	INP_LOCK(inp);
-	soisdisconnected(so);
-	in6_pcbdetach(inp);
-	in6_pcbfree(inp);
+	if (!IN6_IS_ADDR_UNSPECIFIED(&inp->in6p_faddr)) {
+		in6_pcbdisconnect(inp);
+		inp->in6p_laddr = in6addr_any;
+		soisdisconnected(so);
+	}
+	INP_UNLOCK(inp);
 	INP_INFO_WUNLOCK(&udbinfo);
 }
 
@@ -566,6 +579,34 @@
 	return error;
 }
 
+static void
+udp6_close(struct socket *so)
+{
+	struct inpcb *inp;
+
+	inp = sotoinpcb(so);
+	KASSERT(inp != NULL, ("udp6_close: inp == NULL"));
+
+#ifdef INET
+	if (inp->inp_vflag & INP_IPV4) {
+		struct pr_usrreqs *pru;
+
+		pru = inetsw[ip_protox[IPPROTO_UDP]].pr_usrreqs;
+		(*pru->pru_disconnect)(so);
+		return;
+	}
+#endif
+	INP_INFO_WLOCK(&udbinfo);
+	INP_LOCK(inp);
+	if (!IN6_IS_ADDR_UNSPECIFIED(&inp->in6p_faddr)) {
+		in6_pcbdisconnect(inp);
+		inp->in6p_laddr = in6addr_any;
+		soisdisconnected(so);
+	}
+	INP_UNLOCK(inp);
+	INP_INFO_WUNLOCK(&udbinfo);
+}
+
 static int
 udp6_connect(struct socket *so, struct sockaddr *nam, struct thread *td)
 {
@@ -756,5 +797,6 @@
 	.pru_send =		udp6_send,
 	.pru_shutdown =		udp_shutdown,
 	.pru_sockaddr =		in6_mapped_sockaddr,
-	.pru_sosetlabel =	in_pcbsosetlabel
+	.pru_sosetlabel =	in_pcbsosetlabel,
+	.pru_close =		udp6_close
 };
--- //depot/vendor/freebsd/src/sys/netipsec/keysock.c	2006/04/04 10:13:28
+++ //depot/user/rwatson/sockref/src/sys/netipsec/keysock.c	2006/06/16 22:41:14
@@ -439,6 +439,17 @@
 }
 
 /*
+ * key_close()
+ * derived from net/rtsock.c:rts_close().
+ */
+static void
+key_abort(struct socket *so)
+{
+
+	raw_usrreqs.pru_close(so);
+}
+
+/*
  * key_connect()
  * derived from net/rtsock.c:rts_connect()
  */
@@ -553,6 +564,7 @@
 	.pru_send =		key_send,
 	.pru_shutdown =		key_shutdown,
 	.pru_sockaddr =		key_sockaddr,
+	.pru_close =		key_close,
 };
 
 /* sysctl */
--- //depot/vendor/freebsd/src/sys/netipx/ipx_usrreq.c	2006/04/11 23:20:47
+++ //depot/user/rwatson/sockref/src/sys/netipx/ipx_usrreq.c	2006/06/16 22:59:58
@@ -88,6 +88,7 @@
 static	int ipx_shutdown(struct socket *so);
 static	int ripx_attach(struct socket *so, int proto, struct thread *td);
 static	int ipx_output(struct ipxpcb *ipxp, struct mbuf *m0);
+static	void ipx_usr_close(struct socket *so);
 
 struct	pr_usrreqs ipx_usrreqs = {
 	.pru_abort =		ipx_usr_abort,
@@ -101,6 +102,7 @@
 	.pru_send =		ipx_send,
 	.pru_shutdown =		ipx_shutdown,
 	.pru_sockaddr =		ipx_sockaddr,
+	.pru_close =		ipx_usr_close,
 };
 
 struct	pr_usrreqs ripx_usrreqs = {
@@ -115,6 +117,7 @@
 	.pru_send =		ipx_send,
 	.pru_shutdown =		ipx_shutdown,
 	.pru_sockaddr =		ipx_sockaddr,
+	.pru_close =		ipx_usr_close,
 };
 
 /*
@@ -432,14 +435,8 @@
 ipx_usr_abort(so)
 	struct socket *so;
 {
-	struct ipxpcb *ipxp = sotoipxpcb(so);
 
-	KASSERT(ipxp != NULL, ("ipx_usr_abort: ipxp == NULL"));
-	IPX_LIST_LOCK();
-	IPX_LOCK(ipxp);
-	ipx_pcbdetach(ipxp);
-	ipx_pcbfree(ipxp);
-	IPX_LIST_UNLOCK();
+	/* XXXRW: Possibly ipx_disconnect() here? */
 	soisdisconnected(so);
 }
 
@@ -482,6 +479,15 @@
 	return (error);
 }
 
+static void
+ipx_usr_close(so)
+	struct socket *so;
+{
+
+	/* XXXRW: Possibly ipx_disconnect() here? */
+	soisdisconnected(so);
+}
+
 static int
 ipx_connect(so, nam, td)
 	struct socket *so;
@@ -513,6 +519,7 @@
 {
 	struct ipxpcb *ipxp = sotoipxpcb(so);
 
+	/* XXXRW: Should assert detached. */
 	KASSERT(ipxp != NULL, ("ipx_detach: ipxp == NULL"));
 	IPX_LIST_LOCK();
 	IPX_LOCK(ipxp);
--- //depot/vendor/freebsd/src/sys/netipx/spx_usrreq.c	2006/04/01 15:45:41
+++ //depot/user/rwatson/sockref/src/sys/netipx/spx_usrreq.c	2006/06/16 22:59:58
@@ -101,6 +101,7 @@
 static	int spx_accept(struct socket *so, struct sockaddr **nam);
 static	int spx_attach(struct socket *so, int proto, struct thread *td);
 static	int spx_bind(struct socket *so, struct sockaddr *nam, struct thread *td);
+static	void spx_usr_close(struct socket *so);
 static	int spx_connect(struct socket *so, struct sockaddr *nam,
 			struct thread *td);
 static	void spx_detach(struct socket *so);
@@ -131,6 +132,7 @@
 	.pru_send =		spx_send,
 	.pru_shutdown =		spx_shutdown,
 	.pru_sockaddr =		ipx_sockaddr,
+	.pru_close =		spx_usr_close,
 };
 
 struct	pr_usrreqs spx_usrreq_sps = {
@@ -149,6 +151,7 @@
 	.pru_send =		spx_send,
 	.pru_shutdown =		spx_shutdown,
 	.pru_sockaddr =		ipx_sockaddr,
+	.pru_close =		spx_usr_close,
 };
 
 void
@@ -1320,9 +1323,7 @@
 	IPX_LIST_LOCK();
 	IPX_LOCK(ipxp);
 	spx_drop(cb, ECONNABORTED);
-	spx_pcbdetach(ipxp);
-	ipx_pcbdetach(ipxp);
-	ipx_pcbfree(ipxp);
+	IPX_UNLOCK(ipxp);
 	IPX_LIST_UNLOCK();
 }
 
@@ -1459,6 +1460,28 @@
 	return (error);
 }
 
+static void
+spx_usr_close(struct socket *so)
+{
+	struct ipxpcb *ipxp;
+	struct spxpcb *cb;
+
+	ipxp = sotoipxpcb(so);
+	KASSERT(ipxp != NULL, ("spx_usr_close: ipxp == NULL"));
+
+	cb = ipxtospxpcb(ipxp);
+	KASSERT(cb != NULL, ("spx_usr_close: cb == NULL"));
+
+	IPX_LIST_LOCK();
+	IPX_LOCK(ipxp);
+	if (cb->s_state > TCPS_LISTEN)
+		spx_disconnect(cb);
+	else
+		spx_close(cb);
+	IPX_UNLOCK(ipxp);
+	IPX_LIST_UNLOCK();
+}
+
 /*
  * Initiate connection to peer.  Enter SYN_SENT state, and mark socket as
  * connecting.  Start keep-alive timer, setup prototype header, send initial
@@ -1518,6 +1541,9 @@
 	struct ipxpcb *ipxp;
 	struct spxpcb *cb;
 
+	/*
+	 * XXXRW: Should assert appropriately detached.
+	 */
 	ipxp = sotoipxpcb(so);
 	KASSERT(ipxp != NULL, ("spx_detach: ipxp == NULL"));
 
@@ -1526,12 +1552,7 @@
 
 	IPX_LIST_LOCK();
 	IPX_LOCK(ipxp);
-	if (cb->s_state > TCPS_LISTEN)
-		spx_disconnect(cb);
-	else
-		spx_close(cb);
 	spx_pcbdetach(ipxp);
-	ipx_pcbdetach(ipxp);
 	ipx_pcbfree(ipxp);
 	IPX_LIST_UNLOCK();
 }
--- //depot/vendor/freebsd/src/sys/netkey/keysock.c	2006/04/01 15:57:25
+++ //depot/user/rwatson/sockref/src/sys/netkey/keysock.c	2006/06/14 22:56:14
@@ -348,6 +348,17 @@
 }
 
 /*
+ * key_close()
+ * derived from net/rtsock.c:rts_close()
+ */
+static void
+key_close(struct socket *so)
+{
+
+	raw_usrreqs.pru_close(so);
+}
+
+/*
  * key_connect()
  * derived from net/rtsock.c:rts_connect()
  */
@@ -460,6 +471,7 @@
 	.pru_send = 		key_send,
 	.pru_shutdown =		key_shutdown,
 	.pru_sockaddr =		key_sockaddr,
+	.pru_close =		key_close,
 };
 
 /* sysctl */
--- //depot/vendor/freebsd/src/sys/netnatm/natm.c	2006/04/23 16:35:19
+++ //depot/user/rwatson/sockref/src/sys/netnatm/natm.c	2006/06/14 22:56:14
@@ -336,7 +336,12 @@
 natm_usr_abort(struct socket *so)
 {
 
-	natm_usr_detach(so);
+}
+
+static void
+natm_usr_close(struct socket *so)
+{
+
 }
 
 static int
@@ -366,6 +371,7 @@
 	.pru_send =		natm_usr_send,
 	.pru_shutdown =		natm_usr_shutdown,
 	.pru_sockaddr =		natm_usr_sockaddr,
+	.pru_close =		natm_usr_close,
 };
 
 /*
--- //depot/vendor/freebsd/src/sys/sys/protosw.h	2006/06/16 22:36:03
+++ //depot/user/rwatson/sockref/src/sys/sys/protosw.h	2006/06/16 22:37:21
@@ -170,7 +170,8 @@
 #define	PRU_PROTOSEND		21	/* send to below */
 /* end for protocol's internal use */
 #define PRU_SEND_EOF		22	/* send and close */
-#define PRU_NREQ		22
+#define	PRU_CLOSE		23	/* socket close */
+#define PRU_NREQ		23
 
 #ifdef PRUREQUESTS
 const char *prurequests[] = {
@@ -179,8 +180,8 @@
 	"RCVD",		"SEND",		"ABORT",	"CONTROL",
 	"SENSE",	"RCVOOB",	"SENDOOB",	"SOCKADDR",
 	"PEERADDR",	"CONNECT2",	"FASTTIMO",	"SLOWTIMO",
-	"PROTORCV",	"PROTOSEND",
-	"SEND_EOF",
+	"PROTORCV",	"PROTOSEND",	"SEND_EOF",	"SETLABEL",
+	"CLOSE",
 };
 #endif
 
@@ -244,6 +245,7 @@
 	int	(*pru_sopoll)(struct socket *so, int events,
 		    struct ucred *cred, struct thread *td);
 	void	(*pru_sosetlabel)(struct socket *so);
+	void	(*pru_close)(struct socket *so);
 };
 
 /*
@@ -279,6 +281,7 @@
 int	pru_sopoll_notsupp(struct socket *so, int events, struct ucred *cred,
 	    struct thread *td);
 void	pru_sosetlabel_null(struct socket *so);
+void	pru_close_notsupp(struct socket *so);
 
 #endif /* _KERNEL */
 
