snmp v1 get请求优化(上)

1背景

在上一篇snmp v1 get请求响应c++实现里简单实现了一个队oid为system.sysNam的响应,但是实际上不能这么使用,不然就一直是Demo。 在这一篇,计划去借鉴net-snmp的源码,对之前的进行改造优化。

2 net-snmp 源码

github 地址可以从官网的首页找到 www.net-snmp.org/

或者直接访问 github.com/net-snmp/ne...

前面 snmp wireshark 抓包 下载的二进制是5.5.0的,这里用tag为5.9.4的源码版本(5.5的基本是2011年,还是用个最新的吧)

tag所在地址 github.com/net-snmp/ne...

3 分析net-snmp源码

3.1 目录整理

lua 复制代码
agent ---服务器
apps  ---客户端

3.2 从main到初始化agent

以下所有的都是在agent目录 main函数 (snmpd.c)

c++ 复制代码
//snmpd.c

int __cdecl
main(int argc, TCHAR * argv[])
{
    LPCTSTR         lpszServiceName = app_name_long;      /* Service Registry Name */
    LPCTSTR         lpszServiceDisplayName = _T("Net-SNMP Agent");       /* Display Name */
    LPCTSTR         lpszServiceDescription =

    InputParams     InputOptions;
    enum net_snmp_cmd_line_action nRunType = RUN_AS_CONSOLE;
    int             quiet = 0;
    nRunType = ParseCmdLineForServiceOption(argc, argv, &quiet);
    switch (nRunType) {
        case REGISTER_SERVICE:
        /*
         * Register As service 
         */
        InputOptions.Argc = argc;
        InputOptions.Argv = argv;
        return RegisterService(lpszServiceName,
                        lpszServiceDisplayName,
                        lpszServiceDescription, &InputOptions, quiet);
    case UN_REGISTER_SERVICE:
        /*
         * Unregister service 
         */
        return UnregisterService(lpszServiceName, quiet);
    case RUN_AS_SERVICE:
        /*
         * Run as service 
         */
        /*
         * Register Stop Function 
         */
        RegisterStopFunction(StopSnmpAgent);
        return RunAsService(SnmpDaemonMain);
    default:
        /*
         * Run in console mode 
         */
        return SnmpDaemonMain(argc, argv);
    }
}

上面的过滤掉其他用不到的,默认执行的是RUN_AS_CONSOLE ,最终执行的就一个 SnmpDaemonMain。继续往下找

c++ 复制代码
#ifdef WIN32SERVICE
static int
SnmpDaemonMain(int argc, TCHAR * argv[])
#else
int
main(int argc, char *argv[])
#endif
 {

    if (init_agent(app_name) != 0) {
        snmp_log(LOG_ERR, "Agent initialization failed\n");
        goto out;
    }
    init_mib_modules();

    /*
     * start library 
     */
    init_snmp(app_name);

    if ((ret = init_master_agent()) != 0) {
        /*
         * Some error opening one of the specified agent transports.  
         */
        snmp_log(LOG_ERR, "Server Exiting with code 1\n");
        goto out;
    }
}   

上面的处理了解析命令行等等,重点关注 init_master_agent 其他的不在关注的范围内

3.3 绑定udp端口

