SNMP源码分析

  • Post author:
  • Post category:其他

源码下载

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库的。

知识的搬运工,站在巨人肩膀上…


版权声明:本文为qq_42041303原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。