Difference between revisions of "TUT:Simple Application"
m |
|||
(5 intermediate revisions by 4 users not shown) | |||
Line 1: | Line 1: | ||
− | Here we discuss how to write a simple application. | + | 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 | + | 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 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 | + | * '''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 | + | 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 | + | 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 100: | Line 100: | ||
} | } | ||
− | #else /* we'll use the insecure (but | + | #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 | + | 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 | + | 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: | ||
*/ | */ | ||
− | + | 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 | + | 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 */ | /* manipulate the information ourselves */ | ||
Line 184: | Line 182: | ||
int count=1; | int count=1; | ||
if (vars->type == ASN_OCTET_STR) { | if (vars->type == ASN_OCTET_STR) { | ||
− | char *sp = | + | 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. | + | 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
Contents
Homework:
- Change the program to use an SNMP_MSG_GETNEXT PDU instead.
- 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.
- How SNMP Works: About the protocol itself (GETs, GETNEXTs, etc)
- What data is in SNMP: All about SNMP Management Information Bases (MIBs)
- Securing SNMP: How to use the SNMP protocol securely
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!
- snmptranslate: learning about the MIB tree.
- snmpget: retrieving data from a host.
- snmpgetnext: retrieving unknown indexed data.
- snmpwalk: retrieving lots of data at once!
- snmptable: displaying a table.
- snmpset: peforming write operations.
- snmpbulkget: communicates with a network entity using SNMP GETBULK request
- snmpbulkwalk: retrieve a sub-tree of management values using SNMP GETBULK requests.
- snmptrap: Sending and receiving traps, and acting upon them.
- Traps/informs with SNMPv3/USM: Sending and receiving SNMPv3/USM TRAPs and INFORMs
- Sending Traps/Informs via AgentX: Sending notifications from the command line through snmpd
- Common command line options:
- Writing mib2c config files
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.
- SNMP Agent (snmpd) Configuration
- SNMP Notification Receiver (snmptrapd)
- Agent Monitoring
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.
- Client / Manager Coding Tutorials
- Writing a simple application
- Writing a simple asynchronous application
- Agent Coding Tutorials
- The Agent Architecture page might be worth reading before or after the agent coding tutorials, and describes how the Agent Helpers work under the hood.
- Writing a mib module to serve information described by an SNMP MIB, and how to compile it into the net-snmp snmpd agent.
- Writing a Dynamically Loadable Object that can be loaded into the SNMP agent.
- Writing a Subagent that can be run to attach to the snmpd master agent.
- Writing a perl plugin to extend the agent using the NetSNMP::agent module.
- Writing shell scripts to extend the agent
- Using mib2c to help write an agent code template for you
- Header files and autoconf
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:
- Debugging output printed using the -D command line option
- Using -Ddump to display packet breakdowns
- Debugging using GDB
Operating System Specific Tutorials
- Building With Visual Studio 2005 Express
- Building Net-SNMP 64-bit with Visual C++ 2010 Express
- Net-Snmp on Ubuntu
- Net-SNMP and lm-sensors on Ubuntu 10.04
- Net-SNMP for windows:
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.