c++ 复制代码
//snmp_agent.c
int
init_master_agent(void)
{
    char           *cptr;
    char           *buf = NULL;
    char           *st;

    /* default to a default cache size */
    netsnmp_set_lookup_cache_size(-1);

    if (netsnmp_ds_get_boolean(NETSNMP_DS_APPLICATION_ID, 
			       NETSNMP_DS_AGENT_ROLE) != MASTER_AGENT) {
        DEBUGMSGTL(("snmp_agent",
                    "init_master_agent; not master agent\n"));

        netsnmp_assert("agent role !master && !sub_agent");
        
        return 0;               /*  No error if ! MASTER_AGENT  */
    }

#ifndef NETSNMP_NO_LISTEN_SUPPORT
    /*
     * Have specific agent ports been specified?  
     */
    cptr = netsnmp_ds_get_string(NETSNMP_DS_APPLICATION_ID, 
				 NETSNMP_DS_AGENT_PORTS);

    if (cptr) {
        buf = strdup(cptr);
        if (!buf) {
            snmp_log(LOG_ERR,
                     "Error processing transport \"%s\"\n", cptr);
            return 1;
        }
    } else {
        /*
         * No, so just specify the default port.  
         */
        buf = strdup("");
    }

    DEBUGMSGTL(("snmp_agent", "final port spec: \"%s\"\n", buf));
    st = buf;
    do {
        /*
         * Specification format: 
         * 
         * NONE:                      (a pseudo-transport)
         * UDP:[address:]port        (also default if no transport is specified)
         * TCP:[address:]port         (if supported)
         * Unix:pathname              (if supported)
         * AAL5PVC:itf.vpi.vci        (if supported)
         * IPX:[network]:node[/port] (if supported)
         * 
         */

	cptr = st;
	st = strchr(st, ',');
	if (st)
	    *st++ = '\0';

        DEBUGMSGTL(("snmp_agent", "installing master agent on port %s\n",
                    cptr));

        if (strncasecmp(cptr, "none", 4) == 0) {
            DEBUGMSGTL(("snmp_agent",
                        "init_master_agent; pseudo-transport \"none\" "
			"requested\n"));
            break;
        }
        if (-1 == netsnmp_agent_listen_on(cptr)) {
            SNMP_FREE(buf);
            return 1;
        }
    } while(st && *st != '\0');
    SNMP_FREE(buf);
#endif /* NETSNMP_NO_LISTEN_SUPPORT */

#ifdef USING_AGENTX_MASTER_MODULE
    if (netsnmp_ds_get_boolean(NETSNMP_DS_APPLICATION_ID, 
			       NETSNMP_DS_AGENT_AGENTX_MASTER) == 1)
        real_init_master();
#endif
#ifdef USING_SMUX_MODULE
    if(should_init("smux"))
    real_init_smux();
#endif

#ifndef NETSNMP_NO_PDU_STATS
    _pdu_stats_init();
#endif /* NETSNMP_NO_PDU_STATS */

    return 0;
}

这里除了netsnmp_agent_listen_on,还有两种USING_AGENTX_MASTER_MODULE和USING_SMUX_MODULE,可能是给多线程下使用的,跳过

c++ 复制代码
//snmp_agent.c
int netsnmp_agent_listen_on(const char *port)
{
    netsnmp_transport *transport;
    int                handle;

    if (NULL == port)
        return -1;

    transport = netsnmp_transport_open_server("snmp", port);
    if (transport == NULL) {
        snmp_log(LOG_ERR, "Error opening specified endpoint \"%s\"\n", port);
        return -1;
    }

    handle = netsnmp_register_agent_nsap(transport);
    if (handle < 0) {
        snmp_log(LOG_ERR, "Error registering specified transport \"%s\" as an "
                 "agent NSAP\n", port);
        return -1;
    } else {
        DEBUGMSGTL(("snmp_agent",
                    "init_master_agent; \"%s\" registered as an agent NSAP\n",
                    port));
    }

    return handle;
}

这里的netsnmp_register_agent_nsap查看实现

