/*
* 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
* 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) 2011, Joyent Inc. All rights reserved.
*/
#include <inet/tcp_impl.h>
#include <inet/tcp_stats.h>
#include <inet/kstatcom.h>
static int tcp_kstat_update(kstat_t *, int);
static int tcp_kstat2_update(kstat_t *, int);
static void tcp_clr_stats(tcp_stat_t *);
/* Translate TCP state to MIB2 TCP state. */
static int
{
return (0);
case TCPS_CLOSED:
case TCPS_IDLE: /* RFC1213 doesn't have analogue for IDLE & BOUND */
case TCPS_BOUND:
return (MIB2_TCP_closed);
case TCPS_LISTEN:
return (MIB2_TCP_listen);
case TCPS_SYN_SENT:
return (MIB2_TCP_synSent);
case TCPS_SYN_RCVD:
return (MIB2_TCP_synReceived);
case TCPS_ESTABLISHED:
return (MIB2_TCP_established);
case TCPS_CLOSE_WAIT:
return (MIB2_TCP_closeWait);
case TCPS_FIN_WAIT_1:
return (MIB2_TCP_finWait1);
case TCPS_CLOSING:
return (MIB2_TCP_closing);
case TCPS_LAST_ACK:
return (MIB2_TCP_lastAck);
case TCPS_FIN_WAIT_2:
return (MIB2_TCP_finWait2);
case TCPS_TIME_WAIT:
return (MIB2_TCP_timeWait);
default:
return (0);
}
}
/*
* Return SNMP stuff in buffer in mpdata.
*/
mblk_t *
{
int i;
int v4_conn_idx;
int v6_conn_idx;
/*
* make a copy of the original message
*/
return (NULL);
}
if (legacy_req) {
} else {
tcp_mib_size = sizeof (mib2_tcp_t);
tce_size = sizeof (mib2_tcpConnEntry_t);
tce6_size = sizeof (mib2_tcp6ConnEntry_t);
}
/* build table of connections -- need count in fixed part */
ispriv =
v4_conn_idx = v6_conn_idx = 0;
for (i = 0; i < CONN_G_HASH_SIZE; i++) {
while ((connp =
continue; /* not in this zone */
tcp->tcp_ibsegs = 0;
tcp->tcp_obsegs = 0;
}
if (connp->conn_anon_mlp) {
}
switch (connp->conn_mac_mode) {
case CONN_MAC_DEFAULT:
break;
case CONN_MAC_AWARE:
break;
case CONN_MAC_IMPLICIT:
break;
}
}
/* Create a message to report on IPv6 entries */
} else {
}
/* Don't want just anybody seeing these... */
if (ispriv) {
} else {
/*
* Netstat, unfortunately, uses this to
* Why not compute the difference only?
*/
}
if (needattr)
}
/*
* Create an IPv4 table entry for IPv4 entries and also
* for IPv6 entries which are bound to in6addr_any
* but don't have IPV6_V6ONLY set.
* (i.e. anything an IPv4 peer could connect to)
*/
!connp->conn_ipv6_v6only &&
} else {
}
/* Don't want just anybody seeing these... */
if (ispriv) {
} else {
/*
* Netstat, unfortunately, uses this to
* to fix?
* Why not compute the difference only?
*/
}
if (needattr)
(void) snmp_append_data2(
&mp_attr_tail, (char *)&mlp,
sizeof (mlp));
}
}
}
/* Fixed length structure for IPv4 and IPv6 counters */
/*
* Synchronize 32- and 64-bit counters. Note that tcpInSegs and
* tcpOutSegs are not updated anywhere in TCP. The new 64 bits
* counters are used. Hence the old counters' values in tcp_sc_mib
* are always 0.
*/
/* table of connections... */
sizeof (struct T_optmgmt_ack)];
qreply(q, mp_conn_ctl);
/* table of MLP attributes... */
sizeof (struct T_optmgmt_ack)];
else
qreply(q, mp_attr_ctl);
/* table of IPv6 connections... */
sizeof (struct T_optmgmt_ack)];
qreply(q, mp6_conn_ctl);
/* table of IPv6 MLP attributes... */
sizeof (struct T_optmgmt_ack)];
else
qreply(q, mp6_attr_ctl);
return (mp2ctl);
}
/* Return 0 if invalid set request, 1 otherwise, including non-tcp requests */
/* ARGSUSED */
int
{
switch (level) {
case MIB2_TCP:
switch (name) {
case 13:
return (0);
/* TODO: delete entry defined by tce */
return (1);
default:
return (0);
}
default:
return (1);
}
}
/*
* TCP Kstats implementation
*/
void *
{
{ "rtoAlgorithm", KSTAT_DATA_INT32, 0 },
{ "rtoMin", KSTAT_DATA_INT32, 0 },
{ "rtoMax", KSTAT_DATA_INT32, 0 },
{ "maxConn", KSTAT_DATA_INT32, 0 },
{ "activeOpens", KSTAT_DATA_UINT32, 0 },
{ "passiveOpens", KSTAT_DATA_UINT32, 0 },
{ "attemptFails", KSTAT_DATA_UINT32, 0 },
{ "estabResets", KSTAT_DATA_UINT32, 0 },
{ "currEstab", KSTAT_DATA_UINT32, 0 },
{ "inSegs", KSTAT_DATA_UINT64, 0 },
{ "outSegs", KSTAT_DATA_UINT64, 0 },
{ "retransSegs", KSTAT_DATA_UINT32, 0 },
{ "connTableSize", KSTAT_DATA_INT32, 0 },
{ "outRsts", KSTAT_DATA_UINT32, 0 },
{ "outDataSegs", KSTAT_DATA_UINT32, 0 },
{ "outDataBytes", KSTAT_DATA_UINT32, 0 },
{ "retransBytes", KSTAT_DATA_UINT32, 0 },
{ "outAck", KSTAT_DATA_UINT32, 0 },
{ "outAckDelayed", KSTAT_DATA_UINT32, 0 },
{ "outUrg", KSTAT_DATA_UINT32, 0 },
{ "outWinUpdate", KSTAT_DATA_UINT32, 0 },
{ "outWinProbe", KSTAT_DATA_UINT32, 0 },
{ "outControl", KSTAT_DATA_UINT32, 0 },
{ "outFastRetrans", KSTAT_DATA_UINT32, 0 },
{ "inAckSegs", KSTAT_DATA_UINT32, 0 },
{ "inAckBytes", KSTAT_DATA_UINT32, 0 },
{ "inDupAck", KSTAT_DATA_UINT32, 0 },
{ "inAckUnsent", KSTAT_DATA_UINT32, 0 },
{ "inDataInorderSegs", KSTAT_DATA_UINT32, 0 },
{ "inDataInorderBytes", KSTAT_DATA_UINT32, 0 },
{ "inDataUnorderSegs", KSTAT_DATA_UINT32, 0 },
{ "inDataUnorderBytes", KSTAT_DATA_UINT32, 0 },
{ "inDataDupSegs", KSTAT_DATA_UINT32, 0 },
{ "inDataDupBytes", KSTAT_DATA_UINT32, 0 },
{ "inDataPartDupSegs", KSTAT_DATA_UINT32, 0 },
{ "inDataPartDupBytes", KSTAT_DATA_UINT32, 0 },
{ "inDataPastWinSegs", KSTAT_DATA_UINT32, 0 },
{ "inDataPastWinBytes", KSTAT_DATA_UINT32, 0 },
{ "inWinProbe", KSTAT_DATA_UINT32, 0 },
{ "inWinUpdate", KSTAT_DATA_UINT32, 0 },
{ "inClosed", KSTAT_DATA_UINT32, 0 },
{ "rttUpdate", KSTAT_DATA_UINT32, 0 },
{ "rttNoUpdate", KSTAT_DATA_UINT32, 0 },
{ "timRetrans", KSTAT_DATA_UINT32, 0 },
{ "timRetransDrop", KSTAT_DATA_UINT32, 0 },
{ "timKeepalive", KSTAT_DATA_UINT32, 0 },
{ "timKeepaliveProbe", KSTAT_DATA_UINT32, 0 },
{ "timKeepaliveDrop", KSTAT_DATA_UINT32, 0 },
{ "listenDrop", KSTAT_DATA_UINT32, 0 },
{ "listenDropQ0", KSTAT_DATA_UINT32, 0 },
{ "halfOpenDrop", KSTAT_DATA_UINT32, 0 },
{ "outSackRetransSegs", KSTAT_DATA_UINT32, 0 },
{ "connTableSize6", KSTAT_DATA_INT32, 0 }
};
return (NULL);
/*
* If this is an exclusive netstack for a local zone, the global zone
* should still be able to read the kstat.
*/
if (stackid != GLOBAL_NETSTACKID)
return (ksp);
}
void
{
}
}
static int
{
int i;
if (rw == KSTAT_WRITE)
return (EACCES);
return (-1);
return (-1);
}
for (i = 0; i < CONN_G_HASH_SIZE; i++) {
while ((connp =
switch (tcp_snmp_state(tcp)) {
case MIB2_TCP_established:
case MIB2_TCP_closeWait:
break;
}
}
}
/* Fixed length structure for IPv4 and IPv6 counters */
return (0);
}
/*
* kstats related to squeues i.e. not per IP instance
*/
void *
{
{ "tcp_timermp_alloced", KSTAT_DATA_UINT64 },
{ "tcp_timermp_allocfail", KSTAT_DATA_UINT64 },
{ "tcp_timermp_allocdblfail", KSTAT_DATA_UINT64 },
{ "tcp_freelist_cleanup", KSTAT_DATA_UINT64 },
};
return (NULL);
return (ksp);
}
void
{
}
}
void *
{
{ "tcp_time_wait_syn_success", KSTAT_DATA_UINT64, 0 },
{ "tcp_clean_death_nondetached", KSTAT_DATA_UINT64, 0 },
{ "tcp_eager_blowoff_q", KSTAT_DATA_UINT64, 0 },
{ "tcp_eager_blowoff_q0", KSTAT_DATA_UINT64, 0 },
{ "tcp_no_listener", KSTAT_DATA_UINT64, 0 },
{ "tcp_listendrop", KSTAT_DATA_UINT64, 0 },
{ "tcp_listendropq0", KSTAT_DATA_UINT64, 0 },
{ "tcp_wsrv_called", KSTAT_DATA_UINT64, 0 },
{ "tcp_flwctl_on", KSTAT_DATA_UINT64, 0 },
{ "tcp_timer_fire_early", KSTAT_DATA_UINT64, 0 },
{ "tcp_timer_fire_miss", KSTAT_DATA_UINT64, 0 },
{ "tcp_zcopy_on", KSTAT_DATA_UINT64, 0 },
{ "tcp_zcopy_off", KSTAT_DATA_UINT64, 0 },
{ "tcp_zcopy_backoff", KSTAT_DATA_UINT64, 0 },
{ "tcp_fusion_flowctl", KSTAT_DATA_UINT64, 0 },
{ "tcp_fusion_backenabled", KSTAT_DATA_UINT64, 0 },
{ "tcp_fusion_urg", KSTAT_DATA_UINT64, 0 },
{ "tcp_fusion_putnext", KSTAT_DATA_UINT64, 0 },
{ "tcp_fusion_unfusable", KSTAT_DATA_UINT64, 0 },
{ "tcp_fusion_aborted", KSTAT_DATA_UINT64, 0 },
{ "tcp_fusion_unqualified", KSTAT_DATA_UINT64, 0 },
{ "tcp_fusion_rrw_busy", KSTAT_DATA_UINT64, 0 },
{ "tcp_fusion_rrw_msgcnt", KSTAT_DATA_UINT64, 0 },
{ "tcp_fusion_rrw_plugged", KSTAT_DATA_UINT64, 0 },
{ "tcp_in_ack_unsent_drop", KSTAT_DATA_UINT64, 0 },
{ "tcp_sock_fallback", KSTAT_DATA_UINT64, 0 },
{ "tcp_lso_enabled", KSTAT_DATA_UINT64, 0 },
{ "tcp_lso_disabled", KSTAT_DATA_UINT64, 0 },
{ "tcp_lso_times", KSTAT_DATA_UINT64, 0 },
{ "tcp_lso_pkt_out", KSTAT_DATA_UINT64, 0 },
{ "tcp_listen_cnt_drop", KSTAT_DATA_UINT64, 0 },
{ "tcp_listen_mem_drop", KSTAT_DATA_UINT64, 0 },
{ "tcp_zwin_mem_drop", KSTAT_DATA_UINT64, 0 },
{ "tcp_zwin_ack_syn", KSTAT_DATA_UINT64, 0 },
{ "tcp_rst_unsent", KSTAT_DATA_UINT64, 0 },
{ "tcp_reclaim_cnt", KSTAT_DATA_UINT64, 0 },
{ "tcp_reass_timeout", KSTAT_DATA_UINT64, 0 },
#ifdef TCP_DEBUG_COUNTER
{ "tcp_time_wait", KSTAT_DATA_UINT64, 0 },
{ "tcp_rput_time_wait", KSTAT_DATA_UINT64, 0 },
{ "tcp_detach_time_wait", KSTAT_DATA_UINT64, 0 },
{ "tcp_timeout_calls", KSTAT_DATA_UINT64, 0 },
{ "tcp_timeout_cached_alloc", KSTAT_DATA_UINT64, 0 },
{ "tcp_timeout_cancel_reqs", KSTAT_DATA_UINT64, 0 },
{ "tcp_timeout_canceled", KSTAT_DATA_UINT64, 0 },
{ "tcp_timermp_freed", KSTAT_DATA_UINT64, 0 },
{ "tcp_push_timer_cnt", KSTAT_DATA_UINT64, 0 },
{ "tcp_ack_timer_cnt", KSTAT_DATA_UINT64, 0 },
#endif
};
stackid);
return (NULL);
/*
* If this is an exclusive netstack for a local zone, the global zone
* should still be able to read the kstat.
*/
if (stackid != GLOBAL_NETSTACKID)
return (ksp);
}
void
{
}
}
/*
* Sum up all per CPU tcp_stat_t kstat counters.
*/
static int
{
int i;
int cnt;
if (rw == KSTAT_WRITE)
return (EACCES);
return (-1);
return (-1);
}
/*
* tcps_sc_cnt may change in the middle of the loop. It is better
* to get its value first.
*/
for (i = 0; i < cnt; i++)
return (0);
}
/*
* To add stats from one mib2_tcp_t to another. Static fields are not added.
* The caller should set them up propertly.
*/
static void
{
}
/*
* To sum up all MIB2 stats for a tcp_stack_t from all per CPU stats. The
* caller should initialize the target mib2_tcp_t properly as this function
* just adds up all the per CPU stats.
*/
static void
{
int i;
int cnt;
/*
* tcps_sc_cnt may change in the middle of the loop. It is better
* to get its value first.
*/
for (i = 0; i < cnt; i++)
}
/*
* To set all tcp_stat_t counters to 0.
*/
static void
{
#ifdef TCP_DEBUG_COUNTER
#endif
}
/*
* To add counters from the per CPU tcp_stat_counter_t to the stack
* tcp_stat_t.
*/
static void
{
#ifdef TCP_DEBUG_COUNTER
#endif
}