源码下载
http://www.net-snmp.org/download.html
源码目录结构
net-snmp程序逻辑
(1)main主函数
#ifdef WIN32SERVICE //windows系统下使用snmp
static int
SnmpDaemonMain(int argc, TCHAR * argv[])
#else //linux系统
int
main(int argc, char *argv[]) //主函数
#endif
{
static const char options[] = "aAc:CdD::fhHI:l:L:m:M:n:p:P:qrsS:UvV-:Y:" //支持的一些参数,比如snmpd -v
/*下面是一些switch-case,根据输入参数返回不同的内容。比如输入snmpd -v,走到 case 'v':
version();
exit_code = 0;
goto out;
调用version()函数<见下文>,返回
$:snmpd -v
NET-SNMP version: 5.8
Web: http://www.net-snmp.org/
Email: net-snmp-coders@lists.sourceforge.net
*/
业务逻辑代码分析见下文:
static void
version(void)
{
printf("\nNET-SNMP version: %s\n"
"Web: http://www.net-snmp.org/\n"
"Email: net-snmp-coders@lists.sourceforge.net\n\n",
netsnmp_get_version());
}
搜了两篇不错的源码分析:
篇一:
snmpd代理(main主函数位于:net-snmp-5.9.3\agent\snmpd.c)完成两个功能:
1、接收网管发过来的snmp包,并对接收到的snmp包进行解析,校验后,找到并调用相应的处理函数进行处理。
2、调用注册了的告警函数,向网管发送告警信息。
Net-snmp代码流程图:(https://blog.csdn.net/jiangxin04211/category_9271012.html)
篇二:
来源:https://blog.csdn.net/fuyuande/article/details/83047765
以上两篇梳理的已经非常详细,不再赘述。
(2)整体框架
一般的网络框架处理流程如下(net-snmp也类似):
- 使用IO多路复用(linux下的select, poll, epoll)分离网络IO。
- 对分离出来的网络IO进行操作,分为socket句柄可读、可写和出错三种情况。
- 定时器事件,即检测一个定时器事件列表,如果有定时器到期,则执行该定时器事件。
while (netsnmp_running)
{
//更新配置文件
update_config();
//IO multiplexing
count = netsnmp_large_fd_set_select(numfds, &readfds, &writefds, &exceptfds, tvp);
if(count)
{
snmp_read2(&readfds);
}
/*
* 如果更新配置之后,要先保存配置,防止之后崩溃
*/
snmp_store_if_needed();
/*
* 处理定时器事件
*/
run_alarms();
netsnmp_check_outstanding_agent_requests();
}
run_alarms()函数:处理定时器事件。即遍历一个升序定时器列表,将定时器对象与当前时间(t_now)做比较,如果当前时间已经大于或等于定时器设置的时间,则表明定时器时间已经到了,执行定时器对应的回调函数。
void
snmp_read2(netsnmp_large_fd_set * fdset) --> snmp_sess_read2 --> _sess_read函数:
int
_sess_read(void *sessp, netsnmp_large_fd_set * fdset)
{
if (transport->flags & NETSNMP_TRANSPORT_FLAG_LISTEN)
{
int data_sock = transport->f_accept(transport);
return 0;
}
length = netsnmp_transport_recv(transport, rxbuf, rxbuf_len, &opaque,
&olength);
_sess_process_packet(sessp, sp, isp, transport,
ocopy, ocopy?olength:0, pptr,
pdulen)
}
_sess_read()函数根据状态标识transport->flags确定一个socket是侦听的socket还是普通与客户端连接的socket,如果是侦听sokcet则f_accept接收客户端的连接;如果是与客户端连接的socket,则先检测socket上有多少字节可读,如果没有字节可读或者检测字节数时出错,则关闭socket,反之调用处理函数。
snmpd程序使用一个session_list链表来管理所有的socket。
struct session_list *Sessions = NULL; /* MT_LIB_SESSION */
netsnmp_session *
snmp_open(netsnmp_session *session)
{
struct session_list *slp;
slp = (struct session_list *) snmp_sess_open(session);
if (!slp) {
return NULL;
}
snmp_res_lock(MT_LIBRARY_ID, MT_LIB_SESSION);
slp->next = Sessions;
Sessions = slp;
snmp_res_unlock(MT_LIBRARY_ID, MT_LIB_SESSION);
return (slp->session);
}
int
snmp_close(netsnmp_session * session)
{
struct session_list *slp = NULL, *oslp = NULL;
{ /*MTCRITICAL_RESOURCE */
snmp_res_lock(MT_LIBRARY_ID, MT_LIB_SESSION);
if (Sessions && Sessions->session == session) { /* If first entry */
slp = Sessions;
S
来源链接:https://www.jianshu.com/p/4998161c6c6f
(3)网络模块初始化
snmpd进程可以根据用户的配置来决定使用哪种协议来创建server,以某个协议为例子查看到接口注册流程如下:
init_snmp
_init_snmp
netsnmp_tdomain_init
snmp_transport_inits.h
netsnmp_tdomain_register(add domain_list(核心数据结构))
//snmp_transport_inits.h
//使用宏来区分使用udp还是tcp协议,如下:
#ifdef NETSNMP_TRANSPORT_UDP_DOMAIN //为1
netsnmp_udp_ctor();
#endif
#ifdef NETSNMP_TRANSPORT_TCP_DOMAIN //为1
netsnmp_tcp_ctor();
#endif
#ifdef NETSNMP_TRANSPORT_ALIAS_DOMAIN
netsnmp_alias_ctor();
#endif
#ifdef NETSNMP_TRANSPORT_UDPIPV6_DOMAIN
netsnmp_udpipv6_ctor();
#endif
#ifdef NETSNMP_TRANSPORT_TCPIPV6_DOMAIN
netsnmp_tcpipv6_ctor();
#endif
根据用户配置进行初始化流程如下:
init_master_agent
netsnmp_tdomain_transport_full
match = find_tdomain(mystring);//mystring即为用户的配置
match->f_create_from_tstring_new(执行注册好的回调函数创建server)
调用过程可以使用如此命令进行查看,程序执行过程中会将相应的token打印在终端上,方便梳理逻辑:
snmpd -Lo -f -Dsnmp_agent -Dread_config -Dtdomain
关键数据结构与接口:
typedef struct netsnmp_tdomain_s {
const oid *name;
size_t name_length;
const char **prefix;
/*
* The f_create_from_tstring field is deprecated, please do not use it
* for new code and try to migrate old code away from using it.
*/
netsnmp_transport *(*f_create_from_tstring) (const char *, int);
netsnmp_transport *(*f_create_from_ostring) (const u_char *, size_t, int);
struct netsnmp_tdomain_s *next;
netsnmp_transport *(*f_create_from_tstring_new) (const char *, int,
const char*);
} netsnmp_tdomain;
/*
* Our list of supported transport domains.
*/
static netsnmp_tdomain *domain_list = NULL;
int netsnmp_tdomain_register(netsnmp_tdomain *domain);
int netsnmp_tdomain_unregister(netsnmp_tdomain *domain);
static netsnmp_tdomain *find_tdomain(const char* spec);
实现原理
将底层的tcp与udp处理数据的接口封装好,在上层创建一个中间层transport来隐藏底层的具体实现,使用一个链表来将具体的实现作为子节点连接起来,利用反射的方式根据用户的配置去找到对应的接口,去创建server,这是一个需要学习的思路。
来源链接:https://www.jianshu.com/p/6ec214aae144
梳理思路:
1、snmp基础概念:
https://www.jianshu.com/p/6a9e83aa41c0 (图-可参考)
https://blog.csdn.net/JIANGXIN04211/article/details/78444747 (内容-可参考)
以上图+内容两者结合理解。
2、https://blog.csdn.net/JIANGXIN04211/article/details/78444821 源码下载及编译安装
https://blog.csdn.net/JIANGXIN04211/article/details/78475155 扩展get
https://blog.csdn.net/JIANGXIN04211/article/details/78477890 扩展set
https://blog.csdn.net/JIANGXIN04211/article/details/78478429 扩展trap
以上扩展方式需要重新编译安装snmpd源码及重启程序,属于静态扩展方式,需要总结其他动态方式扩展。
3、源代码main-receive函数分析:
https://www.jianshu.com/p/4998161c6c6f
4、新增mib库转.c核心代码分析:(涉及如何修改适配应用这一块)
https://blog.csdn.net/JIANGXIN04211/article/details/78478581
5、梳理 项目 是如何动态扩展mib库的。
知识的搬运工,站在巨人肩膀上…