c++ 复制代码
//snmp_agent.c
int
netsnmp_register_agent_nsap(netsnmp_transport *t)
{
    netsnmp_session *s, *sp = NULL;
    agent_nsap     *a = NULL, *n = NULL, **prevNext = &agent_nsap_list;
    int             handle = 0;
    void           *isp = NULL;

    if (t == NULL) {
        return -1;
    }

    DEBUGMSGTL(("netsnmp_register_agent_nsap", "fd %d\n", t->sock));

    n = (agent_nsap *) malloc(sizeof(agent_nsap));
    if (n == NULL) {
        return -1;
    }
    s = (netsnmp_session *) malloc(sizeof(netsnmp_session));
    if (s == NULL) {
        SNMP_FREE(n);
        return -1;
    }
    snmp_sess_init(s);

    /*
     * Set up the session appropriately for an agent.  
     */

    s->version = SNMP_DEFAULT_VERSION;
    s->callback = handle_snmp_packet;
    s->authenticator = NULL;
    s->flags = netsnmp_ds_get_int(NETSNMP_DS_APPLICATION_ID, 
				  NETSNMP_DS_AGENT_FLAGS);
    s->isAuthoritative = SNMP_SESS_AUTHORITATIVE;

    /* Optional supplimental transport configuration information and
       final call to actually open the transport */
    if (netsnmp_sess_config_transport(s->transport_configuration, t)
        != SNMPERR_SUCCESS) {
        SNMP_FREE(s);
        SNMP_FREE(n);
        return -1;
    }


    if (t->f_open)
        t = t->f_open(t);

    if (NULL == t) {
        SNMP_FREE(s);
        SNMP_FREE(n);
        return -1;
    }

    t->flags |= NETSNMP_TRANSPORT_FLAG_OPENED;

    sp = snmp_add(s, t, netsnmp_agent_check_packet,
                  netsnmp_agent_check_parse);
    if (sp == NULL) {
        SNMP_FREE(s);
        SNMP_FREE(n);
        return -1;
    }

    isp = snmp_sess_pointer(sp);
    if (isp == NULL) {          /*  over-cautious  */
        SNMP_FREE(s);
        SNMP_FREE(n);
        return -1;
    }

    n->s = isp;
    n->t = t;

    if (main_session == NULL) {
        main_session = snmp_sess_session(isp);
    }

    for (a = agent_nsap_list; a != NULL && handle + 1 >= a->handle;
         a = a->next) {
        handle = a->handle;
        prevNext = &(a->next);
    }

    if (handle < INT_MAX) {
        n->handle = handle + 1;
        n->next = a;
        *prevNext = n;
        SNMP_FREE(s);
        DEBUGMSGTL(("netsnmp_register_agent_nsap", "handle %d\n", n->handle));
        return n->handle;
    } else {
        SNMP_FREE(s);
        SNMP_FREE(n);
        return -1;
    }
}

netsnmp_session 像是内部封装的一个处理类。这里有三个地方需要关注,在3.3 重点解析,handle_snmp_packet,netsnmp_agent_check_packet,netsnmp_agent_check_parse

ini 复制代码
 s->callback = handle_snmp_packet;
 
 sp = snmp_add(s, t, netsnmp_agent_check_packet,
            netsnmp_agent_check_parse);

3.3 数据解析

netsnmp_agent_check_packet 好像就缓存了下地址,以及追加统计计数。 在 Net-SNMP 中,NETSNMP_USE_LIBWRAP 是一个 ​编译时宏定义 ,用于启用对 ​TCP Wrappers ​(libwrap)的支持。其核心作用是允许 SNMP 守护进程(snmpd)通过 TCP Wrappers 实现基于主机的访问控制,即通过 /etc/hosts.allow/etc/hosts.deny 文件限制客户端访问

