snmp 代理端扩展源码分析



2008-05-05 17:53


/*example.c*/        

#include <net-snmp/net-snmp-config.h>        
#include <net-snmp/net-snmp-includes.h>        
#include <net-snmp/agent/net-snmp-agent-includes.h>        

#if HAVE_STDLIB_H        
#include <stdlib.h>        
#endif        

#if TIME_WITH_SYS_TIME        

# ifdef WIN32        
# include <sys/timeb.h>        
# else        
# include <sys/time.h>        
# endif        
# include <time.h>        

#else        
# if HAVE_SYS_TIME_H        
# include <sys/time.h>        
# else        
# include <time.h>        
# endif        

#endif        

/*        
* header_generic() comes from here        
*/        
#include "util_funcs.h"        
包含自己的头文件        
#include "Display_time.h"        

#define EXAMPLE_STR_LEN 300        
#define EXAMPLE_STR_DEFAULT "life the universe and everything"        
int             example_int = 42;        
char            example_str[EXAMPLE_STR_LEN];        

void            example_parse_config_exampleint(const char *token, char *cptr);        
void            example_parse_config_examplestr(const char *token, char *cptr);        
void            example_free_config_exampleint(void);        
void            example_free_config_examplestr(void);        

这个数组的类型是struct variableN, 其中N是这个数组中OID号的最长的数,        
即:结构体最后一个成员(这个成员是个数组)定义了MIB Tree OID的底层数字。        
    N定义了MIB Tree OID的底层的层数(也就是这个数组的长度)。        
    所有有效的N数字都定义在了<agent/var_struct.h>文件中。        

struct variableN类型成员的说明:        
    1):FoxmailINT:这个magic number是在自己的头文件Display_time.h中宏定义,        
                       这个参数被用来传递给CallBack 例程,用来决定那个object被查询。        
    2):ASN_INTEGER:这个参数说明了object的类型,所有有效的类型在snmp_impl.h文件中列表说明。        
    3):RONLY:这个参数说明了object是否能够被set。        
    4):var_foxmail:当有object被查询时,这个CallBack 例程被调用。        
                    一般的情况下,同一个文件中的所有的object使用相同的allBack 例程。        
    5):1:MIB Tree OID的底层数字的层数。(这个数字决定了struct variableN中的N)        
    6):{1}:MIB Tree OID的底层数字。        

struct variable2 example_variables[] = {        
    {EXAMPLESTRING, ASN_OCTET_STR, RONLY, var_example, 1, {1}},        
    {EXAMPLEINTEGER, ASN_INTEGER, RWRITE, var_example, 2, {2, 1}},        
    {EXAMPLEOBJECTID, ASN_OBJECT_ID, RONLY, var_example, 2, {2, 2}},        
    {EXAMPLETIMETICKS, ASN_TIMETICKS, RONLY, var_example, 1, {3}},        
    {EXAMPLEIPADDRESS, ASN_IPADDRESS, RONLY, var_example, 1, {4}},        
    {EXAMPLECOUNTER, ASN_COUNTER, RONLY, var_example, 1, {5}},        
    {EXAMPLEGAUGE, ASN_GAUGE, RONLY, var_example, 1, {6}},        
    {EXAMPLETRIGGERTRAP, ASN_INTEGER, RWRITE, var_example, 1, {7}},        
    {EXAMPLETRIGGERTRAP2, ASN_INTEGER, RWRITE, var_example, 1, {8}}        
};        

下面这个数组定义了MIB Tree OID的顶层数字。        
oid             example_variables_oid[] = { 1, 3, 6, 1, 4, 1, 2021, 254 };        



这个例程在Agent程序开始的时候被调用,用来初始化可能被查询的Object。        
void init_example(void)        
{        
        注册我们自己的MIB Tree,以便Agent查询的时候能够处理。        
        参数:        
            1)descr:    描述这个MIB Tree        
            2)var:      变量结构体,类型struct variableN。        
            3)vartype: 类型struct variableN        
            4)theoid:   MIB Tree的顶层数字        
    REGISTER_MIB("example", example_variables, variable2,        
                 example_variables_oid);        

        把example_str变量设上默认字符串。example_int已经在上面初始化了。        
    strncpy(example_str, EXAMPLE_STR_DEFAULT, EXAMPLE_STR_LEN);        

    * Register config handlers for the two objects that can be set        
     * via configuration file directive        
           snmpd_register_config_handler("exampleint",        
                                  example_parse_config_exampleint,        
                                  example_free_config_exampleint,        
                                  "exampleint value");        
           snmpd_register_config_handler("examplestr",        
                                  example_parse_config_examplestr,        
                                  example_free_config_examplestr,        
                                  "examplestr value");        
           snmpd_register_config_handler("examplestring",        
                                  example_parse_config_examplestr,        
                                  example_free_config_examplestr,        
                                  "examplestring value");        

        我们经常需要读取内核中的数据,我们需要在这里进行一些必要的初始化。        
        以加快我们读取这些内核信息的速度,快速反应查询请求。        
    /*        
     * auto_nlist( "example_symbol", 0, 0 );        
     */        

}        


