/* * 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) 2010, Oracle and/or its affiliates. All rights reserved. * Copyright 2019 OmniOS Community Edition (OmniOSce) Association. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include static int udp_kstat_update(kstat_t *, int); static int udp_kstat2_update(kstat_t *, int); static void udp_sum_mib(udp_stack_t *, mib2_udp_t *); static void udp_clr_stats(udp_stat_t *); static void udp_add_stats(udp_stat_counter_t *, udp_stat_t *); static void udp_add_mib(mib2_udp_t *, mib2_udp_t *); /* * return SNMP stuff in buffer in mpdata. We don't hold any lock and report * information that can be changing beneath us. */ mblk_t * udp_snmp_get(queue_t *q, mblk_t *mpctl, boolean_t legacy_req) { mblk_t *mpdata; mblk_t *mp_conn_ctl; mblk_t *mp_attr_ctl; mblk_t *mp_info_ctl; mblk_t *mp6_conn_ctl; mblk_t *mp6_attr_ctl; mblk_t *mp6_info_ctl; mblk_t *mp_conn_tail; mblk_t *mp_attr_tail; mblk_t *mp_info_tail; mblk_t *mp6_conn_tail; mblk_t *mp6_attr_tail; mblk_t *mp6_info_tail; struct opthdr *optp; mib2_udpEntry_t ude; mib2_udp6Entry_t ude6; mib2_transportMLPEntry_t mlp; mib2_socketInfoEntry_t *sie, psie; int state; zoneid_t zoneid; int i; connf_t *connfp; conn_t *connp = Q_TO_CONN(q); int v4_conn_idx; int v6_conn_idx; boolean_t needattr; udp_t *udp; ip_stack_t *ipst = connp->conn_netstack->netstack_ip; udp_stack_t *us = connp->conn_netstack->netstack_udp; mblk_t *mp2ctl; mib2_udp_t udp_mib; size_t udp_mib_size, ude_size, ude6_size; /* * make a copy of the original message */ mp2ctl = copymsg(mpctl); mp_conn_ctl = mp_attr_ctl = mp6_conn_ctl = NULL; if (mpctl == NULL || (mpdata = mpctl->b_cont) == NULL || (mp_conn_ctl = copymsg(mpctl)) == NULL || (mp_attr_ctl = copymsg(mpctl)) == NULL || (mp_info_ctl = copymsg(mpctl)) == NULL || (mp6_conn_ctl = copymsg(mpctl)) == NULL || (mp6_attr_ctl = copymsg(mpctl)) == NULL || (mp6_info_ctl = copymsg(mpctl)) == NULL) { freemsg(mp_conn_ctl); freemsg(mp_attr_ctl); freemsg(mp_info_ctl); freemsg(mp6_conn_ctl); freemsg(mp6_attr_ctl); freemsg(mp6_info_ctl); freemsg(mpctl); freemsg(mp2ctl); return (0); } zoneid = connp->conn_zoneid; if (legacy_req) { udp_mib_size = LEGACY_MIB_SIZE(&udp_mib, mib2_udp_t); ude_size = LEGACY_MIB_SIZE(&ude, mib2_udpEntry_t); ude6_size = LEGACY_MIB_SIZE(&ude6, mib2_udp6Entry_t); } else { udp_mib_size = sizeof (mib2_udp_t); ude_size = sizeof (mib2_udpEntry_t); ude6_size = sizeof (mib2_udp6Entry_t); } bzero(&udp_mib, sizeof (udp_mib)); /* fixed length structure for IPv4 and IPv6 counters */ SET_MIB(udp_mib.udpEntrySize, ude_size); SET_MIB(udp_mib.udp6EntrySize, ude6_size); udp_sum_mib(us, &udp_mib); /* * Synchronize 32- and 64-bit counters. Note that udpInDatagrams and * udpOutDatagrams are not updated anywhere in UDP. The new 64 bits * counters are used. Hence the old counters' values in us_sc_mib * are always 0. */ SYNC32_MIB(&udp_mib, udpInDatagrams, udpHCInDatagrams); SYNC32_MIB(&udp_mib, udpOutDatagrams, udpHCOutDatagrams); optp = (struct opthdr *)&mpctl->b_rptr[sizeof (struct T_optmgmt_ack)]; optp->level = MIB2_UDP; optp->name = 0; (void) snmp_append_data(mpdata, (char *)&udp_mib, udp_mib_size); optp->len = msgdsize(mpdata); qreply(q, mpctl); mp_conn_tail = mp_attr_tail = mp6_conn_tail = mp6_attr_tail = NULL; mp_info_tail = mp6_info_tail = NULL; v4_conn_idx = v6_conn_idx = 0; for (i = 0; i < CONN_G_HASH_SIZE; i++) { connfp = &ipst->ips_ipcl_globalhash_fanout[i]; connp = NULL; while ((connp = ipcl_get_next_conn(connfp, connp, IPCL_UDPCONN))) { sonode_t *so = (sonode_t *)connp->conn_upper_handle; udp = connp->conn_udp; if (zoneid != connp->conn_zoneid) continue; /* * Note that the port numbers are sent in * host byte order */ if (udp->udp_state == TS_UNBND) state = MIB2_UDP_unbound; else if (udp->udp_state == TS_IDLE) state = MIB2_UDP_idle; else if (udp->udp_state == TS_DATA_XFER) state = MIB2_UDP_connected; else state = MIB2_UDP_unknown; 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; } mutex_enter(&connp->conn_lock); if (udp->udp_state == TS_DATA_XFER && 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; } mutex_exit(&connp->conn_lock); /* * Create an IPv4 table entry for IPv4 entries and also * any IPv6 entries which are bound to in6addr_any * (i.e. anything a IPv4 peer could connect/send to). */ if (connp->conn_ipversion == IPV4_VERSION || (udp->udp_state <= TS_IDLE && IN6_IS_ADDR_UNSPECIFIED(&connp->conn_laddr_v6))) { ude.udpEntryInfo.ue_state = state; /* * If in6addr_any this will set it to * INADDR_ANY */ ude.udpLocalAddress = connp->conn_laddr_v4; ude.udpLocalPort = ntohs(connp->conn_lport); if (udp->udp_state == TS_DATA_XFER) { /* * Can potentially get here for * v6 socket if another process * (say, ping) has just done a * sendto(), changing the state * from the TS_IDLE above to * TS_DATA_XFER by the time we hit * this part of the code. */ ude.udpEntryInfo.ue_RemoteAddress = connp->conn_faddr_v4; ude.udpEntryInfo.ue_RemotePort = ntohs(connp->conn_fport); } else { ude.udpEntryInfo.ue_RemoteAddress = 0; ude.udpEntryInfo.ue_RemotePort = 0; } /* * We make the assumption that all udp_t * structs will be created within an address * region no larger than 32-bits. */ ude.udpInstance = (uint32_t)(uintptr_t)udp; ude.udpCreationProcess = (connp->conn_cpid < 0) ? MIB2_UNKNOWN_PROCESS : connp->conn_cpid; ude.udpCreationTime = connp->conn_open_time; (void) snmp_append_data2(mp_conn_ctl->b_cont, &mp_conn_tail, (char *)&ude, ude_size); if (needattr) { mlp.tme_connidx = v4_conn_idx; (void) snmp_append_data2( mp_attr_ctl->b_cont, &mp_attr_tail, (char *)&mlp, sizeof (mlp)); } if ((sie = conn_get_socket_info(connp, &psie)) != NULL) { sie->sie_connidx = v4_conn_idx; if (connp->conn_ipversion == IPV6_VERSION) sie->sie_flags |= MIB2_SOCKINFO_IPV6; (void) snmp_append_data2( mp_info_ctl->b_cont, &mp_info_tail, (char *)sie, sizeof (*sie)); } v4_conn_idx++; } if (connp->conn_ipversion == IPV6_VERSION) { ude6.udp6EntryInfo.ue_state = state; ude6.udp6LocalAddress = connp->conn_laddr_v6; ude6.udp6LocalPort = ntohs(connp->conn_lport); mutex_enter(&connp->conn_lock); if (connp->conn_ixa->ixa_flags & IXAF_SCOPEID_SET) { ude6.udp6IfIndex = connp->conn_ixa->ixa_scopeid; } else { ude6.udp6IfIndex = connp->conn_bound_if; } mutex_exit(&connp->conn_lock); if (udp->udp_state == TS_DATA_XFER) { ude6.udp6EntryInfo.ue_RemoteAddress = connp->conn_faddr_v6; ude6.udp6EntryInfo.ue_RemotePort = ntohs(connp->conn_fport); } else { ude6.udp6EntryInfo.ue_RemoteAddress = sin6_null.sin6_addr; ude6.udp6EntryInfo.ue_RemotePort = 0; } /* * We make the assumption that all udp_t * structs will be created within an address * region no larger than 32-bits. */ ude6.udp6Instance = (uint32_t)(uintptr_t)udp; ude6.udp6CreationProcess = (connp->conn_cpid < 0) ? MIB2_UNKNOWN_PROCESS : connp->conn_cpid; ude6.udp6CreationTime = connp->conn_open_time; (void) snmp_append_data2(mp6_conn_ctl->b_cont, &mp6_conn_tail, (char *)&ude6, ude6_size); if (needattr) { mlp.tme_connidx = v6_conn_idx; (void) snmp_append_data2( mp6_attr_ctl->b_cont, &mp6_attr_tail, (char *)&mlp, sizeof (mlp)); } if ((sie = conn_get_socket_info(connp, &psie)) != NULL) { sie->sie_connidx = v6_conn_idx; (void) snmp_append_data2( mp6_info_ctl->b_cont, &mp6_info_tail, (char *)sie, sizeof (*sie)); } v6_conn_idx++; } } } /* IPv4 UDP endpoints */ optp = (struct opthdr *)&mp_conn_ctl->b_rptr[ sizeof (struct T_optmgmt_ack)]; optp->level = MIB2_UDP; optp->name = MIB2_UDP_ENTRY; optp->len = msgdsize(mp_conn_ctl->b_cont); qreply(q, mp_conn_ctl); /* table of MLP attributes... */ optp = (struct opthdr *)&mp_attr_ctl->b_rptr[ sizeof (struct T_optmgmt_ack)]; optp->level = MIB2_UDP; 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); /* table of socket info... */ optp = (struct opthdr *)&mp_info_ctl->b_rptr[ sizeof (struct T_optmgmt_ack)]; optp->level = MIB2_UDP; optp->name = EXPER_SOCK_INFO; optp->len = msgdsize(mp_info_ctl->b_cont); if (optp->len == 0) freemsg(mp_info_ctl); else qreply(q, mp_info_ctl); /* IPv6 UDP endpoints */ optp = (struct opthdr *)&mp6_conn_ctl->b_rptr[ sizeof (struct T_optmgmt_ack)]; optp->level = MIB2_UDP6; optp->name = MIB2_UDP6_ENTRY; optp->len = msgdsize(mp6_conn_ctl->b_cont); qreply(q, mp6_conn_ctl); /* table of MLP attributes... */ optp = (struct opthdr *)&mp6_attr_ctl->b_rptr[ sizeof (struct T_optmgmt_ack)]; optp->level = MIB2_UDP6; 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); /* table of socket info... */ optp = (struct opthdr *)&mp6_info_ctl->b_rptr[ sizeof (struct T_optmgmt_ack)]; optp->level = MIB2_UDP6; optp->name = EXPER_SOCK_INFO; optp->len = msgdsize(mp6_info_ctl->b_cont); if (optp->len == 0) freemsg(mp6_info_ctl); else qreply(q, mp6_info_ctl); return (mp2ctl); } /* * Return 0 if invalid set request, 1 otherwise, including non-udp requests. * NOTE: Per MIB-II, UDP has no writable data. * TODO: If this ever actually tries to set anything, it needs to be * to do the appropriate locking. */ /* ARGSUSED */ int udp_snmp_set(queue_t *q, t_scalar_t level, t_scalar_t name, uchar_t *ptr, int len) { switch (level) { case MIB2_UDP: return (0); default: return (1); } } void udp_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); } } /* * To add stats from one mib2_udp_t to another. Static fields are not added. * The caller should set them up propertly. */ static void udp_add_mib(mib2_udp_t *from, mib2_udp_t *to) { to->udpHCInDatagrams += from->udpHCInDatagrams; to->udpInErrors += from->udpInErrors; to->udpHCOutDatagrams += from->udpHCOutDatagrams; to->udpOutErrors += from->udpOutErrors; } void * udp_kstat2_init(netstackid_t stackid) { kstat_t *ksp; udp_stat_t template = { { "udp_sock_fallback", KSTAT_DATA_UINT64 }, { "udp_out_opt", KSTAT_DATA_UINT64 }, { "udp_out_err_notconn", KSTAT_DATA_UINT64 }, { "udp_out_err_output", KSTAT_DATA_UINT64 }, { "udp_out_err_tudr", KSTAT_DATA_UINT64 }, #ifdef DEBUG { "udp_data_conn", KSTAT_DATA_UINT64 }, { "udp_data_notconn", KSTAT_DATA_UINT64 }, { "udp_out_lastdst", KSTAT_DATA_UINT64 }, { "udp_out_diffdst", KSTAT_DATA_UINT64 }, { "udp_out_ipv6", KSTAT_DATA_UINT64 }, { "udp_out_mapped", KSTAT_DATA_UINT64 }, { "udp_out_ipv4", KSTAT_DATA_UINT64 }, #endif }; ksp = kstat_create_netstack(UDP_MOD_NAME, 0, "udpstat", "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_update = udp_kstat2_update; ksp->ks_private = (void *)(uintptr_t)stackid; kstat_install(ksp); return (ksp); } void udp_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); } } /* * To copy counters from the per CPU udpp_stat_counter_t to the stack * udp_stat_t. */ static void udp_add_stats(udp_stat_counter_t *from, udp_stat_t *to) { to->udp_sock_fallback.value.ui64 += from->udp_sock_fallback; to->udp_out_opt.value.ui64 += from->udp_out_opt; to->udp_out_err_notconn.value.ui64 += from->udp_out_err_notconn; to->udp_out_err_output.value.ui64 += from->udp_out_err_output; to->udp_out_err_tudr.value.ui64 += from->udp_out_err_tudr; #ifdef DEBUG to->udp_data_conn.value.ui64 += from->udp_data_conn; to->udp_data_notconn.value.ui64 += from->udp_data_notconn; to->udp_out_lastdst.value.ui64 += from->udp_out_lastdst; to->udp_out_diffdst.value.ui64 += from->udp_out_diffdst; to->udp_out_ipv6.value.ui64 += from->udp_out_ipv6; to->udp_out_mapped.value.ui64 += from->udp_out_mapped; to->udp_out_ipv4.value.ui64 += from->udp_out_ipv4; #endif } /* * To set all udp_stat_t counters to 0. */ static void udp_clr_stats(udp_stat_t *stats) { stats->udp_sock_fallback.value.ui64 = 0; stats->udp_out_opt.value.ui64 = 0; stats->udp_out_err_notconn.value.ui64 = 0; stats->udp_out_err_output.value.ui64 = 0; stats->udp_out_err_tudr.value.ui64 = 0; #ifdef DEBUG stats->udp_data_conn.value.ui64 = 0; stats->udp_data_notconn.value.ui64 = 0; stats->udp_out_lastdst.value.ui64 = 0; stats->udp_out_diffdst.value.ui64 = 0; stats->udp_out_ipv6.value.ui64 = 0; stats->udp_out_mapped.value.ui64 = 0; stats->udp_out_ipv4.value.ui64 = 0; #endif } int udp_kstat2_update(kstat_t *kp, int rw) { udp_stat_t *stats; netstackid_t stackid = (netstackid_t)(uintptr_t)kp->ks_private; netstack_t *ns; udp_stack_t *us; int i; int cnt; if (rw == KSTAT_WRITE) return (EACCES); ns = netstack_find_by_stackid(stackid); if (ns == NULL) return (-1); us = ns->netstack_udp; if (us == NULL) { netstack_rele(ns); return (-1); } stats = (udp_stat_t *)kp->ks_data; udp_clr_stats(stats); cnt = us->us_sc_cnt; for (i = 0; i < cnt; i++) udp_add_stats(&us->us_sc[i]->udp_sc_stats, stats); netstack_rele(ns); return (0); } void * udp_kstat_init(netstackid_t stackid) { kstat_t *ksp; udp_named_kstat_t template = { { "inDatagrams", KSTAT_DATA_UINT64, 0 }, { "inErrors", KSTAT_DATA_UINT32, 0 }, { "outDatagrams", KSTAT_DATA_UINT64, 0 }, { "entrySize", KSTAT_DATA_INT32, 0 }, { "entry6Size", KSTAT_DATA_INT32, 0 }, { "outErrors", KSTAT_DATA_UINT32, 0 }, }; ksp = kstat_create_netstack(UDP_MOD_NAME, 0, UDP_MOD_NAME, "mib2", KSTAT_TYPE_NAMED, NUM_OF_FIELDS(udp_named_kstat_t), 0, stackid); if (ksp == NULL) return (NULL); template.entrySize.value.ui32 = sizeof (mib2_udpEntry_t); template.entry6Size.value.ui32 = sizeof (mib2_udp6Entry_t); bcopy(&template, ksp->ks_data, sizeof (template)); ksp->ks_update = udp_kstat_update; ksp->ks_private = (void *)(uintptr_t)stackid; kstat_install(ksp); return (ksp); } /* * To sum up all MIB2 stats for a udp_stack_t from all per CPU stats. The * caller should initialize the target mib2_udp_t properly as this function * just adds up all the per CPU stats. */ static void udp_sum_mib(udp_stack_t *us, mib2_udp_t *udp_mib) { int i; int cnt; cnt = us->us_sc_cnt; for (i = 0; i < cnt; i++) udp_add_mib(&us->us_sc[i]->udp_sc_mib, udp_mib); } static int udp_kstat_update(kstat_t *kp, int rw) { udp_named_kstat_t *udpkp; netstackid_t stackid = (netstackid_t)(uintptr_t)kp->ks_private; netstack_t *ns; udp_stack_t *us; mib2_udp_t udp_mib; if (rw == KSTAT_WRITE) return (EACCES); ns = netstack_find_by_stackid(stackid); if (ns == NULL) return (-1); us = ns->netstack_udp; if (us == NULL) { netstack_rele(ns); return (-1); } udpkp = (udp_named_kstat_t *)kp->ks_data; bzero(&udp_mib, sizeof (udp_mib)); udp_sum_mib(us, &udp_mib); udpkp->inDatagrams.value.ui64 = udp_mib.udpHCInDatagrams; udpkp->inErrors.value.ui32 = udp_mib.udpInErrors; udpkp->outDatagrams.value.ui64 = udp_mib.udpHCOutDatagrams; udpkp->outErrors.value.ui32 = udp_mib.udpOutErrors; netstack_rele(ns); return (0); }