--- /dev/null	2012-08-18 10:37:11.000000000 +0000
+++ new/usr/src/uts/common/inet/dccp/dccp_stats.c	2012-08-18 10:37:10.983047463 +0000
@@ -0,0 +1,583 @@
+/*
+ * 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.
+ */
+
+/*
+ * Functions related to MIB-II and kstat.
+ */
+
+#include <sys/types.h>
+#include <sys/tihdr.h>
+#include <sys/policy.h>
+#include <sys/tsol/tnet.h>
+
+#include <inet/common.h>
+#include <inet/dccp_impl.h>
+#include <inet/ip.h>
+#include <inet/kstatcom.h>
+#include <inet/snmpcom.h>
+
+#include <sys/cmn_err.h>
+
+static int	dccp_snmp_state(dccp_t *);
+static int	dccp_kstat_update(kstat_t *, int);
+static int	dccp_kstat2_update(kstat_t *, int);
+static void	dccp_add_mib(mib2_dccp_t *, mib2_dccp_t *);
+static void	dccp_sum_mib(dccp_stack_t *, mib2_dccp_t *);
+static void	dccp_clr_stats(dccp_stat_t *);
+static void	dccp_add_stats(dccp_stat_counter_t *, dccp_stat_t *);
+
+/*
+ * Translate DCCP state to MIB2 state.
+ */
+static int
+dccp_snmp_state(dccp_t *dccp)
+{
+	if (dccp == NULL) {
+		return (0);
+	}
+
+	switch(dccp->dccp_state) {
+	case DCCPS_CLOSED:
+		return (MIB2_DCCP_closed);
+	default:
+		return (0);
+	}
+}
+
+/*
+ * Get the MIB-II stats.
+ */
+mblk_t *
+dccp_snmp_get(queue_t *q, mblk_t *mpctl, boolean_t legacy_req)
+{
+	conn_t		*connp = Q_TO_CONN(q);
+	connf_t		*connfp;
+	ip_stack_t	*ipst;
+	dccp_stack_t	*dccps;
+	struct opthdr	*optp;
+	mblk_t		*mp2ctl;
+	mblk_t		*mpdata;
+	mblk_t		*mp_conn_ctl = NULL;
+	mblk_t		*mp_conn_tail;
+	mblk_t		*mp_attr_ctl = NULL;
+	mblk_t		*mp_attr_tail;
+	mblk_t		*mp6_conn_ctl = NULL;
+	mblk_t		*mp6_conn_tail;
+	mblk_t		*mp6_attr_ctl = NULL;
+	mblk_t		*mp6_attr_tail;
+	size_t		dccp_mib_size;
+	size_t		dce_size;
+	size_t		dce6_size;
+	boolean_t	ispriv;
+	zoneid_t	zoneid;
+	int		v4_conn_idx;
+	int		v6_conn_idx;
+	int		i;
+	mib2_dccp_t		dccp_mib;
+	mib2_dccpConnEntry_t	dce;
+	mib2_dccp6ConnEntry_t	dce6;
+	mib2_transportMLPEntry_t mlp;
+
+	/*
+	 * Make a copy of the original message.
+	 */
+	mp2ctl = copymsg(mpctl);
+
+	cmn_err(CE_NOTE, "dccp_stats.c: dccp_snmp_get");
+
+	if (mpctl == NULL ||
+	    (mpdata = mpctl->b_cont) == NULL ||
+	    (mp_conn_ctl = copymsg(mpctl)) == NULL ||
+	    (mp_attr_ctl = copymsg(mpctl)) == NULL ||
+	    (mp6_conn_ctl = copymsg(mpctl)) == NULL ||
+	    (mp6_attr_ctl = copymsg(mpctl)) == NULL) {
+		freemsg(mp_conn_ctl);
+		freemsg(mp_attr_ctl);
+		freemsg(mp6_conn_ctl);
+		freemsg(mp6_attr_ctl);
+		freemsg(mpctl);
+		freemsg(mp2ctl);
+		return (NULL);
+	}
+
+	ipst = connp->conn_netstack->netstack_ip;
+	dccps = connp->conn_netstack->netstack_dccp;
+
+	if (legacy_req) {
+		dccp_mib_size = LEGACY_MIB_SIZE(&dccp_mib, mib2_dccp_t);
+		dce_size = LEGACY_MIB_SIZE(&dce, mib2_dccpConnEntry_t);
+		dce6_size = LEGACY_MIB_SIZE(&dce6, mib2_dccp6ConnEntry_t);
+	} else {
+		dccp_mib_size = sizeof (mib2_dccp_t);
+		dce_size = sizeof (mib2_dccpConnEntry_t);
+		dce6_size = sizeof (mib2_dccp6ConnEntry_t);
+	}
+
+	bzero(&dccp_mib, sizeof (dccp_mib));
+
+	ispriv = secpolicy_ip_config((Q_TO_CONN(q))->conn_cred, B_TRUE) == 0;
+	zoneid = Q_TO_CONN(q)->conn_zoneid;
+
+	v4_conn_idx = v6_conn_idx = 0;
+	mp_conn_tail = mp_attr_tail = mp6_conn_tail = mp6_attr_tail = NULL;
+
+	for (i = 0; i < CONN_G_HASH_SIZE; i++) {
+		ipst = dccps->dccps_netstack->netstack_ip;
+
+		connfp = &ipst->ips_ipcl_globalhash_fanout[i];
+		connp = NULL;
+
+		while ((connp = ipcl_get_next_conn(connfp, connp,
+		    IPCL_DCCPCONN)) != NULL) {
+			dccp_t		*dccp;
+			boolean_t	needattr;
+
+			if (connp->conn_zoneid != zoneid) {
+				continue;	/* Not in this zone */
+			}
+
+			dccp = connp->conn_dccp;
+			DCCPS_UPDATE_MIB(dccps, dccpHCInSegs, dccp->dccp_ibsegs);
+			dccp->dccp_ibsegs = 0;
+			DCCPS_UPDATE_MIB(dccps, dccpHCOutSegs, dccp->dccp_obsegs);
+			dccp->dccp_obsegs = 0;
+
+			dce.dccpConnState = dccp_snmp_state(dccp);
+
+			needattr = B_FALSE;
+			bzero(&mlp, sizeof (mlp));
+			if (connp->conn_mlp_type != mlptSingle) {
+				if (connp->conn_mlp_type == mlptShared ||
+				    connp->conn_mlp_type == mlptBoth) {
+					mlp.tme_flags |= MIB2_TMEF_SHARED;
+				}
+
+				if (connp->conn_mlp_type == mlptPrivate ||
+				    connp->conn_mlp_type == mlptBoth) {
+					mlp.tme_flags |= MIB2_TMEF_PRIVATE;
+				}
+
+				needattr = B_TRUE;
+			}
+
+			if (connp->conn_anon_mlp) {
+				mlp.tme_flags |= MIB2_TMEF_ANONMLP;
+				needattr = B_TRUE;
+			}
+
+			switch (connp->conn_mac_mode) {
+			case CONN_MAC_DEFAULT:
+				break;
+			case CONN_MAC_AWARE:
+				mlp.tme_flags |= MIB2_TMEF_MACEXEMPT;
+				needattr = B_TRUE;
+				break;
+			case CONN_MAC_IMPLICIT:
+				mlp.tme_flags |= MIB2_TMEF_MACIMPLICIT;
+				needattr = B_TRUE;
+				break;
+			}
+
+			if (connp->conn_ixa->ixa_tsl != NULL) {
+				ts_label_t	*tsl;
+
+				tsl = connp->conn_ixa->ixa_tsl;
+				mlp.tme_flags |= MIB2_TMEF_IS_LABELED;
+				mlp.tme_doi = label2doi(tsl);
+				mlp.tme_label = *label2bslabel(tsl);
+				needattr = B_TRUE;
+			}
+
+			/* Create a message to report on IPv6 entries */
+			if (connp->conn_ipversion == IPV6_VERSION) {
+				dce6.dccp6ConnLocalAddress =
+				    connp->conn_laddr_v6;
+				dce6.dccp6ConnRemAddress =
+				    connp->conn_faddr_v6;
+				dce6.dccp6ConnLocalPort =
+				    ntohs(connp->conn_lport);
+				dce6.dccp6ConnRemPort =
+				    ntohs(connp->conn_fport);
+
+				if (connp->conn_ixa->ixa_flags &
+				    IXAF_SCOPEID_SET) {
+					dce6.dccp6ConnIfIndex =
+					    connp->conn_ixa->ixa_scopeid;
+				} else {
+					dce6.dccp6ConnIfIndex =
+					    connp->conn_bound_if;
+				}
+
+				/* XXX */
+
+				dce6.dccp6ConnEntryInfo.ce_state =
+				    dccp->dccp_state;
+
+				dce6.dccp6ConnCreationProcess =
+				    (connp->conn_cpid < 0) ?
+				    MIB2_UNKNOWN_PROCESS : connp->conn_cpid;
+				dce6.dccp6ConnCreationTime =
+				    connp->conn_open_time;
+
+				(void) snmp_append_data2(mp6_conn_ctl->b_cont,
+				    &mp6_conn_tail, (char *)&dce6, dce6_size);
+
+				mlp.tme_connidx = v6_conn_idx++;
+				if (needattr) {
+					(void) snmp_append_data2(
+					    mp6_attr_ctl->b_cont,
+					    &mp6_attr_tail, (char *)&mlp,
+					    sizeof (mlp));
+				}
+			}
+
+			if (connp->conn_ipversion == IPV4_VERSION ||
+			    (dccp->dccp_state <= DCCPS_LISTEN &&
+			    !connp->conn_ipv6_v6only &&
+			    IN6_IS_ADDR_UNSPECIFIED(&connp->conn_laddr_v6))) {
+
+				if (connp->conn_ipversion == IPV6_VERSION) {
+					dce.dccpConnRemAddress = INADDR_ANY;
+					dce.dccpConnLocalAddress = INADDR_ANY;
+				} else {
+					dce.dccpConnRemAddress =
+					    connp->conn_faddr_v4;
+					dce.dccpConnLocalAddress =
+					    connp->conn_laddr_v4;
+				}
+
+				dce.dccpConnLocalPort =
+				    ntohs(connp->conn_lport);
+				dce.dccpConnRemPort =
+				    ntohs(connp->conn_fport);
+
+				/* XXX */
+
+				dce.dccpConnEntryInfo.ce_state =
+				    dccp->dccp_state;
+
+				dce.dccpConnCreationProcess =
+				    (connp->conn_cpid < 0) ?
+				    MIB2_UNKNOWN_PROCESS : connp->conn_cpid;
+				dce.dccpConnCreationTime =
+				    connp->conn_open_time;
+
+				(void) snmp_append_data2(mp_conn_ctl->b_cont,
+				    &mp_conn_tail, (char *)&dce, dce_size);
+
+				mlp.tme_connidx = v4_conn_idx++;
+				if (needattr) {
+					(void) snmp_append_data2(
+					    mp_attr_ctl->b_cont,
+					    &mp_attr_tail, (char *)&mlp,
+					    sizeof (mlp));
+				}
+			}
+		}
+	}
+
+	/* Sum up per CPU stats */
+	dccp_sum_mib(dccps, &dccp_mib);
+
+	/* Fixed length structure for IPv4 and IPv6 counters */
+	SET_MIB(dccp_mib.dccpConnTableSize, dce_size);
+	SET_MIB(dccp_mib.dccp6ConnTableSize, dce6_size);
+
+	/* Synchronize 32- and 64-bit counters */
+	SYNC32_MIB(&dccp_mib, dccpInSegs, dccpHCInSegs);
+	SYNC32_MIB(&dccp_mib, dccpOutSegs, dccpHCOutSegs);
+
+	optp = (struct opthdr *)&mpctl->b_rptr[sizeof (struct T_optmgmt_ack)];
+	optp->level = MIB2_DCCP;
+	optp->name = 0;
+	(void) snmp_append_data(mpdata, (char *)&dccp_mib, dccp_mib_size);
+	optp->len = msgdsize(mpdata);
+	qreply(q, mpctl);
+
+	optp = (struct opthdr *)&mp_conn_ctl->b_rptr[
+	    sizeof (struct T_optmgmt_ack)];
+	optp->level = MIB2_DCCP;
+	optp->name = MIB2_DCCP_CONN;
+	optp->len = msgdsize(mp_conn_ctl->b_cont);
+	qreply(q, mp_conn_ctl);
+
+	optp = (struct opthdr *)&mp_attr_ctl->b_rptr[
+	    sizeof (struct T_optmgmt_ack)];
+	optp->level = MIB2_DCCP;
+	optp->name = EXPER_XPORT_MLP;
+	optp->len = msgdsize(mp_attr_ctl->b_cont);
+	if (optp->len == 0) {
+		freemsg(mp_attr_ctl);
+	} else {
+		qreply(q, mp_attr_ctl);
+	}
+
+	optp = (struct opthdr *)&mp6_conn_ctl->b_rptr[
+	    sizeof (struct T_optmgmt_ack)];
+	optp->level = MIB2_DCCP6;
+	optp->name = MIB2_DCCP6_CONN;
+	optp->len = msgdsize(mp6_conn_ctl->b_cont);
+	qreply(q, mp6_conn_ctl);
+
+	optp = (struct opthdr *)&mp6_attr_ctl->b_rptr[
+	    sizeof (struct T_optmgmt_ack)];
+	optp->level = MIB2_DCCP6;
+	optp->name = EXPER_XPORT_MLP;
+	optp->len = msgdsize(mp6_attr_ctl->b_cont);
+	if (optp->len == 0) {
+		freemsg(mp6_attr_ctl);
+	} else {
+		qreply(q, mp6_attr_ctl);
+	}
+
+	return (mp2ctl);
+}
+
+/*
+ * DCCP kernel statistics.
+ */
+void *
+dccp_kstat_init(netstackid_t stackid)
+{
+	kstat_t	*ksp;
+
+	dccp_named_kstat_t template = {
+		{ "activeOpens",	KSTAT_DATA_UINT32, 0 },
+		{ "passiveOpens",	KSTAT_DATA_UINT32, 0 },
+		{ "inSegs",		KSTAT_DATA_UINT64, 0 },
+		{ "outSegs",		KSTAT_DATA_UINT64, 0 },
+	};
+
+	ksp = kstat_create_netstack(DCCP_MOD_NAME, 0, DCCP_MOD_NAME, "mib2",
+	    KSTAT_TYPE_NAMED, NUM_OF_FIELDS(dccp_named_kstat_t), 0, stackid);
+	if (ksp == NULL) {
+		return (NULL);
+	}
+
+	bcopy(&template, ksp->ks_data, sizeof (template));
+	ksp->ks_update = dccp_kstat_update;
+	ksp->ks_private = (void *)(uintptr_t)stackid;
+
+	kstat_install(ksp);
+
+	return (ksp);
+}
+
+/*
+ * Destroy DCCP kernel statistics.
+ */
+void
+dccp_kstat_fini(netstackid_t stackid, kstat_t *ksp)
+{
+
+	if (ksp != NULL) {
+		ASSERT(stackid == (netstackid_t)(uintptr_t)ksp->ks_private);
+		kstat_delete_netstack(ksp, stackid);
+	}
+}
+
+/*
+ * Update DCCP kernel statistics.
+ */
+static int
+dccp_kstat_update(kstat_t *kp, int rw)
+{
+	conn_t			*connp;
+	connf_t			*connfp;
+	dccp_named_kstat_t	*dccpkp;
+	dccp_t			*dccp;
+	dccp_stack_t		*dccps;
+	ip_stack_t		*ipst;
+	netstack_t		*ns;
+	netstackid_t		stackid;
+	mib2_dccp_t		dccp_mib;
+
+	if (rw == KSTAT_WRITE) {
+		return (EACCES);
+	}
+
+	stackid = (netstackid_t)(uintptr_t)kp->ks_private;
+	ns = netstack_find_by_stackid(stackid);
+	if (ns == NULL) {
+		return (-1);
+	}
+
+	dccps = ns->netstack_dccp;
+	if (dccps == NULL) {
+		netstack_rele(ns);
+		return (-1);
+	}
+
+	dccpkp = (dccp_named_kstat_t *)kp->ks_data;
+	ipst = ns->netstack_ip;
+
+	bzero(&dccp_mib, sizeof (dccp_mib));
+	dccp_sum_mib(dccps, &dccp_mib);
+
+	/* Fixed length structure for IPv4 and IPv6 counters */
+	SET_MIB(dccp_mib.dccpConnTableSize, sizeof (mib2_dccpConnEntry_t));
+	SET_MIB(dccp_mib.dccp6ConnTableSize, sizeof (mib2_dccp6ConnEntry_t));
+
+	dccpkp->activeOpens.value.ui32 = dccp_mib.dccpActiveOpens;
+	dccpkp->passiveOpens.value.ui32 = dccp_mib.dccpPassiveOpens;
+	dccpkp->inSegs.value.ui64 = dccp_mib.dccpHCInSegs;
+	dccpkp->outSegs.value.ui64 = dccp_mib.dccpHCOutSegs;
+
+	return (0);
+}
+
+/*
+ *
+ */
+void *
+dccp_kstat2_init(netstackid_t stackid)
+{
+	kstat_t	*ksp;
+
+	dccp_stat_t template = {
+		{ "dccp_sock_fallback",		KSTAT_DATA_UINT64, 0 },
+	};
+
+	ksp = kstat_create_netstack(DCCP_MOD_NAME, 0, "dccpstat", "net",
+	    KSTAT_TYPE_NAMED, sizeof (template) / sizeof (kstat_named_t), 0,
+	    stackid);
+	if (ksp == NULL) {
+		return (NULL);
+	}
+
+	bcopy(&template, ksp->ks_data, sizeof (template));
+	ksp->ks_private = (void *)(uintptr_t)stackid;
+	ksp->ks_update = dccp_kstat2_update;
+
+	kstat_install(ksp);
+
+	return (ksp);
+}
+
+/*
+ * Destroy DCCP kernel statistics.
+ */
+void
+dccp_kstat2_fini(netstackid_t stackid, kstat_t *ksp)
+{
+	if (ksp != NULL) {
+		ASSERT(stackid == (netstackid_t)(uintptr_t)ksp->ks_private);
+		kstat_delete_netstack(ksp, stackid);
+	}
+}
+
+/*
+ * Update routine for .
+ */
+static int
+dccp_kstat2_update(kstat_t *kp, int rw)
+{
+	dccp_stack_t	*dccps;
+	dccp_stat_t	*stats;
+	netstack_t	*ns;
+	netstackid_t	stackid;
+	int		i;
+	int		cnt;
+
+	if (rw == KSTAT_WRITE) {
+		return (EACCES);
+	}
+
+	stackid = (netstackid_t)(uintptr_t)kp->ks_private;
+	ns = netstack_find_by_stackid(stackid);
+	if (ns == NULL) {
+		return (-1);
+	}
+
+	dccps = ns->netstack_dccp;
+	if (dccps == NULL) {
+		netstack_rele(ns);
+		return (-1);
+	}
+
+	stats = (dccp_stat_t *)kp->ks_data;
+	dccp_clr_stats(stats);
+
+	/* Sum up all stats */
+	cnt = dccps->dccps_sc_cnt;
+	for (i = 0; i < cnt; i++) {
+		dccp_add_stats(&dccps->dccps_sc[i]->dccp_sc_stats, stats);
+	}
+
+	netstack_rele(ns);
+
+	return (0);
+}
+
+/*
+ * Add stats from one to another.
+ */
+static void
+dccp_add_mib(mib2_dccp_t *from, mib2_dccp_t *to)
+{
+	to->dccpActiveOpens += from->dccpActiveOpens;
+	to->dccpPassiveOpens += from->dccpPassiveOpens;
+	to->dccpInSegs += from->dccpInSegs;
+	to->dccpOutSegs += from->dccpOutSegs;
+}
+
+/*
+ * Sum up all MIB-II stats for a dccp_stack_t from all per CPU stats.
+ */
+static void
+dccp_sum_mib(dccp_stack_t *dccps, mib2_dccp_t *dccp_mib)
+{
+	int	i;
+	int	cnt;
+
+	cnt = dccps->dccps_sc_cnt;
+	for (i = 0; i < cnt; i++) {
+		dccp_add_mib(&dccps->dccps_sc[i]->dccp_sc_mib, dccp_mib);
+	}
+}
+
+/*
+ * Set all dccp_stat_t counters to zero.
+ */
+static void
+dccp_clr_stats(dccp_stat_t *stats)
+{
+	stats->dccp_sock_fallback.value.ui64 = 0;
+}
+
+/*
+ * Add counters from the per CPU stats.
+ */
+static void
+dccp_add_stats(dccp_stat_counter_t *from, dccp_stat_t *to)
+{
+	to->dccp_sock_fallback.value.ui64 +=
+	    from->dccp_sock_fallback;
+}
