net-snmp子代理(SubAgent)编写详述
net-snmp子代理(SubAgent)编写
可以使用mib2c
生成子代理的代码来编写子代理程序,但是这个方式并不利于我们来学习这个开发过程。
本文由乌合之众 lym瞎编,欢迎转载 blog.cnblogs.net/oloroso
本文由乌合之众 lym瞎编,欢迎转载 my.oschina.net/oloroso
Netsnmp_Node_Handler
先来看一个类型定义
在net-snmp
源码目录include/net-snmp/agent/
下的agent_handler.h
文件中有如下定义:
typedef int (Netsnmp_Node_Handler) (netsnmp_mib_handler *handler,
/** pointer to registration struct */
/** 指针,指向注册结构体 */
netsnmp_handler_registration *reginfo,
/** pointer to current transaction */
/** 指针,指向当前处理信息 */
netsnmp_agent_request_info *reqinfo,
netsnmp_request_info *requests);
这个类型就是一个函数指针,它是用来声明OID
对应的handler
函数的。
MIB/OID定义
先假设我们的MIB
定义为以下形式
可以看这里面的http://www.cnblogs.com/oloroso/p/4599501.html
+--test()
|
+-- -R-- Integer32 readObject()
+-- -RW- OctStr writeObject()
这只是举个例子,这个test
节点下面有两个节点,分别是只读的readObject
和读写的writeObject
。我们要实现的就是这个两个节点的agent
程序。
1、头文件test.h
的编写
先写头文件,这是比较机械的过程。
具体过程是:
- 1、声明
init_xxx
函数(初始化) - 2、声明
handle_xxx
函数(处理请求)
test.h
源代码如下:
#ifndef __TEST_H__
#define __TEST_H__ //1、声明init函数
void init_test(void);
//2、声明两个OID对应的handler函数
Netsnmp_Node_Handler handle_readObject;
Netsnmp_Node_Handler handle_writeObject; #endif //!__TEST_H__
2、test.c
的编写
test.c
的编写是为了实现test.h
中定义的三个函数。
init_test函数编写
init_test
函数主要用来注册
相关OID
的处理程序。这个函数将在main函数中被调用。
void
init_test(void)
{
//oid可能是unsigned char类型,也可能是unsigned long类型
//具体是那种类型,由宏定义EIGHBIT_SUBIDS控制
//具体可见include/net-snmp/library目录下的oid.h文件
const oid readObject_oid[] = { ,,,,,,, };
const oid writeObject_oid[] = { ,,,,,,, }; //这是debug消息输出的,具体可以看net-snmp源码目录下
//的include/net-snmp/library/目录下的snmp_debug.h和
// include/net-snmp/目录下的output_api.h文件
DEBUGMSGTL(("test", "Initializing\n")); //注册readObject节点的处理程序
netsnmp_register_scalar(
netsnmp_create_handler_registration(
"readObject", /*节点名*/
handle_readObject, /*处理函数*/
readObject_oid, /*oid字符串*/
OID_LENGTH(readObject_oid), /*节点长度*/
HANDLER_CAN_RONLY /*读写权限-只读*/
));
//注册writeObject节点的处理程序
netsnmp_register_scalar(
netsnmp_create_handler_registration(
"writeObject",
handle_writeObject,
writeObject_oid,
OID_LENGTH(writeObject_oid),
HANDLER_CAN_RWRITE /*读写权限*/
));
}
handle_readObject函数实现(只读节点)
对于只读节点,处理是比较简单。只需要从reqinfo
参数中获取请求的类型,然后对MODE_GET
类型的请求进行处理,将对应类型的值传出去即可。
int handle_readObject(
netsnmp_mib_handler *handler,
netsnmp_handler_registration *reginfo,
netsnmp_agent_request_info *reqinfo,
netsnmp_request_info *requests)
{
int value = ;
switch(reqinfo->mode) { case MODE_GET: /*读取值模式*/
/*这里将值传给snmpd,再由它组成snmp协议包发送出去*/
snmp_set_var_typed_value(
requests->requestvb,/*vb=>var bind变量绑定*/
ASN_INTEGER, /*值类型*/
&value, /*传出去的值*/
sizeof(value) /*传出去值的字节数*/
);
break;
default:
/*如果进入了这里,表明发生了非常严重的错误*/
snmp_log(LOG_ERR, /*日志类型*/
"unknown mode (%d) in handle_readObject\n",
reqinfo->mode );
return SNMP_ERR_GENERR;
} return SNMP_ERR_NOERROR; //正常返回
}
handle_writeObject函数实现(读写节点)
读写节点的读取部分就不说了,这里只说说set
操作的部分。
set
操作主要是从requests->requestvb->val
获取传过来的数据,关于requests->requestvb->val
,这是一个union netsnmp_vardata
类型。传过来值的长度可以从requests->requestvb->val_len
中获取。
typedef union {
long *integer;
u_char *string;
oid *objid;
u_char *bitstring;
struct counter64 *counter64;
#ifdef NETSNMP_WITH_OPAQUE_SPECIAL_TYPES
float *floatVal;
double *doubleVal;
#endif /*NETSNMP_WITH_OPAQUE_SPECIAL_TYPES */
} netsnmp_vardata;
typedef struct variable_list {
/** NULL for last variable */
struct variable_list *next_variable;
/** Object identifier of variable */
oid *name;
/** number of subid's in name */
size_t name_length;
/** ASN type of variable */
u_char type;
/** value of variable */
netsnmp_vardata val;
/** the length of the value to be copied into buf */
size_t val_len;
/** buffer to hold the OID */
oid name_loc[MAX_OID_LEN];
/** 90 percentile < 40. */
u_char buf[];
/** (Opaque) hook for additional data */
void *data;
/** callback to free above */
void (*dataFreeHook)(void *);
int index;
} netsnmp_variable_list;
具体内容可见net-snmp源码目录下的include/net-snmp/agent/
目录下的snmp_agent.h
文件和include/net-snmp/
目录下的types.h
文件。
在include/net-snmp/library
目录下的snmp.h
文件中定义了大量的宏,包括一些错误处理的。
关于reqinfo->mode
中的几个SET
相关的模式,在net-snmp源代码目录下agent/mibgroup/agentx/
目录下的subagent.c
文件中可以看到对其的处理。
具体大致是这样的,如果reqinfo->mode
的模式是RESERVE1
或者RESERVE2
模式,那么在这两个操作失败的时候就会将其修改为FREE
模式并再调用函数。如果是ACTION
模式,则失败时修改为UNDO
模式并再调用函数。
一次普通的snmpset
操作,会导致这个函数调用多次。一般的顺序是:
- 第一此调用的的时候
reqinfo->mode
为MODE_SET_RESERVE1
- 第二次为
MODE_SET_RESERVE2
如果前两个有一个失败了,那么将变为MODE_SET_FREE
模式,并不再继续下面的调用。 - 第三次为
MODE_SET_ACTION
如果失败,将变为MODE_SET_UNDO
模式。 - 第四次为
MODE_SET_COMMIT
上述过程中的出错,主要是指调用了netsnmp_set_request_error
函数,并且该函数的最后一个参数不是SNMP_ERR_NOERROR
(实际测试,值为SNMP_ERR_COMMITFAILED
程序也是正常的,不会进入出错处理)
int handle_writeObject(
netsnmp_mib_handler *handler,
netsnmp_handler_registration *reginfo,
netsnmp_agent_request_info *reqinfo,
netsnmp_request_info *requests)
{
int ret;
/*用来接收set请求过来的值,当前给了一个初始值*/
static char buf[] = "writeObject"; switch(reqinfo->mode) {
/*Get请求处理*/
case MODE_GET:
snmp_set_var_typed_value(
requests->requestvb,
ASN_OCTET_STR, /*OctStr类型*/
buf, /*传出数据*/
strlen(buf));
break; /* SET REQUEST 下面都是Set请求相关的模式 */
case MODE_SET_RESERVE1:
//检查变量绑定类型
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:
if () {
netsnmp_set_request_error(reqinfo, requests,
SNMP_ERR_RESOURCEUNAVAILABLE);
}
break; case MODE_SET_FREE:
/*释放在RESERVE1或RESERVE2下分配的资源*/
/*或者在某些地方出错的情况*/
break; case MODE_SET_ACTION:
/*在这里执行对set请求处理的操作,来获取传过来的值*/
/*直接将requests->requestvb->val中的数据拷贝到buf*/
if(requests->requestvb->val_len > ){
/*太长了,不要*/
ret = ;
}else if(requests->requestvb->val.string == NULL){
/*val指向NULL也不能要*/
ret = ;
}else{
ret = requests->requestvb->val_len;
memcpy(buf,requests->requestvb->val.string,ret);
buf[ret] = '\0';
}
if ( ret == ) {
/*set 请求出错*/
netsnmp_set_request_error(reqinfo, requests,
SNMP_ERR_BADVALUE);/*坏值*/
}
break; case MODE_SET_COMMIT:
/*这里用于删除临时存储数据*/
if (/* XXX: error? */) {
netsnmp_set_request_error(reqinfo, requests,
SNMP_ERR_COMMITFAILED);
}
break; case MODE_SET_UNDO:
/*撤消并返回到对象以前的值*/
if (/* XXX: error? */) {
netsnmp_set_request_error(reqinfo, requests,
SNMP_ERR_UNDOFAILED);
}
break; default:
snmp_log(LOG_ERR,
"unknown mode (%d) in handle_writeObject\n",
reqinfo->mode );
return SNMP_ERR_GENERR;
} return SNMP_ERR_NOERROR;
}
完整的代码如下
/*
* Note: this file originally auto-generated by mib2c using
* $
*/ #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 readObject_oid[] = { ,,,,,,, };
const oid writeObject_oid[] = { ,,,,,,, }; DEBUGMSGTL(("test", "Initializing\n")); netsnmp_register_scalar(
netsnmp_create_handler_registration("readObject", handle_readObject,
readObject_oid, OID_LENGTH(readObject_oid),
HANDLER_CAN_RONLY
));
netsnmp_register_scalar(
netsnmp_create_handler_registration("writeObject", handle_writeObject,
writeObject_oid, OID_LENGTH(writeObject_oid),
HANDLER_CAN_RWRITE
));
} int
handle_readObject(netsnmp_mib_handler *handler,
netsnmp_handler_registration *reginfo,
netsnmp_agent_request_info *reqinfo,
netsnmp_request_info *requests)
{
int value = ;
switch(reqinfo->mode) { case MODE_GET: /*读取值模式*/
/*这里将值传给snmpd,再由它组成snmp协议包发送出去*/
snmp_set_var_typed_value(
requests->requestvb,/*vb=>var bind变量绑定*/
ASN_INTEGER, /*值类型*/
&value, /*传出去的值*/
sizeof(value) /*传出去值的字节数*/
);
break;
default:
/*如果进入了这里,表明发生了非常严重的错误*/
snmp_log(LOG_ERR, /*日志类型*/
"unknown mode (%d) in handle_readObject\n",
reqinfo->mode );
return SNMP_ERR_GENERR;
} return SNMP_ERR_NOERROR; //正常返回
}
int handle_writeObject(
netsnmp_mib_handler *handler,
netsnmp_handler_registration *reginfo,
netsnmp_agent_request_info *reqinfo,
netsnmp_request_info *requests)
{
int ret;
/*用来接收set请求过来的值,当前给了一个初始值*/
static char buf[] = "writeObject"; switch(reqinfo->mode) {
/*Get请求处理*/
case MODE_GET:
snmp_set_var_typed_value(
requests->requestvb,
ASN_OCTET_STR, /*OctStr类型*/
buf, /*传出数据*/
strlen(buf));
break; /* SET REQUEST 下面都是Set请求相关的模式 */
case MODE_SET_RESERVE1:
//检查变量绑定类型
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:
if () {
netsnmp_set_request_error(reqinfo, requests,
SNMP_ERR_RESOURCEUNAVAILABLE);
}
break; case MODE_SET_FREE:
/*释放在RESERVE1或RESERVE2下分配的资源*/
/*或者在某些地方出错的情况*/
break; case MODE_SET_ACTION:
/*在这里执行对set请求处理的操作,来获取传过来的值*/
/*直接将requests->requestvb->val中的数据拷贝到buf*/
if(requests->requestvb->val_len > ){
/*太长了,不要*/
ret = ;
}else if(requests->requestvb->val == NULL){
/*val指向NULL也不能要*/
ret = ;
}else{
ret = requests->requestvb->val_len;
memncpy(buf,requests->requestvb->val,ret);
buf[ret] = '\0';
}
if ( ret == ) {
/*set 请求出错*/
netsnmp_set_request_error(reqinfo, requests,
SNMP_ERR_BADVALUE);/*坏值*/
}
break; case MODE_SET_COMMIT:
/*这里用于删除临时存储数据*/
if (/* XXX: error? */) {
netsnmp_set_request_error(reqinfo, requests,
SNMP_ERR_COMMITFAILED);
}
break; case MODE_SET_UNDO:
/*撤消并返回到对象以前的值*/
if (/* XXX: error? */) {
netsnmp_set_request_error(reqinfo, requests,
SNMP_ERR_UNDOFAILED);
}
break; default:
snmp_log(LOG_ERR,
"unknown mode (%d) in handle_writeObject\n",
reqinfo->mode );
return SNMP_ERR_GENERR;
} return SNMP_ERR_NOERROR;
}
test.c
3、main函数的编写
写完上面两个文件,其实就可以使用net-snmp-conf
工具来生成带有main
函数的文件了,但是这里不说这种方式。
简述一下这个main
函数的流程,当然,这只是最基本的,还可以添加一些自己需要的。
- 声明本程序是一个子代理
SubAgent
调用函数netsnmp_ds_set_boolean(NETSNMP_DS_APPLICATION_ID, NETSNMP_DS_AGENT_ROLE, 1);
来实现。 - 如果有必要,先初始化
socket
函数库,这是在windows下需要的。
SOCK_STARTUP;
- 初始化net-snmp的
agent
库
init_agent(app_name);
//app_name是一个字符串,通常使用程序的名称。你可以自定义一个名称。这个是用于给snmpd
进程作为标识子代理程序使用的。如果没有这个操作,snmpd
进程将不知道子代理程序的存在。 - 初始化我们自己的
mib
代码
这个就简单了,就是调用我们前面写的init_test()
函数即可 - 调用
init_snmp("test")
函数
这个函数声明在include/net-snmp/library/snmp_api.h
文件中,定义在snmplib/snmp_api.c
中。
如果没有调用这个函数,snmpd
将不知道这个子代理的存在。
这里有一个注释test will be used to read test.conf files.
测试将用于读取test.conf
文件。但是其实质的意思我也不知道。这个函数的参数,也可以自定义填写,貌似也会正常运行。 - 无限循环,等待处理请求
使用while(1){agent_check_and_process(1);}
来处理请求 - 告知
snmpd
,子代理结束了
这里是告知snmpd
,本程序将退出了。调用函数snmp_shutdown(app_name);
来操作。
这里还要说明一点,通常这个程序还应该捕捉三个信号,来进行退出前的处理。一个是SIGTREM
,kill默认。第二个是SIGINT
,这个不用说了,软中断信号,结束代理。第三个是SIGHUP
信号,这个也是一样,防止退出终端时调用其默认处理(退出进程)。
如果进程捕捉到了前两个个信号,则认为snmpd
结束了,那么就可以退出了。如果是后一个,那么可以按需处理。 - 程序结束前的清理工作
先调用shutdown_agent();
来结束agent
库使用。
再调用SOCK_CLEANUP;
来清理Socket
库调用。
main.c完整代码
#include <net-snmp/net-snmp-config.h>
#include <signal.h>
#include <net-snmp/net-snmp-includes.h>
#include <net-snmp/agent/net-snmp-agent-includes.h>
#include "test.h"
const char *app_name = "test";
static int reconfig = ; extern int netsnmp_running; #ifdef __GNUC__
#define UNUSED __attribute__((unused))
#else
#define UNUSED
#endif RETSIGTYPE
stop_server(UNUSED int a) {
puts("捕捉到信号 SIGTERM/SIGINT");
netsnmp_running = ;
} #ifdef SIGHUP
RETSIGTYPE
hup_handler(int sig)
{
puts("捕捉到信号 SIGHUP");
reconfig = ;
signal(SIGHUP, hup_handler);
}
#endif void reg_sig()
{
#ifdef SIGTERM
signal(SIGTERM, stop_server);
#endif
#ifdef SIGINT
signal(SIGINT, stop_server);
#endif
#ifdef SIGHUP
signal(SIGHUP, hup_handler);
#endif
} int
main (int argc, char **argv)
{
int arg;
char* cp = NULL;
int dont_fork = , do_help = ; puts(" 1 we are a subagent");
netsnmp_ds_set_boolean(NETSNMP_DS_APPLICATION_ID,
NETSNMP_DS_AGENT_ROLE, ); puts("2 initialize tcpip, if necessary");
SOCK_STARTUP; puts("3 initialize the agent library");
init_agent(app_name); puts("4 initialize your mib code here");
init_test(); puts("5 test will be used to read test.conf files");
init_snmp("test"); /* In case we received a request to stop (kill -TERM or kill -INT) */
puts("设置信号捕捉函数(SIGINT/SIGTERM/SIGHUP)");
reg_sig(); netsnmp_running = ;
puts("6 main loop here...");
while(netsnmp_running) {
if (reconfig) {
free_config();
read_configs();
reconfig = ;
}
agent_check_and_process();
} puts("7 at shutdown time");
snmp_shutdown(app_name); puts("8 shutdown the agent library");
shutdown_agent();
SOCK_CLEANUP;
exit();
}
main.c
4、写一个简单的makefile
注意,下面代码中的INDIR
和LIBSDIR
需要根据实际路径填写
CFLAGS=-fno-strict-aliasing -g -O2 -Ulinux -Dlinux=linux -D_REENTRANT -D_GNU_SOURCE -DDEBIAN -fwrapv -fno-strict-aliasing -pipe -D_LARGEFILE_SOURCE -D_FILE_OFFSET_BITS=
INDIR=-I/usr/local/include -I. -I/usr/local/net-snmp/include -I/usr/lib/x86_64-linux-gnu/perl/5.20/CORE
LIBSDIR=-L/usr/local/net-snmp/lib
LIBS=-lnetsnmpmibs -lnetsnmpagent -lnetsnmp -lnetsnmpmibs -ldl -lnetsnmpagent -Wl,-E -lnetsnmp CC=gcc test:main.c test.c
${CC} ${CFLAGS} ${INDIR} -o $@ $^ ${LIBSDIR} ${LIBS}
net-snmp子代理(SubAgent)编写详述的更多相关文章
- snmp agent 表格实现(子代理方式实现)
前奏參见例如以下: http://blog.sina.com.cn/s/blog_8f3de3250100xhao.html http://blog.csdn.net/hepeng597/articl ...
- snmpd 子代理模式编译测试
1.参考链接 1)Net-snmp添加子代理示例 https://blog.csdn.net/eyf0917/article/details/39546651 2.操作步骤 1)网络拷贝下面的文件 ...
- Atitit 代理与分销系统(1) 子代理 充值总额功能设计概览 sum() groubpy subagt
Atitit 代理与分销系统(1) 子代理 充值总额功能设计概览 sum() groubpy subagt Keyword 分组与聚合操作. 一个for做分组...里面的做聚合... 数据g操作查 ...
- SONiC架构分析
目录 系统架构 设计原则 核心组件 SWSS 容器 syncd 容器 网络应用容器 内部通信模型 SubscriberStateTable NotificationProducer/Consumer ...
- snmp理论篇
SNMP协议入门 1.引言 基于TCP/IP的网络管理包含3个组成部分: 1) 一个管理信息库MIB(Management Information Base).管理信息库包含所有代理进程的所有可被查询 ...
- SNMP消息传输机制
1.引言 基于TCP/IP的网络管理包含3个组成部分: 1) 一个管理信息库MIB(Management Information Base).管理信息库包含所有代理进程的所有可被查询和修改的参数.RF ...
- 前端学HTTP之代理
前面的话 Web代理(proxy)服务器是网络的中间实体,位于客户端和服务器之间,扮演“中间人”的角色,在各端点之间来回传送HTTP报文.本文将介绍HTTP代理服务器相关内容 中间实体 Web上的代理 ...
- SNMP 原理与实战详解
原文地址:http://freeloda.blog.51cto.com/2033581/1306743 原创作品,允许转载,转载时请务必以超链接形式标明文章 原始出处 .作者信息和本声明.否则将追究法 ...
- db2代理和优化
DB2 的代理 (agent) 是位于 DB2 服务器中的服务于应用程序请求的一些进程或线程.当有外部应用程序连接至 DB2 实例提出访问请求时,DB2 的代理就会被激活去应答这些请求.一般 DB2 ...
随机推荐
- llinux 查看自己的公网ip
如何在LINUX服务器下查看公网IP地址,可以使用下面的方法: [root@web ~]#curl http://members.3322.org/dyndns/getip [root@web ~]# ...
- JavaWeb学习笔记——jsp:setproperty和getproperty
- JPG / TGA
JPG http://www.openjpeg.org http://libjpeg.sourceforge.net TGA http://tgalib.sourceforge.net
- 免费SSL证书Let’s Encrypt
由于我们公司测试环境使用的这个.自己没有亲手搭建使用,但是知道有这个东西.以后使用的话自己直接搞起. 连接文档:http://www.5icool.org/a/201512/a15271.html ...
- Nginx for Windows 使用笔记
一.常见启动错误 1. "No mapping for the Unicode character exists in the target multi-byte code page&quo ...
- 工具介绍 - VSCommands
VSCommands 一个Visual Studio的轻量级扩展工具 地址:http://vscommands.squaredinfinity.com/home 1.可以设置自动隐藏显示主菜单栏,设置 ...
- 装X之写博客
博客作用: 为了温习以前的知识,记录下 前几天和一个前辈聊天,说起看书总是前面学後面忘点的事情· 写个博客试试?
- web性能调优
http://blog.csdn.net/chengzhezhijian/article/details/50680250 Java Web应用调优线程池:没你想的那么复杂 标签: java 线程池 ...
- Linq查询操作语句学习
对于一个集合,我们通常会用foreach或者for循环来判断查找里面的元素. 但这种方法通常会看起来比较复杂,我们可以使用linq. Linq允许编写C#代码以查询数据库相同的方式操作内存数据(写法类 ...
- R语言 常见模型
转自 雪晴网 [R]如何确定最适合数据集的机器学习算法 抽查(Spot checking)机器学习算法是指如何找出最适合于给定数据集的算法模型.本文中我将介绍八个常用于抽查的机器学习算法,文中还包括各 ...