Hi all,
I'm working on an snmp application, written in C, that requests information from many devices (I'm working with many "H3C S3600" switches and some debian hosts).
You can find a simple extract of program with some test cases attached.

When I run the program, some requests working, others gone inexplicably in timeout. (All configuration are equals and all hosts are up)
I tried with "normal" APIs and "threaded" APIs with this configurations:

Sequential requests in "for cycle":
* v2 - 1 host        - multiple request(20000 retries)=> OK
* v2 - multiple host - single/multiple request          => OK
* v3 - 1 host        - multiple request (20000 retries)=> OK
* *v3* - *multiple host* - single/multiple request     => *FAIL*

Sequential requests in single program, one *fork* for request OR one *fork+exec* for request:
* v2 - 1 host        - multiple request (20000 retries)=> OK
* v2 - multiple host - single/multiple request          => OK
* v3 - 1 host        - multiple request (20000 retries)=> OK
* v3 - multiple host - single/multiple request          => *OK*

Here an example of output: (use ./test -h for usage)
$ ./test -r20 -t wrapper-v2 192.168.0.{2,4,5,6,7}
== Run 19 ==
DISMAN-EVENT-MIB::sysUpTimeInstance = Timeticks: (2530553) 7:01:45.53
DISMAN-EVENT-MIB::sysUpTimeInstance = Timeticks: (2530573) 7:01:45.73
DISMAN-EVENT-MIB::sysUpTimeInstance = Timeticks: (2530567) 7:01:45.67
DISMAN-EVENT-MIB::sysUpTimeInstance = Timeticks: (2530516) 7:01:45.16
DISMAN-EVENT-MIB::sysUpTimeInstance = Timeticks: (2530591) 7:01:45.91
... etc. etc. ...
== Run 0 ==
DISMAN-EVENT-MIB::sysUpTimeInstance = Timeticks: (2530622) 7:01:46.22
DISMAN-EVENT-MIB::sysUpTimeInstance = Timeticks: (2530641) 7:01:46.41
DISMAN-EVENT-MIB::sysUpTimeInstance = Timeticks: (2530635) 7:01:46.35
DISMAN-EVENT-MIB::sysUpTimeInstance = Timeticks: (2530584) 7:01:45.84
DISMAN-EVENT-MIB::sysUpTimeInstance = Timeticks: (2530658) 7:01:46.58

$ ./test -r1 -t wrapper *-v3* 192.168.0.{2,4,5,6,7}
== Run 0 ==
DISMAN-EVENT-MIB::sysUpTimeInstance = Timeticks: (2110922) 5:51:49.22
DISMAN-EVENT-MIB::sysUpTimeInstance = Timeticks: (2110947) 5:51:49.47
*Timeout*: No response from 192.168.0.5. <= *FAIL*
*Timeout*: No response from 192.168.0.6.<= *FAIL*
*Timeout*: No response from 192.168.0.7.<= *FAIL*

$ ./test -r2 -t thread -v2 192.168.0.{2,4,5,6,7}
== Run 1 ==
DISMAN-EVENT-MIB::sysUpTimeInstance = Timeticks: (2795135) 7:45:51.35
DISMAN-EVENT-MIB::sysUpTimeInstance = Timeticks: (2795154) 7:45:51.54
DISMAN-EVENT-MIB::sysUpTimeInstance = Timeticks: (2795149) 7:45:51.49
DISMAN-EVENT-MIB::sysUpTimeInstance = Timeticks: (2795095) 7:45:50.95
DISMAN-EVENT-MIB::sysUpTimeInstance = Timeticks: (2795171) 7:45:51.71
== Run 0 ==
DISMAN-EVENT-MIB::sysUpTimeInstance = Timeticks: (2795138) 7:45:51.38
DISMAN-EVENT-MIB::sysUpTimeInstance = Timeticks: (2795157) 7:45:51.57
DISMAN-EVENT-MIB::sysUpTimeInstance = Timeticks: (2795152) 7:45:51.52
DISMAN-EVENT-MIB::sysUpTimeInstance = Timeticks: (2795098) 7:45:50.98
DISMAN-EVENT-MIB::sysUpTimeInstance = Timeticks: (2795174) 7:45:51.74

