Net2/usr/src/contrib/isode/snmp/tcp.c

Compare this file to the similar file:
Show the results in this format:

/* tcp.c - MIB realization of the TCP group */

#ifndef	lint
static char *rcsid = "$Header: /f/osi/snmp/RCS/tcp.c,v 7.12 91/02/22 09:44:42 mrose Interim $";
#endif

/* 
 * $Header: /f/osi/snmp/RCS/tcp.c,v 7.12 91/02/22 09:44:42 mrose Interim $
 *
 * Contributed by NYSERNet Inc.  This work was partially supported by the
 * U.S. Defense Advanced Research Projects Agency and the Rome Air Development
 * Center of the U.S. Air Force Systems Command under contract number
 * F30602-88-C-0016.
 *
 *
 * $Log:	tcp.c,v $
 * Revision 7.12  91/02/22  09:44:42  mrose
 * Interim 6.8
 * 
 * Revision 7.11  91/01/08  12:48:45  mrose
 * update
 * 
 * Revision 7.10  90/12/18  10:14:14  mrose
 * update
 * 
 * Revision 7.9  90/10/17  14:33:18  mrose
 * update
 * 
 * Revision 7.8  90/10/02  09:55:05  mrose
 * normalize again
 * 
 * Revision 7.7  90/07/09  14:49:43  mrose
 * sync
 * 
 * Revision 7.6  90/06/05  20:47:27  mrose
 * touch-up
 * 
 * Revision 7.5  90/04/23  00:08:16  mrose
 * touch-up
 * 
 * Revision 7.4  90/04/18  08:52:06  mrose
 * oid_normalize
 * 
 * Revision 7.3  90/02/27  18:50:07  mrose
 * unix stuff
 * 
 * Revision 7.2  90/01/27  08:22:09  mrose
 * touch-up
 * 
 * Revision 7.1  89/12/11  10:40:40  mrose
 * touch-up
 * 
 * Revision 7.0  89/11/23  22:23:35  mrose
 * Release 6.0
 * 
 */

/*
 *				  NOTICE
 *
 *    Acquisition, use, and distribution of this module and related
 *    materials are subject to the restrictions of a license agreement.
 *    Consult the Preface in the User's Manual for the full terms of
 *    this agreement.
 *
 */


#include <stdio.h>
#include "mib.h"

#include "internet.h"
#if defined(BSD44) || defined (BSD43)
#include <sys/param.h>
#endif
#include <sys/protosw.h>
#include <sys/socketvar.h>
#include <net/route.h>
#include <netinet/in_systm.h>
#ifdef	BSD44
#include <netinet/ip.h>
#endif
#include <netinet/in_pcb.h>
#include <netinet/tcp.h>
#include <netinet/tcp_fsm.h>
#include <netinet/tcp_timer.h>
#include <netinet/tcp_var.h>

/*  */

#define	RTOA_OTHER	1		/* tcpRtoAlgorithm */
#define	RTOA_VANJ	4		/*   ..  */

#define	MXCN_NONE	(-1)		/* tcpMaxConn */


static struct tcpstat tcpstat;

static int tcpConnections;

/*  */

#define	tcpRtoAlgorithm	0
#define	tcpRtoMin	1
#define	tcpRtoMax	2
#define	tcpMaxConn	3
#define	tcpActiveOpens	4
#define	tcpPassiveOpens	5
#define	tcpAttemptFails	6
#define	tcpEstabResets	7
#define	tcpCurrEstab	8
#define	tcpInSegs	9
#define	tcpOutSegs	10
#define	tcpRetransSegs	11
#ifndef	SUNOS4
#define	tcpInErrs	12
#else
#undef	tcpInErrs	12
#endif
#undef	tcpOutRsts	13		/* NOT IMPLEMENTED */


