Skip to content

TUT:Simple_Application

Bill Fenner edited this page Aug 30, 2018 · 1 revision

Here we discuss how to write a simple application. Its only purpose is to retrieve the value of a variable from a remote host.

| snmpdemoapp.c || The C source code |}

First, we must include some header files. After you've installed the net-snmp toolkit, some easy to use header files have been installed that handle most situations you'll run into. We start our snmpdemoapp.c file with these:

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

Next, we'll set up some local definitions that you can toggle if you want to use SNMPv3 or SNMPv1. By default, we're setting up for SNMPv3 here (which is more complex) so make sure you've read about the SNMPv3 Options first. If you don't want to deal with SNMPv3 for now, turn the #define statement below to #undef.

 /* change the word "define" to "undef" to try the (insecure) SNMPv1 version */  #define DEMO_USE_SNMP_VERSION_3    #ifdef DEMO_USE_SNMP_VERSION_3  #include "net-snmp/transform_oids.h"  const char *our_v3_passphrase = "The Net-SNMP Demo Password";  #endif

Next, we declare our main() routine and the variables we need:

 main() {

The first variables we need to declare:

  • struct snmp_session: A structure that holds information about who we're going to be talking to. We need to declare 2 of these, one to fill with information, and second which is a pointer returned by the library.
  • struct snmp_pdu: This structure will hold all of the information that we're going to send to the remote host. We'll declare a second for the information that they are going to send back.
  • oid: An OID is going to hold the location of the information which we want to retrieve. It'll need a size as well.
  • struct variable_list: This will hold the variables that we want to manipulate via SNMP.

   struct snmp_session session, *ss;    struct snmp_pdu *pdu;    struct snmp_pdu *response;                oid anOID[MAX_OID_LEN];    size_t anOID_len = MAX_OID_LEN;        struct variable_list *vars;    int status;    

Then, the first thing we must do is to initialize the snmp library:

   /*     * Initialize the SNMP library     */    init_snmp("snmpapp");    

Next, we'll initialize a session that describes who we want to talk to, what version of SNMP we want to use, how to authenticate to it, etc. A full definition of this session can be found in the net-snmp/snmp_api.h header file.

We've picked SNMPv3 by default above, which is a bit more complex to understand so make sure you've read

    /*     * Initialize a "session" that defines who we're going to talk to     */    snmp_sess_init( &session );                   /* set up defaults */    session.peername = "test.net-snmp.org";        /* set up the authentication parameters for talking to the server */        #ifdef DEMO_USE_SNMP_VERSION_3        /* Use SNMPv3 to talk to the experimental server */        /* set the SNMP version number */    session.version=SNMP_VERSION_3;             /* set the SNMPv3 user name */    session.securityName = strdup("MD5User");    session.securityNameLen = strlen(session.securityName);        /* set the security level to authenticated, but not encrypted */    session.securityLevel = SNMP_SEC_LEVEL_AUTHNOPRIV;        /* set the authentication method to MD5 */    session.securityAuthProto = usmHMACMD5AuthProtocol;    session.securityAuthProtoLen = sizeof(usmHMACMD5AuthProtocol)/sizeof(oid);    session.securityAuthKeyLen = USM_AUTH_KU_LEN;         /* set the authentication key to a MD5 hashed version of our       passphrase "The Net-SNMP Demo Password" (which must be at least 8       characters long) */    if (generate_Ku(session.securityAuthProto,                    session.securityAuthProtoLen,                    (u_char *) our_v3_passphrase, strlen(our_v3_passphrase),                    session.securityAuthKey,                    &session.securityAuthKeyLen) != SNMPERR_SUCCESS) {        snmp_perror(argv[0]);        snmp_log(LOG_ERR,                 "Error generating Ku from authentication pass phrase. \n");        exit(1);    }        #else /* we'll use the insecure (but simpler) SNMPv1 */        /* set the SNMP version number */    session.version = SNMP_VERSION_1;        /* set the SNMPv1 community name used for authentication */    session.community = "demopublic";    session.community_len = strlen(session.community);        #endif /* SNMPv1 */   

After we have established the session, we then need to open it. Opening it returns a pointer to another session that we should use for all our future calls:

   /* windows32 specific initialization (is a noop on unix) */    SOCK_STARTUP;        /*     * Open the session     */    ss = snmp_open(&session);                     /* establish the session */

If the session failed to open properly, print an error message and exit:

   if (!ss) {        snmp_perror("ack");        snmp_log(LOG_ERR, "something horrible happened!!!\n");        exit(2);    }

Next we create the PDU the we are going to send to the remote host when we request information. In this case, we're going to create a SNMP-GET pdu, which is what the snmpget program uses, for instance. It retrieves a value for each oid that you initialize it with.

   /*     * 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);    

So, let's fill it with our requested oid. Let's get the system.sysDescr.0 variable for this example. There are numerous ways you could create the oid in question. You could put in the numerical unsigned integer values yourself into the anOID array we created above, or you could use one of the following function calls to do it. We recommend the first one (get_node), as it is the most powerful and accepts more types of OIDs.

   read_objid(".1.3.6.1.2.1.1.1.0", anOID, &anOID_len);        #if OTHER_METHODS    get_node("sysDescr.0", anOID, &anOID_len);    read_objid("system.sysDescr.0", anOID, &anOID_len);    #endif    

So we add this oid, with a NULL value to the PDU using the following statement: (all oids should be paired with a NULL value for outgoing requests for information. For an SNMP-SET pdu, we'd put in the value we wanted to set the oid to).

   snmp_add_null_var(pdu, anOID, anOID_len);    

Finally, we can send out the request using the session pointer and the pdu and we get back a status and the response, which is stored in the newly malloced response pdu pointer.

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

Now we can examine the information, assuming our request was fulfilled properly. If not, we'll print the error that caused the problem:

   /*     * Process the response.     */    if (status == STAT_SUCCESS && response->errstat == SNMP_ERR_NOERROR) {      /*       * SUCCESS: Print the result variables       */    

Let's print the pdu's variable to the terminal using the print_variable routine:

     for(vars = response->variables; vars; vars = vars->next_variable)        print_variable(vars->name, vars->name_length, vars);        

Then, for kicks, lets get the information and manipulate it ourselves (checking to make sure it's the type we're expecting (a string) first):

     /* manipulate the information ourselves */      for(vars = response->variables; vars; vars = vars->next_variable) {        int count=1;        if (vars->type == ASN_OCTET_STR) {          char *sp = malloc(1 + vars->val_len);          memcpy(sp, vars->val.string, vars->val_len);          sp[vars->val_len] = '\0';          printf("value #%d is a string: %s\n", count++, sp);          free(sp);        }        else          printf("value #%d is NOT a string! Ack!\n", count++);      }        

Finally, print a description of the error in case there was one:

   } else {      /*       * FAILURE: print what went wrong!       */           if (status == STAT_SUCCESS)        fprintf(stderr, "Error in packet\nReason: %s\n",                snmp_errstring(response->errstat));      else        snmp_sess_perror("snmpget", ss);         }        

Last, but not least, free the response, as it is now our job to do so. Exit cleanly by calling snmp_close() as well:

   /*     * Clean up:     *  1) free the response.     *  2) close the session.     */    if (response)      snmp_free_pdu(response);    snmp_close(ss);         /* windows32 specific cleanup (is a noop on unix) */    SOCK_CLEANUP;      } /* main() */    

Ok, we're done. Let's compile it (using the Makefile distributed above):

 % make`` ``snmpdemoapp  cc -I/usr/local/include -g   -c snmpdemoapp.c -o snmpdemoapp.o  cc -o snmpdemoapp snmpdemoapp.o -lsnmp    

And then, run it, which produces the results we were expecting:

 % ./snmpdemoapp  system.sysDescr.0 = HP-UX net-snmp B.10.20 A 9000/715  value #1 is a string: HP-UX net-snmp B.10.20 A 9000/715    

Homework:

  1. Change the program to use an SNMP_MSG_GETNEXT PDU instead.
  2. Change the program to request more variables (like system.sysUpTime.0 and/or system.sysLocation.0).

Follow-on Reading

Internally the snmp library does a lot of things for you. You can learn about what's happening internally by looking at the Command Line Calling Order page.

Clone this wiki locally