/* * 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. */ /* * Copyright (c) 2018, Joyent, Inc. * Copyright 2019 OmniOS Community Edition (OmniOSce) Association. */ #include #include #include #define _SUN_TPI_VERSION 2 #include #include #include #include #include #include #include #include #include #include #include #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->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; mblk_t *mp_info_ctl = NULL; mblk_t *mp_info_data; mblk_t *mp_info_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; mib2_socketInfoEntry_t *sie, psie; 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); mp_info_ctl = copymsg(mpctl); mpdata = mpctl->b_cont; if (mp_conn_ctl == NULL || mp_local_ctl == NULL || mp_rem_ctl == NULL || mp_attr_ctl == NULL || mp_info_ctl == NULL || mpdata == NULL) { freemsg(mp_info_ctl); 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; mp_info_data = mp_info_ctl->b_cont; bzero(&sctp_mib, sizeof (sctp_mib)); /* hostname address parameters are not supported in illumos */ 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)); if ((sie = conn_get_socket_info(connp, &psie)) != NULL) { sie->sie_connidx = idx; (void) snmp_append_data2( mp_info_ctl->b_cont, &mp_info_tail, (char *)sie, sizeof (*sie)); } 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); /* table of socket info... */ optp = (struct opthdr *)&mp_info_ctl->b_rptr[ sizeof (struct T_optmgmt_ack)]; optp->level = MIB2_SCTP; optp->name = EXPER_SOCK_INFO; optp->len = msgdsize(mp_info_data); if (optp->len == 0) freemsg(mp_info_ctl); else qreply(q, mp_info_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; }