$ ./test -r2 -t thread *-v3* 192.168.0.{2,4,5,6,7}
== Run 1 ==
DISMAN-EVENT-MIB::sysUpTimeInstance = Timeticks: (2795664) 7:45:56.64
DISMAN-EVENT-MIB::sysUpTimeInstance = Timeticks: (2795688) 7:45:56.88
192.168.0.5: SNMP write error: Timeout.<= *FAIL*
192.168.0.6: SNMP write error: Timeout.<= *FAIL*
192.168.0.7: SNMP write error: Timeout.<= *FAIL*
== Run 0 ==
192.168.0.2: SNMP write error: Timeout.
DISMAN-EVENT-MIB::sysUpTimeInstance = Timeticks: (2798112) 7:46:21.12
192.168.0.5: SNMP write error: Timeout.<= *FAIL*
192.168.0.6: SNMP write error: Timeout.<= *FAIL*
192.168.0.7: SNMP write error: Timeout.<= *FAIL*

I really appreciate any help on this issue.
Best regards

    Marco Bascetta

--

Marco Bascetta - Sadel SpA
Software Development
Via Serenari 9, Castel Maggiore (BO)
Tel. 051 705884 Int. 2501

CC        = gcc
CFLAGS    = -I. `net-snmp-config --cflags` -g -ggdb -Wall
BUILDLIBS = `net-snmp-config --libs`

all: test

#Compile all
.c.o:
        gcc -c $(CFLAGS) -o $@ $<

#Compile test
test: clean my_api.o 
        $(CC) $(CFLAGS) -o test     test.c     $(BUILDLIBS) my_api.o
        $(CC) $(CFLAGS) -o test_api test_api.c $(BUILDLIBS) my_api.o

#Clean rules
.PHONY clean:
        rm -f test test_api *.o core
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>

#include <sys/time.h>

#include <net-snmp/net-snmp-config.h>
#include <net-snmp/net-snmp-includes.h>

#include "my_api.h"

#define MY_USER "my_username"
#define MY_PASS "my_password"

static void deinitSession(netsnmp_session *session);
static void deinitSession_v3(netsnmp_session *session);

static void initSession(netsnmp_session *session,const char *address);
static void initSession_v3(netsnmp_session *session);
static void initSession_v2c(netsnmp_session *session);

static char* sUpTime   =  "1.3.6.1.2.1.1.3.0" ;
static oid   oUpTime[] = { 1,3,6,1,2,1,1,3,0 };

static int input_version = 0;

int request(const char *address, int type, int version) {
    int iRetVal = -1;
    input_version = version;
    SOCK_STARTUP;
    switch( type ) {
        case D_WRAPPER:
            iRetVal = request_wrapper(address);
            break;
        case D_API:
            iRetVal = request_api(address);
            break;
        case D_THREAD:
            iRetVal = request_thread(address);
            break;
        default:
            break;
    }
    SOCK_CLEANUP;

    return iRetVal;
}

int request_wrapper( const char *address) {
    int iRetVal = 0;
    //netsnmp_session session, *ss;
    netsnmp_session *ss;
    netsnmp_session *session = (netsnmp_session*)malloc(sizeof(netsnmp_session));

    netsnmp_variable_list *vars = NULL;

    // Initialize the SNMP library
    init_snmp(address);

    // Initialize a "session" that defines who we're going to talk to
    memset(session,0,sizeof(netsnmp_session));
    snmp_sess_init( session ); // set up defaults

    initSession(session,address);


    // Open the session
    if( (ss = snmp_open( session )) != NULL) {
        if( ( snmp_varlist_add_variable( &vars  , oUpTime, OID_LENGTH(oUpTime), ASN_NULL, NULL,  0)) != NULL ) {

            if( netsnmp_query_get( vars, ss ) == SNMP_ERR_NOERROR ) {
                netsnmp_variable_list *curVar;
                for( curVar = vars; curVar; curVar = curVar->next_variable ) {
                    print_variable( curVar->name, curVar->name_length, curVar );
                }
            } else {
                snmp_sess_perror( address, ss );
                iRetVal = -1;
            }

            snmp_free_varbind(vars);
        } else {
            fprintf(stderr,"[%s:%d] Unable to add Variable\n",__FUNCTION__,__LINE__);
        }
    } else {
        snmp_sess_perror( "ack", session );
        iRetVal = 1;
    }

    deinitSession(session);

    return iRetVal;
}

