--- /dev/null	2012-08-18 10:37:10.000000000 +0000
+++ new/usr/src/uts/common/inet/dccp/dccp_opt_data.c	2012-08-18 10:37:10.404969938 +0000
@@ -0,0 +1,327 @@
+/*
+ * 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 functions related to getting and setting options
+ * thought the getsockopt and setsockopt socket functions.
+ */
+
+#include <sys/types.h>
+#include <sys/stream.h>
+#define	_SUN_TPI_VERSION 2
+#include <sys/tihdr.h>
+#include <sys/xti_xtiopt.h>
+#include <sys/xti_inet.h>
+#include <sys/policy.h>
+
+#include <inet/common.h>
+#include <inet/dccp_impl.h>
+#include <inet/ip.h>
+#include <inet/optcom.h>
+#include <netinet/ip.h>
+
+#include <sys/cmn_err.h>
+
+static int dccp_opt_default(queue_t *, int, int, uchar_t *);
+
+/*
+ * Supported options.
+ */
+opdes_t dccp_opt_arr[] = {
+{ SO_DEBUG, SOL_SOCKET, OA_RW, OA_RW, OP_NP, 0, sizeof (int), 0 },
+}; 
+
+/*
+ * Supported levels.
+ */
+optlevel_t dccp_valid_levels_arr[] = {
+	SOL_SOCKET,
+};
+
+#define	DCCP_OPT_ARR_CNT	A_CNT(dccp_opt_arr)
+#define	DCCP_VALID_LEVELS_CNT	A_CNT(dccp_valid_levels_arr)
+
+uint_t dccp_max_optsize;
+
+/*
+ * Options database object.
+ */
+optdb_obj_t dccp_opt_obj = {
+	dccp_opt_default,
+	dccp_tpi_opt_get,
+	dccp_tpi_opt_set,
+	DCCP_OPT_ARR_CNT,
+	dccp_opt_arr,
+	DCCP_VALID_LEVELS_CNT,
+	dccp_valid_levels_arr,
+};
+
+/*
+ * Default value for certain options.
+ */
+int
+dccp_opt_default(queue_t *q, int level, int name, uchar_t *ptr)
+{
+	dccp_stack_t	*dccps = Q_TO_DCCP(q)->dccp_dccps;
+	int32_t		*il = (int32_t *)ptr;
+
+	return (sizeof (int));
+}
+
+int
+dccp_opt_get(conn_t *connp, int level, int name, uchar_t *ptr)
+{
+	dccp_t		*dccp = connp->conn_dccp;
+	conn_opt_arg_t	coas;
+	int		*i1 = (int *)ptr;
+	int		retval;
+
+	coas.coa_connp = connp;
+	coas.coa_ixa = connp->conn_ixa;
+	coas.coa_ipp = &connp->conn_xmit_ipp;
+	coas.coa_ancillary = B_FALSE;
+	coas.coa_changed = 0;
+
+	switch (level) {
+	case SOL_SOCKET:
+		break;
+	case IPPROTO_DCCP:
+		switch (name) {
+		case DCCP_NOTIFY_THRESHOLD:
+			*i1 = dccp->dccp_first_timer_threshold;
+			return (sizeof (int));
+		case DCCP_ABORT_THRESHOLD:
+			*i1 = dccp->dccp_second_timer_threshold;
+			return (sizeof (int));
+		case DCCP_CONN_NOTIFY_THRESHOLD:
+			*i1 = dccp->dccp_first_ctimer_threshold;
+			return (sizeof (int));
+		case DCCP_CONN_ABORT_THRESHOLD:
+			*i1 = dccp->dccp_second_ctimer_threshold;
+			return (sizeof (int));
+		case DCCP_KEEPALIVE_THRESHOLD:
+			*i1 = dccp->dccp_ka_interval;
+			return (sizeof (int));
+		case DCCP_KEEPINTVL:
+			*i1 = dccp->dccp_ka_rinterval / 1000;
+			return (sizeof (int));
+		}
+		break;
+	case IPPROTO_IP:
+		break;
+	case IPPROTO_IPV6:
+		break;
+	}
+
+	mutex_enter(&connp->conn_lock);
+	retval = conn_opt_get(&coas, level, name, ptr);
+	mutex_exit(&connp->conn_lock);
+
+	return (retval);
+}
+
+/* ARGSUSED */
+int
+dccp_opt_set(conn_t *connp, uint_t optset_context, int level, int name,
+    uint_t inlen, uchar_t *invalp, uint_t *outlenp, uchar_t *outvalp,
+    void *thisdg_attrs, cred_t *cr)
+{
+	dccp_t		*dccp = connp->conn_dccp;
+	dccp_stack_t	*dccps = dccp->dccp_dccps;
+	conn_opt_arg_t	coas;
+	uint32_t	val = *((uint32_t *)invalp);
+	int		*i1 = (int *)invalp;
+	boolean_t	onoff = (*i1 == 0) ? B_FALSE : B_TRUE;
+	boolean_t	checkonly = B_FALSE;
+	int		error;
+
+	coas.coa_connp = connp;
+	coas.coa_ixa = connp->conn_ixa;
+	coas.coa_ipp = &connp->conn_xmit_ipp;
+	coas.coa_ancillary = B_FALSE;
+	coas.coa_changed = 0;
+
+	switch (optset_context) {
+	case SETFN_OPTCOM_CHECKONLY:
+		checkonly = B_TRUE;
+		if (inlen = 0) {
+			*outlenp = 0;
+			return (0);
+		}
+		break;
+	case SETFN_OPTCOM_NEGOTIATE:
+		break;
+	case SETFN_UD_NEGOTIATE:
+	case SETFN_CONN_NEGOTIATE:
+		break;
+	default:
+		/*
+		 * We should never get here.
+		 */
+		*outlenp = 0;
+		return (EINVAL);
+	}
+
+	ASSERT((optset_context != SETFN_OPTCOM_CHECKONLY) ||
+	    (optset_context == SETFN_OPTCOM_CHECKONLY && inlen != 0));
+
+	/*
+	 * No ancillary data should be sent down.
+	 */
+	ASSERT(thisdg_attrs == NULL);
+
+	switch (level) {
+	case SOL_SOCKET:
+		switch (name) {
+		case SO_KEEPALIVE:
+			if (checkonly) {
+				/* Check only case */
+				break;
+			}
+
+			if (!onoff) {
+				if (connp->conn_keepalive) {
+					if (dccp->dccp_ka_tid != 0) {
+						(void) DCCP_TIMER_CANCEL(dccp,
+						    dccp->dccp_ka_tid);
+						dccp->dccp_ka_tid = 0;
+					}
+					connp->conn_keepalive = 0;
+				}
+				break;
+			}
+
+			if (!connp->conn_keepalive) {
+				/* Crank up the keepalive timer */
+				dccp->dccp_ka_last_intrvl = 0;
+				dccp->dccp_ka_tid = DCCP_TIMER(dccp,
+				    dccp_keepalive_timer, dccp->dccp_ka_interval);
+				connp->conn_keepalive = 1;
+			}
+			break;
+		}
+		break;
+	case IPPROTO_DCCP:
+		switch (name) {
+		case DCCP_NOTIFY_THRESHOLD:
+			if (!checkonly)
+				dccp->dccp_first_timer_threshold = *i1;
+			break;
+		case DCCP_ABORT_THRESHOLD:
+			if (!checkonly)
+				dccp->dccp_second_timer_threshold = *i1;
+		case DCCP_CONN_NOTIFY_THRESHOLD:
+			if (!checkonly)
+				dccp->dccp_first_ctimer_threshold = *i1;
+			break;
+		case DCCP_CONN_ABORT_THRESHOLD:
+			if (!checkonly)
+				dccp->dccp_second_ctimer_threshold = *i1;
+			break;
+		case DCCP_KEEPIDLE:
+			*i1 *= 1000;
+			/* FALLTHRU */
+		case DCCP_KEEPALIVE_THRESHOLD:
+			if (checkonly)
+				break;
+
+			if (*i1 < dccps->dccps_keepalive_interval_low ||
+			    *i1 > dccps->dccps_keepalive_interval_high) {
+				*outlenp = 0;
+				return (EINVAL);
+			}
+
+			if (*i1 != dccp->dccp_ka_interval) {
+				dccp->dccp_ka_interval = *i1;
+
+				if (dccp->dccp_ka_tid != 0) {
+					ASSERT(connp->conn_keepalive);
+					(void) DCCP_TIMER_CANCEL(dccp,
+					    dccp->dccp_ka_tid);
+					dccp->dccp_ka_last_intrvl = 0;
+					dccp->dccp_ka_tid = DCCP_TIMER(dccp,
+					    dccp_keepalive_timer,
+					    dccp->dccp_ka_interval);
+				}
+			}
+			break;
+		}
+		break;
+	case IPPROTO_IP:
+		break;
+	case IPPROTO_IPV6:
+		break;
+	}
+
+	error = conn_opt_set(&coas, level, name, inlen, invalp,
+	    B_FALSE, cr);
+	if (error !=0) {
+		*outlenp = 0;
+		return (error);
+	}
+
+	/*
+	 * Common case of OK return with outval same as inval.
+	 */
+	if (invalp != outvalp) {
+		(void) bcopy(invalp, outvalp, inlen);
+	}
+	*outlenp = inlen;
+
+	if (coas.coa_changed && COA_HEADER_CHANGED) {
+		/* If we are connected we rebuilt the headers */
+		if (!IN6_IS_ADDR_UNSPECIFIED(&connp->conn_faddr_v6) &&
+		    !IN6_IS_ADDR_V4MAPPED_ANY(&connp->conn_faddr_v6)) {
+			error = dccp_build_hdrs(dccp);
+			if (error != 0) {
+				return (error);
+			}
+		}
+	}
+
+	if (coas.coa_changed & COA_ROUTE_CHANGED) {
+		in6_addr_t	nexthop;
+
+		ip_attr_nexthop(&connp->conn_xmit_ipp, connp->conn_ixa,
+		    &connp->conn_faddr_v6, &nexthop);
+
+		if (!IN6_IS_ADDR_UNSPECIFIED(&connp->conn_faddr_v6) &&
+		    !IN6_IS_ADDR_V4MAPPED_ANY(&connp->conn_faddr_v6)) {
+			(void) ip_attr_connect(connp, connp->conn_ixa,
+			    &connp->conn_laddr_v6, &connp->conn_faddr_v6,
+			    &nexthop, connp->conn_fport, NULL, NULL,
+			    IPDF_VERIFY_DST);
+		}
+	}
+
+	/* XXX */
+
+	return (0);
+}