配置文件处理函数        
void        
example_parse_config_exampleint(const char *token, char *cptr)        
{        
    example_int = atoi(cptr);        
}        
void        
example_parse_config_examplestr(const char *token, char *cptr)        
{        
        必须确保字符串长度小于分配的空间。        
    if (strlen(cptr) < EXAMPLE_STR_LEN)        
        strcpy(example_str, cptr);        
    else {        
        如果需要的话,截断这个字符串。        
        strncpy(example_str, cptr, EXAMPLE_STR_LEN - 4);        
        example_str[EXAMPLE_STR_LEN - 4] = 0;        
        strcat(example_str, "...");        
        example_str[EXAMPLE_STR_LEN - 1] = 0;        
       }        
}        

当关闭Agent时需要的清除工作。        
void        
example_free_config_exampleint(void)        
{        
}        

void        
example_free_config_examplestr(void)        
{        
}        


当有请求访问这个MIB Tree的object时,就会调用这个处理函数。        
参数:        
    1)vp      被请求访问的object的example_variables的入口地址        
    2)name    被请求访问的object的OID        
    3)length OID的长度        
    4)exact   指示这个request是“exact”(GET/SET)请求,还是“inexact”(GETNEXT/GETBULK)请求        
四个参数被用来返回信息:        
    1)name                被请求访问的object的OID        
    2)length             OID的长度        
    3)var_len             返回应答的长度        
    4)write_method   被请求访问的object的SET函数的指针        

u_char *        
var_example(struct variable *vp,        
            oid * name,        
            size_t * length,        
            int exact, size_t * var_len, WriteMethod ** write_method)        
{        
        从这个函数返回的值必须是一个static data的指针,这样才能够从函数外来访问。        
               
    static char     string[EXAMPLE_STR_LEN];    /* for EXAMPLESTRING   */        
    static oid      oid_ret[8]; /* for EXAMPLEOBJECTID */        
    static long     long_ret;   /* for everything else */        

        在进行应答请求之前,需要检查这个请求object OID是否是一个有效的OID。        
        header_generic()函数能够用来检查scalar objects。        
        header_simple_table()函数能够用来检查simple table。        
        这些函数也当检查正确时,设置默认的返回值。        
    * The name and length are set suitably for the current object,        
     * var_len assumes that the result is an integer of some form,        
     * and write_method assumes that the object cannot be set.        

    DEBUGMSGTL(("example", "var_example entered\n"));        
    if (header_generic(vp, name, length, exact, var_len, write_method) ==        
        MATCH_FAILED)        
        return NULL;        


        我们使用struct variableN结构体中的magic number来决定那个object被请求。               
    switch (vp->magic) {        
    case EXAMPLESTRING:        
        sprintf(string, example_str);        
            在上面时假设返回值是integer,但是并不是,所以需要重新设置var_len。        
        *var_len = strlen(string);        
        return (u_char *) string;        

    case EXAMPLEINTEGER:        
            这种情况,上面的假设的长度是正确的,但是这个object是可以写的,所以需要设置write_method。        
        long_ret = example_int;        
        *write_method = write_exampleint;        
        return (u_char *) & long_ret;        

    case EXAMPLEOBJECTID:        
        oid_ret[0] = 1;        
        oid_ret[1] = 3;        
        oid_ret[2] = 6;        
        oid_ret[3] = 1;        
        oid_ret[4] = 4;        
        oid_ret[5] = oid_ret[6] = oid_ret[7] = 42;        
            这种情况,上面的假设的长度是错误的。        
        *var_len = 8 * sizeof(oid);        
        return (u_char *) oid_ret;        

    case EXAMPLETIMETICKS:        
            这种情况,上面的假设的长度是正确的,直接返回。        
        long_ret = 363136200;   /* 42 days, 42 minutes and 42.0 seconds */        
        return (u_char *) & long_ret;        

    case EXAMPLEIPADDRESS:        
        long_ret = ntohl(INADDR_LOOPBACK);        
        return (u_char *) & long_ret;        

    case EXAMPLECOUNTER:        
        long_ret = 42;        
        return (u_char *) & long_ret;        

    case EXAMPLEGAUGE:        
        long_ret = 42;          /* Do we detect a theme running through these answers? */        
        return (u_char *) & long_ret;        

    case EXAMPLETRIGGERTRAP:        
            这个object是只能够写的“write-only”。        
            它的作用是只能够产生一个“trap”,当读它的时候只能够返回0。        
        long_ret = 0;        
        *write_method = write_exampletrap;        
        return (u_char *) & long_ret;        

    case EXAMPLETRIGGERTRAP2:        
            这个object是只能够写的“write-only”。        
            它的作用是只能够产生一个SNMP v2版本的“trap”,当读它的时候只能够返回0。        

        long_ret = 0;        
        *write_method = write_exampletrap2;        
        return (u_char *) & long_ret;        

    default:        
            这种情况,报告一个错误,并把错误写入log。        
        DEBUGMSGTL(("snmpd", "unknown sub-id %d in examples/var_example\n",        
                    vp->magic));        
    }        
    return NULL;        
}        

