| |
| /* |
| * TCP MIB group implementation - tcp.c |
| * |
| */ |
| |
| #include <config.h> |
| #include <unistd.h> |
| #include "mib_module_config.h" |
| #if TIME_WITH_SYS_TIME |
| # ifdef WIN32 |
| # include <sys/timeb.h> |
| # else |
| # include <sys/time.h> |
| # endif |
| # include <time.h> |
| #else |
| # if HAVE_SYS_TIME_H |
| # include <sys/time.h> |
| # else |
| # include <time.h> |
| # endif |
| #endif |
| |
| #include <sys/types.h> |
| #include <sys/param.h> |
| #include <sys/socket.h> |
| #if HAVE_SYS_PROTOSW_H |
| #include <sys/protosw.h> |
| #endif |
| |
| #if HAVE_SYS_SYSMP_H |
| #include <sys/sysmp.h> |
| #endif |
| #if defined(IFNET_NEEDS_KERNEL) && !defined(_KERNEL) |
| #define _KERNEL 1 |
| #define _I_DEFINED_KERNEL |
| #endif |
| #include <net/if.h> |
| #if HAVE_NET_IF_VAR_H |
| #include <net/if_var.h> |
| #endif |
| #ifdef _I_DEFINED_KERNEL |
| #undef _KERNEL |
| #endif |
| #if HAVE_SYS_STREAM_H |
| #include <sys/stream.h> |
| #endif |
| #ifdef HAVE_NET_ROUTE_H |
| #include <net/route.h> |
| #endif |
| #if HAVE_NETINET_IN_H |
| #include <netinet/in.h> |
| #endif |
| #include <netinet/in_systm.h> |
| #include <netinet/ip.h> |
| #if HAVE_SYS_QUEUE_H |
| #include <sys/queue.h> |
| #endif |
| #if HAVE_NETINET_IP_VAR_H |
| #include <netinet/ip_var.h> |
| #endif |
| #if HAVE_SYS_SOCKETVAR_H |
| #include <sys/socketvar.h> |
| #endif |
| #if HAVE_NETINET_IN_PCB_H |
| #include <netinet/in_pcb.h> |
| #endif |
| #if HAVE_INET_MIB2_H |
| #include <inet/mib2.h> |
| #endif |
| #if STDC_HEADERS |
| #include <string.h> |
| #include <stdlib.h> |
| #else |
| #if HAVE_STDLIB_H |
| #include <stdlib.h> |
| #endif |
| #endif |
| #ifdef solaris2 |
| #include "kernel_sunos5.h" |
| #else |
| #include "kernel.h" |
| #endif |
| #include "../mibincl.h" |
| #include "../../../snmplib/system.h" |
| #if HAVE_SYS_SYSCTL_H |
| #include <sys/sysctl.h> |
| #endif |
| #if HAVE_ARPA_INET_H |
| #include <arpa/inet.h> |
| #endif |
| |
| #if defined(osf4) || defined(aix4) || defined(hpux10) |
| /* these are undefed to remove a stupid warning on osf compilers |
| because they get redefined with a slightly different notation of the |
| same value. -- Wes */ |
| #undef TCP_NODELAY |
| #undef TCP_MAXSEG |
| #endif |
| #include <netinet/tcp.h> |
| #if HAVE_NETINET_TCPIP_H |
| #include <netinet/tcpip.h> |
| #endif |
| #if HAVE_NETINET_TCP_TIMER_H |
| #include <netinet/tcp_timer.h> |
| #endif |
| #if HAVE_NETINET_TCP_VAR_H |
| #include <netinet/tcp_var.h> |
| #endif |
| #if HAVE_NETINET_TCP_FSM_H |
| #include <netinet/tcp_fsm.h> |
| #endif |
| #if HAVE_SYS_TCPIPSTATS_H |
| #include <sys/tcpipstats.h> |
| #endif |
| |
| #include "auto_nlist.h" |
| |
| #ifdef hpux |
| #include <sys/mib.h> |
| #include <netinet/mib_kern.h> |
| #endif /* hpux */ |
| |
| /* #include "../common_header.h" */ |
| #include "tcp.h" |
| |
| /********************* |
| * |
| * Kernel & interface information, |
| * and internal forward declarations |
| * |
| *********************/ |
| |
| #ifdef linux |
| static void linux_read_tcp_stat (struct tcp_mib *); |
| #endif |
| |
| /********************* |
| * |
| * Initialisation & common implementation functions |
| * |
| *********************/ |
| |
| struct variable13 tcp_variables[] = { |
| {TCPRTOALGORITHM, ASN_INTEGER, RONLY, var_tcp, 1, {1}}, |
| {TCPRTOMIN, ASN_INTEGER, RONLY, var_tcp, 1, {2}}, |
| #ifndef sunV3 |
| {TCPRTOMAX, ASN_INTEGER, RONLY, var_tcp, 1, {3}}, |
| #endif |
| {TCPMAXCONN, ASN_INTEGER, RONLY, var_tcp, 1, {4}}, |
| #ifndef sunV3 |
| {TCPACTIVEOPENS, ASN_COUNTER, RONLY, var_tcp, 1, {5}}, |
| {TCPPASSIVEOPENS, ASN_COUNTER, RONLY, var_tcp, 1, {6}}, |
| {TCPATTEMPTFAILS, ASN_COUNTER, RONLY, var_tcp, 1, {7}}, |
| {TCPESTABRESETS, ASN_COUNTER, RONLY, var_tcp, 1, {8}}, |
| #endif |
| { TCPCURRESTAB, ASN_GAUGE, RONLY, var_tcp, 1, {9}}, |
| #ifndef sunV3 |
| {TCPINSEGS, ASN_COUNTER, RONLY, var_tcp, 1, {10}}, |
| {TCPOUTSEGS, ASN_COUNTER, RONLY, var_tcp, 1, {11} }, |
| {TCPRETRANSSEGS, ASN_COUNTER, RONLY, var_tcp, 1, {12}}, |
| #endif |
| {TCPCONNSTATE, ASN_INTEGER, RONLY, var_tcpEntry, 3, {13, 1, 1}}, |
| {TCPCONNLOCALADDRESS, ASN_IPADDRESS, RONLY, var_tcpEntry, 3, {13, 1, 2}}, |
| {TCPCONNLOCALPORT, ASN_INTEGER, RONLY, var_tcpEntry, 3, {13, 1, 3}}, |
| {TCPCONNREMADDRESS, ASN_IPADDRESS, RONLY, var_tcpEntry, 3, {13, 1, 4}}, |
| {TCPCONNREMPORT, ASN_INTEGER, RONLY, var_tcpEntry, 3, {13, 1, 5}}, |
| {TCPINERRS, ASN_COUNTER, RONLY, var_tcp, 1, {14}}, |
| {TCPOUTRSTS, ASN_COUNTER, RONLY, var_tcp, 1, {15}} |
| }; |
| |
| /* Define the OID pointer to the top of the mib tree that we're |
| registering underneath */ |
| oid tcp_variables_oid[] = { 1,3,6,1,2,1,6 }; |
| |
| void init_tcp(void) |
| { |
| /* register ourselves with the agent to handle our mib tree */ |
| REGISTER_MIB("mibII/tcp", tcp_variables, variable13, tcp_variables_oid); |
| |
| #ifdef TCPSTAT_SYMBOL |
| auto_nlist( TCPSTAT_SYMBOL,0,0 ); |
| #endif |
| #ifdef TCP_SYMBOL |
| auto_nlist( TCP_SYMBOL,0,0 ); |
| #endif |
| |
| } |
| |
| /* |
| header_tcp(... |
| Arguments: |
| vp IN - pointer to variable entry that points here |
| name IN/OUT - IN/name requested, OUT/name found |
| length IN/OUT - length of IN/OUT oid's |
| exact IN - TRUE if an exact match was requested |
| var_len OUT - length of variable or 0 if function returned |
| write_method |
| |
| */ |
| |
| static int |
| header_tcp(struct variable *vp, |
| oid *name, |
| size_t *length, |
| int exact, |
| size_t *var_len, |
| WriteMethod **write_method) |
| { |
| #define TCP_NAME_LENGTH 8 |
| oid newname[MAX_OID_LEN]; |
| int result; |
| char c_oid[SPRINT_MAX_LEN]; |
| |
| if (snmp_get_do_debugging()) { |
| sprint_objid (c_oid, name, *length); |
| DEBUGMSGTL(("mibII/tcp", "var_tcp: %s %d\n", c_oid, exact)); |
| } |
| |
| memcpy( (char *)newname,(char *)vp->name, vp->namelen * sizeof(oid)); |
| newname[TCP_NAME_LENGTH] = 0; |
| result = snmp_oid_compare(name, *length, newname, vp->namelen + 1); |
| if ((exact && (result != 0)) || (!exact && (result >= 0))) |
| return(MATCH_FAILED); |
| memcpy( (char *)name,(char *)newname, (vp->namelen + 1) * sizeof(oid)); |
| *length = vp->namelen + 1; |
| |
| *write_method = 0; |
| *var_len = sizeof(long); /* default to 'long' results */ |
| return(MATCH_SUCCEEDED); |
| } |
| |
| /********************* |
| * |
| * System specific implementation functions |
| * |
| *********************/ |
| |
| #ifndef solaris2 |
| |
| #ifdef HAVE_SYS_TCPIPSTATS_H |
| |
| u_char * |
| var_tcp(struct variable *vp, |
| oid *name, |
| size_t *length, |
| int exact, |
| size_t *var_len, |
| WriteMethod **write_method) |
| { |
| static struct kna tcpipstats; |
| |
| /* |
| * Allow for a kernel w/o TCP |
| */ |
| |
| if (header_tcp(vp, name, length, exact, var_len, write_method) == MATCH_FAILED ) |
| return NULL; |
| |
| /* |
| * Get the TCP statistics from the kernel... |
| */ |
| |
| if (sysmp (MP_SAGET, MPSA_TCPIPSTATS, &tcpipstats, sizeof tcpipstats) == -1) { |
| perror ("sysmp(MP_SAGET)(MPSA_TCPIPSTATS)"); |
| } |
| #define tcpstat tcpipstats.tcpstat |
| |
| switch (vp->magic){ |
| case TCPRTOALGORITHM: |
| #ifndef linux |
| long_return = 4; /* Van Jacobsen's algorithm *//* XXX */ |
| #else |
| if (! tcpstat.TcpRtoAlgorithm) { |
| /* 0 is illegal: assume `other' algorithm: */ |
| long_return = 1; |
| return (u_char *) &long_return; |
| } |
| return (u_char *) &tcpstat.TcpRtoAlgorithm; |
| #endif |
| return (u_char *) &long_return; |
| case TCPRTOMIN: |
| #ifndef linux |
| long_return = TCPTV_MIN / PR_SLOWHZ * 1000; |
| return (u_char *) &long_return; |
| #else |
| return (u_char *) &tcpstat.TcpRtoMin; |
| #endif |
| case TCPRTOMAX: |
| #ifndef linux |
| long_return = TCPTV_REXMTMAX / PR_SLOWHZ * 1000; |
| return (u_char *) &long_return; |
| #else |
| return (u_char *) &tcpstat.TcpRtoMax; |
| #endif |
| case TCPMAXCONN: |
| #ifndef linux |
| long_return = -1; |
| return (u_char *) &long_return; |
| #else |
| return (u_char *) &tcpstat.TcpMaxConn; |
| #endif |
| case TCPACTIVEOPENS: |
| long_return = tcpstat.tcps_connattempt; |
| return (u_char *) &long_return; |
| case TCPPASSIVEOPENS: |
| long_return = tcpstat.tcps_accepts; |
| return (u_char *) &long_return; |
| case TCPATTEMPTFAILS: |
| #ifdef hpux |
| long_return = MIB_tcpcounter[7]; |
| #else |
| long_return = tcpstat.tcps_conndrops; /* XXX */ |
| #endif |
| return (u_char *) &long_return; |
| case TCPESTABRESETS: |
| #ifdef hpux |
| long_return = MIB_tcpcounter[8]; |
| #else |
| long_return = tcpstat.tcps_drops; /* XXX */ |
| #endif |
| return (u_char *) &long_return; |
| /* |
| * NB: tcps_drops is actually the sum of the two MIB |
| * counters tcpAttemptFails and tcpEstabResets. |
| */ |
| case TCPCURRESTAB: |
| #ifndef linux |
| long_return = TCP_Count_Connections(); |
| return (u_char *) &long_return; |
| #else |
| return (u_char *) &tcpstat.TcpCurrEstab; |
| #endif |
| case TCPINSEGS: |
| long_return = tcpstat.tcps_rcvtotal; |
| return (u_char *) &long_return; |
| case TCPOUTSEGS: |
| long_return = tcpstat.tcps_sndtotal |
| - tcpstat.tcps_sndrexmitpack; |
| /* |
| * RFC 1213 defines this as the number of segments sent |
| * "excluding those containing only retransmitted octets" |
| */ |
| return (u_char *) &long_return; |
| case TCPRETRANSSEGS: |
| long_return = tcpstat.tcps_sndrexmitpack; |
| return (u_char *) &long_return; |
| #ifndef linux |
| case TCPINERRS: |
| long_return = tcpstat.tcps_rcvbadsum + tcpstat.tcps_rcvbadoff |
| #ifdef STRUCT_TCPSTAT_HAS_TCPS_RCVMEMDROP |
| + tcpstat.tcps_rcvmemdrop |
| #endif |
| + tcpstat.tcps_rcvshort; |
| return (u_char *) &long_return; |
| case TCPOUTRSTS: |
| long_return = tcpstat.tcps_sndctrl - tcpstat.tcps_closed; |
| return (u_char *) &long_return; |
| #endif /* linux */ |
| default: |
| ERROR_MSG(""); |
| } |
| return NULL; |
| } |
| |
| #else /* not HAVE_SYS_TCPIPSTATS_H */ |
| |
| u_char * |
| var_tcp(struct variable *vp, |
| oid *name, |
| size_t *length, |
| int exact, |
| size_t *var_len, |
| WriteMethod **write_method) |
| { |
| static struct tcpstat tcpstat; |
| #ifdef hpux |
| static counter MIB_tcpcounter[MIB_tcpMAXCTR+1]; |
| #endif |
| |
| /* |
| * Allow for a kernel w/o TCP |
| */ |
| #ifndef linux |
| #ifndef TCPSTAT_SYMBOL |
| return NULL; |
| #else |
| if (auto_nlist_value(TCPSTAT_SYMBOL) == -1) return(NULL); |
| #endif |
| #endif |
| |
| if (header_tcp(vp, name, length, exact, var_len, write_method) == MATCH_FAILED ) |
| return NULL; |
| |
| /* |
| * Get the TCP statistics from the kernel... |
| */ |
| #if !defined(CAN_USE_SYSCTL) || !defined(TCPCTL_STATS) |
| #ifndef linux |
| auto_nlist(TCPSTAT_SYMBOL, (char *)&tcpstat, sizeof (tcpstat)); |
| #ifdef MIB_TCPCOUNTER_SYMBOL |
| auto_nlist(MIB_TCPCOUNTER_SYMBOL, (char *)&MIB_tcpcounter, |
| (MIB_tcpMAXCTR+1)*sizeof (counter)); |
| #endif |
| #else /* linux */ |
| linux_read_tcp_stat (&tcpstat); |
| #endif /* linux */ |
| #else /* can use sysctl and net.inet.tcp.stats exists */ |
| { |
| int sname[] = { CTL_NET, PF_INET, IPPROTO_TCP, TCPCTL_STATS }; |
| size_t len; |
| |
| len = sizeof tcpstat; |
| if (sysctl(sname, 4, &tcpstat, &len, 0, 0) < 0) |
| return NULL; |
| } |
| #endif |
| |
| switch (vp->magic) { |
| case TCPRTOALGORITHM: |
| #ifndef linux |
| long_return = 4; /* Van Jacobsen's algorithm *//* XXX */ |
| #else |
| if (! tcpstat.TcpRtoAlgorithm) { |
| /* 0 is illegal: assume `other' algorithm: */ |
| long_return = 1; |
| return (u_char *) &long_return; |
| } |
| return (u_char *) &tcpstat.TcpRtoAlgorithm; |
| #endif |
| return (u_char *) &long_return; |
| case TCPRTOMIN: |
| #ifndef linux |
| long_return = TCPTV_MIN / PR_SLOWHZ * 1000; |
| return (u_char *) &long_return; |
| #else |
| return (u_char *) &tcpstat.TcpRtoMin; |
| #endif |
| case TCPRTOMAX: |
| #ifndef linux |
| long_return = TCPTV_REXMTMAX / PR_SLOWHZ * 1000; |
| return (u_char *) &long_return; |
| #else |
| return (u_char *) &tcpstat.TcpRtoMax; |
| #endif |
| case TCPMAXCONN: |
| #ifndef linux |
| long_return = -1; |
| return (u_char *) &long_return; |
| #else |
| return (u_char *) &tcpstat.TcpMaxConn; |
| #endif |
| case TCPACTIVEOPENS: |
| long_return = tcpstat.tcps_connattempt; |
| return (u_char *) &long_return; |
| case TCPPASSIVEOPENS: |
| long_return = tcpstat.tcps_accepts; |
| return (u_char *) &long_return; |
| case TCPATTEMPTFAILS: |
| #ifdef hpux |
| long_return = MIB_tcpcounter[7]; |
| #else |
| long_return = tcpstat.tcps_conndrops; /* XXX */ |
| #endif |
| return (u_char *) &long_return; |
| case TCPESTABRESETS: |
| #ifdef hpux |
| long_return = MIB_tcpcounter[8]; |
| #else |
| long_return = tcpstat.tcps_drops; /* XXX */ |
| #endif |
| return (u_char *) &long_return; |
| /* |
| * NB: tcps_drops is actually the sum of the two MIB |
| * counters tcpAttemptFails and tcpEstabResets. |
| */ |
| case TCPCURRESTAB: |
| #ifndef linux |
| long_return = TCP_Count_Connections(); |
| #else |
| long_return = tcpstat.TcpCurrEstab; |
| #endif |
| return (u_char *) &long_return; |
| case TCPINSEGS: |
| long_return = tcpstat.tcps_rcvtotal; |
| return (u_char *) &long_return; |
| case TCPOUTSEGS: |
| long_return = tcpstat.tcps_sndtotal |
| - tcpstat.tcps_sndrexmitpack; |
| /* |
| * RFC 1213 defines this as the number of segments sent |
| * "excluding those containing only retransmitted octets" |
| */ |
| return (u_char *) &long_return; |
| case TCPRETRANSSEGS: |
| long_return = tcpstat.tcps_sndrexmitpack; |
| return (u_char *) &long_return; |
| #ifndef linux |
| case TCPINERRS: |
| long_return = tcpstat.tcps_rcvbadsum + tcpstat.tcps_rcvbadoff |
| #ifdef STRUCT_TCPSTAT_HAS_TCPS_RCVMEMDROP |
| + tcpstat.tcps_rcvmemdrop |
| #endif |
| + tcpstat.tcps_rcvshort; |
| return (u_char *) &long_return; |
| case TCPOUTRSTS: |
| long_return = tcpstat.tcps_sndctrl - tcpstat.tcps_closed; |
| return (u_char *) &long_return; |
| #endif /* linux */ |
| default: |
| ERROR_MSG(""); |
| } |
| return NULL; |
| } |
| |
| #endif /* not HAVE_SYS_TCPIPSTATS_H */ |
| |
| u_char * |
| var_tcpEntry(struct variable *vp, |
| oid *name, |
| size_t *length, |
| int exact, |
| size_t *var_len, |
| WriteMethod **write_method) |
| { |
| int i; |
| oid newname[MAX_OID_LEN], lowest[MAX_OID_LEN], *op; |
| u_char *cp; |
| int State, LowState; |
| static struct inpcb inpcb, Lowinpcb; |
| |
| /* |
| * Allow for a kernel w/o TCP |
| */ |
| #ifdef TCPSTAT_SYMBOL |
| #ifndef linux |
| if (auto_nlist_value(TCPSTAT_SYMBOL) == -1) return(NULL); |
| #endif |
| #endif |
| |
| memcpy( (char *)newname,(char *)vp->name, (int)vp->namelen * sizeof(oid)); |
| lowest[0] = 9999; |
| |
| /* find "next" connection */ |
| Again: |
| LowState = -1; /* Don't have one yet */ |
| TCP_Scan_Init(); |
| for (;;) { |
| if ((i = TCP_Scan_Next(&State, &inpcb)) < 0) goto Again; |
| if (i == 0) break; /* Done */ |
| cp = (u_char *)&inpcb.inp_laddr.s_addr; |
| op = newname + 10; |
| *op++ = *cp++; |
| *op++ = *cp++; |
| *op++ = *cp++; |
| *op++ = *cp++; |
| |
| newname[14] = ntohs(inpcb.inp_lport); |
| |
| cp = (u_char *)&inpcb.inp_faddr.s_addr; |
| op = newname + 15; |
| *op++ = *cp++; |
| *op++ = *cp++; |
| *op++ = *cp++; |
| *op++ = *cp++; |
| |
| newname[19] = ntohs(inpcb.inp_fport); |
| |
| if (exact){ |
| if (snmp_oid_compare(newname, 20, name, *length) == 0){ |
| memcpy( (char *)lowest,(char *)newname, 20 * sizeof(oid)); |
| LowState = State; |
| Lowinpcb = inpcb; |
| break; /* no need to search further */ |
| } |
| } else { |
| if (snmp_oid_compare(newname, 20, name, *length) > 0 && |
| snmp_oid_compare(newname, 20, lowest, 20) < 0){ |
| /* |
| * if new one is greater than input and closer to input than |
| * previous lowest, save this one as the "next" one. |
| */ |
| memcpy( (char *)lowest,(char *)newname, 20 * sizeof(oid)); |
| LowState = State; |
| Lowinpcb = inpcb; |
| } |
| } |
| } |
| if (LowState < 0) return(NULL); |
| memcpy( (char *)name,(char *)lowest, (vp->namelen + 10) * sizeof(oid)); |
| *length = vp->namelen + 10; |
| *write_method = 0; |
| *var_len = sizeof(long); |
| switch (vp->magic) { |
| case TCPCONNSTATE: { |
| #ifndef hpux |
| static int StateMap[]={1, 2, 3, 4, 5, 8, 6, 10, 9, 7, 11}; |
| #else |
| static int StateMap[]={1, 2, 3, -1, 4, 5, 8, 6, 10, 9, 7, 11}; |
| #endif |
| return (u_char *) &StateMap[LowState]; |
| } |
| case TCPCONNLOCALADDRESS: |
| return (u_char *) &Lowinpcb.inp_laddr.s_addr; |
| case TCPCONNLOCALPORT: |
| long_return = ntohs(Lowinpcb.inp_lport); |
| return (u_char *) &long_return; |
| case TCPCONNREMADDRESS: |
| return (u_char *) &Lowinpcb.inp_faddr.s_addr; |
| case TCPCONNREMPORT: |
| long_return = ntohs(Lowinpcb.inp_fport); |
| return (u_char *) &long_return; |
| } |
| return NULL; |
| } |
| |
| #else /* solaris2 - tcp */ |
| |
| |
| static int |
| TCP_Cmp(void *addr, void *ep) |
| { |
| if (memcmp((mib2_tcpConnEntry_t *)ep,(mib2_tcpConnEntry_t *)addr, |
| sizeof(mib2_tcpConnEntry_t)) == 0) |
| return (0); |
| else |
| return (1); |
| } |
| |
| u_char * |
| var_tcp(struct variable *vp, |
| oid *name, |
| size_t *length, |
| int exact, |
| size_t *var_len, |
| WriteMethod **write_method) |
| { |
| mib2_tcp_t tcpstat; |
| mib2_ip_t ipstat; |
| |
| if (header_tcp(vp, name, length, exact, var_len, write_method) == MATCH_FAILED ) |
| return NULL; |
| |
| /* |
| * Get the TCP statistics from the kernel... |
| */ |
| if (getMibstat(MIB_TCP, &tcpstat, sizeof(mib2_tcp_t), GET_FIRST, &Get_everything, NULL) < 0) |
| return (NULL); /* Things are ugly ... */ |
| |
| switch (vp->magic){ |
| case TCPRTOALGORITHM: |
| long_return = tcpstat.tcpRtoAlgorithm; |
| return(u_char *) &long_return; |
| case TCPRTOMIN: |
| long_return = tcpstat.tcpRtoMin; |
| return(u_char *) &long_return; |
| case TCPRTOMAX: |
| long_return = tcpstat.tcpRtoMax; |
| return(u_char *) &long_return; |
| case TCPMAXCONN: |
| long_return = tcpstat.tcpMaxConn; |
| return(u_char *) &long_return; |
| case TCPACTIVEOPENS: |
| long_return = tcpstat.tcpActiveOpens; |
| return(u_char *) &long_return; |
| case TCPPASSIVEOPENS: |
| long_return = tcpstat.tcpPassiveOpens; |
| return(u_char *) &long_return; |
| case TCPATTEMPTFAILS: |
| long_return = tcpstat.tcpAttemptFails; |
| return(u_char *) &long_return; |
| case TCPESTABRESETS: |
| long_return = tcpstat.tcpEstabResets; |
| return(u_char *) &long_return; |
| case TCPCURRESTAB: |
| long_return = tcpstat.tcpCurrEstab; |
| return(u_char *) &long_return; |
| case TCPINSEGS: |
| long_return = tcpstat.tcpInSegs; |
| return(u_char *) &long_return; |
| case TCPOUTSEGS: |
| long_return = tcpstat.tcpOutSegs; |
| return(u_char *) &long_return; |
| case TCPRETRANSSEGS: |
| long_return = tcpstat.tcpRetransSegs; |
| return(u_char *) &long_return; |
| case TCPINERRS: |
| if (getMibstat(MIB_IP, &ipstat, sizeof(mib2_ip_t), GET_FIRST, &Get_everything, NULL) < 0) |
| return (NULL); /* Things are ugly ... */ |
| long_return = ipstat.tcpInErrs; |
| return(u_char *) &long_return; |
| default: |
| ERROR_MSG(""); |
| return (NULL); |
| } |
| } |
| |
| |
| u_char * |
| var_tcpEntry(struct variable *vp, |
| oid *name, |
| size_t *length, |
| int exact, |
| size_t *var_len, |
| WriteMethod **write_method) |
| { |
| oid newname[MAX_OID_LEN], lowest[MAX_OID_LEN], *op; |
| u_char *cp; |
| |
| #define TCP_CONN_LENGTH 20 |
| #define TCP_LOCADDR_OFF 10 |
| #define TCP_LOCPORT_OFF 14 |
| #define TCP_REMADDR_OFF 15 |
| #define TCP_REMPORT_OFF 19 |
| mib2_tcpConnEntry_t Lowentry, Nextentry, entry; |
| req_e req_type; |
| int Found = 0; |
| |
| memset (&Lowentry, 0, sizeof (Lowentry)); |
| memcpy( (char *)newname,(char *)vp->name, vp->namelen * sizeof(oid)); |
| if (*length == TCP_CONN_LENGTH) /* Assume that the input name is the lowest */ |
| memcpy( (char *)lowest,(char *)name, TCP_CONN_LENGTH * sizeof(oid)); |
| for (Nextentry.tcpConnLocalAddress = (u_long)-1, req_type = GET_FIRST; |
| ; |
| Nextentry = entry, req_type = GET_NEXT) { |
| if (getMibstat(MIB_TCP_CONN, &entry, sizeof(mib2_tcpConnEntry_t), |
| req_type, &TCP_Cmp, &entry) != 0) |
| break; |
| COPY_IPADDR(cp, (u_char *)&entry.tcpConnLocalAddress, op, newname + TCP_LOCADDR_OFF); |
| newname[TCP_LOCPORT_OFF] = entry.tcpConnLocalPort; |
| COPY_IPADDR(cp, (u_char *)&entry.tcpConnRemAddress, op, newname + TCP_REMADDR_OFF); |
| newname[TCP_REMPORT_OFF] = entry.tcpConnRemPort; |
| |
| if (exact){ |
| if (snmp_oid_compare(newname, TCP_CONN_LENGTH, name, *length) == 0){ |
| memcpy( (char *)lowest,(char *)newname, TCP_CONN_LENGTH * sizeof(oid)); |
| Lowentry = entry; |
| Found++; |
| break; /* no need to search further */ |
| } |
| } else { |
| if ((snmp_oid_compare(newname, TCP_CONN_LENGTH, name, *length) > 0) && |
| ((Nextentry.tcpConnLocalAddress == (u_long)-1) |
| || (snmp_oid_compare(newname, TCP_CONN_LENGTH, lowest, TCP_CONN_LENGTH) < 0) |
| || (snmp_oid_compare(name, *length, lowest, TCP_CONN_LENGTH) == 0))){ |
| |
| /* if new one is greater than input and closer to input than |
| * previous lowest, and is not equal to it, save this one as the "next" one. |
| */ |
| memcpy( (char *)lowest,(char *)newname, TCP_CONN_LENGTH * sizeof(oid)); |
| Lowentry = entry; |
| Found++; |
| } |
| } |
| } |
| if (Found == 0) |
| return(NULL); |
| memcpy((char *)name, (char *)lowest, |
| (vp->namelen + TCP_CONN_LENGTH - TCP_LOCADDR_OFF) * sizeof(oid)); |
| *length = vp->namelen + TCP_CONN_LENGTH - TCP_LOCADDR_OFF; |
| *write_method = 0; |
| *var_len = sizeof(long); |
| switch (vp->magic) { |
| case TCPCONNSTATE: |
| long_return = Lowentry.tcpConnState; |
| return(u_char *) &long_return; |
| case TCPCONNLOCALADDRESS: |
| long_return = Lowentry.tcpConnLocalAddress; |
| return(u_char *) &long_return; |
| case TCPCONNLOCALPORT: |
| long_return = Lowentry.tcpConnLocalPort; |
| return(u_char *) &long_return; |
| case TCPCONNREMADDRESS: |
| long_return = Lowentry.tcpConnRemAddress; |
| return(u_char *) &long_return; |
| case TCPCONNREMPORT: |
| long_return = Lowentry.tcpConnRemPort; |
| return(u_char *) &long_return; |
| default: |
| ERROR_MSG(""); |
| return (NULL); |
| } |
| } |
| |
| #endif /* solaris2 - tcp */ |
| |
| |
| /********************* |
| * |
| * Internal implementation functions |
| * |
| *********************/ |
| |
| |
| #ifdef linux |
| /* |
| * lucky days. since 1.1.16 the tcp statistics are avail by the proc |
| * file-system. |
| */ |
| |
| static void |
| linux_read_tcp_stat (struct tcp_mib *tcpstat) |
| { |
| FILE *in = fopen ("/proc/net/snmp", "r"); |
| char line [1024]; |
| |
| memset ((char *) tcpstat, (0), sizeof (*tcpstat)); |
| |
| if (! in) |
| return; |
| |
| while (line == fgets (line, sizeof(line), in)) |
| { |
| if (12 == sscanf (line, "Tcp: %lu %lu %lu %lu %lu %lu %lu %lu %lu %lu %lu %lu\n", |
| &tcpstat->TcpRtoAlgorithm, &tcpstat->TcpRtoMin, &tcpstat->TcpRtoMax, |
| &tcpstat->TcpMaxConn, &tcpstat->TcpActiveOpens, &tcpstat->TcpPassiveOpens, |
| &tcpstat->TcpAttemptFails, &tcpstat->TcpEstabResets, &tcpstat->TcpCurrEstab, |
| &tcpstat->TcpInSegs, &tcpstat->TcpOutSegs, &tcpstat->TcpRetransSegs)) |
| break; |
| } |
| fclose (in); |
| } |
| |
| #endif /* linux */ |
| |
| #ifndef solaris2 |
| #ifndef linux |
| /* |
| * Print INTERNET connections |
| */ |
| |
| int TCP_Count_Connections (void) |
| { |
| int Established; |
| struct inpcb cb; |
| register struct inpcb *next; |
| #if !(defined(freebsd2) || defined(netbsd2) || defined(openbsd2)) |
| register struct inpcb *prev; |
| #endif |
| struct inpcb inpcb; |
| struct tcpcb tcpcb; |
| |
| Again: /* |
| * Prepare to scan the control blocks |
| */ |
| Established = 0; |
| |
| auto_nlist(TCP_SYMBOL, (char *)&cb, sizeof(struct inpcb)); |
| inpcb = cb; |
| #if !(defined(freebsd2) || defined(netbsd1) || defined(openbsd2)) |
| prev = (struct inpcb *) auto_nlist_value(TCP_SYMBOL); |
| #endif /* !(defined(freebsd2) || defined(netbsd1) || defined(openbsd2)) */ |
| /* |
| * Scan the control blocks |
| */ |
| #if defined(freebsd2) || defined(netbsd1) || defined(openbsd2) |
| while ((inpcb.INP_NEXT_SYMBOL != NULL) && (inpcb.INP_NEXT_SYMBOL != (struct inpcb *) auto_nlist_value(TCP_SYMBOL))) { |
| #else /* defined(freebsd2) || defined(netbsd1) || defined(openbsd2) */ |
| while (inpcb.INP_NEXT_SYMBOL != (struct inpcb *) auto_nlist_value(TCP_SYMBOL)) { |
| #endif /* defined(freebsd2) || defined(netbsd1) */ |
| next = inpcb.INP_NEXT_SYMBOL; |
| |
| if((klookup((unsigned long)next, (char *)&inpcb, sizeof (inpcb)) == 0)) { |
| perror("TCP_Count_Connections - inpcb"); |
| break; |
| } |
| #if !(defined(freebsd2) || defined(netbsd1) || defined(openbsd2)) |
| if (inpcb.INP_PREV_SYMBOL != prev) { /* ??? */ |
| sleep(1); |
| goto Again; |
| } |
| #endif /* !(defined(freebsd2) || defined(netbsd1) || defined(openbsd2)) */ |
| if (inet_lnaof(inpcb.inp_laddr) == INADDR_ANY) { |
| #if !(defined(freebsd2) || defined(netbsd1) || defined(openbsd2)) |
| prev = next; |
| #endif /* !(defined(freebsd2) || defined(netbsd1) || defined(openbsd2)) */ |
| continue; |
| } |
| if(klookup((unsigned long)inpcb.inp_ppcb, (char *)&tcpcb, sizeof (tcpcb)) == 0) { |
| perror("TCP_Count_Connections - tcpcb"); |
| break; |
| } |
| |
| if ((tcpcb.t_state == TCPS_ESTABLISHED) || |
| (tcpcb.t_state == TCPS_CLOSE_WAIT)) |
| Established++; |
| #if !(defined(freebsd2) || defined(netbsd1) || defined(openbsd2)) |
| prev = next; |
| #endif /* !(defined(freebsd2) || defined(netbsd1) || defined(openbsd2)) */ |
| } |
| return(Established); |
| } |
| #endif |
| |
| static struct inpcb tcp_inpcb, *tcp_prev; |
| #ifdef PCB_TABLE |
| static struct inpcb *tcp_next, *tcp_head; |
| #endif |
| #ifdef linux |
| static struct inpcb *inpcb_list; |
| #endif |
| |
| #if defined(CAN_USE_SYSCTL) && defined(TCPCTL_PCBLIST) |
| static char *tcpcb_buf = NULL; |
| static struct xinpgen *xig = NULL; |
| #endif /* !defined(CAN_USE_SYSCTL) || !define(TCPCTL_PCBLIST) */ |
| |
| void TCP_Scan_Init (void) |
| { |
| #if !defined(CAN_USE_SYSCTL) || !defined(TCPCTL_PCBLIST) |
| #ifdef PCB_TABLE |
| struct inpcbtable table; |
| #endif |
| #ifndef linux |
| #ifdef PCB_TABLE |
| auto_nlist(TCP_SYMBOL, (char *)&table, sizeof(table)); |
| tcp_head = tcp_prev = (struct inpcb *)&((struct inpcbtable *)auto_nlist_value(TCP_SYMBOL))->inpt_queue.cqh_first; |
| tcp_next = table.inpt_queue.cqh_first; |
| #else /* PCB_TABLE */ |
| auto_nlist(TCP_SYMBOL, (char *)&tcp_inpcb, sizeof(tcp_inpcb)); |
| #if !(defined(freebsd2) || defined(netbsd1) || defined(openbsd2)) |
| tcp_prev = (struct inpcb *) auto_nlist_value(TCP_SYMBOL); |
| #endif |
| #endif /* PCB_TABLE */ |
| #else /* linux */ |
| FILE *in; |
| char line [256]; |
| struct inpcb **pp; |
| struct timeval now; |
| static unsigned long Time_Of_Last_Reload = 0; |
| |
| /* |
| * save some cpu-cycles, and reload after 5 secs... |
| */ |
| gettimeofday (&now, (struct timezone *) 0); |
| if (Time_Of_Last_Reload + 5 > now.tv_sec) |
| { |
| tcp_prev = inpcb_list; |
| return; |
| } |
| Time_Of_Last_Reload = now.tv_sec; |
| |
| |
| if (! (in = fopen ("/proc/net/tcp", "r"))) |
| { |
| fprintf (stderr, "snmpd: cannot open /proc/net/tcp ...\n"); |
| tcp_prev = NULL; |
| return; |
| } |
| |
| /* free old chain: */ |
| while (inpcb_list) |
| { |
| struct inpcb *p = inpcb_list; |
| inpcb_list = inpcb_list->INP_NEXT_SYMBOL; |
| free (p); |
| } |
| |
| /* scan proc-file and append: */ |
| |
| pp = &inpcb_list; |
| |
| while (line == fgets (line, sizeof(line), in)) |
| { |
| struct inpcb pcb, *nnew; |
| static int linux_states [12] = { 0, 4, 2, 3, 6, 9, 10, 0, 5, 8, 1, 7 }; |
| int state, lp, fp, uid; |
| |
| if (6 != sscanf (line, |
| "%*d: %x:%x %x:%x %x %*X:%*X %*X:%*X %*X %d", |
| &pcb.inp_laddr.s_addr, &lp, |
| &pcb.inp_faddr.s_addr, &fp, |
| &state, &uid)) |
| continue; |
| |
| pcb.inp_lport = htons ((unsigned short) lp); |
| pcb.inp_fport = htons ((unsigned short) fp); |
| |
| pcb.inp_state = (state & 0xf) < 12 ? linux_states [state & 0xf] : 1; |
| pcb.uid = uid; |
| |
| nnew = (struct inpcb *) malloc (sizeof (struct inpcb)); |
| *nnew = pcb; |
| nnew->INP_NEXT_SYMBOL = 0; |
| |
| *pp = nnew; |
| pp = & nnew->INP_NEXT_SYMBOL; |
| } |
| |
| fclose (in); |
| |
| /* first entry to go: */ |
| tcp_prev = inpcb_list; |
| |
| #endif /* linux */ |
| #else /* !defined(CAN_USE_SYSCTL) || !defined(TCPCTL_PCBLIST) */ |
| { |
| size_t len; |
| int sname[] = { CTL_NET, PF_INET, IPPROTO_TCP, TCPCTL_PCBLIST }; |
| |
| if (tcpcb_buf) { |
| free(tcpcb_buf); |
| tcpcb_buf = NULL; |
| } |
| xig = NULL; |
| |
| len = 0; |
| if (sysctl(sname, 4, 0, &len, 0, 0) < 0) { |
| return; |
| } |
| if ((tcpcb_buf = malloc(len)) == NULL) { |
| return; |
| } |
| if (sysctl(sname, 4, tcpcb_buf, &len, 0, 0) < 0) { |
| free(tcpcb_buf); |
| tcpcb_buf = NULL; |
| return; |
| } |
| |
| xig = (struct xinpgen *)tcpcb_buf; |
| xig = (struct xinpgen *)((char *)xig + xig->xig_len); |
| return; |
| } |
| #endif /* !defined(CAN_USE_SYSCTL) || !defined(TCPCTL_PCBLIST) */ |
| } |
| |
| int TCP_Scan_Next(int *State, |
| struct inpcb *RetInPcb) |
| { |
| #if !defined(CAN_USE_SYSCTL) || !defined(TCPCTL_PCBLIST) |
| register struct inpcb *next; |
| #ifndef linux |
| struct tcpcb tcpcb; |
| |
| #ifdef PCB_TABLE |
| if (tcp_next == tcp_head) { |
| #elif defined(freebsd2) || defined(netbsd1) || defined(openbsd2) |
| if (tcp_inpcb.INP_NEXT_SYMBOL == NULL || |
| tcp_inpcb.INP_NEXT_SYMBOL == (struct inpcb *) auto_nlist_value(TCP_SYMBOL)) { |
| #else |
| if (tcp_inpcb.INP_NEXT_SYMBOL == (struct inpcb *) auto_nlist_value(TCP_SYMBOL)) { |
| #endif |
| return(0); /* "EOF" */ |
| } |
| |
| #ifdef PCB_TABLE |
| klookup((unsigned long)tcp_next, (char *)&tcp_inpcb, sizeof(tcp_inpcb)); |
| tcp_next = tcp_inpcb.inp_queue.cqe_next; |
| #else |
| next = tcp_inpcb.INP_NEXT_SYMBOL; |
| klookup((unsigned long)next, (char *)&tcp_inpcb, sizeof (tcp_inpcb)); |
| #if !(defined(netbsd1) || defined(freebsd2)) || defined(openbsd2) |
| if (tcp_inpcb.INP_PREV_SYMBOL != tcp_prev) /* ??? */ |
| return(-1); /* "FAILURE" */ |
| #endif /* !(defined(netbsd1) || defined(freebsd2) || defined(openbsd2)) */ |
| #endif /* PCB_TABLE */ |
| klookup ( (int)tcp_inpcb.inp_ppcb, (char *)&tcpcb, sizeof (tcpcb)); |
| *State = tcpcb.t_state; |
| #else /* linux */ |
| if (! tcp_prev) |
| return 0; |
| |
| tcp_inpcb = *tcp_prev; |
| *State = tcp_inpcb.inp_state; |
| next = tcp_inpcb.INP_NEXT_SYMBOL; |
| #endif |
| |
| *RetInPcb = tcp_inpcb; |
| #if !(defined(netbsd1) || defined(freebsd2) || defined(openbsd2)) |
| tcp_prev = next; |
| #endif |
| #else /* !defined(CAN_USE_SYSCTL) || !defined(TCPCTL_PCBLIST) */ |
| /* Are we done? */ |
| if ((xig == NULL) || |
| (xig->xig_len <= sizeof(struct xinpgen))) |
| return(0); |
| |
| *State = ((struct xtcpcb *)xig)->xt_tp.t_state; |
| *RetInPcb = ((struct xtcpcb *)xig)->xt_inp; |
| |
| /* Prepare for Next read */ |
| xig = (struct xinpgen *)((char *)xig + xig->xig_len); |
| #endif /* !defined(CAN_USE_SYSCTL) || !defined(TCPCTL_PCBLIST) */ |
| return(1); /* "OK" */ |
| } |
| #endif /* solaris2 */ |