int request_api( const char *address ) {
    int status, iRetVal = 0;

    netsnmp_session session, *ss;
    netsnmp_pdu *pdu      = NULL,
                *response = NULL;
    netsnmp_variable_list *vars;

    oid anOID[MAX_OID_LEN];
    size_t anOID_len;

    // Initialize the SNMP library
    init_snmp(address);
    // Initialize a "session" that defines who we're going to talk to
    snmp_sess_init(&session); // set up defaults

    initSession(&session,address);

    // Open the session
    ss = snmp_open( &session ); // establish the session
    if (!ss) {
        snmp_perror("snmp_open");
        return -1;
    }

    // Create the PDU for the data for our request.
    // 1) We're going to GET the system.sysDescr.0 node.
    pdu = snmp_pdu_create( SNMP_MSG_GET );
    anOID_len = MAX_OID_LEN;
    if (!snmp_parse_oid( sUpTime, anOID, &anOID_len )) {
        snmp_perror( sUpTime );
        return -1;
    }

    snmp_add_null_var( pdu, anOID, anOID_len );

    // Send the Request out.
    status = snmp_synch_response( ss, pdu, &response );

    // Process the response.
    if( status==STAT_SUCCESS && response->errstat==SNMP_ERR_NOERROR ) {
        // SUCCESS: Print the result variables
        for( vars = response->variables; vars; vars = vars->next_variable ) {
            print_variable( vars->name, vars->name_length, vars );
        }
    } else {
        // FAILURE: print what went wrong!
        if( status == STAT_SUCCESS )
            fprintf( stderr, "Error in packet\nReason: %s\n", snmp_errstring( response->errstat ) );
        else if( status == STAT_TIMEOUT )
            fprintf( stderr, "Timeout: No response from %s.\n", session.peername );
        else
            snmp_perror( address );

        iRetVal = -1;
    }

    // Clean up:
    //  1) free the response.
    //  2) close the session.
    if (response)
        snmp_free_pdu( response );
    snmp_close( ss );

    deinitSession(&session);
//    free(session.peername);

//    snmp_shutdown(address); //TRY...
    return iRetVal;
}

