Looking for sample code for implementing an SNMP table with AgentX

I wrote an AgentX application (Linux, gcc, g ++) that works well when sending scanners back. Here is what I am doing now:

init_agent( "blah" ); netsnmp_register_read_only_scalar( netsnmp_create_handler_registration( "foo1", handle_foo1, oid, oid.size(), HANDLER_CAN_RONLY ) ); init_snmp( "blah" ); while ( true ) { // internal stuff agent_check_and_process(1); // where 1==block } 

Functions of type handle_foo1(...) call snmp_set_var_typed_value(...) to return the values ​​that are cached in the global C structure in the application.

What I'm trying to do now is modify this code to also support the SNMP table. The contents of the table are stored / cached as an STL container in the application. This is a relatively simple SNMP table with consecutive rows, and all columns are composed of types such as Integer32, Gauge32, InetAddress, and TruthValue. The problem is that I don't see great code examples on the net-snmp website, just on the doxygen pages.

My question is:

Which API should I look at? Are they correct:

 netsnmp_register_read_only_table_data(); netsnmp_create_table_data(); netsnmp_create_table_data_row(); netsnmp_table_data_add_row(); 

... or is there something simpler that I should use?

+7
source share
1 answer

I think the biggest pain when it comes to net-snmp is all those Doxygen pages that contain Google indexes, but which provide almost zero useful content. Reading .h files is probably already obvious to most developers, and the truth is that net-snmp provides many different levels of APIs with very little documentation that I found useful. We need not a few dozen identical copies of the websites that host Doxygen, but some good examples instead.

After all, the mib2c tool is how I got enough sample code to make it all work. I think I tried running mib2c with every net-snmp .conf file and spent a lot of time reading the code generated for better understanding. Here are the ones I found gave me the best tips:

  • mib2c -c mib2c.create-dataset.conf MyMib
  • mib2c -c mib2c.table_data.conf MyMib

The .conf files are located here: /etc/snmp/mib2c.*

The following pages were also useful:

From what I understand, there are many helpers / layers available in the net-snmp API. Thus, this pseudo code for example may not work for everyone, but that’s how I personally got my tables to work with net-snmp v5.4:

A variable is needed for several functions (make it global or a member of the structure?)

 netsnmp_tdata *table = NULL; 

Structure for representing one row of the table (must comply with the MIB definition)

 struct MyTable_entry { long myTableIndex; ...insert one line here for each column of the table... int valid; // add this one to the end } 

Initialize a table using snmpd

 std::string name( "name_of_the_table_from_mib" ); table = netsnmp_tdata_create_table( name.c_str(), 0 ); netsnmp_table_registration_info *table_info = SNMP_MALLOC_TYPEDEF( netsnmp_table_registration_info ); netsnmp_table_helper_add_indexes( table_info, ASN_INTEGER, 0 ); // index: myTableIndex // specify the number of columns in the table (exclude the index which was already added) table_info->min_column = COLUMN_BLAH; table_info->max_column = MAX_COLUMN_INDEX; netsnmp_handler_registration *reg = netsnmp_create_handler_registration( name.c_str(), MyTable_handler, oid, oid.size(), HANDLER_CAN_RONLY ); netsnmp_tdata_register( reg, table, table_info ); 

Request Handler

 int myTable_handler( netsnmp_mib_handler *handler, netsnmp_handler_registration *reginfo, netsnmp_agent_request_info *reqinfo, netsnmp_request_info *requests ) { if ( reqInfo->mode != MODE_GET ) return SNMP_ERR_NOERROR; for ( netsnmp_request_info *request = requests; request; request = request->next ) { MyTable_entry *table_entry = (MyTable_entry*)netsnmp_tdata_extract_entry( request ); netsnmp_table_request_info *table_info = netsnmp_extract_table_info( request ); if ( table_entry == NULL ) { netsnmp_set_request_error( reqinfo, request, SNMP_NOSUCHINSTANCE); continue; } switch ( table_info->colnum ) { // ...this is similar to non-table situations, eg: case COLUMN_BLAH: snmp_set_var_typed_integer( request->requestvb, ASN_INTEGER, table_entry->blah ); break; // ... default: netsnmp_set_request_error( reqinfo, request, SNMP_NOSUCHOBJECT ); } } return SNMP_ERR_NOERROR; } 

Creating / adding rows to a table

 if ( table == NULL ) return; // remember our "global" variable named "table"? // start by deleting all of the existing rows while ( netsnmp_tdata_row_count(table) > 0 ) { netsnmp_tdata_row *row = netsnmp_tdata_row_first( table ); netsnmp_tdata_remove_and_delete_row( table, row ); } for ( ...loop through all the data you want to add as rows into the table... ) { MyTable_entry *entry = SNMP_MALLOC_TYPEDEF( MyTable_entry ); if ( entry == NULL ) ... return; netsnmp_tdata_row *row = netsnmp_tdata_create_row(); if ( row == NULL ) SNMP_FREE( entry ); .... return; entry->myTableIndex = 123; // the row index number // populate the table the way you need entry->blah = 456; // ... // add the data into the row, then add the row to the table entry->valid = 1; row->data = entry; netsnmp_tdata_row_add_index( row, ASN_INTEGER, &(entry->myTableIndex), sizeof(entry->myTableIndex) ); netsnmp_tdata_add_row( table, row ); } 

Combination

In my case, this last function, which builds strings, is periodically triggered by some other events in the system. Therefore, every time new statistics are available, the table is rebuilt, all old rows are deleted, and new ones are inserted. I did not try to change the existing lines. Instead, I found it easier to just rebuild the table from scratch.

+15
source

All Articles