c++ 复制代码
//snmp_agent.c
int
netsnmp_agent_check_packet(netsnmp_session * session,
                           netsnmp_transport *transport,
                           void *transport_data, int transport_data_length)
{
    char           *addr_string = NULL;
#ifdef  NETSNMP_USE_LIBWRAP
    char *tcpudpaddr = NULL, *name;
    short not_log_connection;

    name = netsnmp_ds_get_string(NETSNMP_DS_LIBRARY_ID,
                                 NETSNMP_DS_LIB_APPTYPE);

    /* not_log_connection will be 1 if we should skip the messages */
    not_log_connection = netsnmp_ds_get_boolean(NETSNMP_DS_APPLICATION_ID,
                                                NETSNMP_DS_AGENT_DONT_LOG_TCPWRAPPERS_CONNECTS);

    /*
     * handle the error case
     * default to logging the messages
     */
    if (not_log_connection == SNMPERR_GENERR) not_log_connection = 0;
#endif

    /*
     * Log the message and/or dump the message.
     * Optionally cache the network address of the sender.
     */

    if (transport != NULL && transport->f_fmtaddr != NULL) {
        /*
         * Okay I do know how to format this address for logging.  
         */
        addr_string = transport->f_fmtaddr(transport, transport_data,
                                           transport_data_length);
        /*
         * Don't forget to free() it.  
         */
    }
#ifdef  NETSNMP_USE_LIBWRAP
    /* Catch udp,udp6,tcp,tcp6 transports using "[" */
    if (addr_string)
        tcpudpaddr = strstr(addr_string, "[");
    if ( tcpudpaddr != 0 ) {
        char sbuf[64];
        char *xp;

        strlcpy(sbuf, tcpudpaddr + 1, sizeof(sbuf));
        xp = strstr(sbuf, "]");
        if (xp)
            *xp = '\0';
 
        if (hosts_ctl(name, STRING_UNKNOWN, sbuf, STRING_UNKNOWN)) {
            if (!not_log_connection) {
                snmp_log(allow_severity, "Connection from %s\n", addr_string);
            }
        } else {
            snmp_log(deny_severity, "Connection from %s REFUSED\n",
                     addr_string);
            SNMP_FREE(addr_string);
            return 0;
        }
    } else {
        /*
         * don't log callback connections.
         * What about 'Local IPC', 'IPX' and 'AAL5 PVC'?
         */
        if (0 == strncmp(addr_string, "callback", 8))
            ;
        else if (hosts_ctl(name, STRING_UNKNOWN, STRING_UNKNOWN, STRING_UNKNOWN)){
            if (!not_log_connection) {
                snmp_log(allow_severity, "Connection from <UNKNOWN> (%s)\n", addr_string);
            };
            SNMP_FREE(addr_string);
            addr_string = strdup("<UNKNOWN>");
        } else {
            snmp_log(deny_severity, "Connection from <UNKNOWN> (%s) REFUSED\n", addr_string);
            SNMP_FREE(addr_string);
            return 0;
        }
    }
#endif                          /*NETSNMP_USE_LIBWRAP */

    snmp_increment_statistic(STAT_SNMPINPKTS);

    if (addr_string != NULL) {
        netsnmp_addrcache_add(addr_string);
        SNMP_FREE(addr_string);
    }
    return 1;
}

netsnmp_agent_check_parse 主要功能是 ​解析并记录 SNMP 请求的调试信息。它在 Agent 处理 SNMP 请求时被调用,用于在日志中输出详细的协议操作信息,帮助开发者调试或监控 SNMP 请求的流程

