Brief Introductions

For a general discussion of the dos and don’ts first look at the article – “So You Wand to Write A Poller…” http://bit.ly/Yf1Uyr

Well after expounding on the should or should nots about a poller I figured an example was in order. At some point you have to stick a stake in the sand and let folks kick at it. The example I chose was a poller I wrote over a decade ago in 2000.  Of course being a working, real world example of my youth I fell into the trap of every eager, ADHD ridden, developer.  I:

  • Implemented only 1 protocol
  • Didn’t plug into a database
  • Made the error of synchronous collection
  • No asynch handling of DNS resolution
  • No distributed polling or message queue architecture

However, I did manage to dodge a few bullets.

  • Leverages the existing code of NET-SNMP (also embedded in IBM, HP, CA, and BMC software btw)
  • The poller is async down to the OS using select()
  • Worked around hardware issues with the Dell chip set by throttling
  • Documented what the heck I was doing so that 12 years later I can shar

Any async implementation is messy by definition. This is because by nature it is not procedural, top down driven. Instead the code start out and sends off packets and then waits for the outside world (in this case the network interface) to respond.  This magic occurs around the “seclect()” call. This is where it waits for the response.  The Asynchronous() registers the procedure that will handle the responses and it is the asynch_response() function that actually does the heavy lifting.  Back in 2000 this code could handle up to 10K packets stuffed with up to 50 OIDs in 1 minute.

 

The Code

Without further ado – here is the code:

/*****************************************************************************
* PROGRAM: async.c           Version 1.0          Daniel L. Needles          *
* CREATED: 11/18/00                                                          *
* PURPOSE:                                                                   *
*   Low level utility to perform async SNMP polls against multiple           *
*   devices.                                                                 *
* COMPILING:                                                                 *
*   see project/install                                                      *
* DESCRIPTION:                                                               *
*   1. Benchmark start time.                                                 *
*   2. Increase resources (max conccurent open files per user)               *
*   3. Load in hosts, community string, oids into HOST structure.            *
*   4. Loop through host structure and create a session for a callback       *
*      and then send out a snmpget packet. Limit the total number of         *
*      outstanding snmpget packets to prevent socket from exploding.         *
*   5. As the packets come in print out the values they return or if         *
*      timeout or error print that out as well.                              *
*   6. Once done close any existing sessions and exit.                       *
* NOTE: TIMEOUT is equal to trunc(6*Tvalue+15/1,000,000)                     *
* NOTE: T set to (Time*100+35)*10,000/6)                                     *
* NOTE: S will slow things down more evenly. For 8826 nodes:                 *
*  S=5 takes 201 secs; 10 takes 201 secs; 50 takes 83-89; 75 takes 66        *
*  S=100 takes 58-64 secs; 250 takes 40; 500 takes 38 and 10000 takes 34.    *
* SETUP:                                                                     *
* PROBLEMS:                                                                  *
*   PROBLEM 1: Each host requires a session which requires a UDP port which  *
*     is a file. Most UNIX systems only allow 1K of open files per user.     *
*     It would generate the following errors:                                *
*        For IP address: snmp_open: No socket (Too many open files)          *
*        For HOST name: snmp_open: Unknown host (hostname)                   *
*   SOLUTION 1:                                                              *
*     The solution is to run the program as root which allows you to         *
*     arbitrarily increase file handles using setrlimit() Not sure why this  *
*     works. This does the same thing as the ulimit -n <FILENO> command.     *
*   PROBLEM 2: It appears select() only allows 1K of outstanding multiplexed *
*     I/O (network) requests. This is set by FD_SETSIZE. If it is exceeded   *
*     The select statement barfs with the error invalid argument. (From      *
*     errno.h – EINVAL 22.)                                                  *
*   SOLUTION 2:                                                              *
*     Limit outstanding SNMP requests to 1K by only opening a new session    *
*     if less than 1K sessions are outstanding.                              *
*   PROBLEM 3: Occasional core dumps on snmp_read in Asyncronous() call.     *
*      This was caused by a snmp packet returning after select() but before  *
*      the snmp_read. Since the callback function closed the session some    *
*      of the selects fds would be already closed causing the core dump.     *
*   SOLUTION 3: Only signal closure. Place all closure in same spot.         *
*   PROBLEM 4: Open files would decrease causing snmp_open() to fail.        *
*   SOLUTION 4: Use a variable instead of a costant for maxconcurrentsess    *
*      Then rachet down resource usage as errors occur.                      *
*   PROBLEM 5: Segment fault occurs for some devices when error returned.    *
*   SOLUTION 5: Some devices just return the error with NO oids. So we use   *
*      what we sent out to generate 1 error return followed by multiple      *
*      failures.                                                             *
******************************************************************************
* 20000122 DLN Work around the bad clock in Intel chips.                     *
* 20050302 DLN Errored SNMP packet with no OIDs caused segment fault         *
*              Added debugging at level 128. Increased MAXOIDS               *
*              No longer required DEBUG 1 to quit when OIDS or HOSTs exceeded*
*              Now we ALWAYS exit.                                           *
******************************************************************************
* TODO: async_5.c:950: dereferencing pointer to incomplete type
* TODO: -s should be -O, output options
* TODO: get debug 2 to works, problems with timing.
*****************************************************************************/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <sys/time.h>
#include <sys/resource.h> /* For increaseing socket count */
#include <sys/select.h>

/* INCLUDE UCD SNMP LIBRARIES */
#include <net-snmp/library/asn1.h>
#include <net-snmp/library/snmp.h>
#include <net-snmp/library/snmp_api.h>
#include <net-snmp/library/snmp_client.h>
#include <net-snmp/library/mib.h>
/* FOR MAKING ONLY VALUE NOT VALUE TYPE RETURNED */
/*
#include <net-snmp/default_store.h>
*/