static int  o_tcp (oi, v, offset)
OI	oi;
register struct type_SNMP_VarBind *v;
int	offset;
{
    int	    ifvar;
    register struct tcpstat *tcps = &tcpstat;
    register OID    oid = oi -> oi_name;
    register OT	    ot = oi -> oi_type;
    static   int lastq = -1;

    ifvar = (int) ot -> ot_info;
    switch (offset) {
	case type_SNMP_PDUs_get__request:
	    if (oid -> oid_nelem != ot -> ot_name -> oid_nelem + 1
		    || oid -> oid_elements[oid -> oid_nelem - 1] != 0)
		return int_SNMP_error__status_noSuchName;
	    break;

	case type_SNMP_PDUs_get__next__request:
	    if (oid -> oid_nelem == ot -> ot_name -> oid_nelem) {
		OID	new;

		if ((new = oid_extend (oid, 1)) == NULLOID)
		    return NOTOK;
		new -> oid_elements[new -> oid_nelem - 1] = 0;

		if (v -> name)
		    free_SNMP_ObjectName (v -> name);
		v -> name = new;
	    }
	    else
		return NOTOK;
	    break;

	default:
	    return int_SNMP_error__status_genErr;
    }

    switch (ifvar) {
	case tcpCurrEstab:
	    if (get_connections (type_SNMP_PDUs_get__request) == NOTOK)
		return generr (offset);
	    break;

	default:
	    if (quantum != lastq) {
		lastq = quantum;

		if (getkmem (nl + N_TCPSTAT, (caddr_t) tcps, sizeof *tcps)
		        == NOTOK)
		    return generr (offset);
	    }
	    break;
    }

    switch (ifvar) {
	case tcpRtoAlgorithm:
#ifdef	TCPTV_REXMTMAX
	    return o_integer (oi, v, RTOA_VANJ);
#else
	    return o_integer (oi, v, RTOA_OTHER);
#endif

	case tcpRtoMin:
	    return o_integer (oi, v, TCPTV_MIN * 100);	/* milliseconds */

#ifdef	TCPTV_REXMTMAX
	case tcpRtoMax:
	    return o_integer (oi, v, TCPTV_REXMTMAX * 100); /*   .. */
#endif

	case tcpMaxConn:
	    return o_integer (oi, v, MXCN_NONE);

#ifdef	TCPTV_REXMTMAX
	case tcpActiveOpens:
	    return o_integer (oi, v, tcps -> tcps_connattempt);

	case tcpPassiveOpens:
	    return o_integer (oi, v, tcps -> tcps_accepts);

	case tcpAttemptFails:
	    return o_integer (oi, v, tcps -> tcps_conndrops);

	case tcpEstabResets:
	    return o_integer (oi, v, tcps -> tcps_drops);
#endif

	case tcpCurrEstab:
	    return o_integer (oi, v, tcpConnections);

#ifdef	TCPTV_REXMTMAX
	case tcpInSegs:
	    return o_integer (oi, v, tcps -> tcps_rcvtotal);

	case tcpOutSegs:
	    return o_integer (oi, v, tcps -> tcps_sndtotal
			           - tcps -> tcps_sndrexmitpack);

	case tcpRetransSegs:
	    return o_integer (oi, v, tcps -> tcps_sndrexmitpack);
#endif

#ifdef	tcpInErrs
	case tcpInErrs:
#if	!defined(BSD44) && !(defined(BSD43) && defined(ultrix))
	    return o_integer (oi, v, tcps -> tcps_badsegs);
#else
	    return o_integer (oi, v, tcps -> tcps_rcvbadsum
				   + tcps -> tcps_rcvbadoff
				   + tcps -> tcps_rcvshort);
#endif
#endif

	default:
	    return int_SNMP_error__status_noSuchName;
    }
}

/*  */

static	int	tcp_states[TCP_NSTATES];


struct tcptab {
#define	TT_SIZE	10			/* object instance */
    unsigned int   tt_instance[TT_SIZE];

    struct inpcb   tt_pcb;		/* protocol control block */

    struct socket  tt_socb;		/* socket info */

    struct tcpcb   tt_tcpb;		/* TCP info */

    struct tcptab *tt_next;
};

static struct tcptab *tts = NULL;

static	int	flush_tcp_cache = 0;


struct tcptab *get_tcpent ();

/*  */

#define	tcpConnState	0
#define	tcpConnLocalAddress 1
#define	tcpConnLocalPort 2
#define	tcpConnRemAddress 3
#define	tcpConnRemPort	4
#define	unixTcpConnSendQ 5
#define	unixTcpConnRecvQ 6


