snmp v1 get请求优化(上)

133 阅读4分钟

1背景

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

2 net-snmp 源码

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

image.png

或者直接访问 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 目录整理

agent ---服务器
apps  ---客户端

3.2 从main到初始化agent

以下所有的都是在agent目录 main函数 (snmpd.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。继续往下找

#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端口

//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,可能是给多线程下使用的,跳过

//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查看实现

//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;
    }
}