int request_thread( const char *address ) {
    int status, iRetVal = 0;

    int liberr, syserr;
    char *errstr;
    void *sessp; /* <-- an opaque pointer, not a struct pointer */
    struct snmp_session session, *sptr;

    netsnmp_pdu *pdu      = NULL,
                *response = NULL;
    netsnmp_variable_list *vars;

    oid anOID[MAX_OID_LEN];
    size_t anOID_len;

    // Initialize the SNMP library
    init_snmp(address);
    // Initialize a "session" that defines who we're going to talk to
    snmp_sess_init(&session); // set up defaults

    initSession(&session,address);

    // Open the session
    sessp = snmp_sess_open( &session );
    if (sessp==NULL) {
        /* Error codes found in open calling argument */
        snmp_error( &session, &liberr, &syserr, &errstr );
        printf("SNMP create error %s.\n", errstr );
        free( errstr );
        return -1;
    }
    sptr = snmp_sess_session( sessp ); /* <-- get the snmp_session pointer */

    // Create the PDU for the data for our request.
    // 1) We're going to GET the system.sysDescr.0 node.
    pdu = snmp_pdu_create( SNMP_MSG_GET );
    anOID_len = MAX_OID_LEN;
    if (!snmp_parse_oid( sUpTime, anOID, &anOID_len )) {
        snmp_sess_perror( sUpTime,sptr );
        return -1;
    }

    snmp_add_null_var( pdu, anOID, anOID_len );

//    /* Pass sptr to snmp_sess_error from here forward */
//    /* Change the community name */
//    free(sptr->community);
//    sptr->community = (u_char*)strdup( "public" );
//    sptr->community_len = strlen( "public" );

    status = snmp_sess_synch_response(sessp, pdu,&response);

    if( status==STAT_SUCCESS && response->errstat==SNMP_ERR_NOERROR ) {
        // SUCCESS: Print the result variables
        for( vars = response->variables; vars; vars = vars->next_variable ) {
            print_variable( vars->name, vars->name_length, vars );
        }

    } else {
        snmp_sess_error(sessp, &liberr, &syserr, &errstr);
        fprintf(stderr,"%s: SNMP write error: %s.\n",address,errstr);
        free(errstr);
        iRetVal = -1;
    }

    // Clean up:
    //  1) free the response.
    //  2) close the session.
    if (response)
        snmp_free_pdu( response );
    snmp_sess_close( sessp );

    deinitSession(&session);

    return iRetVal;
}

static void deinitSession(netsnmp_session *session) {
    free(session->peername);
    if( input_version == 3 )
        deinitSession_v3(session);
}

static void deinitSession_v3(netsnmp_session *session) {
    free(session->securityName);
}

static void initSession(netsnmp_session *session,const char *address) {
    session->peername = strdup(address);
    if( input_version == 3 ) {
        initSession_v3(session);
    } else if( input_version == 2 ) {
        initSession_v2c(session);
    } else {
        fprintf(stderr,"version urecognized: %d\n",input_version);
    }
}

static void initSession_v2c(netsnmp_session *session) {
    // set the SNMP version number
    session->version=SNMP_VERSION_2c;

    session->community = (u_char*)"public";
    session->community_len = strlen((char*)session->community);
}

static void initSession_v3(netsnmp_session *session) {
    // set the SNMP version number
    session->version=SNMP_VERSION_3;

    // set the SNMPv3 user name
    session->securityName    = strdup(MY_USER);
    session->securityNameLen = strlen(session->securityName);

    // set the security level to authenticated, but not encrypted
    session->securityLevel = SNMP_SEC_LEVEL_AUTHPRIV;

    // set the authentication method to SHA1
    session->securityAuthProto    = usmHMACSHA1AuthProtocol;
    session->securityAuthProtoLen = USM_AUTH_PROTO_SHA_LEN;
    session->securityAuthKeyLen   = USM_AUTH_KU_LEN;

    // set the privacy method to AES
    session->securityPrivProto    = usmAESPrivProtocol;
    session->securityPrivProtoLen = USM_PRIV_PROTO_AES_LEN;
    session->securityPrivKeyLen   = USM_PRIV_KU_LEN;

    // set the authentication key to a SHA1 hashed version of our passphrase
    if (generate_Ku(session->securityAuthProto, session->securityAuthProtoLen,
                    (u_char *) MY_PASS, strlen(MY_PASS),
                    session->securityAuthKey, &session->securityAuthKeyLen) != SNMPERR_SUCCESS) {
        snmp_perror("test_ authentication");
//        snmp_log(LOG_ERR, "Error generating Ku from authentication pass phrase. \n");
        exit(1);
    }

    // set the privacy key to a AES hashed version of our passphrase
    if (generate_Ku(session->securityAuthProto, session->securityAuthProtoLen,
                    (u_char *) MY_PASS, strlen(MY_PASS),
                    session->securityPrivKey, &session->securityPrivKeyLen) != SNMPERR_SUCCESS) {
        snmp_perror("test_ privacy");
//        snmp_log(LOG_ERR, "Error generating Ku from privacy pass phrase. \n");
        exit(1);
    }
}
#ifndef __TEST_API_H__
#define __TEST_API_H__

    #define D_NULL          0
    #define D_API           1
    #define D_WRAPPER       2
    #define D_THREAD        3

    int request(const char *address, int type, int version);
    int request_wrapper(const char *address);
    int request_api(const char *address);
    int request_thread(const char *address);
