snmp 代理端扩展源码分析
2008-05-05 17:53
/*example.c*/
#include #include #include #if HAVE_STDLIB_H #include #endif #if TIME_WITH_SYS_TIME # ifdef WIN32 # include # else # include # endif # include #else # if HAVE_SYS_TIME_H # include # else # include # 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数字都定义在了 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()进行注册。 |