/* GLOBALS */
char *prog; /* NAME OF THIS PROGRAM DYNAMICALLY CALCULATED */
char *filename=NULL;
int  debug=0;
int  format=99;  /* DEfault to Default Format */
long int sess_timeout=225000; /* DEAFAULT TO 1 SECOND */
long int intra_timeout=0; /* DEAFAULT TO NOTHING */
/* DLN20000122 MAKE TIMEOUTS NICE SINCE INTEL’S CLOCK IS MESSED */
u_int skips = 0;
u_int select_time = 0;

/* EXTERNAL GLOBALS */
extern char *optarg;  /* USED BY GETOPT TO PARSE COMMAND LINE ARGUMENTS */
extern int optind,opterr; /* USED BY GETOPT TO PARSE COMMAND LINE ARGS */

/* FOR SYNC IO MULTIPLEXING ERRORS */
extern int errno;

/** OID TABLE **/
#define OIDSIZE 255
struct oid {
char *Name;
oid Oid[MAX_OID_LEN];
size_t OidLen;
int Instance;
};

/** HOST TABLE **/
#define MAXHOSTS 15000
#define MAXOIDS  100
struct host {
char *hashkey;
char *num;
char *name;
char *community;
int  oidmax;
ushort snmpver;
struct oid oid[MAXOIDS];
int hostid;
} hosts[MAXHOSTS];

#define MAXCONCURRENTSESS 1000  /* ATTEMPT TO RESERVE 1000 FILE HANDLES */
#define CONCURRENTBUFF 20       /* LEAVE 10 FILE HANDLES UNUSED */
int maxconcurrentsess;

/** SNMP POLLING TABLE **/
struct session {
struct snmp_session *sess; /* SNMP session data */
struct oid *current_oid;   /* How far in our poll are we */
int hostid;
struct host *hostptr;
} sessions[MAXHOSTS];

int active_hosts;    /* OUTSTANDING SNMPGETS */