static int  o_tcp_conn (oi, v, offset)
OI	oi;
register struct type_SNMP_VarBind *v;
int	offset;
{
    int	    ifvar;
    register int    i;
    register unsigned int *ip,
			  *jp;
    register struct tcptab *tt;
    struct sockaddr_in netaddr;
    register OID    oid = oi -> oi_name;
    OID	    new;
    register OT	    ot = oi -> oi_type;

    if (get_connections (offset) == NOTOK)
	return generr (offset);

    ifvar = (int) ot -> ot_info;
    switch (offset) {
	case type_SNMP_PDUs_get__request:
	    if (oid -> oid_nelem != ot -> ot_name -> oid_nelem + TT_SIZE)
		return int_SNMP_error__status_noSuchName;
	    if ((tt = get_tcpent (oid -> oid_elements + oid -> oid_nelem
				      - TT_SIZE, 0)) == NULL)
		return int_SNMP_error__status_noSuchName;
	    break;

	case type_SNMP_PDUs_get__next__request:
	    if ((i = oid -> oid_nelem - ot -> ot_name -> oid_nelem) != 0
		    && i < TT_SIZE) {
		for (jp = (ip = oid -> oid_elements + ot -> ot_name -> oid_nelem - 1) + i;
		         jp > ip;
		         jp--)
		    if (*jp != 0)
			break;
		if (jp == ip)
		    oid -> oid_nelem = ot -> ot_name -> oid_nelem;
		else {
		    if ((new = oid_normalize (oid, TT_SIZE - i, 65536))
			    == NULLOID)
			return NOTOK;
		    if (v -> name)
			free_SNMP_ObjectName (v -> name);
		    v -> name = oid = new;
		}
	    }
	    else
		if (i > TT_SIZE)
		    oid -> oid_nelem = ot -> ot_name -> oid_nelem + TT_SIZE;

	    if (oid -> oid_nelem == ot -> ot_name -> oid_nelem) {
		if ((tt = tts) == NULL)
		    return NOTOK;

		if ((new = oid_extend (oid, TT_SIZE)) == NULLOID)
		    return NOTOK;
		ip = new -> oid_elements + new -> oid_nelem - TT_SIZE;
		jp = tt -> tt_instance;
		for (i = TT_SIZE; i > 0; i--)
		    *ip++ = *jp++;

		if (v -> name)
		    free_SNMP_ObjectName (v -> name);
		v -> name = new;
	    }
	    else {
		if ((tt = get_tcpent (ip = oid -> oid_elements
				         + oid -> oid_nelem - TT_SIZE, 1))
		        == NULL)
		    return NOTOK;

		jp = tt -> tt_instance;
		for (i = TT_SIZE; i > 0; i--)
		    *ip++ = *jp++;
	    }
	    break;

	default:
	    return int_SNMP_error__status_genErr;
    }

    switch (ifvar) {
	case tcpConnState:
	    if ((i = tt -> tt_tcpb.t_state) < 0
		    || i >= sizeof tcp_states / sizeof tcp_states[0])
		return generr (offset);
	    return o_integer (oi, v, tcp_states[i]);
	
	case tcpConnLocalAddress:
	    netaddr.sin_addr = tt -> tt_pcb.inp_laddr;	/* struct copy */
	    return o_ipaddr (oi, v, &netaddr);
	    
	case tcpConnLocalPort:
	    return o_integer (oi, v, ntohs (tt -> tt_pcb.inp_lport) & 0xffff);

	case tcpConnRemAddress:
	    netaddr.sin_addr = tt -> tt_pcb.inp_faddr;	/* struct copy */
	    return o_ipaddr (oi, v, &netaddr);

	case tcpConnRemPort:
	    return o_integer (oi, v, ntohs (tt -> tt_pcb.inp_fport) & 0xffff);

	case unixTcpConnSendQ:
	    return o_integer (oi, v, tt -> tt_socb.so_snd.sb_cc);
	
	case unixTcpConnRecvQ:
	    return o_integer (oi, v, tt -> tt_socb.so_rcv.sb_cc);
	
	default:
	    return int_SNMP_error__status_noSuchName;
    }
}

/*  */

static int  tt_compar (a, b)
struct tcptab **a,
	      **b;
{
    return elem_cmp ((*a) -> tt_instance, TT_SIZE,
		     (*b) -> tt_instance, TT_SIZE);
}