#endif
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>

#include <sys/types.h>
#include <sys/wait.h>

#include <unistd.h>
#include <getopt.h>

#include "my_api.h"

//extern char **environ;

static void usage( char *program );
static void parseOptions( int argc, char **argv );
static void checkOptions( int argc, char *program );
static void exit_check_fail( char *program, char opt );

static char *input_version_str = NULL;
static int   input_version     = 0;
static char *input_type_str    = NULL;
static int   input_type        = 0;
static int   input_repeat      = 0;
static int   verboseMode       = 0;
static int   execMode          = 0;
static int   forkMode          = 0;

int main( int argc, char *argv[] ) {
    int iRetVal = 0;

    parseOptions( argc, argv );
    checkOptions( argc, argv[0] );

    while( input_repeat-- ) {
        int idx = optind;
        printf( "== Run %d ==\n", input_repeat );

        for( ; idx<argc; idx++ ) {
            
            if( execMode || forkMode ) {
                int pid = fork();

                if( pid < 0 ) {
                    fprintf(stderr,"CAZZO, unable to fork\n");
                } else if( pid == 0 ) { //SON
                    printf("  Host[%d]: %s === ", getpid(), argv[idx] ); fflush(stdout);
                    
                    if( execMode ) {
                        execl("./test_api","test_api","-v",input_version_str,"-t",input_type_str,argv[idx],NULL);
                    } else {
                        request(argv[idx],input_type,input_version);
                    }
                    return 0;
                } else { //padre
                    int status;
                    wait(&status);
                }
            } else {
                request(argv[idx],input_type,input_version);
            }
        }
    }

    return iRetVal;
}


static void parseOptions( int argc, char **argv ) {
    int opt;
    int longindex;
    static struct option long_options[] = {
            { "exec"   , 0, 0, 'e' },
            { "fork"   , 0, 0, 'f' },
            { "help"   , 0, 0, 'h' },
            { "repeat" , 1, 0, 'r' },
            { "type"   , 1, 0, 't' },
            { "version", 1, 0, 'v' },
            { "verbose", 0, 0, 'V' },
            { 0, 0, 0, 0 }
    };

    while( ( opt = getopt_long( argc, argv, "efhr:t:v:V", long_options, &longindex ) ) !=-1 ) {
        switch( opt ) {
            case 'e':
                execMode = 1;
                break;
            case 'f':
                forkMode = 1;
                break;
            case 'r':
                input_repeat = atoi( optarg );
                break;
            case 't':
                input_type_str = optarg;
                if( strcmp("api", optarg) == 0 ) {
                    input_type = D_API;
                } else if( strcmp("thread", optarg) == 0 ) {
                    input_type = D_THREAD;
                } else if( strcmp("wrapper", optarg) == 0 ) {
                    input_type = D_WRAPPER;
                } else {
                    input_type     = D_NULL;
                    input_type_str = NULL;
                }

                break;
            case 'v':
                input_version = atoi( optarg );
                input_version_str = optarg;
                break;
            case 'V':
                verboseMode = 1;
                break;
            case 'h':
            default:
                usage( argv[0] );
                exit( 0 );
        }
    }
}

static void checkOptions( int argc, char *program ) {
    if(input_repeat<1) {
        exit_check_fail( program, 'r' );
    }
    if( input_version != 2 && input_version != 3  ) {
        exit_check_fail( program, 'v' );
    }
    
    if( input_type == D_NULL ) {
        exit_check_fail( program, 't' );
    }

    if( forkMode && execMode ) {
        fprintf( stderr, "Could not use -f and -e flags together.\n" );
        usage( program );
        exit( -1 );
    }

    if (optind==argc) {
        fprintf( stderr, "No hostname specified.\n" );
        usage( program );
        exit( -1 );
    }
}

