/*
* 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 (c) 2004, 2010, Oracle and/or its affiliates. All rights reserved.
*/
#include <sys/types.h>
#include <sys/stream.h>
#include <sys/cmn_err.h>
#define _SUN_TPI_VERSION 2
#include <sys/tihdr.h>
#include <sys/ddi.h>
#include <sys/sunddi.h>
#include <sys/tsol/tndb.h>
#include <netinet/in.h>
#include <inet/common.h>
#include <inet/ip.h>
#include <inet/mib2.h>
#include <inet/snmpcom.h>
#include <inet/kstatcom.h>
#include <inet/ipclassifier.h>
#include "sctp_impl.h"
#include "sctp_addr.h"
static void sctp_clr_kstats2(sctp_kstat_t *);
static void sctp_add_kstats2(sctp_kstat_counter_t *, sctp_kstat_t *);
static int sctp_snmp_state(sctp_t *);
static void sctp_sum_mib(sctp_stack_t *, mib2_sctp_t *);
static void sctp_add_mib(mib2_sctp_t *, mib2_sctp_t *);
static int
sctp_kstat_update(kstat_t *kp, int rw)
{
sctp_named_kstat_t *sctpkp;
sctp_t *sctp, *sctp_prev;
zoneid_t myzoneid;
netstackid_t stackid = (netstackid_t)(uintptr_t)kp->ks_private;
netstack_t *ns;
sctp_stack_t *sctps;
mib2_sctp_t sctp_mib;
if (kp == NULL|| kp->ks_data == NULL)
return (EIO);
if (rw == KSTAT_WRITE)
return (EACCES);
ns = netstack_find_by_stackid(stackid);
if (ns == NULL)
return (-1);
sctps = ns->netstack_sctp;
if (sctps == NULL) {
netstack_rele(ns);
return (-1);
}
/*
* For all exclusive netstacks, the zone ID is always GLOBAL_ZONEID.
*/
if (stackid != GLOBAL_NETSTACKID)
myzoneid = GLOBAL_ZONEID;
else
myzoneid = curproc->p_zone->zone_id;
bzero(&sctp_mib, sizeof (sctp_mib));
/*
* Get the number of current associations and gather their
* individual set of statistics.
*/
sctp_prev = NULL;
mutex_enter(&sctps->sctps_g_lock);
sctp = list_head(&sctps->sctps_g_list);
while (sctp != NULL) {
mutex_enter(&sctp->sctp_reflock);
if (sctp->sctp_condemned) {
mutex_exit(&sctp->sctp_reflock);
sctp = list_next(&sctps->sctps_g_list, sctp);
continue;
}
sctp->sctp_refcnt++;
mutex_exit(&sctp->sctp_reflock);
mutex_exit(&sctps->sctps_g_lock);
if (sctp_prev != NULL)
SCTP_REFRELE(sctp_prev);
if (sctp->sctp_connp->conn_zoneid != myzoneid)
goto next_sctp;
if (sctp->sctp_state == SCTPS_ESTABLISHED ||
sctp->sctp_state == SCTPS_SHUTDOWN_PENDING ||
sctp->sctp_state == SCTPS_SHUTDOWN_RECEIVED) {
/*
* Just bump the local sctp_mib. The number of
* existing associations is not kept in kernel.
*/
BUMP_MIB(&sctp_mib, sctpCurrEstab);
}
if (sctp->sctp_opkts) {
SCTPS_UPDATE_MIB(sctps, sctpOutSCTPPkts,
sctp->sctp_opkts);
sctp->sctp_opkts = 0;
}
if (sctp->sctp_obchunks) {
SCTPS_UPDATE_MIB(sctps, sctpOutCtrlChunks,
sctp->sctp_obchunks);
UPDATE_LOCAL(sctp->sctp_cum_obchunks,
sctp->sctp_obchunks);
sctp->sctp_obchunks = 0;
}
if (sctp->sctp_odchunks) {
SCTPS_UPDATE_MIB(sctps, sctpOutOrderChunks,
sctp->sctp_odchunks);
UPDATE_LOCAL(sctp->sctp_cum_odchunks,
sctp->sctp_odchunks);
sctp->sctp_odchunks = 0;
}
if (sctp->sctp_oudchunks) {
SCTPS_UPDATE_MIB(sctps, sctpOutUnorderChunks,
sctp->sctp_oudchunks);
UPDATE_LOCAL(sctp->sctp_cum_oudchunks,
sctp->sctp_oudchunks);
sctp->sctp_oudchunks = 0;
}
if (sctp->sctp_rxtchunks) {
SCTPS_UPDATE_MIB(sctps, sctpRetransChunks,
sctp->sctp_rxtchunks);
UPDATE_LOCAL(sctp->sctp_cum_rxtchunks,
sctp->sctp_rxtchunks);
sctp->sctp_rxtchunks = 0;
}
if (sctp->sctp_ipkts) {
SCTPS_UPDATE_MIB(sctps, sctpInSCTPPkts,
sctp->sctp_ipkts);
sctp->sctp_ipkts = 0;
}
if (sctp->sctp_ibchunks) {
SCTPS_UPDATE_MIB(sctps, sctpInCtrlChunks,
sctp->sctp_ibchunks);
UPDATE_LOCAL(sctp->sctp_cum_ibchunks,
sctp->sctp_ibchunks);
sctp->sctp_ibchunks = 0;
}
if (sctp->sctp_idchunks) {
SCTPS_UPDATE_MIB(sctps, sctpInOrderChunks,
sctp->sctp_idchunks);
UPDATE_LOCAL(sctp->sctp_cum_idchunks,
sctp->sctp_idchunks);
sctp->sctp_idchunks = 0;
}
if (sctp->sctp_iudchunks) {
SCTPS_UPDATE_MIB(sctps, sctpInUnorderChunks,
sctp->sctp_iudchunks);
UPDATE_LOCAL(sctp->sctp_cum_iudchunks,
sctp->sctp_iudchunks);
sctp->sctp_iudchunks = 0;
}
if (sctp->sctp_fragdmsgs) {
SCTPS_UPDATE_MIB(sctps, sctpFragUsrMsgs,
sctp->sctp_fragdmsgs);
sctp->sctp_fragdmsgs = 0;
}
if (sctp->sctp_reassmsgs) {
SCTPS_UPDATE_MIB(sctps, sctpReasmUsrMsgs,
sctp->sctp_reassmsgs);
sctp->sctp_reassmsgs = 0;
}
next_sctp:
sctp_prev = sctp;
mutex_enter(&sctps->sctps_g_lock);
sctp = list_next(&sctps->sctps_g_list, sctp);
}
mutex_exit(&sctps->sctps_g_lock);
if (sctp_prev != NULL)
SCTP_REFRELE(sctp_prev);
sctp_sum_mib(sctps, &sctp_mib);
/* Copy data from the SCTP MIB */
sctpkp = (sctp_named_kstat_t *)kp->ks_data;
/* These are from global ndd params. */
sctpkp->sctpRtoMin.value.ui32 = sctps->sctps_rto_ming;
sctpkp->sctpRtoMax.value.ui32 = sctps->sctps_rto_maxg;
sctpkp->sctpRtoInitial.value.ui32 = sctps->sctps_rto_initialg;
sctpkp->sctpValCookieLife.value.ui32 = sctps->sctps_cookie_life;
sctpkp->sctpMaxInitRetr.value.ui32 = sctps->sctps_max_init_retr;
/* Copy data from the local sctp_mib to the provided kstat. */
sctpkp->sctpCurrEstab.value.i32 = sctp_mib.sctpCurrEstab;
sctpkp->sctpActiveEstab.value.i32 = sctp_mib.sctpActiveEstab;
sctpkp->sctpPassiveEstab.value.i32 = sctp_mib.sctpPassiveEstab;
sctpkp->sctpAborted.value.i32 = sctp_mib.sctpAborted;
sctpkp->sctpShutdowns.value.i32 = sctp_mib.sctpShutdowns;
sctpkp->sctpOutOfBlue.value.i32 = sctp_mib.sctpOutOfBlue;
sctpkp->sctpChecksumError.value.i32 = sctp_mib.sctpChecksumError;
sctpkp->sctpOutCtrlChunks.value.i64 = sctp_mib.sctpOutCtrlChunks;
sctpkp->sctpOutOrderChunks.value.i64 = sctp_mib.sctpOutOrderChunks;
sctpkp->sctpOutUnorderChunks.value.i64 = sctp_mib.sctpOutUnorderChunks;
sctpkp->sctpRetransChunks.value.i64 = sctp_mib.sctpRetransChunks;
sctpkp->sctpOutAck.value.i32 = sctp_mib.sctpOutAck;
sctpkp->sctpOutAckDelayed.value.i32 = sctp_mib.sctpOutAckDelayed;
sctpkp->sctpOutWinUpdate.value.i32 = sctp_mib.sctpOutWinUpdate;
sctpkp->sctpOutFastRetrans.value.i32 = sctp_mib.sctpOutFastRetrans;
sctpkp->sctpOutWinProbe.value.i32 = sctp_mib.sctpOutWinProbe;
sctpkp->sctpInCtrlChunks.value.i64 = sctp_mib.sctpInCtrlChunks;
sctpkp->sctpInOrderChunks.value.i64 = sctp_mib.sctpInOrderChunks;
sctpkp->sctpInUnorderChunks.value.i64 = sctp_mib.sctpInUnorderChunks;
sctpkp->sctpInAck.value.i32 = sctp_mib.sctpInAck;
sctpkp->sctpInDupAck.value.i32 = sctp_mib.sctpInDupAck;
sctpkp->sctpInAckUnsent.value.i32 = sctp_mib.sctpInAckUnsent;
sctpkp->sctpFragUsrMsgs.value.i64 = sctp_mib.sctpFragUsrMsgs;
sctpkp->sctpReasmUsrMsgs.value.i64 = sctp_mib.sctpReasmUsrMsgs;
sctpkp->sctpOutSCTPPkts.value.i64 = sctp_mib.sctpOutSCTPPkts;
sctpkp->sctpInSCTPPkts.value.i64 = sctp_mib.sctpInSCTPPkts;
sctpkp->sctpInInvalidCookie.value.i32 = sctp_mib.sctpInInvalidCookie;
sctpkp->sctpTimRetrans.value.i32 = sctp_mib.sctpTimRetrans;
sctpkp->sctpTimRetransDrop.value.i32 = sctp_mib.sctpTimRetransDrop;
sctpkp->sctpTimHeartBeatProbe.value.i32 =
sctp_mib.sctpTimHeartBeatProbe;
sctpkp->sctpTimHeartBeatDrop.value.i32 = sctp_mib.sctpTimHeartBeatDrop;
sctpkp->sctpListenDrop.value.i32 = sctp_mib.sctpListenDrop;
sctpkp->sctpInClosed.value.i32 = sctp_mib.sctpInClosed;
netstack_rele(ns);
return (0);
}
void *
sctp_kstat_init(netstackid_t stackid)
{
kstat_t *ksp;
sctp_named_kstat_t template = {
{ "sctpRtoAlgorithm", KSTAT_DATA_INT32, 0 },
{ "sctpRtoMin", KSTAT_DATA_UINT32, 0 },
{ "sctpRtoMax", KSTAT_DATA_UINT32, 0 },
{ "sctpRtoInitial", KSTAT_DATA_UINT32, 0 },
{ "sctpMaxAssocs", KSTAT_DATA_INT32, 0 },
{ "sctpValCookieLife", KSTAT_DATA_UINT32, 0 },
{ "sctpMaxInitRetr", KSTAT_DATA_UINT32, 0 },
{ "sctpCurrEstab", KSTAT_DATA_INT32, 0 },
{ "sctpActiveEstab", KSTAT_DATA_INT32, 0 },
{ "sctpPassiveEstab", KSTAT_DATA_INT32, 0 },
{ "sctpAborted", KSTAT_DATA_INT32, 0 },
{ "sctpShutdowns", KSTAT_DATA_INT32, 0 },
{ "sctpOutOfBlue", KSTAT_DATA_INT32, 0 },
{ "sctpChecksumError", KSTAT_DATA_INT32, 0 },
{ "sctpOutCtrlChunks", KSTAT_DATA_INT64, 0 },
{ "sctpOutOrderChunks", KSTAT_DATA_INT64, 0 },
{ "sctpOutUnorderChunks", KSTAT_DATA_INT64, 0 },
{ "sctpRetransChunks", KSTAT_DATA_INT64, 0 },
{ "sctpOutAck", KSTAT_DATA_INT32, 0 },
{ "sctpOutAckDelayed", KSTAT_DATA_INT32, 0 },
{ "sctpOutWinUpdate", KSTAT_DATA_INT32, 0 },
{ "sctpOutFastRetrans", KSTAT_DATA_INT32, 0 },
{ "sctpOutWinProbe", KSTAT_DATA_INT32, 0 },
{ "sctpInCtrlChunks", KSTAT_DATA_INT64, 0 },
{ "sctpInOrderChunks", KSTAT_DATA_INT64, 0 },
{ "sctpInUnorderChunks", KSTAT_DATA_INT64, 0 },
{ "sctpInAck", KSTAT_DATA_INT32, 0 },
{ "sctpInDupAck", KSTAT_DATA_INT32, 0 },
{ "sctpInAckUnsent", KSTAT_DATA_INT32, 0 },
{ "sctpFragUsrMsgs", KSTAT_DATA_INT64, 0 },
{ "sctpReasmUsrMsgs", KSTAT_DATA_INT64, 0 },
{ "sctpOutSCTPPkts", KSTAT_DATA_INT64, 0 },
{ "sctpInSCTPPkts", KSTAT_DATA_INT64, 0 },
{ "sctpInInvalidCookie", KSTAT_DATA_INT32, 0 },
{ "sctpTimRetrans", KSTAT_DATA_INT32, 0 },
{ "sctpTimRetransDrop", KSTAT_DATA_INT32, 0 },
{ "sctpTimHearBeatProbe", KSTAT_DATA_INT32, 0 },
{ "sctpTimHearBeatDrop", KSTAT_DATA_INT32, 0 },
{ "sctpListenDrop", KSTAT_DATA_INT32, 0 },
{ "sctpInClosed", KSTAT_DATA_INT32, 0 }
};
ksp = kstat_create_netstack(SCTP_MOD_NAME, 0, "sctp", "mib2",
KSTAT_TYPE_NAMED, NUM_OF_FIELDS(sctp_named_kstat_t), 0, stackid);
if (ksp == NULL)
return (NULL);
/* These won't change. */
template.sctpRtoAlgorithm.value.i32 = MIB2_SCTP_RTOALGO_VANJ;
template.sctpMaxAssocs.value.i32 = -1;
bcopy(&template, ksp->ks_data, sizeof (template));
ksp->ks_update = sctp_kstat_update;
ksp->ks_private = (void *)(uintptr_t)stackid;
kstat_install(ksp);
return (ksp);
}
/*
* To set all sctp_stat_t counters to 0.
*/
static void
sctp_clr_kstats2(sctp_kstat_t *stats)
{
stats->sctp_add_faddr.value.ui64 = 0;
stats->sctp_add_timer.value.ui64 = 0;
stats->sctp_conn_create.value.ui64 = 0;
stats->sctp_find_next_tq.value.ui64 = 0;
stats->sctp_fr_add_hdr.value.ui64 = 0;
stats->sctp_fr_not_found.value.ui64 = 0;
stats->sctp_output_failed.value.ui64 = 0;
stats->sctp_rexmit_failed.value.ui64 = 0;
stats->sctp_send_init_failed.value.ui64 = 0;
stats->sctp_send_cookie_failed.value.ui64 = 0;
stats->sctp_send_cookie_ack_failed.value.ui64 = 0;
stats->sctp_send_err_failed.value.ui64 = 0;
stats->sctp_send_sack_failed.value.ui64 = 0;
stats->sctp_send_shutdown_failed.value.ui64 = 0;
stats->sctp_send_shutdown_ack_failed.value.ui64 = 0;
stats->sctp_send_shutdown_comp_failed.value.ui64 = 0;
stats->sctp_send_user_abort_failed.value.ui64 = 0;
stats->sctp_send_asconf_failed.value.ui64 = 0;
stats->sctp_send_asconf_ack_failed.value.ui64 = 0;
stats->sctp_send_ftsn_failed.value.ui64 = 0;
stats->sctp_send_hb_failed.value.ui64 = 0;
stats->sctp_return_hb_failed.value.ui64 = 0;
stats->sctp_ss_rexmit_failed.value.ui64 = 0;
stats->sctp_cl_connect.value.ui64 = 0;
stats->sctp_cl_assoc_change.value.ui64 = 0;
stats->sctp_cl_check_addrs.value.ui64 = 0;
stats->sctp_reclaim_cnt.value.ui64 = 0;
stats->sctp_listen_cnt_drop.value.ui64 = 0;
}
/*
* To add counters from the per CPU sctp_kstat_counter_t to the stack
* sctp_kstat_t.
*/
static void
sctp_add_kstats2(sctp_kstat_counter_t *from, sctp_kstat_t *to)
{
to->sctp_add_faddr.value.ui64 += from->sctp_add_faddr;
to->sctp_add_timer.value.ui64 += from->sctp_add_timer;
to->sctp_conn_create.value.ui64 += from->sctp_conn_create;
to->sctp_find_next_tq.value.ui64 += from->sctp_find_next_tq;
to->sctp_fr_add_hdr.value.ui64 += from->sctp_fr_add_hdr;
to->sctp_fr_not_found.value.ui64 += from->sctp_fr_not_found;
to->sctp_output_failed.value.ui64 += from->sctp_output_failed;
to->sctp_rexmit_failed.value.ui64 += from->sctp_rexmit_failed;
to->sctp_send_init_failed.value.ui64 += from->sctp_send_init_failed;
to->sctp_send_cookie_failed.value.ui64 += from->sctp_send_cookie_failed;
to->sctp_send_cookie_ack_failed.value.ui64 +=
from->sctp_send_cookie_ack_failed;
to->sctp_send_err_failed.value.ui64 += from->sctp_send_err_failed;
to->sctp_send_sack_failed.value.ui64 += from->sctp_send_sack_failed;
to->sctp_send_shutdown_failed.value.ui64 +=
from->sctp_send_shutdown_failed;
to->sctp_send_shutdown_ack_failed.value.ui64 +=
from->sctp_send_shutdown_ack_failed;
to->sctp_send_shutdown_comp_failed.value.ui64 +=
from->sctp_send_shutdown_comp_failed;
to->sctp_send_user_abort_failed.value.ui64 +=
from->sctp_send_user_abort_failed;
to->sctp_send_asconf_failed.value.ui64 += from->sctp_send_asconf_failed;
to->sctp_send_asconf_ack_failed.value.ui64 +=
from->sctp_send_asconf_ack_failed;
to->sctp_send_ftsn_failed.value.ui64 += from->sctp_send_ftsn_failed;
to->sctp_send_hb_failed.value.ui64 += from->sctp_send_hb_failed;
to->sctp_return_hb_failed.value.ui64 += from->sctp_return_hb_failed;
to->sctp_ss_rexmit_failed.value.ui64 += from->sctp_ss_rexmit_failed;
to->sctp_cl_connect.value.ui64 += from->sctp_cl_connect;
to->sctp_cl_assoc_change.value.ui64 += from->sctp_cl_assoc_change;
to->sctp_cl_check_addrs.value.ui64 += from->sctp_cl_check_addrs;
}
/*
* Sum up all per CPU tcp_stat_t kstat counters.
*/
static int
sctp_kstat2_update(kstat_t *kp, int rw)
{
netstackid_t stackid = (netstackid_t)(uintptr_t)kp->ks_private;
netstack_t *ns;
sctp_stack_t *sctps;
sctp_kstat_t *stats;
int i;
int cnt;
if (rw == KSTAT_WRITE)
return (EACCES);
ns = netstack_find_by_stackid(stackid);
if (ns == NULL)
return (-1);
sctps = ns->netstack_sctp;
if (sctps == NULL) {
netstack_rele(ns);
return (-1);
}
stats = (sctp_kstat_t *)kp->ks_data;
sctp_clr_kstats2(stats);
/*
* sctps_sc_cnt may change in the middle of the loop. It is better
* to get its value first.
*/
cnt = sctps->sctps_sc_cnt;
for (i = 0; i < cnt; i++)
sctp_add_kstats2(&sctps->sctps_sc[i]->sctp_sc_stats, stats);
netstack_rele(ns);
return (0);
}
/*
* The following kstats are for debugging purposes. They keep
* track of problems which should not happen normally. But in
* those cases which they do happen, these kstats would be handy
* for engineers to diagnose the problems. They are not intended
* to be consumed by customers.
*/
void *
sctp_kstat2_init(netstackid_t stackid)
{
kstat_t *ksp;
sctp_kstat_t template = {
{ "sctp_add_faddr", KSTAT_DATA_UINT64 },
{ "sctp_add_timer", KSTAT_DATA_UINT64 },
{ "sctp_conn_create", KSTAT_DATA_UINT64 },
{ "sctp_find_next_tq", KSTAT_DATA_UINT64 },
{ "sctp_fr_add_hdr", KSTAT_DATA_UINT64 },
{ "sctp_fr_not_found", KSTAT_DATA_UINT64 },
{ "sctp_output_failed", KSTAT_DATA_UINT64 },
{ "sctp_rexmit_failed", KSTAT_DATA_UINT64 },
{ "sctp_send_init_failed", KSTAT_DATA_UINT64 },
{ "sctp_send_cookie_failed", KSTAT_DATA_UINT64 },
{ "sctp_send_cookie_ack_failed", KSTAT_DATA_UINT64 },
{ "sctp_send_err_failed", KSTAT_DATA_UINT64 },
{ "sctp_send_sack_failed", KSTAT_DATA_UINT64 },
{ "sctp_send_shutdown_failed", KSTAT_DATA_UINT64 },
{ "sctp_send_shutdown_ack_failed", KSTAT_DATA_UINT64 },
{ "sctp_send_shutdown_comp_failed", KSTAT_DATA_UINT64 },
{ "sctp_send_user_abort_failed", KSTAT_DATA_UINT64 },
{ "sctp_send_asconf_failed", KSTAT_DATA_UINT64 },
{ "sctp_send_asconf_ack_failed", KSTAT_DATA_UINT64 },
{ "sctp_send_ftsn_failed", KSTAT_DATA_UINT64 },
{ "sctp_send_hb_failed", KSTAT_DATA_UINT64 },
{ "sctp_return_hb_failed", KSTAT_DATA_UINT64 },
{ "sctp_ss_rexmit_failed", KSTAT_DATA_UINT64 },
{ "sctp_cl_connect", KSTAT_DATA_UINT64 },
{ "sctp_cl_assoc_change", KSTAT_DATA_UINT64 },
{ "sctp_cl_check_addrs", KSTAT_DATA_UINT64 },
{ "sctp_reclaim_drop", KSTAT_DATA_UINT64 },
{ "sctp_listen_cnt_drop", KSTAT_DATA_UINT64 },
};
ksp = kstat_create_netstack(SCTP_MOD_NAME, 0, "sctpstat", "net",
KSTAT_TYPE_NAMED, NUM_OF_FIELDS(template), 0, stackid);
if (ksp == NULL)
return (NULL);
bcopy(&template, ksp->ks_data, sizeof (template));
ksp->ks_private = (void *)(uintptr_t)stackid;
ksp->ks_update = sctp_kstat2_update;
kstat_install(ksp);
return (ksp);
}
void
sctp_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);
}
}
void
sctp_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);
}
}
/*
* Return SNMP global stats in buffer in mpdata.
* Return associatiation table in mp_conn_data,
* local address table in mp_local_data, and
* remote address table in mp_rem_data.
*/
mblk_t *
sctp_snmp_get_mib2(queue_t *q, mblk_t *mpctl, sctp_stack_t *sctps)
{
mblk_t *mpdata, *mp_ret;
mblk_t *mp_conn_ctl = NULL;
mblk_t *mp_conn_data;
mblk_t *mp_conn_tail = NULL;
mblk_t *mp_local_ctl = NULL;
mblk_t *mp_local_data;
mblk_t *mp_local_tail = NULL;
mblk_t *mp_rem_ctl = NULL;
mblk_t *mp_rem_data;
mblk_t *mp_rem_tail = NULL;
mblk_t *mp_attr_ctl = NULL;
mblk_t *mp_attr_data;
mblk_t *mp_attr_tail = NULL;
struct opthdr *optp;
sctp_t *sctp, *sctp_prev = NULL;
sctp_faddr_t *fp;
mib2_sctpConnEntry_t sce;
mib2_sctpConnLocalEntry_t scle;
mib2_sctpConnRemoteEntry_t scre;
mib2_transportMLPEntry_t mlp;
int i;
int l;
int scanned = 0;
zoneid_t zoneid = Q_TO_CONN(q)->conn_zoneid;
conn_t *connp;
boolean_t needattr;
int idx;
mib2_sctp_t sctp_mib;
/*
* Make copies of the original message.
* mpctl will hold SCTP counters,
* mp_conn_ctl will hold list of connections.
*/
mp_ret = copymsg(mpctl);
mp_conn_ctl = copymsg(mpctl);
mp_local_ctl = copymsg(mpctl);
mp_rem_ctl = copymsg(mpctl);
mp_attr_ctl = copymsg(mpctl);
mpdata = mpctl->b_cont;
if (mp_conn_ctl == NULL || mp_local_ctl == NULL ||
mp_rem_ctl == NULL || mp_attr_ctl == NULL || mpdata == NULL) {
freemsg(mp_attr_ctl);
freemsg(mp_rem_ctl);
freemsg(mp_local_ctl);
freemsg(mp_conn_ctl);
freemsg(mp_ret);
freemsg(mpctl);
return (NULL);
}
mp_conn_data = mp_conn_ctl->b_cont;
mp_local_data = mp_local_ctl->b_cont;
mp_rem_data = mp_rem_ctl->b_cont;
mp_attr_data = mp_attr_ctl->b_cont;
bzero(&sctp_mib, sizeof (sctp_mib));
/* hostname address parameters are not supported in Solaris */
sce.sctpAssocRemHostName.o_length = 0;
sce.sctpAssocRemHostName.o_bytes[0] = 0;
/* build table of connections -- need count in fixed part */
idx = 0;
mutex_enter(&sctps->sctps_g_lock);
sctp = list_head(&sctps->sctps_g_list);
while (sctp != NULL) {
mutex_enter(&sctp->sctp_reflock);
if (sctp->sctp_condemned) {
mutex_exit(&sctp->sctp_reflock);
sctp = list_next(&sctps->sctps_g_list, sctp);
continue;
}
sctp->sctp_refcnt++;
mutex_exit(&sctp->sctp_reflock);
mutex_exit(&sctps->sctps_g_lock);
if (sctp_prev != NULL)
SCTP_REFRELE(sctp_prev);
if (sctp->sctp_connp->conn_zoneid != zoneid)
goto next_sctp;
if (sctp->sctp_state == SCTPS_ESTABLISHED ||
sctp->sctp_state == SCTPS_SHUTDOWN_PENDING ||
sctp->sctp_state == SCTPS_SHUTDOWN_RECEIVED) {
/*
* Just bump the local sctp_mib. The number of
* existing associations is not kept in kernel.
*/
BUMP_MIB(&sctp_mib, sctpCurrEstab);
}
SCTPS_UPDATE_MIB(sctps, sctpOutSCTPPkts, sctp->sctp_opkts);
sctp->sctp_opkts = 0;
SCTPS_UPDATE_MIB(sctps, sctpOutCtrlChunks, sctp->sctp_obchunks);
UPDATE_LOCAL(sctp->sctp_cum_obchunks,
sctp->sctp_obchunks);
sctp->sctp_obchunks = 0;
SCTPS_UPDATE_MIB(sctps, sctpOutOrderChunks,
sctp->sctp_odchunks);
UPDATE_LOCAL(sctp->sctp_cum_odchunks,
sctp->sctp_odchunks);
sctp->sctp_odchunks = 0;
SCTPS_UPDATE_MIB(sctps, sctpOutUnorderChunks,
sctp->sctp_oudchunks);
UPDATE_LOCAL(sctp->sctp_cum_oudchunks,
sctp->sctp_oudchunks);
sctp->sctp_oudchunks = 0;
SCTPS_UPDATE_MIB(sctps, sctpRetransChunks,
sctp->sctp_rxtchunks);
UPDATE_LOCAL(sctp->sctp_cum_rxtchunks,
sctp->sctp_rxtchunks);
sctp->sctp_rxtchunks = 0;
SCTPS_UPDATE_MIB(sctps, sctpInSCTPPkts, sctp->sctp_ipkts);
sctp->sctp_ipkts = 0;
SCTPS_UPDATE_MIB(sctps, sctpInCtrlChunks, sctp->sctp_ibchunks);
UPDATE_LOCAL(sctp->sctp_cum_ibchunks,
sctp->sctp_ibchunks);
sctp->sctp_ibchunks = 0;
SCTPS_UPDATE_MIB(sctps, sctpInOrderChunks, sctp->sctp_idchunks);
UPDATE_LOCAL(sctp->sctp_cum_idchunks,
sctp->sctp_idchunks);
sctp->sctp_idchunks = 0;
SCTPS_UPDATE_MIB(sctps, sctpInUnorderChunks,
sctp->sctp_iudchunks);
UPDATE_LOCAL(sctp->sctp_cum_iudchunks,
sctp->sctp_iudchunks);
sctp->sctp_iudchunks = 0;
SCTPS_UPDATE_MIB(sctps, sctpFragUsrMsgs, sctp->sctp_fragdmsgs);
sctp->sctp_fragdmsgs = 0;
SCTPS_UPDATE_MIB(sctps, sctpReasmUsrMsgs, sctp->sctp_reassmsgs);
sctp->sctp_reassmsgs = 0;
sce.sctpAssocId = ntohl(sctp->sctp_lvtag);
sce.sctpAssocLocalPort = ntohs(sctp->sctp_connp->conn_lport);
sce.sctpAssocRemPort = ntohs(sctp->sctp_connp->conn_fport);
RUN_SCTP(sctp);
if (sctp->sctp_primary != NULL) {
fp = sctp->sctp_primary;
if (IN6_IS_ADDR_V4MAPPED(&fp->sf_faddr)) {
sce.sctpAssocRemPrimAddrType =
MIB2_SCTP_ADDR_V4;
} else {
sce.sctpAssocRemPrimAddrType =
MIB2_SCTP_ADDR_V6;
}
sce.sctpAssocRemPrimAddr = fp->sf_faddr;
sce.sctpAssocLocPrimAddr = fp->sf_saddr;
sce.sctpAssocHeartBeatInterval = TICK_TO_MSEC(
fp->sf_hb_interval);
} else {
sce.sctpAssocRemPrimAddrType = MIB2_SCTP_ADDR_V4;
bzero(&sce.sctpAssocRemPrimAddr,
sizeof (sce.sctpAssocRemPrimAddr));
bzero(&sce.sctpAssocLocPrimAddr,
sizeof (sce.sctpAssocLocPrimAddr));
sce.sctpAssocHeartBeatInterval =
sctps->sctps_heartbeat_interval;
}
/*
* Table for local addresses
*/
scanned = 0;
for (i = 0; i < SCTP_IPIF_HASH; i++) {
sctp_saddr_ipif_t *obj;
if (sctp->sctp_saddrs[i].ipif_count == 0)
continue;
obj = list_head(&sctp->sctp_saddrs[i].sctp_ipif_list);
for (l = 0; l < sctp->sctp_saddrs[i].ipif_count; l++) {
sctp_ipif_t *sctp_ipif;
in6_addr_t addr;
sctp_ipif = obj->saddr_ipifp;
addr = sctp_ipif->sctp_ipif_saddr;
scanned++;
scle.sctpAssocId = ntohl(sctp->sctp_lvtag);
if (IN6_IS_ADDR_V4MAPPED(&addr)) {
scle.sctpAssocLocalAddrType =
MIB2_SCTP_ADDR_V4;
} else {
scle.sctpAssocLocalAddrType =
MIB2_SCTP_ADDR_V6;
}
scle.sctpAssocLocalAddr = addr;
(void) snmp_append_data2(mp_local_data,
&mp_local_tail, (char *)&scle,
sizeof (scle));
if (scanned >= sctp->sctp_nsaddrs)
goto done;
obj = list_next(&sctp->
sctp_saddrs[i].sctp_ipif_list, obj);
}
}
done:
/*
* Table for remote addresses
*/
for (fp = sctp->sctp_faddrs; fp; fp = fp->sf_next) {
scre.sctpAssocId = ntohl(sctp->sctp_lvtag);
if (IN6_IS_ADDR_V4MAPPED(&fp->sf_faddr)) {
scre.sctpAssocRemAddrType = MIB2_SCTP_ADDR_V4;
} else {
scre.sctpAssocRemAddrType = MIB2_SCTP_ADDR_V6;
}
scre.sctpAssocRemAddr = fp->sf_faddr;
if (fp->sf_state == SCTP_FADDRS_ALIVE) {
scre.sctpAssocRemAddrActive =
scre.sctpAssocRemAddrHBActive =
MIB2_SCTP_ACTIVE;
} else {
scre.sctpAssocRemAddrActive =
scre.sctpAssocRemAddrHBActive =
MIB2_SCTP_INACTIVE;
}
scre.sctpAssocRemAddrRTO = TICK_TO_MSEC(fp->sf_rto);
scre.sctpAssocRemAddrMaxPathRtx = fp->sf_max_retr;
scre.sctpAssocRemAddrRtx = fp->sf_T3expire;
(void) snmp_append_data2(mp_rem_data, &mp_rem_tail,
(char *)&scre, sizeof (scre));
}
connp = sctp->sctp_connp;
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 (sctp->sctp_connp->conn_ixa->ixa_tsl != NULL) {
ts_label_t *tsl;
tsl = sctp->sctp_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;
}
WAKE_SCTP(sctp);
sce.sctpAssocState = sctp_snmp_state(sctp);
sce.sctpAssocInStreams = sctp->sctp_num_istr;
sce.sctpAssocOutStreams = sctp->sctp_num_ostr;
sce.sctpAssocMaxRetr = sctp->sctp_pa_max_rxt;
/* A 0 here indicates that no primary process is known */
sce.sctpAssocPrimProcess = 0;
sce.sctpAssocT1expired = sctp->sctp_T1expire;
sce.sctpAssocT2expired = sctp->sctp_T2expire;
sce.sctpAssocRtxChunks = sctp->sctp_T3expire;
sce.sctpAssocStartTime = sctp->sctp_assoc_start_time;
sce.sctpConnEntryInfo.ce_sendq = sctp->sctp_unacked +
sctp->sctp_unsent;
sce.sctpConnEntryInfo.ce_recvq = sctp->sctp_rxqueued;
sce.sctpConnEntryInfo.ce_swnd = sctp->sctp_frwnd;
sce.sctpConnEntryInfo.ce_rwnd = sctp->sctp_rwnd;
sce.sctpConnEntryInfo.ce_mss = sctp->sctp_mss;
(void) snmp_append_data2(mp_conn_data, &mp_conn_tail,
(char *)&sce, sizeof (sce));
mlp.tme_connidx = idx++;
if (needattr)
(void) snmp_append_data2(mp_attr_ctl->b_cont,
&mp_attr_tail, (char *)&mlp, sizeof (mlp));
next_sctp:
sctp_prev = sctp;
mutex_enter(&sctps->sctps_g_lock);
sctp = list_next(&sctps->sctps_g_list, sctp);
}
mutex_exit(&sctps->sctps_g_lock);
if (sctp_prev != NULL)
SCTP_REFRELE(sctp_prev);
sctp_sum_mib(sctps, &sctp_mib);
optp = (struct opthdr *)&mpctl->b_rptr[sizeof (struct T_optmgmt_ack)];
optp->level = MIB2_SCTP;
optp->name = 0;
(void) snmp_append_data(mpdata, (char *)&sctp_mib, sizeof (sctp_mib));
optp->len = msgdsize(mpdata);
qreply(q, mpctl);
/* table of connections... */
optp = (struct opthdr *)&mp_conn_ctl->b_rptr[
sizeof (struct T_optmgmt_ack)];
optp->level = MIB2_SCTP;
optp->name = MIB2_SCTP_CONN;
optp->len = msgdsize(mp_conn_data);
qreply(q, mp_conn_ctl);
/* assoc local address table */
optp = (struct opthdr *)&mp_local_ctl->b_rptr[
sizeof (struct T_optmgmt_ack)];
optp->level = MIB2_SCTP;
optp->name = MIB2_SCTP_CONN_LOCAL;
optp->len = msgdsize(mp_local_data);
qreply(q, mp_local_ctl);
/* assoc remote address table */
optp = (struct opthdr *)&mp_rem_ctl->b_rptr[
sizeof (struct T_optmgmt_ack)];
optp->level = MIB2_SCTP;
optp->name = MIB2_SCTP_CONN_REMOTE;
optp->len = msgdsize(mp_rem_data);
qreply(q, mp_rem_ctl);
/* table of MLP attributes */
optp = (struct opthdr *)&mp_attr_ctl->b_rptr[
sizeof (struct T_optmgmt_ack)];
optp->level = MIB2_SCTP;
optp->name = EXPER_XPORT_MLP;
optp->len = msgdsize(mp_attr_data);
if (optp->len == 0)
freemsg(mp_attr_ctl);
else
qreply(q, mp_attr_ctl);
return (mp_ret);
}
/* Translate SCTP state to MIB2 SCTP state. */
static int
sctp_snmp_state(sctp_t *sctp)
{
if (sctp == NULL)
return (0);
switch (sctp->sctp_state) {
case SCTPS_IDLE:
case SCTPS_BOUND:
return (MIB2_SCTP_closed);
case SCTPS_LISTEN:
return (MIB2_SCTP_listen);
case SCTPS_COOKIE_WAIT:
return (MIB2_SCTP_cookieWait);
case SCTPS_COOKIE_ECHOED:
return (MIB2_SCTP_cookieEchoed);
case SCTPS_ESTABLISHED:
return (MIB2_SCTP_established);
case SCTPS_SHUTDOWN_PENDING:
return (MIB2_SCTP_shutdownPending);
case SCTPS_SHUTDOWN_SENT:
return (MIB2_SCTP_shutdownSent);
case SCTPS_SHUTDOWN_RECEIVED:
return (MIB2_SCTP_shutdownReceived);
case SCTPS_SHUTDOWN_ACK_SENT:
return (MIB2_SCTP_shutdownAckSent);
default:
return (0);
}
}
/*
* To sum up all MIB2 stats for a sctp_stack_t from all per CPU stats. The
* caller should initialize the target mib2_sctp_t properly as this function
* just adds up all the per CPU stats.
*/
static void
sctp_sum_mib(sctp_stack_t *sctps, mib2_sctp_t *sctp_mib)
{
int i;
int cnt;
/* Static componets of mib2_sctp_t. */
SET_MIB(sctp_mib->sctpRtoAlgorithm, MIB2_SCTP_RTOALGO_VANJ);
SET_MIB(sctp_mib->sctpRtoMin, sctps->sctps_rto_ming);
SET_MIB(sctp_mib->sctpRtoMax, sctps->sctps_rto_maxg);
SET_MIB(sctp_mib->sctpRtoInitial, sctps->sctps_rto_initialg);
SET_MIB(sctp_mib->sctpMaxAssocs, -1);
SET_MIB(sctp_mib->sctpValCookieLife, sctps->sctps_cookie_life);
SET_MIB(sctp_mib->sctpMaxInitRetr, sctps->sctps_max_init_retr);
/* fixed length structure for IPv4 and IPv6 counters */
SET_MIB(sctp_mib->sctpEntrySize, sizeof (mib2_sctpConnEntry_t));
SET_MIB(sctp_mib->sctpLocalEntrySize,
sizeof (mib2_sctpConnLocalEntry_t));
SET_MIB(sctp_mib->sctpRemoteEntrySize,
sizeof (mib2_sctpConnRemoteEntry_t));
/*
* sctps_sc_cnt may change in the middle of the loop. It is better
* to get its value first.
*/
cnt = sctps->sctps_sc_cnt;
for (i = 0; i < cnt; i++)
sctp_add_mib(&sctps->sctps_sc[i]->sctp_sc_mib, sctp_mib);
}
static void
sctp_add_mib(mib2_sctp_t *from, mib2_sctp_t *to)
{
to->sctpActiveEstab += from->sctpActiveEstab;
to->sctpPassiveEstab += from->sctpPassiveEstab;
to->sctpAborted += from->sctpAborted;
to->sctpShutdowns += from->sctpShutdowns;
to->sctpOutOfBlue += from->sctpOutOfBlue;
to->sctpChecksumError += from->sctpChecksumError;
to->sctpOutCtrlChunks += from->sctpOutCtrlChunks;
to->sctpOutOrderChunks += from->sctpOutOrderChunks;
to->sctpOutUnorderChunks += from->sctpOutUnorderChunks;
to->sctpRetransChunks += from->sctpRetransChunks;
to->sctpOutAck += from->sctpOutAck;
to->sctpOutAckDelayed += from->sctpOutAckDelayed;
to->sctpOutWinUpdate += from->sctpOutWinUpdate;
to->sctpOutFastRetrans += from->sctpOutFastRetrans;
to->sctpOutWinProbe += from->sctpOutWinProbe;
to->sctpInCtrlChunks += from->sctpInCtrlChunks;
to->sctpInOrderChunks += from->sctpInOrderChunks;
to->sctpInUnorderChunks += from->sctpInUnorderChunks;
to->sctpInAck += from->sctpInAck;
to->sctpInDupAck += from->sctpInDupAck;
to->sctpInAckUnsent += from->sctpInAckUnsent;
to->sctpFragUsrMsgs += from->sctpFragUsrMsgs;
to->sctpReasmUsrMsgs += from->sctpReasmUsrMsgs;
to->sctpOutSCTPPkts += from->sctpOutSCTPPkts;
to->sctpInSCTPPkts += from->sctpInSCTPPkts;
to->sctpInInvalidCookie += from->sctpInInvalidCookie;
to->sctpTimRetrans += from->sctpTimRetrans;
to->sctpTimRetransDrop += from->sctpTimRetransDrop;
to->sctpTimHeartBeatProbe += from->sctpTimHeartBeatProbe;
to->sctpTimHeartBeatDrop += from->sctpTimHeartBeatDrop;
to->sctpListenDrop += from->sctpListenDrop;
to->sctpInClosed += from->sctpInClosed;
}