--- /dev/null	2012-06-13 12:04:32.000000000 +0000
+++ new/usr/src/uts/common/inet/dccp/dccp_bind.c	2012-06-13 12:04:32.181136259 +0000
@@ -0,0 +1,450 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+
+/*
+ * Copyright 2010 Sun Microsystems, Inc.  All rights reserved.
+ * Use is subject to license terms.
+ */
+
+/*
+ * Copyright 2012 David Hoeppner.  All rights reserved.
+ */
+
+/*
+ * This file contains function related to binding.
+ */
+
+#include <sys/types.h>
+#include <sys/stream.h>
+#include <sys/strsun.h>
+#include <sys/strsubr.h>
+#include <sys/stropts.h>
+#include <sys/strlog.h>
+#define	_SUN_TPI_VERSION 2
+#include <sys/tihdr.h>
+#include <sys/suntpi.h>
+#include <sys/xti_inet.h>
+#include <sys/squeue_impl.h>
+#include <sys/squeue.h>
+#include <sys/tsol/tnet.h>
+
+#include <inet/common.h>
+#include <inet/ip.h>
+#include <inet/proto_set.h>
+
+#include <sys/cmn_err.h>
+
+#include "dccp_impl.h"
+
+/* Setable in /etc/system */
+static uint32_t dccp_random_anon_port = 1;
+
+static int dccp_bind_select_lport(dccp_t *, in_port_t *, boolean_t,
+    cred_t *);
+
+void
+dccp_bind_hash_insert(dccp_df_t *tbf, dccp_t *dccp, int caller_holds_lock)
+{
+	conn_t	*connp = dccp->dccp_connp;
+	conn_t	*connext;
+	dccp_t	**dccpp;
+	dccp_t	*dccpnext;
+	dccp_t	*dccphash;
+
+	cmn_err(CE_NOTE, "dccp_bind.c: dccp_bind_hash_insert");
+
+	/* XXX:DCCP */
+
+	dccpp = &tbf->df_dccp;
+	if (!caller_holds_lock) {
+		mutex_enter(&tbf->df_lock);
+	} else {
+		ASSERT(MUTEX_HELD(&tbf->df_lock));
+	}
+	dccphash = dccpp[0];
+	dccpnext = NULL;
+
+	if (dccphash != NULL) {
+		/* XXX:DCCP */
+	}
+
+insert:
+	dccp->dccp_bind_hash_port = dccpnext;
+	dccp->dccp_bind_hash = dccphash;
+	dccp->dccp_ptpbhn = dccpp;
+	dccpp[0] = dccp;
+
+	if (!caller_holds_lock) {
+		mutex_exit(&tbf->df_lock);
+	}
+}
+
+void
+dccp_bind_hash_remove(dccp_t *dccp)
+{
+}
+
+/*
+ * Check for a valid address and get a local port.
+ */
+int
+dccp_bind_check(conn_t *connp, struct sockaddr *sa, socklen_t len, cred_t *cr,
+    boolean_t bind_to_req_port_only)
+{
+	dccp_t		*dccp = connp->conn_dccp;
+	ip_stack_t	*ips = connp->conn_netstack->netstack_ip;
+	ip_xmit_attr_t	*ixa = connp->conn_ixa;
+	sin_t		*sin;
+	sin6_t		*sin6;
+	ipaddr_t	v4addr;
+	in6_addr_t	v6addr;
+	ip_laddr_t	laddr_type = IPVL_UNICAST_UP;
+	zoneid_t	zoneid = IPCL_ZONEID(connp);
+	in_port_t	requested_port;
+	uint_t		scopeid = 0;
+	int		error;
+
+	cmn_err(CE_NOTE, "dccp_bind.c: dccp_bind_check");
+
+	ASSERT((uintptr_t)len <= (uintptr_t)INT_MAX);
+
+	/*
+	 * We should be in a pre-listen state.
+	 */
+	if (dccp->dccp_state == DCCPS_LISTEN) {
+		return (0);
+	} else if (dccp->dccp_state > DCCPS_LISTEN) {
+		if (connp->conn_debug) {
+			(void) strlog(DCCP_MOD_ID, 0, 1, SL_ERROR|SL_TRACE,
+			    "dccp_bind: bad state, %d", dccp->dccp_state);
+		}
+		return (-TOUTSTATE);
+	}
+
+	/*
+	 * Check for a valid address parameter. Then validate the
+	 * addresses and copy them and the required port in.
+	 */
+	ASSERT(sa != NULL && len != 0);
+	if (!OK_32PTR((char *)sa)) {
+		if (connp->conn_debug) {
+			(void) strlog(DCCP_MOD_ID, 0, 1, SL_ERROR|SL_TRACE,
+			    "dccp_bind: bad address parameter, "
+			    "address %p, len %d", (void *)sa, len);
+		}
+		return (-TPROTO);
+	}
+
+	error = proto_verify_ip_addr(connp->conn_family, sa, len);
+	if (error != 0) {
+		return (error);
+	}
+
+	switch (len) {
+	case sizeof (sin_t):
+		sin = (sin_t *)sa;
+		v4addr = sin->sin_addr.s_addr;
+		requested_port = ntohs(sin->sin_port);
+		IN6_IPADDR_TO_V4MAPPED(v4addr, &v6addr);
+		if (v4addr != INADDR_ANY) {
+			laddr_type = ip_laddr_verify_v4(v4addr, zoneid, ips,
+			    B_FALSE);
+		}
+		break;
+
+	case sizeof (sin6_t):
+		sin6 = (sin6_t *)sa;
+		v6addr = sin6->sin6_addr;
+		requested_port = ntohs(sin6->sin6_port);
+		if (IN6_IS_ADDR_V4MAPPED(&v6addr)) {
+			if (connp->conn_ipv6_v6only) {
+				return (EADDRNOTAVAIL);
+			}
+
+			IN6_V4MAPPED_TO_IPADDR(&v6addr, v4addr);
+			if (v4addr != INADDR_ANY) {
+				laddr_type = ip_laddr_verify_v4(v4addr, zoneid,
+				    ips, B_FALSE);
+			}
+		} else {
+			if (!IN6_IS_ADDR_UNSPECIFIED(&v6addr)) {
+				if (IN6_IS_ADDR_LINKSCOPE(&v6addr)) {
+					scopeid = sin6->sin6_scope_id;
+					laddr_type = ip_laddr_verify_v6(&v6addr,
+					    zoneid, ips, B_FALSE, scopeid);
+				}
+			}
+		}
+		break;
+
+	default:
+		if (connp->conn_debug) {
+			(void) strlog(DCCP_MOD_ID, 0, 1, SL_ERROR|SL_TRACE,
+			    "dccp_bind: bad address length, %d", len);
+		}
+		return (EAFNOSUPPORT);
+	}
+
+	if (laddr_type == IPVL_BAD) {
+		return (EADDRNOTAVAIL);
+	}
+
+	connp->conn_bound_addr_v6 = v6addr;
+	if (scopeid != 0) {
+		ixa->ixa_flags |= IXAF_SCOPEID_SET;
+		ixa->ixa_scopeid = scopeid;
+		connp->conn_incoming_ifindex = scopeid;
+	} else {
+		ixa->ixa_flags &= ~IXAF_SCOPEID_SET;
+		connp->conn_incoming_ifindex = connp->conn_bound_if;
+	}
+
+	connp->conn_laddr_v6 = v6addr;
+	connp->conn_saddr_v6 = v6addr;
+
+	bind_to_req_port_only = requested_port != 0 && bind_to_req_port_only;
+
+	error = dccp_bind_select_lport(dccp, &requested_port,
+	    bind_to_req_port_only, cr);
+	if (error != 0) {
+		connp->conn_laddr_v6 = ipv6_all_zeros;
+		connp->conn_saddr_v6 = ipv6_all_zeros;
+		connp->conn_bound_addr_v6 = ipv6_all_zeros;
+	}
+
+	return (error);
+}
+
+/*
+ * Bind to a local port.
+ */
+static int
+dccp_bind_select_lport(dccp_t *dccp, in_port_t *requested_port_ptr,
+    boolean_t bind_to_req_port_only, cred_t *cr)
+{
+	dccp_stack_t	*dccps = dccp->dccp_dccps;
+	conn_t		*connp = dccp->dccp_connp;
+	zone_t		*zone;
+	in_port_t	allocated_port;
+	in_port_t	requested_port = *requested_port_ptr;
+	in6_addr_t	v6addr = connp->conn_laddr_v6;
+	boolean_t	user_specified;
+
+	cmn_err(CE_NOTE, "dccp_bind.c: dccp_bind_select_lport");
+
+	ASSERT(cr != NULL);
+
+	if (requested_port == 0) {
+		requested_port =
+		    dccp_update_next_port(dccps->dccps_next_port_to_try,
+		    dccp, B_TRUE);
+		if (requested_port == 0) {
+			return (-TNOADDR);
+		}
+		user_specified = B_FALSE;
+
+	}  else {
+		int		i;
+		boolean_t	priv = B_FALSE;
+
+		if (requested_port < dccps->dccps_smallest_nonpriv_port) {
+			priv = B_TRUE;
+		} else {
+			for (i = 0; i < dccps->dccps_num_epriv_ports; i++) {
+				if (requested_port ==
+				    dccps->dccps_epriv_ports[i]) {
+					priv = B_TRUE;
+					break;
+				}
+			}
+		}
+
+		if (priv) {
+			if (secpolicy_net_privaddr(cr, requested_port,
+			    IPPROTO_DCCP) != 0) {
+				if (connp->conn_debug) {
+					(void) strlog(DCCP_MOD_ID, 0, 1,
+					    SL_ERROR|SL_TRACE,
+					    "tcp_bind: no priv for port %d",
+					    requested_port);
+				}
+				return (-TACCES);
+			}
+		}
+
+		user_specified = B_TRUE;
+		//connp = dccp->dccp_connp;
+
+		/* XXX */
+	}
+
+	allocated_port = dccp_bindi(dccp, requested_port, &v6addr,
+	    connp->conn_reuseaddr, B_FALSE, bind_to_req_port_only,
+	    user_specified);
+
+	if (allocated_port == 0) {
+		/* XXX */
+		if (bind_to_req_port_only) {
+			if (connp->conn_debug) {
+				(void) strlog(DCCP_MOD_ID, 0, 1,
+				    SL_ERROR|SL_TRACE,
+				    "dccp_bind: requested addr busy");
+			}
+			return (-TADDRBUSY);
+		} else {
+			if (connp->conn_debug) {
+				(void) strlog(DCCP_MOD_ID, 0, 1,
+				    SL_ERROR|SL_TRACE,
+				    "dccp_bind: out of ports?");
+			}
+			return (-TNOADDR);
+		}
+	}
+
+	*requested_port_ptr = allocated_port;
+	return (0);
+}
+
+in_port_t
+dccp_bindi(dccp_t *dccp, in_port_t port, const in6_addr_t *laddr,
+    int reuseaddr, boolean_t quick_connect, boolean_t bind_to_req_port_only,
+    boolean_t user_specified)
+{
+	dccp_stack_t	*dccps = dccp->dccp_dccps;
+	conn_t		*connp = dccp->dccp_connp;
+	int		count = 0;
+	int		loopmax;
+
+	cmn_err(CE_NOTE, "dccp_bind.c: dccp_bindi");
+
+	if (bind_to_req_port_only) {
+		loopmax = 1;
+	} else {
+		if (connp->conn_anon_priv_bind) {
+			loopmax = IPPORT_RESERVED -
+			    dccps->dccps_min_anonpriv_port;
+		} else {
+			loopmax = (dccps->dccps_largest_anon_port -
+			    dccps->dccps_smallest_anon_port + 1);
+		}
+	}
+
+	do {
+		conn_t		*lconnp;
+		dccp_t		*ldccp;
+		dccp_df_t	*ldf;
+		uint16_t	lport;
+
+		lport = htons(port);
+
+		dccp_bind_hash_remove(dccp);
+		ldf = &dccps->dccps_bind_fanout[DCCP_BIND_HASH(lport,
+		    dccps->dccps_bind_fanout_size)];
+		mutex_enter(&ldf->df_lock);
+		for (ldccp = ldf->df_dccp; ldccp != NULL;
+		    ldccp = ldccp->dccp_bind_hash) {
+			if (lport == ldccp->dccp_connp->conn_lport) {
+				break;
+			}
+		}
+
+		if (ldccp != NULL) {
+			mutex_exit(&ldf->df_lock);
+		} else {
+			dccp->dccp_state = DCCPS_BOUND;
+
+			connp->conn_lport = htons(port);
+
+			ASSERT(&dccps->dccps_bind_fanout[DCCP_BIND_HASH(
+			     connp->conn_lport,
+			     dccps->dccps_bind_fanout_size)] == ldf);
+			dccp_bind_hash_insert(ldf, dccp, 1);
+
+			mutex_exit(&ldf->df_lock);
+
+			if (user_specified) {
+				return (port);
+			}
+
+			if (!connp->conn_anon_priv_bind) {
+				dccps->dccps_next_port_to_try = port + 1;
+			}
+
+			return (port);
+		}
+
+		if (port == 0) {
+			break;
+		}
+
+	} while (++count < loopmax);
+
+	cmn_err(CE_NOTE, "dccp_bind.c: dccp_bindi exit");
+
+	return (0);
+}
+
+in_port_t
+dccp_update_next_port(in_port_t port, const dccp_t *dccp, boolean_t random)
+{
+	dccp_stack_t	*dccps = dccp->dccp_dccps;
+	boolean_t	restart = B_FALSE;
+	int		i;
+
+	cmn_err(CE_NOTE, "dccp_bind.c: dccp_update_next_port");
+
+	if (random && dccp_random_anon_port != 0) {
+		(void) random_get_pseudo_bytes((uint8_t *)&port,
+		    sizeof (in_port_t));
+
+		if (port < dccps->dccps_smallest_anon_port) {
+			port = dccps->dccps_smallest_anon_port +
+			    port % (dccps->dccps_largest_anon_port -
+			    dccps->dccps_smallest_anon_port);
+		}
+	}
+
+retry:
+	if (port < dccps->dccps_smallest_anon_port) {
+		port = (in_port_t)dccps->dccps_smallest_anon_port;
+	}
+
+	if (port > dccps->dccps_largest_anon_port) {
+		if (restart) {
+			return (0);
+		}
+		restart = B_TRUE;
+		port = (in_port_t)dccps->dccps_smallest_anon_port;
+	}
+
+	if (port < dccps->dccps_smallest_nonpriv_port) {
+		port = (in_port_t)dccps->dccps_smallest_nonpriv_port;
+	}
+
+	for (i = 0; i < dccps->dccps_num_epriv_ports; i++) {
+		if (port == dccps->dccps_epriv_ports[i]) {
+			port++;
+			goto retry;
+		}
+	}
+
+	return (port);
+}
