anent开发过程中主要参考这篇文章net-snmp的MIBs扩展,但是在开发的过程中遇到一些问题,在这里记录下来。
Net-SNMP的安装参考见这里
--开始
TEST-MIB DEFINITIONS ::= BEGIN
--引入部分
IMPORTS
enterprises
FROM RFC1155-SMI
Integer32,OBJECT-TYPE
FROM SNMPv2-SMI
DisplayString
FROM SNMPv2-TC
TEXTUAL-CONVENTION
FROM SNMPv2-TC; --引用结束,用分号
--定义节点
--enterprises的OID是1.3.6.1.4
test OBJECT IDENTIFIER ::= {enterprises 77587}
readObject OBJECT IDENTIFIER ::= {test 1}
writeObject OBJECT IDENTIFIER ::= {test 2}
readObject OBJECT-TYPE --对象名称 --原文中为readobject
SYNTAX Integer32 --类型
MAX-ACCESS read-only --访问方式
STATUS current --状态
DESCRIPTION "test read" --描述
::= {test 1} --父节点
writeObject OBJECT-TYPE --对象名称
SYNTAX DisplayString --类型
MAX-ACCESS read-write --访问方式
STATUS current --状态
DESCRIPTION "test write" --描述
::= {test 2} --父节点
--结束定义
END
/usr/local/share/snmp/mibs
目录下。可以使用下面两个命令来查看你的MIB系统目录:in@v-m:~/learn$ net-snmp-config --default-mibdirs
/home/in/.snmp/mibs:/usr/local/share/snmp/mibs
in@v-m:~/learn$ snmpget -Dparse-mibs 2>&1 | grep directory
parse-mibs: Scanning directory /home/in/.snmp/mibs
parse-mibs: cannot open MIB directory /home/in/.snmp/mibs
parse-mibs: Scanning directory /usr/local/share/snmp/mibs
test
在测试时找不到节点,改为TEST-MIB:test
。in@v-m:~/learn$ snmptranslate -Tp -IR TEST-MIB:test
+--test(77587)
|
+-- -R-- Integer32 readObject(1)
+-- -RW- String writeObject(2)
Textual Convention: DisplayString
Size: 0..255
使用mib2c命令来产生.c和.h文件
in@v-m:~/learn$ mib2c -c mib2c.int_watch.conf readObject
You didn't give mib2c a valid OID to start with. IE, I could not find
any information about the mib node "readObject". This could be caused
because you supplied an incorrectly node, or by the MIB that you're
trying to generate code from isn't loaded. To make sure your mib is
loaded, run mib2c using this as an example:
env MIBS="+MY-PERSONAL-MIB" mib2c -c mib2c.int_watch.conf readObject
You might wish to start by reading the MIB loading tutorial at:
http://www.net-snmp.org/tutorial-5/commands/mib-options.html
And making sure you can get snmptranslate to display information about
your MIB node. Once snmptranslate works, then come back and try mib2c
again.
并没有产生.c和.h文件,提示我们要指定MIB节点
in@v-m:~/learn$ env MIBS="+TEST-MIB" mib2c -c mib2c.int_watch.conf readObject
writing to -
*** Warning: only generating code for nodes of MIB type INTEGER
writing to readObject.h
writing to readObject.c
running indent on readObject.h
running indent on readObject.c
in@v-m:~/learn$ env MIBS="+TEST-MIB" mib2c -c mib2c.scalar.conf writeObject
writing to writeObject.h
writing to writeObject.c
running indent on writeObject.c
running indent on writeObject.h
成功产生readObject.c readObject.h writeObject.c writeObject.h
修改后的readObject.c
#include
#include
#include
#include "readObject.h"
//这一句是mib2c生成的,默认值设为0
long readObject = 0; /* XXX: set default value */
void
init_readObject(void)
{
netsnmp_handler_registration *reg;
const oid readObject_oid[] = { 1,3,6,1,4,1,77587,1 };
static netsnmp_watcher_info readObject_winfo;
DEBUGMSGTL(("readObject", "Initializing the readObject module\n"));
/******************************************************/
//这里是我修改的,以便于验证其有效
readObject = 12345;
/******************************************************/
DEBUGMSGTL(("readObject",
"Initializing readObject scalar integer. Default value = %ld\n",
readObject));
reg = netsnmp_create_handler_registration(
"readObject", NULL,
readObject_oid, OID_LENGTH(readObject_oid),
HANDLER_CAN_RONLY);
netsnmp_init_watcher_info(&readObject_winfo, &readObject, sizeof(long),
ASN_INTEGER, WATCHER_FIXED_SIZE);
if (netsnmp_register_watched_scalar( reg, &readObject_winfo ) < 0 ) {
snmp_log( LOG_ERR, "Failed to register watched readObject" );
}
DEBUGMSGTL(("readObject",
"Done initalizing readObject module\n"));
}
修改后的writeObject.c
#include
#include
#include
#include "writeObject.h"
//这里是添加的,buf用于保存控制端设置的值,也用于返回。
#define BUFSIZE 1024
static char buf[BUFSIZE] = "test Write"; //给一个默认值
/** Initializes the writeObject module */
void
init_writeObject(void)
{
const oid writeObject_oid[] = { 1,3,6,1,4,1,77587,2 };
DEBUGMSGTL(("writeObject", "Initializing\n"));
netsnmp_register_scalar(
netsnmp_create_handler_registration("writeObject", handle_writeObject,
writeObject_oid, OID_LENGTH(writeObject_oid),
HANDLER_CAN_RWRITE
));
}
int
handle_writeObject(netsnmp_mib_handler *handler,
netsnmp_handler_registration *reginfo,
netsnmp_agent_request_info *reqinfo,
netsnmp_request_info *requests)
{
int ret;
switch(reqinfo->mode) {
//是获取操作
case MODE_GET:
snmp_set_var_typed_value(requests->requestvb, ASN_OCTET_STR,
/*这里填buf,用于返回数据给控制端*/ buf /* XXX: a pointer to the scalar's data */,
/*这里是buf数据字节数,注意writeObject类型*/ strlen(buf) /* XXX: the length of the data in bytes */);
break;
/*
* SET REQUEST
*
* multiple states in the transaction. See:
* http://www.net-snmp.org/tutorial-5/toolkit/mib_module/set-actions.jpg
*/
//下面是设置操作的,也就是snmpset
case MODE_SET_RESERVE1: //这个不管它
/* or you could use netsnmp_check_vb_type_and_size instead */
ret = netsnmp_check_vb_type(requests->requestvb, ASN_OCTET_STR);
if ( ret != SNMP_ERR_NOERROR ) {
netsnmp_set_request_error(reqinfo, requests, ret );
}
break;
case MODE_SET_RESERVE2: //这个也不管它
/* XXX malloc "undo" storage buffer */
//我们不需要动态申请内存,直接略过
if ( 0 /* 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. */
break;
/****************************************************************/
// 这里是我们的重点,控制端传过来的数据就在这里获取
case MODE_SET_ACTION:
/* XXX: perform the value change here */
/* 获取控制端使用snmpset传来的数据 */
memcpy(buf,requests->requestvb->buf,requests->requestvb->val_len);
if (0/* XXX: error? */) { //这个先不管了
netsnmp_set_request_error(reqinfo, requests, 0 /* some error */);
}
break;
//下面的都不管了
case MODE_SET_COMMIT:
/* XXX: delete temporary storage */
if (0 /* 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:
/* XXX: UNDO and return to previous value for the object */
if (0 /* XXX: error? */) {
/* try _really_really_ hard to never get to this point */
netsnmp_set_request_error(reqinfo, requests, SNMP_ERR_UNDOFAILED);
}
break;
default:
/* we should never get here, so this is a really bad error */
snmp_log(LOG_ERR, "unknown mode (%d) in handle_writeObject\n", reqinfo->mode );
return SNMP_ERR_GENERR;
}
return SNMP_ERR_NOERROR;
}
编译的时候需要使用到另一个工具 net-snmp-config 。这个工具用来做两件事,一个是生成中间代码,然后使用gcc来编译它。
为什么要生成中间代码呢?你看上面的生存的 readObject.c 中没有main函数就知道了吧。中间代码包含有main函数,在中间代码中实现了对agent的初始化,mib的初始化等。
具体使用如下
net-snmp-config --compile-subagent readObject readObject.c
in@v-m:~/learn$ net-snmp-config --compile-subagent readObject readObject.c
generating the temporary code file: netsnmptmp.39742.c
void init_readObject(void);
checking for init_readObject in readObject.c
init_readObject(void)
checking for shutdown_readObject in readObject.c
running: gcc -fno-strict-aliasing -g -O2 -Ulinux -Dlinux=linux -D_REENTRANT -D_GNU_SOURCE -DDEBIAN -fwrapv -fno-strict-aliasing -pipe -I/usr/local/include -D_LARGEFILE_SOURCE -D_FILE_OFFSET_BITS=64 -I/usr/lib/x86_64-linux-gnu/perl/5.22/CORE -I. -I/usr/local/include -o readObject netsnmptmp.39742.c readObject.c -lsensors -L/usr/local/lib -lnetsnmpmibs -lnetsnmpagent -lnetsnmp -lnetsnmpmibs -lsensors -ldl -lnetsnmpagent -Wl,-E -lnetsnmp -lcrypto
In file included from /usr/local/include/net-snmp/output_api.h:64:0,
from /usr/local/include/net-snmp/library/snmp_client.h:32,
from /usr/local/include/net-snmp/varbind_api.h:102,
from /usr/local/include/net-snmp/library/snmp_api.h:28,
from /usr/local/include/net-snmp/types.h:421,
from /usr/local/include/net-snmp/definitions.h:22,
from /usr/local/include/net-snmp/net-snmp-includes.h:67,
from readObject.c:7:
readObject.c: In function ‘init_readObject’:
readObject.c:53:17: warning: format ‘%d’ expects argument of type ‘int’, but argument 3 has type ‘long int’ [-Wformat=]
"Initializing readObject scalar integer. Default value = %d\n"
^
readObject.c:53:17: warning: format ‘%d’ expects argument of type ‘int’, but argument 3 has type ‘long int’ [-Wformat=]
"Initializing readObject scalar integer. Default value = %d\n"
^
removing the temporary code file: netsnmptmp.39742.c
subagent program readObject created
in@v-m:~/learn$ net-snmp-config --compile-subagent writeObject writeObject.c
generating the temporary code file: netsnmptmp.39906.c
void init_writeObject(void);
checking for init_writeObject in writeObject.c
init_writeObject(void)
checking for shutdown_writeObject in writeObject.c
running: gcc -fno-strict-aliasing -g -O2 -Ulinux -Dlinux=linux -D_REENTRANT -D_GNU_SOURCE -DDEBIAN -fwrapv -fno-strict-aliasing -pipe -I/usr/local/include -D_LARGEFILE_SOURCE -D_FILE_OFFSET_BITS=64 -I/usr/lib/x86_64-linux-gnu/perl/5.22/CORE -I. -I/usr/local/include -o writeObject netsnmptmp.39906.c writeObject.c -lsensors -L/usr/local/lib -lnetsnmpmibs -lnetsnmpagent -lnetsnmp -lnetsnmpmibs -lsensors -ldl -lnetsnmpagent -Wl,-E -lnetsnmp -lcrypto
removing the temporary code file: netsnmptmp.39906.c
subagent program writeObject created
产生readObject
和writeObject
可执行文件。
在启动主代理之前我们还要修改配置文件snmpd.conf
,在文件中增加master agentx
这行代码,开启子代理
rocommunity public
rwcommunity private
trapcommunity public
master agentx
启动主代理,可以看到打开主代理对子代理的支持,如果snmpd.conf
中没有增加这句话的话,是没有第一条的打印的。其中-Le -f
是打开调试,也可不带这两个参数。
in@v-m:~/learn$ sudo snmpd -Le -f
Turning on AgentX master support.
NET-SNMP version 5.7.3
打开一个新的终端串口,启动子代理(启动时要以管理员权限运行否则会提示Warning: Failed to connect to the agentx master agent ([NIL]):
,有提示也是需要运行是加-Le -f
打开调试才会输出的,如果没有打开调试,则会正常运行什么输出也没有,但当你使用snmpget -v 2c -c public localhost 1.3.6.1.4.1.77587.1.0
访问readObject节点时会出现SNMPv2-SMI::enterprises.77587.1.0 = No Such Object available on this agent at this OID
这样的提示。)
所以正确启动子代理的命令如下,其中-Le -f
是打开调试选项。
in@v-m:~/learn$ sudo ./readObject -Le -f
NET-SNMP version 5.7.3 AgentX subagent connected
在打开一个新的窗口,使用snmpget
命令测试。
in@v-m:~$ snmpget -v 2c -c public localhost 1.3.6.1.4.1.77587.1.0
SNMPv2-SMI::enterprises.77587.1.0 = INTEGER: 12345
也可这样写去访问readObject
节点:
in@v-m:~$ snmpget -v 2c -c public localhost TEST-MIB:readObject.0
TEST-MIB::readObject.0 = INTEGER: 12345
原文中节点写的是1.3.6.1.4.1.77587.1
这样写,但不能正确访问,提示如下:
in@v-m:~$ snmpget -v 2c -c public localhost 1.3.6.1.4.1.77587.1
SNMPv2-SMI::enterprises.77587.1 = No Such Instance currently exists at this OID
readObject
节点测试完了,下面开始测试writeObject
节点。
启动writeObject
子代理服务:
in@v-m:~/learn$ sudo ./writeObject -Le -f
NET-SNMP version 5.7.3 AgentX subagent connected
使用snmpget
命令测试writeObject
节点的读。
in@v-m:~$ snmpget -v 2c -c public localhost 1.3.6.1.4.1.77587.2.0
SNMPv2-SMI::enterprises.77587.2.0 = STRING: "test Wriet"
或者
in@v-m:~$ snmpget -v 2c -c public localhost TEST-MIB:writeObject.0
TEST-MIB::writeObject.0 = STRING: test Wriet
原文中的写法还是不能正确获取结果,提示如下:
in@v-m:~$ snmpget -v 2c -c public localhost writeObject.0
writeObject.0: Unknown Object Identifier (Sub-id not found: (top) -> writeObject)
使用snmpset
命令测试writeObject
节点的写。
in@v-m:~$ snmpset -v 2c -c private localhost TEST-MIB:writeObject.0 s "Hello Net-SNMP"
TEST-MIB::writeObject.0 = STRING: Hello Net-SNMP
注意:-c
后面的共同体是private
,否则会出现下面的错误。
in@v-m:~$ snmpset -v 2c -c public localhost TEST-MIB:writeObject.0 s "Hello Net-SNMP"
Error in packet.
Reason: noAccess
Failed object: TEST-MIB::writeObject.0
设置成功后再次用snmpget
查看节点内容,节点字符串已经是我们设置的值了。
in@v-m:~$ snmpget -v 2c -c public localhost TEST-MIB:writeObject.0
TEST-MIB::writeObject.0 = STRING: Hello Net-SNMP