当某个object是可写的时候,需要设置SET处理例程。        

int        
write_exampleint(int action,        
                 u_char * var_val,        
                 u_char var_val_type,        
                 size_t var_val_len,        
                 u_char * statP, oid * name, size_t name_len)        
{        
    定义一个允许访问的最大数值,它是任意的。        
#define MAX_EXAMPLE_INT 100        
    static long     intval;        
    static long     old_intval;        

    switch (action) {        
    case RESERVE1:        
                检查要设置的值是否符合条件。        
        if (var_val_type != ASN_INTEGER) {        
            DEBUGMSGTL(("example", "%x not integer type", var_val_type));        
            return SNMP_ERR_WRONGTYPE;        
                }        
        if (var_val_len > sizeof(long)) {        
            DEBUGMSGTL(("example", "wrong length %x", var_val_len));        
            return SNMP_ERR_WRONGLENGTH;        
                    }        

        intval = *((long *) var_val);        
        if (intval > MAX_EXAMPLE_INT) {        
            DEBUGMSGTL(("example", "wrong value %x", intval));        
            return SNMP_ERR_WRONGVALUE;        
                }        
        break;        

    case RESERVE2:        
        break;        

    case FREE:        
        break;        

    case ACTION:        
            按照请求设置数值,但是这个请求可能被撤销,所以需要保存原来的值。        
        old_intval = example_int;        
        example_int = intval;        
        break;        

    case UNDO:        
            撤销上一个请求,恢复原来的值。        
        example_int = old_intval;        
        break;        

    case COMMIT:        
        break;        

    }        
    return SNMP_ERR_NOERROR;        
}        




int        
write_exampletrap(int action,        
                  u_char * var_val,        
                  u_char var_val_type,        
                  size_t var_val_len,        
                  u_char * statP, oid * name, size_t name_len)        
{        
    long            intval;        

    DEBUGMSGTL(("example", "write_exampletrap entered: action=%d\n",        
                action));        
    switch (action) {        
    case RESERVE1:        
        if (var_val_type != ASN_INTEGER) {        
            DEBUGMSGTL(("example", "%x not integer type", var_val_type));        
            return SNMP_ERR_WRONGTYPE;        
                }        
        if (var_val_len > sizeof(long)) {        
            DEBUGMSGTL(("example", "wrong length %x", var_val_len));        
            return SNMP_ERR_WRONGLENGTH;        
                }        

        intval = *((long *) var_val);        
        if (intval != 1) {        
            DEBUGMSGTL(("example", "wrong value %x", intval));        
            return SNMP_ERR_WRONGVALUE;        
                   }        
        break;        

    case RESERVE2:        
        break;        

    case FREE:        
        break;        

    case ACTION:        
        break;        

    case UNDO:        
        break;        

    case COMMIT:        
            产生一个“trap”        
        DEBUGMSGTL(("example", "write_exampletrap sending the trap\n",        
                    action));        
        send_easy_trap(SNMP_TRAP_ENTERPRISESPECIFIC, 99);        
        DEBUGMSGTL(("example", "write_exampletrap trap sent\n", action));        
        break;        

    }        
    return SNMP_ERR_NOERROR;        
}        



