Difference between revisions of "TUT:Simple Application"

From Net-SNMP Wiki
Jump to: navigation, search
m
 
(7 intermediate revisions by 6 users not shown)
Line 1: Line 1:
Here we discuss how to write a simple application. It's only purpose is to retrieve the value of a variable from a remote host.  
+
Here we discuss how to write a simple application. Its only purpose is to retrieve the value of a variable from a remote host.  
  
 
{{TUT:File-Table-Top-and-basics}}
 
{{TUT:File-Table-Top-and-basics}}
Line 11: Line 11:
  
  
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.  
+
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 */
 
   /* change the word "define" to "undef" to try the (insecure) SNMPv1 version */
Line 18: Line 18:
 
   #ifdef DEMO_USE_SNMP_VERSION_3
 
   #ifdef DEMO_USE_SNMP_VERSION_3
 
   #include "net-snmp/transform_oids.h"
 
   #include "net-snmp/transform_oids.h"
   const char *our_v3_passphrase = "The UCD Demo Password";
+
   const char *our_v3_passphrase = "The Net-SNMP Demo Password";
 
   #endif
 
   #endif
  
Line 32: Line 32:
 
* '''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_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 they they are going to send back.  
+
* '''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.  
 
* '''oid:''' An OID is going to hold the location of the information which we want to retrieve. It'll need a size as well.  
Line 48: Line 48:
 
     int status;
 
     int status;
 
      
 
      
Then, the first thing we must do is to inialize the snmp library:  
+
Then, the first thing we must do is to initialize the snmp library:  
  
 
     /*
 
     /*
Line 55: Line 55:
 
     init_snmp("snmpapp");
 
     init_snmp("snmpapp");
 
      
 
      
Next, we'll inialize 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.  
+
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  
 
We've picked SNMPv3 by default above, which is a bit more complex to understand so make sure you've read  
Line 87: Line 87:
 
      
 
      
 
     /* set the authentication key to a MD5 hashed version of our
 
     /* set the authentication key to a MD5 hashed version of our
       passphrase "The UCD Demo Password" (which must be at least 8
+
       passphrase "The Net-SNMP Demo Password" (which must be at least 8
 
       characters long) */
 
       characters long) */
 
     if (generate_Ku(session.securityAuthProto,
 
     if (generate_Ku(session.securityAuthProto,
Line 100: Line 100:
 
     }
 
     }
 
      
 
      
     #else /* we'll use the insecure (but simplier) SNMPv1 */
+
     #else /* we'll use the insecure (but simpler) SNMPv1 */
 
      
 
      
 
     /* set the SNMP version number */
 
     /* set the SNMP version number */
Line 111: Line 111:
 
     #endif /* SNMPv1 */
 
     #endif /* SNMPv1 */
 
    
 
    
After we have established the session, we then need to open
+
After we have established the session, we then need to open it. Opening it returns a pointer to another session that we should use
it. Opening it returns a pointer to another session that we should use
+
 
for all our future calls:
 
for all our future calls:
  
Line 134: Line 133:
 
when we request information. In this case, we're going to create a
 
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
 
SNMP-GET pdu, which is what the snmpget program uses, for instance. It
retrieves a value for each oid that you initalize it with.
+
retrieves a value for each oid that you initialize it with.
  
 
     /*
 
     /*
Line 142: Line 141:
 
     pdu = snmp_pdu_create(SNMP_MSG_GET);
 
     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 numerious 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.  
+
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);
 
     read_objid(".1.3.6.1.2.1.1.1.0", anOID, &anOID_len);
Line 162: Line 161:
 
     status = snmp_synch_response(ss, pdu, &response);
 
     status = snmp_synch_response(ss, pdu, &response);
 
      
 
      
Now we can examine the information, assuming our request was fulfilled
+
Now we can examine the information, assuming our request was fulfilled properly. If not, we'll print the error that caused the problem:
properly. If not, we'll print the error that caused the problem:
+
  
 
     /*
 
     /*
Line 173: Line 171:
 
       */
 
       */
 
      
 
      
Lets print the pdu's variable to the terminal using the print_variable routine:  
+
Let's print the pdu's variable to the terminal using the print_variable routine:  
  
 
       for(vars = response->variables; vars; vars = vars->next_variable)
 
       for(vars = response->variables; vars; vars = vars->next_variable)
 
         print_variable(vars->name, vars->name_length, vars);
 
         print_variable(vars->name, vars->name_length, vars);
 
          
 
          
Then, for kicks, lets get the information and manipulate it ourselves (checking to make sure its the type we're expecting (a string) first):  
+
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):  
  
       /* manipuate the information ourselves */
+
       /* manipulate the information ourselves */
 
       for(vars = response->variables; vars; vars = vars->next_variable) {
 
       for(vars = response->variables; vars; vars = vars->next_variable) {
 
         int count=1;
 
         int count=1;
 
         if (vars->type == ASN_OCTET_STR) {
 
         if (vars->type == ASN_OCTET_STR) {
           char *sp = (char *)malloc(1 + vars->val_len);
+
           char *sp = malloc(1 + vars->val_len);
 
           memcpy(sp, vars->val.string, vars->val_len);
 
           memcpy(sp, vars->val.string, vars->val_len);
 
           sp[vars->val_len] = '\0';
 
           sp[vars->val_len] = '\0';
Line 225: Line 223:
 
   } /* main() */
 
   } /* main() */
 
      
 
      
Ok, we're done. lets compile it (using the Makefile distributed above):  
+
Ok, we're done. Let's compile it (using the Makefile distributed above):  
  
 
   % '''make snmpdemoapp'''
 
   % '''make snmpdemoapp'''
Line 243: Line 241:
  
 
{{TUT:LIST}}
 
{{TUT:LIST}}
 +
 +
=== 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.

Latest revision as of 15:20, 11 November 2014

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

Here are the files discussed in this example so you can download them:

File Description
Makefile A simple makefile used to build the projects
NET-SNMP-TUTORIAL-MIB.txt The MIB we'll be writing code for in the various pieces of the agent extension tutorial
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).

Tutorial Sections

About the SNMP Protocol

These tutorial links talk about SNMP generically and how the protocol itself works. They are good introductory reading material and the concepts are important to understand before diving into the later tutorials about Net-SNMP itself.

Net-SNMP Command Line Applications

These tutorial pages discuss the command line tools provided in the Net-SNMP suite of tools. Nearly all the example commands in these tutorials works if you try it yourself, as they're all examples that talk to our online Net-SNMP test agent. Given them a shot!

Application Configuration

All of our applications support configuration to allow you to customize how they behave.

Net-SNMP Daemons

Net-SNMP comes with two long-running daemons: a SNMP agent (snmpd) for responding to management requests and a notification receiver (snmptrapd) for receiving SNMP notifications.

Coding Tutorials

Net-SNMP comes with a highly flexible and extensible API. The API allows you to create your own commands, add extensions to the agent to support your own MIBs and perform specialized processing of notifications.

Debugging SNMP Applications and Agents

All our tools and applications have extensive debugging output. These tutorials talk about how the debugging system works and how you can add your own debugging statements to you code:

Operating System Specific Tutorials

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.