static int  get_connections (offset)
int	offset;
{
    register int    i;
    register unsigned int  *cp;
    register struct tcptab *ts,
			   *tp,
			  **tsp;
    register struct inpcb  *ip;
    struct inpcb *head,
		  tcb;
    struct nlist nzs;
    register struct nlist *nz = &nzs;
    static   int first_time = 1;
    static   int lastq = -1;

    if (quantum == lastq)
	return OK;
    if (!flush_tcp_cache
	    && offset == type_SNMP_PDUs_get__next__request
	    && quantum == lastq + 1) {			/* XXX: caching! */
	lastq = quantum;
	return OK;
    }
    lastq = quantum, flush_tcp_cache = 0;

    for (ts = tts; ts; ts = tp) {
	tp = ts -> tt_next;

	free ((char *) ts);
    }
    tts = NULL;

    if (getkmem (nl + N_TCB, (char *) &tcb, sizeof tcb) == NOTOK)
	return NOTOK;
    head = (struct inpcb *) nl[N_TCB].n_value;

    tsp = &tts, i = 0;
    ip = &tcb;
    while (ip -> inp_next != head) {
	if ((ts = (struct tcptab *) calloc (1, sizeof *ts)) == NULL)
	    adios (NULLCP, "out of memory");
			    /* no check needed for duplicate connections... */
	*tsp = ts, tsp = &ts -> tt_next, i++;

	nz -> n_name = "struct inpcb",
	nz -> n_value = (unsigned long) ip -> inp_next;
	if (getkmem (nz, (caddr_t) &ts -> tt_pcb, sizeof ts -> tt_pcb)
	        == NOTOK)
	    return NOTOK;
	ip = &ts -> tt_pcb;

	nz ->n_name = "struct socket",
	nz -> n_value = (unsigned long) ip -> inp_socket;
	if (getkmem (nz, (caddr_t) &ts -> tt_socb, sizeof ts -> tt_socb)
	        == NOTOK)
	    return NOTOK;

	nz ->n_name = "struct tcb",
	nz -> n_value = (unsigned long) ip -> inp_ppcb;
	if (getkmem (nz, (caddr_t) &ts -> tt_tcpb, sizeof ts -> tt_tcpb)
	        == NOTOK)
	    return NOTOK;

	cp = ts -> tt_instance;
	cp += ipaddr2oid (cp, &ip -> inp_laddr);
	*cp++ = ntohs (ip -> inp_lport) & 0xffff;
	cp += ipaddr2oid (cp, &ip -> inp_faddr);
	*cp++ = ntohs (ip -> inp_fport) & 0xffff;

	if (debug && first_time) {
	    OIDentifier	oids;

	    oids.oid_elements = ts -> tt_instance;
	    oids.oid_nelem = TT_SIZE;
	    advise (LLOG_DEBUG, NULLCP,
		    "add connection: %s", sprintoid (&oids));
	}
    }
    first_time = 0;

    if ((tcpConnections = i) > 1) {
	register struct tcptab **base,
			       **tse;

	if ((base = (struct tcptab **) malloc ((unsigned) (i * sizeof *base)))
	        == NULL)
	    adios (NULLCP, "out of memory");

	tse = base;
	for (ts = tts; ts; ts = ts -> tt_next)
	    *tse++ = ts;

	qsort ((char *) base, i, sizeof *base, tt_compar);

	tsp = base;
	ts = tts = *tsp++;

	while (tsp < tse) {
	    ts -> tt_next = *tsp;
	    ts = *tsp++;
	}
	ts -> tt_next = NULL;

	free ((char *) base);
    }

    return OK;    
}

/*  */

static struct tcptab *get_tcpent (ip, isnext)
register unsigned int *ip;
int	isnext;
{
    register struct tcptab *tt;

    for (tt = tts; tt; tt = tt -> tt_next)
	switch (elem_cmp (tt -> tt_instance, TT_SIZE, ip, TT_SIZE)) {
	    case 0:
	        if (!isnext)
		    return tt;
		if ((tt = tt -> tt_next) == NULL)
		    goto out;
		/* else fall... */

	    case 1:
		return (isnext ? tt : NULL);
	}

out: ;
    flush_tcp_cache = 1;

    return NULL;
}

/*  */