int        
write_exampletrap2(int action,        
                   u_char * var_val,        
                   u_char var_val_type,        
                   size_t var_val_len,        
                   u_char * statP, oid * name, size_t name_len)        
{        
    long            intval;        

    oid             objid_snmptrap[] = { 1, 3, 6, 1, 6, 3, 1, 1, 4, 1, 0 };     /* snmpTrapOID.0 */        
    oid             demo_trap[] = { 1, 3, 6, 1, 4, 1, 2021, 13, 990 }; /*demo-trap */        
    oid             example_string_oid[] =        
        { 1, 3, 6, 1, 4, 1, 2021, 254, 1, 0 };        
    static netsnmp_variable_list var_trap;        
    static netsnmp_variable_list var_obj;        

    DEBUGMSGTL(("example", "write_exampletrap2 entered: action=%d\n",        
                action));        
    switch (action) {        
    case RESERVE1:        
        if (var_val_type != ASN_INTEGER) {        
            DEBUGMSGTL(("example", "%x not integer type", var_val_type));        
            return SNMP_ERR_WRONGTYPE;        
                }        
        if (var_val_len > sizeof(long)) {        
            DEBUGMSGTL(("example", "wrong length %x", var_val_len));        
            return SNMP_ERR_WRONGLENGTH;        
                }        

        intval = *((long *) var_val);        
        if (intval != 1) {        
            DEBUGMSGTL(("example", "wrong value %x", intval));        
            return SNMP_ERR_WRONGVALUE;        
                   }        
        break;        

    case RESERVE2:        
        break;        

    case FREE:        
        break;        

    case ACTION:        
        break;        

    case UNDO:        
        break;        

    case COMMIT:        
            产生一个 SNMP v2版本的“trap”        

        var_trap.next_variable = &var_obj;      /* next variable */        
        var_trap.name = objid_snmptrap; /* snmpTrapOID.0 */        
        var_trap.name_length = sizeof(objid_snmptrap) / sizeof(oid);    /* number of sub-ids */        
        var_trap.type = ASN_OBJECT_ID;        
        var_trap.val.objid = demo_trap; /* demo-trap objid */        
        var_trap.val_len = sizeof(demo_trap);   /* length in bytes (not number of subids!) */        


        var_obj.next_variable = NULL;   /* No more variables after this one */        
        var_obj.name = example_string_oid;        
        var_obj.name_length = sizeof(example_string_oid) / sizeof(oid); /* number of sub-ids */        
        var_obj.type = ASN_OCTET_STR;   /* type of variable */        
        var_obj.val.string = example_str;       /* value */        
        var_obj.val_len = strlen(example_str);        
        DEBUGMSGTL(("example", "write_exampletrap2 sending the v2 trap\n",        
                    action));        
        send_v2trap(&var_trap);        
        DEBUGMSGTL(("example", "write_exampletrap2 v2 trap sent\n",        
                    action));        

        break;        

        }        
    return SNMP_ERR_NOERROR;        
}        






REGISTER_MIB()宏定义最终调用的是netsnmp_register_old_api函数。        

里面用到了这个结构体。        
typedef struct netsnmp_handler_registration_s {        

    /** for mrTable listings, and other uses */        
        char           *handlerName;        
    /** NULL = default context */        
        char           *contextName;        

        /**        
         * where are we registered at?        
         */        
        oid            *rootoid;        
        size_t          rootoid_len;        
        /**        
         * handler details        
         */        
        netsnmp_mib_handler *handler;        
        int             modes;        

        /**        
         * more optional stuff        
         */        
        int             priority;        
        int             range_subid;        
        oid             range_ubound;        
        int             timeout;        
        int             global_cacheid;        
        /**        
         * void ptr for registeree        
         */        
        void *          my_reg_void;        

} netsnmp_handler_registration;        



在调用netsnmp_register_handler(reginfo)函数注册netsnmp_handler_registration 类型的reginfo变量。        

netsnmp_register_handler()在agent/agent_handler.c文件中定义。        
有调用netsnmp_register_mib()函数        
这个函数构建一个netsnmp_subtree 类型的变量subtree进行注册。        

typedef struct netsnmp_subtree_s {        
    oid        *name_a; /* objid prefix of registered subtree */        
    u_char          namelen;    /* number of subid's in name above */        
    oid            *start_a;    /* objid of start of covered range */        
    u_char          start_len; /* number of subid's in start name */        
    oid            *end_a; /* objid of end of covered range   */        
    u_char          end_len;    /* number of subid's in end name */        
    struct variable *variables; /* pointer to variables array */        
    int             variables_len;      /* number of entries in above array */        
    int             variables_width;    /* sizeof each variable entry */        
    char           *label_a;    /* calling module's label */        
    netsnmp_session *session;        
    u_char          flags;        
    u_char          priority;        
    int             timeout;        
    struct netsnmp_subtree_s *next;       /* List of 'sibling' subtrees */        
    struct netsnmp_subtree_s *prev;       /* (doubly-linked list) */        
    struct netsnmp_subtree_s *children;   /* List of 'child' subtrees */        
    int             range_subid;        
    oid             range_ubound;        
    netsnmp_handler_registration *reginfo;      /* new API */        
    int             cacheid;        
    int             global_cacheid;        
} netsnmp_subtree;        

调用这个函数netsnmp_subtree_load()进行注册。

https://blog.51cto.com/baigoogledu/153815