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-UPDATED200902051626ZORGANIZATIONOrganization.”
CONTACT-INFOContact-info.”
DESCRIPTIONDescriptioN.”
::= { enterprises 1 }

julienMIB OBJECT IDENTIFIER ::= { julien 1 }
julienConf OBJECT IDENTIFIER ::= { julienMIB 1 }
julienGroups OBJECT IDENTIFIER ::= { julienConf 1 }
julienObjectGroup OBJECT-GROUP
OBJECTS { test }
STATUS current
DESCRIPTIONDescription.”
::= { julienGroups 1 }

julienObjs OBJECT IDENTIFIER ::= { julienMIB 2 }

test OBJECT-TYPE
SYNTAX Integer32
MAX-ACCESS read-write
STATUS current
DESCRIPTIONDescription.”
::= { julienObjs 1 }

test2 OBJECT-TYPE
SYNTAX Integer32
MAX-ACCESS read-write
STATUS current
DESCRIPTIONDescription.”
::= { 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.