Ce petit tutoriel montre comment ajouter une MIB personnalisée par la construction d’un sous-agent (subagent) SNMP en C.
Nouvelle MIB et configuration SNMP
Afin d’étendre la base des MIB existante, il vous faut un fichier de définition MIB. La plupart du temps, celui-ci devra se trouver dans /usr/share/snmp/mibs. Ensuite, pour que le sous-agent fonctionne avec SNMP, assurez-vous d’avoir la directive “master agentx” dans votre fichier de configuration SNMP.
Un petit rehash du serveur SNMP afin qu’il prenne ces changements en compte:
/etc/init.d/snmpd reload
Voici un exemple de fichier MIB:
JULIEN -MIB DEFINITIONS ::= BEGIN
IMPORTS
OBJECT -GROUP
FROM SNMPv2 -CONF
enterprises , Integer32 , OBJECT -TYPE , MODULE -IDENTITY
FROM SNMPv2 -SMI ;
julien MODULE -IDENTITY
LAST -UPDATED “200902051626 Z ”
ORGANIZATION
“Organization .”
CONTACT -INFO
“Contact -info .”
DESCRIPTION
“DescriptioN .”
::= { enterprises 1 }
julienMIB OBJECT IDENTIFIER ::= { julien 1 }
julienConf OBJECT IDENTIFIER ::= { julienMIB 1 }
julienGroups OBJECT IDENTIFIER ::= { julienConf 1 }
julienObjectGroup OBJECT -GROUP
OBJECTS { test }
STATUS current
DESCRIPTION
“Description .”
::= { julienGroups 1 }
julienObjs OBJECT IDENTIFIER ::= { julienMIB 2 }
test OBJECT -TYPE
SYNTAX Integer32
MAX -ACCESS read -write
STATUS current
DESCRIPTION
“Description .”
::= { julienObjs 1 }
test2 OBJECT -TYPE
SYNTAX Integer32
MAX -ACCESS read -write
STATUS current
DESCRIPTION
“Description .”
::= { julienObjs 2 }
END
Ecriture du sous-agent
Voici le script de compilation:
#!/bin/sh
mib2c -c ./mib2c.scalar.conf $1
net-snmp-config –compile-subagent snmp-subagent $1 .c
ansi que le fichier template correspondant:
## -*- c -*-
######################################################################
## Do the .h file
######################################################################
@ open $ { name }. h @
/*
* Note: this file originally auto-generated by mib2c using
* $Id: mib2c.scalar.conf,v 1.8 2004/10/14 12:57:34 dts12 Exp $
*/
#ifndef $name.uc_H
#define $name.uc_H
#define sizeof_long 4
#define sizeof_char 64
/* function declarations */
void init_ $ name ( void );
@ foreach $ i scalar @
Netsnmp_Node_Handler handle_ $ { i };
@ end @
#endif /* $name.uc_H */
######################################################################
## Do the .c file
######################################################################
@ open $ { name }. c @
/*
* Note: this file originally auto-generated by mib2c using
* $Id: mib2c.scalar.conf,v 1.8 2004/10/14 12:57:34 dts12 Exp $
*/
#include <net-snmp/net-snmp-config.h>
#include <net-snmp/net-snmp-includes.h>
#include <net-snmp/agent/net-snmp-agent-includes.h>
#include “${name}.h”
/** Initializes the $name module */
void
init_ $ name ( void )
{
@ foreach $ i scalar @
static oid $ { i } _oid [] = { $ i . commaoid };
@ end @
DEBUGMSGTL (( “$ name ” , “ Initializing \ n ” ));
@ foreach $ i scalar @
netsnmp_register_scalar (
netsnmp_create_handler_registration ( “$ i ” , handle_ $ i ,
$ { i } _oid , OID_LENGTH ( $ { i } _oid ),
@ if ! $ i . settable @
HANDLER_CAN_RONLY
@ end @
@ if $ i . settable @
HANDLER_CAN_RWRITE
@ end @
));
@ end @
}
@ foreach $ i scalar @
$ i . decl * var_ $ i = NULL ;
int lenvar_ $ i ;
int
handle_ $ i ( netsnmp_mib_handler * handler ,
netsnmp_handler_registration * reginfo ,
netsnmp_agent_request_info * reqinfo ,
netsnmp_request_info * requests )
{
/* We are never called for a GETNEXT if it’s registered as a
“instance”, as it’s “magically” handled for us. */
/* a instance handler also only hands us one request at a time, so
we don’t need to loop over a list of requests; we’ll only get one. */
switch ( reqinfo -> mode ) {
case MODE_GET :
snmp_set_var_typed_value ( requests -> requestvb , $ i . type ,
( u_char * ) var_ $ i /* XXX: a pointer to the scalar’s data */ ,
lenvar_ $ i /* XXX: the length of the data in bytes */ );
break ;
@ if $ i . settable @
/*
* SET REQUEST
*
* multiple states in the transaction. See:
* http://www.net-snmp.org/tutorial-5/toolkit/mib_module/set-actions.jpg
*/
case MODE_SET_RESERVE1 :
//if (/* XXX: check incoming data in requests->requestvb->val.XXX for failures, like an incorrect type or an illegal value or … */) {
// netsnmp_set_request_error(reqinfo, requests, /* XXX: set error code depending on problem (like SNMP_ERR_WRONGTYPE or SNMP_ERR_WRONGVALUE or … */);
//}
break ;
case MODE_SET_RESERVE2 :
/* XXX malloc “undo” storage buffer */
if ( var_ $ i != NULL )
{
free ( var_ $ i );
}
var_ $ i = malloc ( requests -> requestvb -> val_len );
lenvar_ $ i = requests -> requestvb -> val_len ;
if ( var_ $ i == NULL /* XXX if malloc, or whatever, failed: */ ) {
netsnmp_set_request_error ( reqinfo , requests , SNMP_ERR_RESOURCEUNAVAILABLE );
}
break ;
case MODE_SET_FREE :
/* XXX: free resources allocated in RESERVE1 and/or
RESERVE2. Something failed somewhere, and the states
below won’t be called. */
if ( var_ $ i != NULL )
free ( var_ $ i );
break ;
case MODE_SET_ACTION :
/* XXX: perform the value change here */
memcpy ( var_ $ i , ( $ i . decl * )( requests -> requestvb -> val . string ), lenvar_ $ i );
//if (/* XXX: error? */) {
// netsnmp_set_request_error(reqinfo, requests, /* some error */);
//}
break ;
case MODE_SET_COMMIT :
/* XXX: delete temporary storage */
//if (/* XXX: error? */) {
// /* try _really_really_ hard to never get to this point */
// netsnmp_set_request_error(reqinfo, requests, SNMP_ERR_COMMITFAILED);
//}
break ;
case MODE_SET_UNDO :
printf ( “ SET_UNDO \ n ” );
/* XXX: UNDO and return to previous value for the object */
//if (/* XXX: error? */) {
// /* try _really_really_ hard to never get to this point */
// netsnmp_set_request_error(reqinfo, requests, SNMP_ERR_UNDOFAILED);
//}
break ;
@ end @
default:
/* we should never get here, so this is a really bad error */
snmp_log ( LOG_ERR , “ unknown mode ( % d ) in handle_ $ { i } \ n ” , reqinfo -> mode );
return SNMP_ERR_GENERR ;
}
return SNMP_ERR_NOERROR ;
}
@ end @
@ open snmp - subagent . c @
#include <net-snmp/net-snmp-config.h>
#include <net-snmp/net-snmp-includes.h>
#include <net-snmp/agent/net-snmp-agent-includes.h>
#include <signal.h>
#include “${name}.h”
static int keep_running ;
RETSIGTYPE
stop_server ( int a ) {
keep_running = 0 ;
}
int
main ( int argc , char ** argv ) {
int agentx_subagent = 1 ; /* change this if you want to be a SNMP master agent */
int background = 0 ; /* change this if you want to run in the background */
int syslog = 0 ; /* change this if you want to use syslog */
if ( syslog )
snmp_enable_calllog ();
else
snmp_enable_stderrlog ();
if ( agentx_subagent ) {
netsnmp_ds_set_boolean ( NETSNMP_DS_APPLICATION_ID , NETSNMP_DS_AGENT_ROLE , 1 );
}
if ( background && netsnmp_daemonize ( 1 , ! syslog ))
exit ( 1 );
SOCK_STARTUP ;
init_agent ( “ snmp - subagent ” );
@ foreach $ i scalar @
init_ $ i ();
@ end @
if ( ! agentx_subagent ) {
init_vacm_vars ();
init_usmUser ();
}
init_snmp ( “ snmp - subagent ” );
if ( ! agentx_subagent )
init_master_agent (); /* open the port to listen on (defaults to udp:161) */
keep_running = 1 ;
signal ( SIGTERM , stop_server );
signal ( SIGINT , stop_server );
snmp_log ( LOG_INFO , ” snmp - subagent is up and running . \ n ” );
while ( keep_running ) {
agent_check_and_process ( 1 ); /* 0 == don’t block */
}
snmp_shutdown ( “ snmp - subagent ” );
SOCK_CLEANUP ;
return 0 ;
}
@ end @
L’OID vers notre nouvelle MIB sera donc “.1.3.6.1.4.1.34576″. Ce chemin sera reporté automatiquement par le script de compilation dans le code source du sous-agent.
Il ne vous reste plus qu’à lancer le sous-agent afin que le démon SNMP prenne en compte cette nouvelle MIB, et que vous puissiez interroger la base et y définir les nouvelles variables.