init_tcp () {
    register OT	    ot;

    if (ot = text2obj ("tcpRtoAlgorithm"))
	ot -> ot_getfnx = o_tcp,
	ot -> ot_info = (caddr_t) tcpRtoAlgorithm;
    if (ot = text2obj ("tcpRtoMin"))
	ot -> ot_getfnx = o_tcp,
	ot -> ot_info = (caddr_t) tcpRtoMin;
#ifdef	TCPTV_REXMTMAX
    if (ot = text2obj ("tcpRtoMax"))
	ot -> ot_getfnx = o_tcp,
	ot -> ot_info = (caddr_t) tcpRtoMax;
#endif
    if (ot = text2obj ("tcpMaxConn"))
	ot -> ot_getfnx = o_tcp,
	ot -> ot_info = (caddr_t) tcpMaxConn;
#ifdef	TCPTV_REXMTMAX
    if (ot = text2obj ("tcpActiveOpens"))
	ot -> ot_getfnx = o_tcp,
	ot -> ot_info = (caddr_t) tcpActiveOpens;
    if (ot = text2obj ("tcpPassiveOpens"))
	ot -> ot_getfnx = o_tcp,
	ot -> ot_info = (caddr_t) tcpPassiveOpens;
    if (ot = text2obj ("tcpAttemptFails"))
	ot -> ot_getfnx = o_tcp,
	ot -> ot_info = (caddr_t) tcpAttemptFails;
    if (ot = text2obj ("tcpEstabResets"))
	ot -> ot_getfnx = o_tcp,
	ot -> ot_info = (caddr_t) tcpEstabResets;
#endif
    if (ot = text2obj ("tcpCurrEstab"))
	ot -> ot_getfnx = o_tcp,
	ot -> ot_info = (caddr_t) tcpCurrEstab;
#ifdef	TCPTV_REXMTMAX
    if (ot = text2obj ("tcpInSegs"))
	ot -> ot_getfnx = o_tcp,
	ot -> ot_info = (caddr_t) tcpInSegs;
    if (ot = text2obj ("tcpOutSegs"))
	ot -> ot_getfnx = o_tcp,
	ot -> ot_info = (caddr_t) tcpOutSegs;
    if (ot = text2obj ("tcpRetransSegs"))
	ot -> ot_getfnx = o_tcp,
	ot -> ot_info = (caddr_t) tcpRetransSegs;
#endif
#ifdef	tcpInErrs
    if (ot = text2obj ("tcpInErrs"))
	ot -> ot_getfnx = o_tcp,
	ot -> ot_info = (caddr_t) tcpInErrs;
#endif
#ifdef	tcpOutRsts
    if (ot = text2obj ("tcpOutRsts"))
	ot -> ot_getfnx = o_tcp,
	ot -> ot_info = (caddr_t) tcpOutRsts;
#endif

    if (ot = text2obj ("tcpConnState"))
	ot -> ot_getfnx = o_tcp_conn,
	ot -> ot_info = (caddr_t) tcpConnState;
    if (ot = text2obj ("tcpConnLocalAddress"))
	ot -> ot_getfnx = o_tcp_conn,
	ot -> ot_info = (caddr_t) tcpConnLocalAddress;
    if (ot = text2obj ("tcpConnLocalPort"))
	ot -> ot_getfnx = o_tcp_conn,
	ot -> ot_info = (caddr_t) tcpConnLocalPort;
    if (ot = text2obj ("tcpConnRemAddress"))
	ot -> ot_getfnx = o_tcp_conn,
	ot -> ot_info = (caddr_t) tcpConnRemAddress;
    if (ot = text2obj ("tcpConnRemPort"))
	ot -> ot_getfnx = o_tcp_conn,
	ot -> ot_info = (caddr_t) tcpConnRemPort;

    if (ot = text2obj ("unixTcpConnSendQ"))
	ot -> ot_getfnx = o_tcp_conn,
	ot -> ot_info = (caddr_t) unixTcpConnSendQ;
    if (ot = text2obj ("unixTcpConnRecvQ"))
	ot -> ot_getfnx = o_tcp_conn,
	ot -> ot_info = (caddr_t) unixTcpConnRecvQ;

    tcp_states[TCPS_CLOSED] = 1;
    tcp_states[TCPS_LISTEN] = 2;
    tcp_states[TCPS_SYN_SENT] = 3;
    tcp_states[TCPS_SYN_RECEIVED] = 4;
    tcp_states[TCPS_ESTABLISHED] = 5;
    tcp_states[TCPS_CLOSE_WAIT] = 8;
    tcp_states[TCPS_FIN_WAIT_1] = 6;
    tcp_states[TCPS_CLOSING] = 10;
    tcp_states[TCPS_LAST_ACK] = 9;
    tcp_states[TCPS_FIN_WAIT_2] = 7;
    tcp_states[TCPS_TIME_WAIT] = 11;
}