static void exit_check_fail( char *program, char opt ) {
    fprintf( stderr,
            "Option -%c omitted or wrong value\n", opt );
    usage( program );
    exit( -1 );
}

static void usage( char *program ) {
    printf( "usage: %s <options> hostname...\n", program );
    printf( "\t -r, --repeat <repeat_times>\n\t\t Numbers of repeat\n" );
    printf( "\t -t, --type <type of API used>\n\t\t Type of api used to query hosts (api|thread|wrapper) \n" );
    printf( "\t -v, --version\n\t\t Specify SNMP version (2|3)\n" );
    printf( "\t -V, --verbose\n\t\t Verbose output\n" );
    printf( "\t -h, --help\n\t\t Show this help\n" );
}
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>

#include <net-snmp/net-snmp-config.h>
#include <net-snmp/net-snmp-includes.h>

#include "my_api.h"

static void usage( char *program );
static void parseOptions( int argc, char **argv );
static void checkOptions( int argc, char *program );
static void exit_check_fail( char *program, char opt );

static int input_type    = 0;
static int input_version = 0;
static int verboseMode   = 0;

int main( int argc, char *argv[] ) {
    int iRetVal = 0;

    parseOptions( argc, argv );
    checkOptions( argc, argv[0] );

    request(argv[optind],input_type,input_version);
    
    return iRetVal;
}

static void parseOptions( int argc, char **argv ) {
    int opt;
    int longindex;
    static struct option long_options[] = {
            { "help"   , 0, 0, 'h' },
            { "type"   , 1, 0, 't' },
            { "version", 1, 0, 'v' },
            { "verbose", 0, 0, 'V' },
            { 0, 0, 0, 0 }
    };

    while( ( opt = getopt_long( argc, argv, "ht:v:V", long_options, &longindex ) ) !=-1 ) {
        switch( opt ) {
            case 't':
                if( strcmp("api", optarg) == 0 ) {
                    input_type = D_API;
                } else if( strcmp("thread", optarg) == 0 ) {
                    input_type = D_THREAD;
                } else if( strcmp("wrapper", optarg) == 0 ) {
                    input_type = D_WRAPPER;
                } else {
                    input_type = D_NULL;
                }
                break;
            case 'v':
                input_version = atoi( optarg );
                break;
            case 'V':
                verboseMode = 1;
                break;
            case 'h':
            default:
                usage( argv[0] );
                exit( 0 );
        }
    }
}

static void checkOptions( int argc, char *program ) {
    if( input_version != 2 && input_version != 3  ) {
        exit_check_fail( program, 'v' );
    }

    if( input_type == D_NULL ) {
        exit_check_fail( program, 't' );
    }

    if (optind==argc) {
        fprintf( stderr, "No hostname specified.\n" );
        usage( program );
        exit( -1 );
    }
}

static void exit_check_fail( char *program, char opt ) {
    fprintf( stderr,
            "Option -%c omitted or wrong value\n", opt );
    usage( program );
    exit( -1 );
}

static void usage( char *program ) {
    printf( "usage: %s <options> hostname...\n", program );
    printf( "\t -t, --type <type of API used>\n\t\t Type of api used to query hosts (api|thread|wrapper) \n" );
    printf( "\t -v, --version\n\t\t Specify SNMP version (2|3)\n" );
    printf( "\t -V, --verbose\n\t\t Verbose output\n" );
    printf( "\t -h, --help\n\t\t Show this help\n" );
}
------------------------------------------------------------------------------
Achieve unprecedented app performance and reliability
What every C/C++ and Fortran developer should know.
Learn how Intel has extended the reach of its next-generation tools
to help boost performance applications - inlcuding clusters.
http://p.sf.net/sfu/intel-dev2devmay
_______________________________________________
Net-snmp-coders mailing list
Net-snmp-coders@lists.sourceforge.net
https://lists.sourceforge.net/lists/listinfo/net-snmp-coders

Reply via email to