/**************************************************************************
* USAGE: Reports usage to user.
**************************************************************************/
void Usage() {
fprintf(stderr,”\nUsage: %s [options]\n”,prog);
fprintf(stderr,”       -d <debug level>\n”);
fprintf(stderr,”       -f <file_name>\n”);
fprintf(stderr,”       -i <Between send timeout>\n”);
fprintf(stderr,”       -o <output format>\n”);
fprintf(stderr,”          0 = Group all oids into 1 record.\n”);
fprintf(stderr,”          1 = One oid per record.\n”);
fprintf(stderr,”         99 = (Default) Kirk’s choice.\n”);
fprintf(stderr,”       -s <\”String of single char options\”>\n”);
fprintf(stderr,”          n=numeric oids\n”);
fprintf(stderr,”          f=full oids\n”);
fprintf(stderr,”          b=Don’t break out oids(?)\n”);
fprintf(stderr,”          e=Print numeric ENUM (?)\n”);
fprintf(stderr,”          q= No type included in value\n”);
fprintf(stderr,”          s= Only 1 suffix with instance\n”);
fprintf(stderr,”          S= Only 1 suffix with instance AND SNMP
Version info\n”);
fprintf(stderr,”       -t <Session timeout in seconds>\n”);
/* DLN20000122 MAKE TIMEOUTS NICE SINCE INTEL’S CLOCK IS MESSED */
fprintf(stderr,”       -S <Number of selects to skip over before doing a
sleep>”);
fprintf(stderr,”           This works around an Intel bug on the timers.”);

fprintf(stderr,”INPUT  RECORD:\n”);
fprintf(stderr,”  Hashkey Num IP:Port read_community_string
Version\n  OID1\n  …\n  OIDn\n  ~\n”);
fprintf(stderr,”OUTPUT RECORD:\n”);
fprintf(stderr,”  Hashkey\n  Num\n  Error Num (-1=timeout,0=good,n=error
(i.e. 2=nosuch)\n  Error OID\n  Error Instance\n  Error
Msg\n  OID1\n  Instance1\n  Type1\n  Value1\n  …\n  OIDn\n  InstanceN\n
TypeN\n  ValueN\n~\n~~\n  …\n  After last node  …\n~~~\n”);
exit(5);
}

/**************************************************************************
* GETOPTIONS: PARSE COMMAND LINE.
**************************************************************************/
void GetOptions(int argc, char **argv) {
char c2;  /* TEMP HOLDER OF CMD LINE SWITCH */
int  c=0; /* TEMP HOLDER OF CMD LINE SWITCH */

/* DLN20000122 MAKE TIMEOUTS NICE SINCE INTEL’S CLOCK IS MESSED */
while ((c = getopt(argc,argv,”f:d:o:s:t:i:S:”)) != EOF) {
c2=(char) c;
switch (c2) {
case ‘f’: filename=optarg; break;
case ‘d’: debug= atoi(optarg); break;
case ‘o’: format=atoi(optarg); break;
case ‘t’: sess_timeout=atoi(optarg); break;
case ‘i’: intra_timeout=atoi(optarg); break;
/* DLN20000122 MAKE TIMEOUTS NICE SINCE INTEL’S CLOCK IS MESSED */
case ‘S’: skips=atoi(optarg); break;
/* VIA MIB.C IN UCD STUFF INITIALIZE SNMP ENVIRONMENT
n=numeric oids;
f=full oids;
q= No type included in value
s= Only 1 suffix with instance*/
case ‘s’: snmp_out_toggle_options(optarg); break;
default : Usage(); break;
}
}
/* DLN20000122 MAKE TIMEOUTS NICE SINCE INTEL’S CLOCK IS MESSED */
if (debug & 1) {
fprintf(stderr, “INTEL SKIP TIMEOUT AT %d\n”,skips);
}
}

/**************************************************************************
* TIMEVAL_DIFF: RETURNS HUNDRETHS OF MILLISECONDS (SECOND/100000)
**************************************************************************/
double timeval_diff(struct timeval *a,struct timeval *b)
{
double temp;

temp =
(((a->tv_sec*1000000)+ a->tv_usec) –
((b->tv_sec*1000000)+ b->tv_usec))/10;
/*
fprintf(stderr,”%ld:%d => %ld:%d =
%e\n”,a->tv_sec,a->tv_usec,b->tv_sec,b->tv_usec,temp);
*/
return temp;

}

/**************************************************************************
* LOADHOSTTABLE: PULL IN HOST, COMMUNITY STRING AND OIDS TO BE DONE.
**************************************************************************/
int LoadHostTable() {
int hostcnt=0;  /* NUMBER OF HOST LOADED */
FILE *hostfile; /* FILE HANDLE FOR HOSTS */
char line[OIDSIZE]; /* BUFFER ONE LINE FROM HOST FILE */
char hashkey[OIDSIZE]; /* BUFFER HASHKEY FROM HOST FILE (FROM LINE) */
char num[OIDSIZE]; /* BUFFER NUM FROM HOST FILE (FROM LINE) */
char host[OIDSIZE]; /* BUFFER HOST NAME FROM HOST FILE (FROM LINE) */
char snmpread[OIDSIZE]; /* BUFFER COMMUNITY STRING FROM HOST FILE FROM LINE */
char sver[OIDSIZE]; /* BUFFER SNMP VERSION STRING FROM HOST FILE FROM LINE */
char oid[OIDSIZE]; /* BUFFER COMMUNITY STRING FROM HOST FILE FROM LINE */
char instance[OIDSIZE]; /* BUFFER COMMUNITY STRING FROM HOST FILE FROM LINE */
ushort snmpver;
struct host *q; /* INDEX FOR HOST TABLE AS IT LOADS */
struct oid *op; /* SHORT HAND TO FOR READABILITY AND SPEED */
int oididx=0;   /* INDEX TO OIDS IN HOST TABLE */
int failures=0; /* FAILURES TO PARSE OIDS */

/* DETERMINE SOURCE OF HOSTS */
if (!filename) {
hostfile=fdopen(0,”r”);
} else {
hostfile=fopen(filename,”r”);
}
if (!hostfile) {
fprintf(stderr,”%s: Could not open the file %s\n”,prog,filename);
exit(4);
}

/* MAKE A TEMP POINTER TO HOST TABLE */
q=hosts;

/* PARSE HOSTS */
while(fgets(line,OIDSIZE,hostfile)) {
if (line[0] == ‘~’) {
break;
}

/* NODE VALID SO COUNT IT  AND GIVE IT AS AN ID*/
hostcnt++;
if ( hostcnt >= MAXHOSTS ) {
fprintf(stderr,”Maximum node count reached: %d. Aborting
program\n”,hostcnt);
exit(1);
break;
}
q->hostid=hostcnt;
if ( debug & 128 ) {
fprintf(stderr,”%s: LINE:   %s\n”,prog,line);
fprintf(stderr,”%s: HOSTID: %d\n”,prog,hostcnt);
}

/* GRAB HOST AND SNMP READ STRING FROM LINE */
sscanf(line,”%s %s %s %s %s”,hashkey,num,host,snmpread,sver);
/* SKIP COMMENTS */
if ((!hashkey) || (hashkey[0]==’#’)) continue;

/* BUFFER THE HOST IN THE HOST TABLE ENTRY */
q->name=(char *)malloc(strlen(host)+1);
if (!q->name) {
fprintf(stderr,”%s: Out of memory (malloc host.)\n”,prog);
exit(4);
}
strcpy(q->name,host);

/* BUFFER THE HASHKEY IN THE HOST TABLE ENTRY */
q->hashkey=(char *)malloc(strlen(hashkey)+1);
if (!q->hashkey) {
fprintf(stderr,”%s: Out of memory (malloc host.)\n”,prog);
exit(4);
}
strcpy(q->hashkey,hashkey);

/* BUFFER THE num     IN THE HOST TABLE ENTRY */
q->num=(char *)malloc(strlen(num    )+1);
if (!q->num    ) {
fprintf(stderr,”%s: Out of memory (malloc host.)\n”,prog);
exit(4);
}
strcpy(q->num    ,num    );

/* BUFFER THE COMMUNITY STRING IN THE HOST TABLE ENTRY */
q->community=(char *)malloc(strlen(snmpread)+1);
if (!q->community) {
fprintf(stderr,”%s: Out of memory (malloc community.)\n”,prog);
exit(4);
}
strcpy(q->community,snmpread);

/* BUFFER THE PORT TO PULL IN THE HOST TABLE ENTRY */
snmpver=atoi(sver);
if (snmpver == 0 || snmpver == 1 || snmpver == 3)  {
q->snmpver = (ushort) snmpver;
} else {
fprintf(stderr,”%s: Bad version for %s instance %s (%s); using 1
default\n”,prog,hashkey,num,host);
q->snmpver=1;
}
if ( 128 & debug ) {
fprintf(stderr,”%s: NAME: %s\n”,prog,q->name);
fprintf(stderr,”%s:   HASH KEY: %s\n”,prog,q->hashkey);
fprintf(stderr,”%s:   NUM: %s\n”,prog,q->num);
fprintf(stderr,”%s:   COMMUNITY: %s\n”,prog,q->community);
fprintf(stderr,”%s:   SNMP VER: %d\n”,prog,q->snmpver);
}

/* BUFFER THE OIDS */
oididx=0;
while(fgets(line,OIDSIZE,hostfile)) {
if (line[0] == ‘~’) {
break;
}

/* KILL NEWLINE (PREVENTS ERROR) */
if ( line[strlen(line)-1] == ‘\n’ ) line[strlen(line)-1]=’\0′;
/* PARSE OID AND INSTANCE */
sscanf(line,”%s %s”,oid,instance);
/* CAT INSTANCE TO OID */
strcat(oid,”.\0″);
strcat(oid,instance);

if ( debug & 16 ) {
fprintf(stderr, “OID=%s;INST=%s\n”,oid,instance);
}

/* SAVE TIME BY CLOSER REFERENCE */
op = &(q->oid[oididx]);
/* MAKE A COPY OF TEXT MIB INTO THE HOST STRUCT */
op->Name=(char *)malloc(strlen(oid)+1);
strcpy(op->Name,oid);
/* SET LENGTH TO MAXIMUM */
op->OidLen=MAX_OID_LEN;
/* COUNT . DELIMITERS IN INSTANCE */
op->Instance=strcnt(instance,strlen(instance));
if (!read_objid(op->Name,op->Oid,&(op->OidLen))) {
snmp_perror(op->Name);
printf( “BAD ERRROR\n”);
failures++;
}
if ( 128 & debug ) {
fprintf(stderr,”%s:   OID ID: %d\n”,prog,oididx);
fprintf(stderr,”%s:     OID NAME: %s\n”,prog,op->Name);
fprintf(stderr,”%s:     OID LENGTH: %d\n”,prog,op->OidLen);
fprintf(stderr,”%s:     OID INSTANCE: %d\n”,prog,op->Instance);
}

oididx++;
if (oididx >= MAXOIDS ) {
fprintf(stderr,”%s: E: More than the allowed %d oids.
Aborting.\n”,prog,MAXOIDS);
exit(2);
while(fgets(line,OIDSIZE,hostfile)) {
if (line[0] == ‘~’) {
break;
} else {
if ( debug & 1 ) {
fprintf(stderr,”%s:    Ignoring:%s\n”,prog,line);
}
}
}
break;
}
}
if (failures) {
if ( debug & 1 ) {
fprintf(stderr,”%s: %d failures attempting to parse
oids.\n”,prog,failures);
}
}

/* MARK LAST OID FOR HOST */
q->oidmax=oididx;

if ( debug & 128 ) {
fprintf(stderr,”%s: TOTAL OID CNT: %d\n”,prog,q->oidmax);
}

/* SKIP TO THE NEXT HOST ENTRY */
q++;
}
fclose(hostfile);

/* VALIDATE WE GOT AT LEAST 1 HOST */
if (!hostcnt) exit(0);

/* ELSE REPORT TO CALLER HOW MANY HOST WERE ADDED */
return(hostcnt);
}
/** SHOULD WORK BUT NEVER RETURNED
int strcnt(char * buff)
{
int cnt=0;
for (; buff !=’\0′; buff++) {
cnt+= (*buff == ‘.’) ? 1 : 0;
}
return(cnt);
}
*/

if (strstr(src, needle) != NULL) {

for (srcidx=strlen(needle); srcidx<maxlen; srcidx++) {
c=src[srcidx];
c2=(int) c;
/* IGNORE NON 0-9 */
if ( c2 >= 48 && c2 <= 57) {
dest[destidx++]=c;
}
/* SKIP EVERYTHING ELSE */
}
dest[destidx++]=’\0′;
fprintf(stderr, “FOUND  NEEDLE: –>%s<–\n”,dest);
return(destidx);
} else {
strcpy(dest,src);
fprintf(stderr, “UNSEEN NEEDLE: %s\n”,src);
}
return(destidx);
}
#endif

int stresc(char *dest, char *src, int maxlen) {
int srcidx=0;
int destidx=0;
char c;
int c2;
int isquoted=0;

isquoted=(src[0]=='”‘ && src[maxlen-1]=='”‘) ? 1 : 0;
maxlen-=isquoted;
for (srcidx=isquoted; srcidx<maxlen; srcidx++) {
c=src[srcidx];
c2=(int) c;
/* IGNORE CTRL M’s AND SUCH */
if ( c2 >= 32 && c2 <= 125) {
dest[destidx++]=c;
} else {
/* ESCAPE \n (10) and ~ (126) */
if ( c == ‘\n’ || c == ‘~’ ) {
dest[destidx++]=’\\’;
dest[destidx++]=’\\’;
dest[destidx++]=c;
}
/* SKIP EVERYTHING ELSE */
}
}
dest[destidx++]=’\0′;
return(destidx);
}

char *strncnt(char * buff, int n, int maxlen)
{
int i=0;
int cnt=0;

for (i=0; i<maxlen; i++) {
if ( buff[i]==’.’) {
cnt++;
if (cnt >= n) {
buff[i]=’\0′;
return(&buff[i+1]);
}
}
}
return(NULL);
}

int strcnt(char * buff, int maxlen)
{
int i=0;
int cnt=0;

for (i=0; i<maxlen; i++) {
if ( buff[i]==’.’) {
cnt++;
}
}
return(cnt);
}
/***************************************************************************
* PRINT_RESULT: PRINT DATA (SUCCESS, FAILURE, TIMEOUTS)
*   NOTE: RETURN VALUES ENABLE ASYNC_RESPONCE TO REPOLL ON A 0. THIS IS NOT
*         IMPLEMENTED. THIS WILL PROBABLY BE HANDLED IN A PERL WRAPPER.
***************************************************************************/
int print_result (int status, struct snmp_session *sp, struct snmp_pdu
*pdu, struct host *hostptr)
{
char buf[1024];  /* BUFFER FOR OID */
char* buf2;      /* BUFFER FOR INSTANCE */
char pbuf[1512]; /* PRINTABLE (ESCAPED) BUFFER FOR OID */
char pbuf2[256]; /* PRINTABLE (ESCAPED) BUFFER FOR INSTANCE */
struct variable_list *vp;
int count;
int count2;
int i;           /* INDEX TO MIMIC LINK BETWEEN RETURN OIDS AND STRUCTURE */

#ifdef HAVE_CA_SPOOF_COUNTER64
char pbuf3[1512]; /* PRINTABLE (ESCAPED) BUFFER FOR OID */
#endif

switch (status) {
case STAT_SUCCESS:
vp = pdu->variables;
if (pdu->errstat == SNMP_ERR_NOERROR) {
if ( debug & 128 ) {
fprintf(stderr,”%s: GOOD SNMP RESPONSE. Format: %d\n”,prog,format);
}
switch ( format ) {
case 0:
fprintf(stdout, “%s\n%s\n”,hostptr->hashkey,hostptr->num);
fprintf(stdout, “%s\n0\n”,sp->peername);
while (vp) {
fprint_objid(stdout,vp->name,vp->name_length);
fprint_value(stdout,vp->name,vp->name_length,vp);
vp = vp->next_variable;
}
fprintf(stdout, “~\n”);
break;
case 1:
while (vp) {
fprintf(stdout, “%s\n%s\n”,hostptr->hashkey,hostptr->num);
fprintf(stdout, “%s\n0\n”,sp->peername);
fprint_objid(stdout,vp->name,vp->name_length);
fprint_value(stdout,vp->name,vp->name_length,vp);
fprintf(stdout, “~\n”);
vp = vp->next_variable;
}
break;
default:
printf(“%s\n%s\n”,hostptr->hashkey,hostptr->num);
printf(“0\nNULL\nNULL\n\n”); /* ERR NO, OID, INSTANCE, MSG */
/** NOTE BIG ASSUMPTION: WE ASSUME THE PACKET RETURNS THE SAME
WAY WE SENT IT OUT IN ORDER TO SPLIT OID AND INSTANCE UP */
i=0;
while (vp) {
/* PRINT OID AND INSTANCE */
snprint_objid(buf,sizeof(buf),vp->name,vp->name_length);
if ( (hostptr->oid[i].Instance)>0) {

buf2=strncnt(buf,(strcnt(buf,strlen(buf))-hostptr->oid[i].Instance),strlen(buf))
;
} else {
buf2=strrchr(buf,’.’);
*buf2=’\0′;
buf2++;
}
stresc(pbuf,buf,strlen(buf));
stresc(pbuf2,buf2,strlen(buf2));
printf(“%s\n%s\n”,pbuf,pbuf2);

/* PRINT VAL AND TYPE */
/* NOTE TYPE INFO IS FAKE AND REQURIES -s “q” OPTION */
printf(“-\n”);
snprint_value(buf,sizeof(buf),vp->name,vp->name_length,vp);
#ifdef HAVE_CA_SPOOF_COUNTER64
wtint64str(pbuf3,buf,strlen(buf));
stresc(pbuf,pbuf3,strlen(pbuf3));
#else
stresc(pbuf,buf,strlen(buf));
#endif
printf(“%s\n”,pbuf);
/* PRINT TERMINATOR FOR OID */
printf(“~\n”);
i++;
vp = vp->next_variable;
}
printf(“~~\n”);
break;
}
return 1; /* ITS GOOD */
} else {
if ( debug & 128 ) {
fprintf(stderr,”%s: ERRORED SNMP RESPONSE. Format:
%d\n”,prog,format);
}
/** REPORT THE PROBLEM */
switch ( format ) {
case 0:
fprintf(stdout, “%s\n%s\n”,hostptr->hashkey,hostptr->num);
fprintf(stdout, “%s\n1\n”,sp->peername);
for (count=1,vp=pdu->variables; vp; vp=vp->next_variable, count++) {
fprint_objid(stdout,vp->name,vp->name_length);
if ( count == pdu->errindex ) {
fprintf(stdout,”%s~\n”,snmp_errstring(pdu->errstat));
} else {
fprintf(stdout,”untested\n~\n”);
}
}
fprintf(stdout,”~\n”);
break;
case 1:
for (count=1,vp=pdu->variables; vp; vp=vp->next_variable, count++) {
fprintf(stdout, “%s\n%s\n”,hostptr->hashkey,hostptr->num);
fprintf(stdout, “%s\n1\n”,sp->peername);
fprint_objid(stdout,vp->name,vp->name_length);
if ( count == pdu->errindex ) {
fprintf(stdout,”%s~\n”,snmp_errstring(pdu->errstat));
} else {
fprintf(stdout,”untested\n~\n”);
}
}
break;
default:
/* IDENTIFY SESSION */
printf(“%s\n%s\n”,hostptr->hashkey,hostptr->num);
/* PRINT ERROR NUMBER */
printf(“%d\n”,pdu->errstat);

/* PRINT ERR OID AND INSTANCE */
/* IF NO VARS GIVEN BACK, THEN SKIP. */
if ( pdu->variables ) {
if ( debug & 128 ) {
fprintf(stderr,”%s: Normal process (%d)\n”,prog,pdu->errindex);
}
/* FIND ERROR */
for (count=1,vp=pdu->variables; count<pdu->errindex;
vp=vp->next_variable, count++);

snprint_objid(buf,sizeof(buf),vp->name,vp->name_length);
if ((hostptr->oid[i].Instance)>0) {

buf2=strncnt(buf,(strcnt(buf,strlen(buf))-hostptr->oid[i].Instance),strlen(buf))
;
} else {
buf2=strrchr(buf,’.’);
*buf2=’\0′;
buf2++;
}
stresc(pbuf,buf,strlen(buf));
stresc(pbuf2,buf2,strlen(buf2));
printf(“%s\n%s\n”,pbuf,pbuf2);
/* PRINT ERR MSG */
printf(“%s\n”,snmp_errstring(pdu->errstat));
/** NOTE BIG ASSUMPTION: WE ASSUME THE PACKET RETURNS THE SAME
WAY WE SENT IT OUT IN ORDER TO SPLIT OID AND INSTANCE UP */
i=0;
vp=pdu->variables;
while (vp) {
/* PRINT OID AND INSTANCE */
snprint_objid(buf,sizeof(buf),vp->name,vp->name_length);
if ( (hostptr->oid[i].Instance)>0) {
buf2=strncnt(buf,(strcnt(buf,strlen(buf))-hostptr->oid[i].Instance),strlen(buf));
} else {
buf2=strrchr(buf,’.’);
*buf2=’\0′;
buf2++;
}
stresc(pbuf,buf,strlen(buf));
stresc(pbuf2,buf2,strlen(buf2));
printf(“%s\n%s\n”,pbuf,pbuf2);

/* PRINT VAL AND TYPE */
/* NOTE TYPE INFO IS FAKE AND REQURIES -s “q” OPTION */
printf(“-\n”);
if ( i == count<pdu->errindex) {
printf(“%s\n”,snmp_errstring(pdu->errstat));
} else {
printf(“NULL\n”);
}
/* PRINT TERMINATOR FOR OID */
printf(“~\n”);
i++;
vp = vp->next_variable;
}

/** DLN20050302: Some don’t return any oids on error. Guess. */
} else {
if ( debug & 128 ) {
fprintf(stderr,”%s: No OIDs found in SNMP packet. Probably
a bug. We’ll try to fake it with what we go back.\n”,prog);
}

/* FIRST ENTRY HAS REAL DATA */
count2=0;
for (count=0; hostptr->oid[0].Name[count]; count++) {
count2=(hostptr->oid[0].Name[count] == ‘.’)?count:count2;
}
stresc(pbuf,hostptr->oid[0].Name,count2);

/** ADDED ***/
if ( 128 & debug ) {
fprintf(stderr,”%s:CREATING ENTRY WITH: NAME: %s\n”,prog,hostptr->name);
fprintf(stderr,”%s:   HASH KEY: %s\n”,prog,hostptr->hashkey);
fprintf(stderr,”%s:   NUM: %s\n”,prog,q->num);
fprintf(stderr,”%s:   COMMUNITY: %s\n”,prog,hostptr->community);
fprintf(stderr,”%s:   SNMP VER: %d\n”,prog,hostptr->snmpver);
fprintf(stderr,”%s:   OID  IDX USED: %d\n”,prog,i);
fprintf(stderr,”%s:   OID: %s\n”,prog,hostptr->oid[i].Name);
fprintf(stderr,”%s:   OID LAST DOT: %d\n”,prog,count2);
fprintf(stderr,”%s:   DEST %s SOURCE %s BUFFERS,prog,pbuf,hostptr->oid[0]);
}

printf(“%s\n0\n%s\n%s\n%d\n-\n%s\n”,
pbuf,
snmp_errstring(pdu->errstat),
pbuf,
hostptr->oid[0].Instance,
snmp_errstring(pdu->errstat)
);
printf(“~\n”);

/* NULL OUT EVERYTHING ELSE */
for (i=1; i<hostptr->oidmax; i++) {
count2=0;
for (count=0; hostptr->oid[i].Name[count]; count++) {
count2=(hostptr->oid[i].Name[count] == ‘.’)?count:count2;
}
stresc(pbuf,hostptr->oid[i].Name,count2);
printf(“%s\n0\n-\nNULL\n~\n”,pbuf);
}
}
printf(“~~\n”);
break;
}
return 0; /* OK RETRY SINCE WE GOT THROUGH BUT 1 OID BAD */
}
case STAT_TIMEOUT:
if ( debug & 128 ) {
fprintf(stderr,”%s: TIMEOUT ON SNMP RESPONSE. Format:
%d\n”,prog,format);
}
vp = pdu->variables;
switch ( format ) {
case 0:
fprintf(stdout, “%s\n%s\n”,hostptr->hashkey,hostptr->num);
fprintf(stdout, “%s\n2\n”,sp->peername);
while (vp) {
fprint_objid(stdout,vp->name,vp->name_length);
fprintf(stdout, “timeout\n”);
vp = vp->next_variable;
}
fprintf(stdout,”~\n”);
break;
case 1:
while (vp) {
fprintf(stdout, “%s\n%s\n”,hostptr->hashkey,hostptr->num);
fprintf(stdout, “%s\n2\n”,sp->peername);
fprint_objid(stdout,vp->name,vp->name_length);
fprintf(stdout, “timeout\n~\n”);
vp = vp->next_variable;
}
break;
default:
printf(“%s\n%s\n”,hostptr->hashkey,hostptr->num);
printf(“-1\nNULL\nNULL\ntimeout\n”); /* ERR NUM, OID, INSTANCE,
ERR MSG */
/** NOTE BIG ASSUMPTION: WE ASSUME THE PACKET RETURNS THE SAME
WAY WE SENT IT OUT IN ORDER TO SPLIT OID AND INSTANCE UP */
i=0;
while (vp) {
/* PRINT OID AND INSTANCE */
snprint_objid(buf,sizeof(buf),vp->name,vp->name_length);
if ( (hostptr->oid[i].Instance)>0) {

buf2=strncnt(buf,(strcnt(buf,strlen(buf))-hostptr->oid[i].Instance),strlen(buf))
;
} else {
buf2=strrchr(buf,’.’);
*buf2=’\0′;
buf2++;
}
stresc(pbuf,buf,strlen(buf));
stresc(pbuf2,buf2,strlen(buf2));
printf(“%s\n%s\n”,pbuf,pbuf2);

/* PRINT VAL AND TYPE */
/* NOTE TYPE INFO IS FAKE AND REQURIES -s “q” OPTION */
printf(“-\nNULL\n”);
/* PRINT TERMINATOR FOR OID */
printf(“~\n”);
i++;
vp = vp->next_variable;
}
printf(“~~\n”);
break;
}
return 1;
case STAT_ERROR:
if ( debug & 128 ) {
fprintf(stderr,”%s: FAULTY SNMP RESPONSE.\n”,prog);
}
if ( debug & 128 ) {
fprintf(stderr,”%s: ERROR: %d.\n”,prog,status);
}
fprintf(stdout, “%s\n%s\n”,hostptr->hashkey,hostptr->num);
fprintf(stdout, “%s\n3\n”,sp->peername);
fprintf(stdout,”Unknown_ObjID_Massive_Error_Detected”);
snmp_perror(sp->peername);
fprintf(stdout,”~\n”);
return 1;
}
return 1;
}

/***************************************************************************
* ASYNCH_RESPONSE: RESPOND TO SNMP RESPONSE FROM REMOTE AGENT
***************************************************************************/
int asynch_response(int operation, struct snmp_session *sp, int reqid,
struct snmp_pdu *pdu, void *magic)
{
struct session *host = (struct session *)magic;
int count;
struct variable_list *vars;

if ( debug & 128 ) {
fprintf(stderr,”%s: Entering Asynch_Response: “,prog);
}
if (operation == NETSNMP_CALLBACK_OP_RECEIVED_MESSAGE) {
if ( debug & 128 ) {
fprintf(stderr,”SUCCESS!\n”);
}
print_result(STAT_SUCCESS, host->sess, pdu, host->hostptr);
} else {
if ( debug & 128 ) {
fprintf(stderr,”TIMEOUT!\n”);
}
print_result(STAT_TIMEOUT, host->sess, pdu, host->hostptr);
}
/* FLAG HOSTS TO FREE (SELECT() MIGHT HAVE ALREADY OCCURED SO
WE CANNOT JUST snmp_close(host->sess) HERE PREMATURELY */
host->hostid=-1;
return 1;
}

/***************************************************************************
* ASYNCHRONOUS: SET UP SESSIONS AND RECEIVE PACKETS.
***************************************************************************/
void Asynchronous(void)
{
struct session *hs;
struct session *hs2;
struct host *hp;
struct oid *q;
int i;
int fds = 0, block = 1;
int opensocks=0;
fd_set fdset;
struct timeval timeout;

/* startup all hosts */
hs = sessions;
hp = hosts;
while(hp->name) {
/* INITIATE A NEW SESSION AND SEND A PACKET IF NOT TOO MANY */
if ( active_hosts < maxconcurrentsess ) {
struct snmp_pdu *req;
struct snmp_session sess;
snmp_sess_init(&sess);            /* initialize session */
sess.version = hp->snmpver;
sess.peername = hp->name;
sess.community = hp->community;
sess.community_len = strlen(sess.community);
sess.timeout = sess_timeout;
sess.callback = asynch_response;        /* default callback */
sess.callback_magic = hs;
/* PROBLEM RACHET BACK TOTAL NUMBER OF OPEN FILES */
if (!(hs->sess = snmp_open(&sess))) {
if ( debug & 4 || debug & 8) {
fprintf(stderr,”Opened %d sockets; wanted %d; will use only
%d\n”,active_hosts,maxconcurrentsess,active_hosts-CONCURRENTBUFF);
snmp_perror(“snmp_open”);
}
maxconcurrentsess=active_hosts-CONCURRENTBUFF;
if ( maxconcurrentsess < 100 ) {
fprintf(stderr,”Repetative racheting back of sockets failed to
fix chronic problem:\n”);
snmp_perror(“snmp_open”);
exit(4);
} else {
continue;
}
} else {
hs->current_oid = &(hp->oid[0]);
/* USED TO TRACK FINISHED SESSIONS */
hs->hostid = hp->hostid;
/* USED TO ACCESS TEH HOST STRUCTURE TO PRINT OUT HASHKEY AND NUM */
hs->hostptr = hp;

if ( debug & 4 ) {
fprintf(stderr,”ADDING: %d\nACTIVE HOSTS: %d\nMAXCONCURRENT:
%d\n”,hs->hostid,active_hosts,maxconcurrentsess);
}
q = &(hp->oid[0]);
req = snmp_pdu_create(SNMP_MSG_GET);    /* send the first GET */
/*req = snmp_pdu_create(SNMP_MSG_GETNEXT); *//* send the first GET */
/* STACK UP OIDS FOR A PARTICULAR NODE */
for ( q=&(hp->oid[0]); q->Name; q++) {
if ( debug & 4 ) {
fprintf(stderr,”ADDING: %d:%s %s (%d)
%d\n”,hs->hostid,hp->name,q->Name,q->OidLen,hp->snmpver);
}
select_time++;
snmp_add_null_var(req, q->Oid, q->OidLen);
}
/* DLN20000122 MAKE TIMEOUTS NICE SINCE INTEL’S CLOCK IS MESSED */
if ( select_time > skips ) {
if ( intra_timeout ) {
usleep( intra_timeout);
} else {
usleep( 1 );
}
select_time=0;
}
/* DLN20000122 END */
if (snmp_send(hs->sess, req)) {
active_hosts++;
} else {
snmp_perror(“snmp_send”);
snmp_free_pdu(req);
}
hs++; hp++;
}
} else {
/* TOO MANY OUTSTANDING SESSION; PROCESS A FEW */
fds = 0, block = 1; opensocks=0;
FD_ZERO(&fdset);
/* CLOSE SESSIONS NOT ALREADY CLOSED MARKED FOR CLOSURE */
for (hs2 = sessions; hs2->sess; hs2++) {
if ((hs2->hostid==-1) && (hs2->sess)) {
snmp_close(hs2->sess);
active_hosts–;
hs2->hostid=-2;
}
}
/* OK NOW ENTER AND DO WORK */
opensocks=snmp_select_info(&fds, &fdset, &timeout, &block);
/* WAIT TIME (BLOCK=0 MEANS USE IT) */
fds = select(fds, &fdset, NULL, NULL, block ? NULL : &timeout);
if (fds > 0) {
snmp_read(&fdset);
} else if ( fds == 0 ) {
snmp_timeout();
} else {
fprintf(stderr,”select failed with errno==%d\n”,errno);
exit(4);
}
}
}
if ( debug & 128 ) {
fprintf(stderr,”%s: Done sending packets.\n”,prog);
}

while (active_hosts) {
fds = 0, block = 1; opensocks=0;
FD_ZERO(&fdset);
/* CLOSE SESSIONS NOT ALREADY CLOSED MARKED FOR CLOSURE */
/* loop while any active hosts */
if ( debug & 128 ) {
fprintf(stderr,”%s: Closing marked sessions.\n”,prog);
}
for (hs2 = sessions; hs2->sess; hs2++) {
if ((hs2->hostid==-1) && (hs2->sess)) {
snmp_close(hs2->sess);
active_hosts–;
hs2->hostid=-2;
}
}
/* IF LAST ONE CLOSED DON’T WAIT ON SNMP */
if ( debug & 128 ) {
fprintf(stderr,”%s: Active Sessions: %d\n”,prog,active_hosts);
}
if (!active_hosts) {
if ( debug & 128 ) {
fprintf(stderr,”%s: No more sessions. Returning.\n”,prog);
}
break;
}
if ( debug & 128 ) {
fprintf(stderr,”%s: Open Socks.\n”,prog);
}
opensocks=snmp_select_info(&fds, &fdset, &timeout, &block);
if ( debug & 128 ) {
fprintf(stderr,”%s: Perform Select.\n”,prog);
}
fds = select(fds, &fdset, NULL, NULL, block ? NULL : &timeout);
if (fds > 0) {
if ( debug & 128 ) {
fprintf(stderr,”%s: snmp_read fds>0.\n”,prog);
}
snmp_read(&fdset);
} else if ( fds == 0 ) {
if ( debug & 128 ) {
fprintf(stderr,”%s: snmp_timeout fds=0.\n”,prog);
}
snmp_timeout();
} else {
fprintf(stderr,”select failed with errno==%d\n”,errno);
exit(4);
}
}
/* CLOSE ANY SESSIONS STILL OPEN */
if ( debug & 128 ) {
fprintf(stderr,”%s: Close outstanding sessions\n”,prog);
}
for (hp = hosts, hs = sessions; hp->name; hs++, hp++) {
if (hs->sess) snmp_close(hs->sess);
}
}

int main(int argc, char **argv) {

/* IF DEBUG & 2 THEN BENCHMARK RUN TIME */
struct timeval now;   /* START TIME */
struct timeval then;   /* END TIME */
struct timezone tz;   /* TIMEZONE */
struct tm *tm;        /* USED FOR PRINTING TIMES */
double ft;            /* TOTAL RUN TIME IN SECONDS */

struct rlimit resource; /* USED FOR getrlimit,setrlimit */

/* SET GLOBALS */
prog=argv[0];  /* PROGRAM NAME */
opterr=0;      /* GET OPTION ERROR CODE */

/* GET OPTIONS */
GetOptions(argc,argv);

/* BENCHMARK START TIME. SEE END OF MAIN FOR RUNTIME CALC */
/* CAN ONLY OCCUR AFTER GETOPT SINCE DEBUG SET IN GETOPT */
if ( debug & 2) {
/*
gettimeofday(&now, &tz);
tm = localtime((time_t *)&now.tv_sec);
*/
}

/* MAKE SURE WE CAN OPEN ALL THES SOCKETS (FILES) */
getrlimit(RLIMIT_NOFILE,&resource);
maxconcurrentsess=MAXCONCURRENTSESS;
if (resource.rlim_cur<maxconcurrentsess) {
resource.rlim_cur=maxconcurrentsess;
if (resource.rlim_cur<maxconcurrentsess) {
resource.rlim_max=maxconcurrentsess;
}
if (setrlimit(RLIMIT_NOFILE,&resource) < 0 ) {
getrlimit(RLIMIT_NOFILE,&resource);
if ( debug & 1 ) {
fprintf(stderr,”%s: W: Needed %d file handles only have %d for
sockets.\n”,prog,maxconcurrentsess,resource.rlim_cur);
}
maxconcurrentsess=resource.rlim_cur;
}
}
maxconcurrentsess-=CONCURRENTBUFF;
getrlimit(RLIMIT_NOFILE,&resource);

init_snmp(NULL);

/* LOAD HOSTS TABLE */
if ( debug & 2 ) {
fprintf(stderr,”%s: LOADED %d HOSTS\n”,prog,LoadHostTable());
} else {
LoadHostTable();
}
/* REGISTER CALL BACKS AND SEND PACKETS */

if ( debug & 128 ) {
fprintf(stderr,”%s: Enter Async.\n”,prog);
}
Asynchronous();
if ( debug & 128 ) {
fprintf(stderr,”%s: Leaving Async.\n”,prog);
}

/* BENCHMARK */
if ( debug & 2) {
/*
fprintf(stderr, “From %.2d:%.2d:%.2d.%.6d “, tm->tm_hour, tm->tm_min,
tm->tm_sec, now.tv_usec);
gettimeofday(&then, &tz);
tm = localtime((time_t *)&then.tv_sec);
fprintf(stderr, “to %.2d:%.2d:%.2d.%.6d\n”, tm->tm_hour, tm->tm_min,
tm->tm_sec, then.tv_usec);
ft=timeval_diff(&then,&now)/100000;
fprintf(stderr, “TOTAL: %f\n”,ft);
*/
}
/* PRINT FINAL TERMINATOR */
printf(“~~~\n”);
}

Leave a Reply

*
To prove you're a person (not a spam script), type the security word shown in the picture. Click on the picture to hear an audio file of the word.
Anti-spam image