前言
本篇主要介绍如何使用开源代码net-snmp来构建自己的代理程序,实现自己的专属业务。net-snmp支持静态编译、动态库加载、子代理加载三种方式扩展MIB,构建代理。
一、下载编译net-snmp
1、从net-snmp官方网站http://www.net-snmp.org/download.html下载net-snmp-5.9.3源码。
2、配置编译环境
./configure --prefix=/usr/local/snmpd --enable-ipv6 --with-openssl
–prefix=/usr/local/snmpd 编译生成目标文件放置目录
–enable-ipv6 支持IPV6
–with-openssl 编译带openssl
中间过程全部选择默认,完成后显示配置信息如下:
3、编译
make -j4
-j是多线程编译,提高编译速度
4、拷贝生成文件到/usr/local/snmpd目录
make install
二、创建MIB文件
1.创建MIB
我们先创建一个简单的MIB文件myTest.mib,含有一个读写权限的节点testObject ,保存到 /usr/local/snmpd/share/snmp/mibs/ 目录。
--开始
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.1
test OBJECT IDENTIFIER ::= {enterprises 8888}
testObject OBJECT IDENTIFIER ::= {test 1}
testObject OBJECT-TYPE --对象名称
SYNTAX Integer32 --类型
MAX-ACCESS read-write --访问方式
STATUS current --状态
DESCRIPTION "test write" --描述
::= {test 1} --父节点
--结束定义
END
2.使用 snmptranslate查看MIB加载
然后使用 snmptranslate 查看自定义的 TEST-MIB 是否被加载
/usr/local/snmpd/bin/snmptranslate -Tp -IR TEST-MIB::test
+--test(8888)
|
+-- -RW- Integer32 testObject(1)
3.查询OID
我们先来获取一下前面定义的 testObject 节点试试。 因为 enterprises 的OID是 1.3.6.1.4.1 ,而 test 是 enterprises 的叶子节点(8888),而 testObject 又是 test 的叶子节点(1)。所以其OID为 1.3.6.1.4.1.8888.1 。
下面使用snmpget来测试一下 ,找不到OID
./snmpget -v2c -c public localhost 1.3.6.1.4.1.8888.1
SNMPv2-SMI::enterprises.8888.1 = No Such Object available on this agent at this OID
三、使用MIB2C生成.c和.h文件
执行以下命令生成.c和.h文件,其中test表示要生成文件的MIB节点名称。
MIBS="+/usr/local/snmpd/share/snmp/mibs/myTest.mib" /usr/local/snmpd/bin/mib2c test
命令执行后显示如下:
writing to -
mib2c has multiple configuration files depending on the type of
code you need to write. You must pick one depending on your need.
You requested mib2c to be run on the following part of the MIB tree:
OID: test
numeric translation: .1.3.6.1.4.1.8888
number of scalars within: 1
number of tables within: 0
number of notifications within: 0
First, do you want to generate code that is compatible with the
ucd-snmp 4.X line of code, or code for the newer Net-SNMP 5.X code
base (which provides a much greater choice of APIs to pick from). You
can also generate C-header files containing define statements for
column numbers, enums, etc. Or do you simply want to translate the
MIB structures into documentation?
1) ucd-snmp style code
2) Net-SNMP style code
3) Generate header files
4) Generate documentation
Select your choice : 2
**********************************************************************
GENERATING CODE FOR SCALAR OBJECTS:
**********************************************************************
It looks like you have some scalars in the mib you requested, so I
will now generate code for them if you wish. You have two choices
for scalar API styles currently. Pick between them, or choose not
to generate any code for the scalars:
1) If you're writing code for some generic scalars
(by hand use: "mib2c -c mib2c.scalar.conf test")
2) If you want to magically "tie" integer variables to integer
scalars
(by hand use: "mib2c -c mib2c.int_watch.conf test")
3) Don't generate any code for the scalars
Select your choice: 1
using the mib2c.scalar.conf configuration file to generate your code.
writing to test.h
writing to test.c
**********************************************************************
* NOTE WELL: The code generated by mib2c is only a template. *YOU* *
* must fill in the code before it'll work most of the time. In many *
* cases, spots that MUST be edited within the files are marked with *
* /* XXX */ or /* TODO */ comments. *
**********************************************************************
running indent on test.h
running indent on test.c
生成test.c文件如下:
/*
* Note: this file originally auto-generated by mib2c
* using mib2c.scalar.conf
*/
#include <net-snmp/net-snmp-config.h>
#include <net-snmp/net-snmp-includes.h>
#include <net-snmp/agent/net-snmp-agent-includes.h>
#include "test.h"
/** Initializes the test module */
void
init_test(void)
{
const oid testObject_oid[] = { 1,3,6,1,4,1,8888,1 };
DEBUGMSGTL(("test", "Initializing\n"));
netsnmp_register_scalar(
netsnmp_create_handler_registration("testObject", handle_testObject,
testObject_oid, OID_LENGTH(testObject_oid),
HANDLER_CAN_RWRITE
));
}
int
handle_testObject(netsnmp_mib_handler *handler,
netsnmp_handler_registration *reginfo,
netsnmp_agent_request_info *reqinfo,
netsnmp_request_info *requests)
{
int ret;
/* 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, ASN_INTEGER,
/* XXX: a pointer to the scalar's data */,
/* 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
*/
case MODE_SET_RESERVE1:
/* or you could use netsnmp_check_vb_type_and_size instead */
ret = netsnmp_check_vb_type(requests->requestvb, ASN_INTEGER);
if ( ret != SNMP_ERR_NOERROR ) {
netsnmp_set_request_error(reqinfo, requests, ret );
}
break;
case MODE_SET_RESERVE2:
/* XXX malloc "undo" storage buffer */
if (/* 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 */
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:
/* 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;
default:
/* we should never get here, so this is a really bad error */
snmp_log(LOG_ERR, "unknown mode (%d) in handle_testObject\n", reqinfo->mode );
return SNMP_ERR_GENERR;
}
return SNMP_ERR_NOERROR;
生成的.c文件是一个模板,我们需要手动修改几个地方:
/*
* Note: this file originally auto-generated by mib2c
* using mib2c.scalar.conf
*/
#include <net-snmp/net-snmp-config.h>
#include <net-snmp/net-snmp-includes.h>
#include <net-snmp/agent/net-snmp-agent-includes.h>
#include "test.h"
//add
int value = 10;
/** Initializes the test module */
void
init_test(void)
{
const oid testObject_oid[] = { 1,3,6,1,4,1,8888,1 };
DEBUGMSGTL(("test", "Initializing\n"));
netsnmp_register_scalar(
netsnmp_create_handler_registration("testObject", handle_testObject,
testObject_oid, OID_LENGTH(testObject_oid),
HANDLER_CAN_RWRITE
));
}
int
handle_testObject(netsnmp_mib_handler *handler,
netsnmp_handler_registration *reginfo,
netsnmp_agent_request_info *reqinfo,
netsnmp_request_info *requests)
{
int ret;
/* 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:
//add
snmp_set_var_typed_value(requests->requestvb, ASN_INTEGER,
&value/* XXX: a pointer to the scalar's data */,
4/* 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
*/
case MODE_SET_RESERVE1:
/* or you could use netsnmp_check_vb_type_and_size instead */
ret = netsnmp_check_vb_type(requests->requestvb, ASN_INTEGER);
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 */
//add
value = *requests->requestvb->val.integer;
printf("set: %d\n", value);
if (0/* XXX: error? */) {
netsnmp_set_request_error(reqinfo, requests, SNMP_ERR_BADVALUE/* 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_testObject\n", reqinfo->mode );
return SNMP_ERR_GENERR;
}
return SNMP_ERR_NOERROR;
}
四、构建代理
1.静态编译
1)将test.c和test.h放到net-snmp源码的agent/mibgroup/目录下,进行编译:
./configure --prefix=/usr/local/snmpd --enable-ipv6 --with-openssl --with-mib-modules=test
–with-mib-modules=test是将test模块加载到snmpd中,这样编译出来的snmpd程序就支持test中的节点操作,假如要将test1、test2二个模块加载进去,则–with-mib-modules=test1 test2。生成配置信息如下:
---------------------------------------------------------
Net-SNMP configuration summary:
---------------------------------------------------------
SNMP Versions Supported: 1 2c 3
Building for: linux
Net-SNMP Version: 5.9.3
Network transport support: Callback Unix Alias TCP UDP TCPIPv6 UDPIPv6 IPv4Base SocketBase TCPBase UDPIPv4Base UDPBase IPBase IPv6Base
SNMPv3 Security Modules: usm
Agent MIB code: default_modules test => snmpv3mibs mibII ucd_snmp notification notification-log-mib target agent_mibs agentx disman/event disman/schedule utilities host
MYSQL Trap Logging: unavailable
Embedded Perl support: enabled
SNMP Perl modules: building -- embeddable
SNMP Python modules: disabled
Crypto support from: use_pkg_config_for_openssl
Authentication support: MD5 SHA1 SHA224 SHA256 SHA384 SHA512
Encryption support: DES AES AES128 AES192 AES192C AES256 AES256C
Local DNSSEC validation: disabled
---------------------------------------------------------
重新编译生成snmpd
make -j4
make install
2)创建snmpd.conf配置文件:
sudo cat ../share/snmp/snmpd.conf
rwcommunity public
3)启动snmpd
./snmpd -c ../share/snmp/snmpd.conf -C -Lo
NET-SNMP version 5.9.3
4)查询、设置OID的值
./snmpget -v2c -c public localhost 1.3.6.1.4.1.8888.1.0
SNMPv2-SMI::enterprises.8888.1.0 = INTEGER: 10
./snmpset -v2c -c public localhost 1.3.6.1.4.1.8888.1.0 i 12
SNMPv2-SMI::enterprises.8888.1.0 = INTEGER: 12
./snmpget -v2c -c public localhost 1.3.6.1.4.1.8888.1.0
SNMPv2-SMI::enterprises.8888.1.0 = INTEGER: 12
2.动态库加载
1)编译动态库libtest.so
gcc -c -fpic test.c -o test.o -I/usr/local/snmpd/include
gcc -shared test.o -o libtest.so
2)修改snmpd.conf配置文件支持加载libtest.so动态库
cat ../share/snmp/snmpd.conf
rwcommunity public
dlmod test /usr/local/snmpd/share/snmp/mibs/libtest.so
3)同上启动snmpd,查询设置OID的值。
注意动态库加载和子代理加载使用的snmpd为没有编译test.c的snmpd。
3.子代理加载
1)编译的时候需要用到 net-snmp-config工具 ,用来做两件事,一个是生成中间代码,然后用gcc来编译它。因为test.c文件中没有main函数, 所以要生成中间代码,其实就是主程序。
具体使用如下
net-snmp-config --compile-subagent test test.c
–compile-subagent的意思是编译为subagent程序。
test 是编译后输出程序名
test.c是要编译的文件
还可以加上 --norm 参数来阻止编译后删除生成的中间代码文件。我们可以试一下
net-snmp-config --compile-subagent --norm test ../share/snmp/mibs/test.c
generating the temporary code file: netsnmptmp.19614.c
checking for init_test in ../share/snmp/mibs/test.c
init_test(void)
checking for shutdown_test in ../share/snmp/mibs/test.c
running: gcc -fno-strict-aliasing -g -O2 -Ulinux -Dlinux=linux -I. -I/usr/include -o test netsnmptmp.19614.c ../share/snmp/mibs/test.c -L/usr/lib -lnetsnmpmibs -lnetsnmpagent -lnetsnmp -lnetsnmpmibs -lpci -ldl -lnetsnmpagent -lnetsnmp
leaving the temporary code file: netsnmptmp.19614.c
subagent program test created
编译后我们可以看到,当前目录下出现了编译后的程序 test 和一个 netsnmptmp.19614.c 的文件。我们打开netsnmptmp.19614.c文件,就可以看到这里有一个 main 函数。netsnmptmp.19614.c的代码如下,有兴趣的可以看一下
/* generated from net-snmp-config */
#include <net-snmp/net-snmp-config.h>
#ifdef HAVE_SIGNAL
#include <signal.h>
#endif /* HAVE_SIGNAL */
#include <net-snmp/net-snmp-includes.h>
#include <net-snmp/agent/net-snmp-agent-includes.h>
const char *app_name = "test";
static int reconfig = 0;
extern int netsnmp_running;
#ifdef __GNUC__
#define UNUSED __attribute__((unused))
#else
#define UNUSED
#endif
RETSIGTYPE
stop_server(UNUSED int a) {
netsnmp_running = 0;
}
#ifdef SIGHUP
RETSIGTYPE
hup_handler(int sig)
{
reconfig = 1;
signal(SIGHUP, hup_handler);
}
#endif
static void
usage(const char *prog)
{
fprintf(stderr,
"USAGE: %s [OPTIONS]\n"
"\n"
"OPTIONS:\n", prog);
fprintf(stderr,
" -d\t\t\tdump all traffic\n"
" -D TOKEN[,...]\tturn on debugging output for the specified "
"TOKENs\n"
"\t\t\t (ALL gives extremely verbose debugging output)\n"
" -f\t\t\tDo not fork() from the calling shell.\n"
" -h\t\t\tdisplay this help message\n"
" -H\t\t\tdisplay a list of configuration file directives\n"
" -L LOGOPTS\t\tToggle various defaults controlling logging:\n");
snmp_log_options_usage("\t\t\t ", stderr);
#ifndef DISABLE_MIB_LOADING
fprintf(stderr,
" -m MIB[" ENV_SEPARATOR "...]\t\tload given list of MIBs (ALL loads "
"everything)\n"
" -M DIR[" ENV_SEPARATOR "...]\t\tlook in given list of directories for MIBs\n");
#endif /* DISABLE_MIB_LOADING */
#ifndef DISABLE_MIB_LOADING
fprintf(stderr,
" -P MIBOPTS\t\tToggle various defaults controlling mib "
"parsing:\n");
snmp_mib_toggle_options_usage("\t\t\t ", stderr);
#endif /* DISABLE_MIB_LOADING */
fprintf(stderr,
" -v\t\t\tdisplay package version number\n"
" -x TRANSPORT\tconnect to master agent using TRANSPORT\n");
exit(1);
}
static void
version(void)
{
fprintf(stderr, "NET-SNMP version: %s\n", netsnmp_get_version());
exit(0);
}
int
main (int argc, char **argv)
{
int arg;
char* cp = NULL;
int dont_fork = 0, do_help = 0;
while ((arg = getopt(argc, argv, "dD:fhHL:"
#ifndef DISABLE_MIB_LOADING
"m:M:"
#endif /* DISABLE_MIB_LOADING */
"n:"
#ifndef DISABLE_MIB_LOADING
"P:"
#endif /* DISABLE_MIB_LOADING */
"vx:")) != EOF) {
switch (arg) {
case 'd':
netsnmp_ds_set_boolean(NETSNMP_DS_LIBRARY_ID,
NETSNMP_DS_LIB_DUMP_PACKET, 1);
break;
case 'D':
debug_register_tokens(optarg);
snmp_set_do_debugging(1);
break;
case 'f':
dont_fork = 1;
break;
case 'h':
usage(argv[0]);
break;
case 'H':
do_help = 1;
break;
case 'L':
if (snmp_log_options(optarg, argc, argv) < 0) {
exit(1);
}
break;
#ifndef DISABLE_MIB_LOADING
case 'm':
if (optarg != NULL) {
setenv("MIBS", optarg, 1);
} else {
usage(argv[0]);
}
break;
case 'M':
if (optarg != NULL) {
setenv("MIBDIRS", optarg, 1);
} else {
usage(argv[0]);
}
break;
#endif /* DISABLE_MIB_LOADING */
case 'n':
if (optarg != NULL) {
app_name = optarg;
netsnmp_ds_set_string(NETSNMP_DS_LIBRARY_ID,
NETSNMP_DS_LIB_APPTYPE, app_name);
} else {
usage(argv[0]);
}
break;
#ifndef DISABLE_MIB_LOADING
case 'P':
cp = snmp_mib_toggle_options(optarg);
if (cp != NULL) {
fprintf(stderr, "Unknown parser option to -P: %c.\n", *cp);
usage(argv[0]);
}
break;
#endif /* DISABLE_MIB_LOADING */
case 'v':
version();
break;
case 'x':
if (optarg != NULL) {
netsnmp_ds_set_string(NETSNMP_DS_APPLICATION_ID,
NETSNMP_DS_AGENT_X_SOCKET, optarg);
} else {
usage(argv[0]);
}
break;
default:
fprintf(stderr, "invalid option: -%c\n", arg);
usage(argv[0]);
break;
}
}
if (do_help) {
netsnmp_ds_set_boolean(NETSNMP_DS_APPLICATION_ID,
NETSNMP_DS_AGENT_NO_ROOT_ACCESS, 1);
} else {
/* we are a subagent */
netsnmp_ds_set_boolean(NETSNMP_DS_APPLICATION_ID,
NETSNMP_DS_AGENT_ROLE, 1);
if (!dont_fork) {
if (netsnmp_daemonize(1, snmp_stderrlog_status()) != 0)
exit(1);
}
/* initialize tcpip, if necessary */
SOCK_STARTUP;
}
/* initialize the agent library */
init_agent(app_name);
/* initialize your mib code here */
init_test();
/* test will be used to read test.conf files. */
init_snmp("test");
if (do_help) {
fprintf(stderr, "Configuration directives understood:\n");
read_config_print_usage(" ");
exit(0);
}
/* In case we received a request to stop (kill -TERM or kill -INT) */
netsnmp_running = 1;
#ifdef SIGTERM
signal(SIGTERM, stop_server);
#endif
#ifdef SIGINT
signal(SIGINT, stop_server);
#endif
#ifdef SIGHUP
signal(SIGHUP, hup_handler);
#endif
/* main loop here... */
while(netsnmp_running) {
if (reconfig) {
free_config();
read_configs();
reconfig = 0;
}
agent_check_and_process(1);
}
/* at shutdown time */
snmp_shutdown(app_name);
/* deinitialize your mib code here */
/* shutdown the agent library */
shutdown_agent();
SOCK_CLEANUP;
exit(0);
}
2)修改snmpd.conf配置文件支持子代理模式
sudo cat ../share/snmp/snmpd.conf
rwcommunity public
master agentx
3)启动snmpd
./snmpd -c ../share/snmp/snmpd.conf -C -Lo
Turning on AgentX master support.
NET-SNMP version 5.9.3
4)启动子代理
./test
5)同上,查询、设置OID的值文章来源:https://www.toymoban.com/news/detail-762063.html
总结
本篇主要通过一个简单的MIB测试文件,演示了如何使用net-snmp来构建自己业务代理程序的过程,分别测试了三种方式: 静态编译、动态库加载、子代理加载。但是要真正构建一个业务代理程序,还涉及到表节点以及业务数据获取等内容,需要进一步深入的研究。文章来源地址https://www.toymoban.com/news/detail-762063.html
到了这里,关于如何使用net-snmp构建代理程序的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!