c++ 复制代码
//snmp_agent.c
int
netsnmp_agent_check_parse(netsnmp_session * session, netsnmp_pdu *pdu,
                          int result)
{
    if (result == 0) {
        if (snmp_get_do_logging() &&
	    netsnmp_ds_get_boolean(NETSNMP_DS_APPLICATION_ID, 
				   NETSNMP_DS_AGENT_VERBOSE)) {
            netsnmp_variable_list *var_ptr;

            switch (pdu->command) {
            case SNMP_MSG_GET:
                snmp_log(LOG_DEBUG, "  GET message\n");
                break;
            case SNMP_MSG_GETNEXT:
                snmp_log(LOG_DEBUG, "  GETNEXT message\n");
                break;
            case SNMP_MSG_RESPONSE:
                snmp_log(LOG_DEBUG, "  RESPONSE message\n");
                break;
#ifndef NETSNMP_NO_WRITE_SUPPORT
            case SNMP_MSG_SET:
                snmp_log(LOG_DEBUG, "  SET message\n");
                break;
#endif /* NETSNMP_NO_WRITE_SUPPORT */
            case SNMP_MSG_TRAP:
                snmp_log(LOG_DEBUG, "  TRAP message\n");
                break;
            case SNMP_MSG_GETBULK:
                snmp_log(LOG_DEBUG, "  GETBULK message, non-rep=%ld, max_rep=%ld\n",
                         pdu->errstat, pdu->errindex);
                break;
            case SNMP_MSG_INFORM:
                snmp_log(LOG_DEBUG, "  INFORM message\n");
                break;
            case SNMP_MSG_TRAP2:
                snmp_log(LOG_DEBUG, "  TRAP2 message\n");
                break;
            case SNMP_MSG_REPORT:
                snmp_log(LOG_DEBUG, "  REPORT message\n");
                break;

#ifndef NETSNMP_NO_WRITE_SUPPORT
            case SNMP_MSG_INTERNAL_SET_RESERVE1:
                snmp_log(LOG_DEBUG, "  INTERNAL RESERVE1 message\n");
                break;

            case SNMP_MSG_INTERNAL_SET_RESERVE2:
                snmp_log(LOG_DEBUG, "  INTERNAL RESERVE2 message\n");
                break;

            case SNMP_MSG_INTERNAL_SET_ACTION:
                snmp_log(LOG_DEBUG, "  INTERNAL ACTION message\n");
                break;

            case SNMP_MSG_INTERNAL_SET_COMMIT:
                snmp_log(LOG_DEBUG, "  INTERNAL COMMIT message\n");
                break;

            case SNMP_MSG_INTERNAL_SET_FREE:
                snmp_log(LOG_DEBUG, "  INTERNAL FREE message\n");
                break;

            case SNMP_MSG_INTERNAL_SET_UNDO:
                snmp_log(LOG_DEBUG, "  INTERNAL UNDO message\n");
                break;
#endif /* NETSNMP_NO_WRITE_SUPPORT */

            default:
                snmp_log(LOG_DEBUG, "  UNKNOWN message, type=%02X\n",
                         pdu->command);
                snmp_increment_statistic(STAT_SNMPINASNPARSEERRS);
                return 0;
            }

            for (var_ptr = pdu->variables; var_ptr != NULL;
                 var_ptr = var_ptr->next_variable) {
                size_t          c_oidlen = 256, c_outlen = 0;
                u_char         *c_oid = (u_char *) malloc(c_oidlen);

                if (c_oid) {
                    if (!sprint_realloc_objid
                        (&c_oid, &c_oidlen, &c_outlen, 1, var_ptr->name,
                         var_ptr->name_length)) {
                        snmp_log(LOG_DEBUG, "    -- %s [TRUNCATED]\n",
                                 c_oid);
                    } else {
                        snmp_log(LOG_DEBUG, "    -- %s\n", c_oid);
                    }
                    SNMP_FREE(c_oid);
                }
            }
        }
        return 1;
    }
    return 0;                   /* XXX: does it matter what the return value
                                 * is?  Yes: if we return 0, then the PDU is
                                 * dumped.  */
}

handle_snmp_packet 的实现如下

c++ 复制代码
int
handle_snmp_packet(int op, netsnmp_session * session, int reqid,
                   netsnmp_pdu *pdu, void *magic)
{
    netsnmp_agent_session *asp;
    int             status, access_ret, rc;

    /*
     * We only support receiving here.  
     */
    if (op != NETSNMP_CALLBACK_OP_RECEIVED_MESSAGE) {
        return 1;
    }

    /*
     * RESPONSE messages won't get this far, but TRAP-like messages
     * might.  
     */
    if (pdu->command == SNMP_MSG_TRAP || pdu->command == SNMP_MSG_INFORM ||
        pdu->command == SNMP_MSG_TRAP2) {
        DEBUGMSGTL(("snmp_agent", "received trap-like PDU (%02x)\n",
                    pdu->command));
        pdu->command = SNMP_MSG_TRAP2;
        snmp_increment_statistic(STAT_SNMPUNKNOWNPDUHANDLERS);
        return 1;
    }

    /*
     * send snmpv3 authfail trap.
     */
    if (pdu->version  == SNMP_VERSION_3 && 
        session->s_snmp_errno == SNMPERR_USM_AUTHENTICATIONFAILURE) {
           send_easy_trap(SNMP_TRAP_AUTHFAIL, 0);
           return 1;
    } 
	
    if (magic == NULL) {
        asp = init_agent_snmp_session(session, pdu);
        status = SNMP_ERR_NOERROR;
    } else {
        asp = (netsnmp_agent_session *) magic;
        status = asp->status;
    }

#if defined(NETSNMP_DISABLE_SET_SUPPORT) && !defined(NETSNMP_NO_WRITE_SUPPORT)
    if (pdu->command == SNMP_MSG_SET) {
        /** Silvercreek protocol tests send set with 0 varbinds */
        if (NULL == pdu->variables)
            return netsnmp_wrap_up_request(asp, SNMP_ERR_NOERROR);
        asp->index = 1;
        return netsnmp_wrap_up_request(asp, SNMP_ERR_NOTWRITABLE);
    }
#endif /* NETSNMP_DISABLE_SET_SUPPORT && !NETSNMP_NO_WRITE_SUPPORT */

    if ((access_ret = check_access(asp->pdu)) != 0) {
        if (access_ret == VACM_NOSUCHCONTEXT) {
            /*
             * rfc3413 section 3.2, step 5 says that we increment the
             * counter but don't return a response of any kind 
             */

            /*
             * we currently don't support unavailable contexts, as
             * there is no reason to that I currently know of 
             */
            snmp_increment_statistic(STAT_SNMPUNKNOWNCONTEXTS);

            /*
             * drop the request 
             */
            netsnmp_remove_and_free_agent_snmp_session(asp);
            return 0;
        } else {
            /*
             * access control setup is incorrect 
             */
            send_easy_trap(SNMP_TRAP_AUTHFAIL, 0);
#if !defined(NETSNMP_DISABLE_SNMPV1) || !defined(NETSNMP_DISABLE_SNMPV2C)
#if defined(NETSNMP_DISABLE_SNMPV1)
            if (asp->pdu->version != SNMP_VERSION_2c) {
#else
#if defined(NETSNMP_DISABLE_SNMPV2C)
            if (asp->pdu->version != SNMP_VERSION_1) {
#else
            if (asp->pdu->version != SNMP_VERSION_1
                && asp->pdu->version != SNMP_VERSION_2c) {
#endif
#endif
                asp->pdu->errstat = SNMP_ERR_AUTHORIZATIONERROR;
                asp->pdu->command = SNMP_MSG_RESPONSE;
                snmp_increment_statistic(STAT_SNMPOUTPKTS);
                if (!snmp_send(asp->session, asp->pdu))
                    snmp_free_pdu(asp->pdu);
                asp->pdu = NULL;
                netsnmp_remove_and_free_agent_snmp_session(asp);
                return 1;
            } else {
#endif /* support for community based SNMP */
                /*
                 * drop the request 
                 */
                netsnmp_remove_and_free_agent_snmp_session(asp);
                return 0;
#if !defined(NETSNMP_DISABLE_SNMPV1) || !defined(NETSNMP_DISABLE_SNMPV2C)
            }
#endif /* support for community based SNMP */
        }
    }

    rc = netsnmp_handle_request(asp, status);

    /*
     * done 
     */
    DEBUGMSGTL(("snmp_agent", "end of handle_snmp_packet, asp = %8p\n",
                asp));
    return rc;
}
相关推荐
Maple_land2 分钟前
C++初阶——类和对象(二)
c++
讨厌下雨的天空20 分钟前
C++之多态
开发语言·c++
进击的jerk2 小时前
力扣 11.盛水最多的容器(双指针)
c++·算法·leetcode
啊吧怪不啊吧2 小时前
C++相关基础概念之入门讲解(上)
c语言·开发语言·c++
azaz_plus2 小时前
C++ priority_queue 堆
开发语言·c++·stl··priority_queue
TANGLONG2223 小时前
【C++】STL全面简介与string类的使用(万字解析)
java·c语言·开发语言·c++·python·面试·蓝桥杯
Ethon_王5 小时前
C++模板:基础解析
c++
手拿菜刀5 小时前
c++学习系列----002.写文件
开发语言·c++·学习
zym大哥大5 小时前
C++之stack_queue扩展
java·前端·c++