net-snmp agent开发详解,非常简单

转载请标明出处 原文地址:http://blog.csdn.net/hepeng597/article/details/8782868


花了一两天时间测试和整理一下。

用net-snmp扩展MIB库,实现方法可归结为四种:

1)一是静态库方式,通过修改配置头文件,在相应地方包含新引入的mib模块的.c和.h文件,然后重新编译库文件和扩展代码;这种方式不够灵活,每次修改扩展的MIB后,都需要重新编译snmpd和扩展的代码,再重新安装snmpd到系统中。

2)二是编译动态共享库,只需把新引入的MIB模块的.c和.h文件编译成动态库,通过设置能够让代理程序载入。

对于第二种方式,一需要编译成.so动态共享库,二需要原代理程序是否包含dlmod或load命令,三还要看系统是否支持。一般情况下仅支持Unix平台。

3)三是扩展一个子代理,让SNMPD以主代理的模式运行,对于SNMPD我们只要让它启动就可以,不需要任何的更改和配置,把子代理编译生成的程序运行起来就可以扩展自定义的MIB库。

4)用shell脚本来扩展

本文我们以第三种方法在linux上开发和测试


一、安装snmpd

既然要测试,而且我们用的也是net-snmp工具,当然要先安装snmpd,本文采用5.7.2版本的snmpd,需安装net-snmp-5.7.2-1, net-snmp-devel-5.7.2-1,net-snmp-perlmods-5.7.2-1,安装可以是编译net-snmp的源码,也可以下载rpm包安装,这里略过。

安装好后测试一下service snmpd status,如果没有反应,可能是没有配置文件,可以手动建立/etc/snmp目录,在snmp目录下建立snmpd.conf文件,填入一些基本的配置信息(或者通过snmpconf程序一步一步生成,snmpconf程序比较容易懂,适合童鞋们,snmpconf会提示你创建什么配置文件,需不需要把snmpd作为主代理等等强大的提示):

  1. master agentx  
  2. rocommunity public  
  3. rwcommunity public  
master agentx
rocommunity public
rwcommunity public

master 是说该snmpd以主代理方式运行,目前主代理snmpd只支持agentx类型,而我们要开发的程序是一种子代理(subagent),是需要连snmpd的master agent的。rocommunity (只读对象)和rwcommunity(读写对象)的密码都是public.(这个密码就是客户端访问OID时需要提供的密码,比如在任一一个装有snmpd的linux机器上,执行snmpwalk -v2c -c public localhost 1.3.6.1.2.1.1, 这里的public就是密码,分别有只读OID密码,和读写OID密码),本文测试就以public作为默认的密码吧。

现在测试一下snmpd是否正常,启动service snmpd restart,执行snmpwalk -v2c -c public localhost 1.3.6.1.2.1.1, 如果有SNMPv2-MIB:xxxx之类的输出就表示我们的主代理已经工作了。


二、自己的MIB库

首先MIB库有什么用?其实作为子代理来说,在server机器上,可以不用MIB库,MIB库只为了让用户访问时方便,有了MIB库,用户就不用记那么多和长的OID,比如把MIB放在windows机器上,在windows机器装一个支持MIB的软件,用该软件打开MIB库,只要点击相应的对象就可以自动发送snmp请求到server端,所以server端是可以不要MIB库的。如果把MIB库放在linux客户端机器上,以下面自定义的MIB库为例,那么就可以直接执行snmpget -v2c -c public Test-MIB::GetTime.0,当然需要linux客户端装有snmp,而且自定义的MIB库必须能让snmpd程序找到。 

这里用就一个OID建一个MIB库来简化,命名Test-MIB.my,放在/usr/local/share/snmp/mibs目录下,因为这个目录是snmpd的默认目录,只要把MIB库放入该目录就可以自动加载MIB库,否则需要修改/etc/snmp/snmp.conf文件,添加mibs +/path/to/Test-MIB.my 并重启snmpd。

自定义MIB库,如下:

  1. -- Test-MIB.my  
  2.     Test-MIB DEFINITIONS ::= BEGIN  
  3.    
  4.         IMPORTS  
  5.             OBJECT-GROUP, MODULE-COMPLIANCE, NOTIFICATION-GROUP      
  6.                 FROM SNMPv2-CONF      
  7.             enterprises, Integer32, Unsigned32, OBJECT-TYPE, MODULE-IDENTITY,   
  8.             NOTIFICATION-TYPE      
  9.                 FROM SNMPv2-SMI      
  10.             DisplayString      
  11.                 FROM SNMPv2-TC;  
  12.       
  13. -- October 09, 2002 at 14:50 GMT  
  14.         -- 1.3.6.1.4.1.16535  
  15.         Test MODULE-IDENTITY   
  16.             LAST-UPDATED "200210091450Z"        -- October 09, 2002 at 14:50 GMT  
  17.             ORGANIZATION   
  18.                 ""    
  19.             CONTACT-INFO   
  20.                 ""    
  21.             DESCRIPTION   
  22.                 "Video's Server MIB."  
  23.             ::= { enterprises 16535 }  
  24.       
  25. --  Node definitions  
  26. -- This part will include all details about the Test.  
  27.         -- 1.3.6.1.4.1.16535.1  
  28.         Time OBJECT IDENTIFIER ::= { Test 1 }   
  29.   
  30.       
  31.         -- 1.3.6.1.4.1.16535.1.1  
  32.         GetTime OBJECT-TYPE  
  33.             SYNTAX DisplayString (SIZE (0..100))  
  34.             MAX-ACCESS read-only  
  35.             STATUS current  
  36.             DESCRIPTION  
  37.                 "Example : 2013/4/11"  
  38.             ::= { Time 1 }  
  39.     END  
  40.   
  41. -- Test-MIB.my  
-- Test-MIB.my
    Test-MIB DEFINITIONS ::= BEGIN
 
        IMPORTS
            OBJECT-GROUP, MODULE-COMPLIANCE, NOTIFICATION-GROUP    
                FROM SNMPv2-CONF    
            enterprises, Integer32, Unsigned32, OBJECT-TYPE, MODULE-IDENTITY, 
            NOTIFICATION-TYPE    
                FROM SNMPv2-SMI    
            DisplayString    
                FROM SNMPv2-TC;
    
-- October 09, 2002 at 14:50 GMT
        -- 1.3.6.1.4.1.16535
        Test MODULE-IDENTITY 
            LAST-UPDATED "200210091450Z"        -- October 09, 2002 at 14:50 GMT
            ORGANIZATION 
                ""  
            CONTACT-INFO 
                ""  
            DESCRIPTION 
                "Video's Server MIB."
            ::= { enterprises 16535 }
    
--  Node definitions
-- This part will include all details about the Test.
        -- 1.3.6.1.4.1.16535.1
        Time OBJECT IDENTIFIER ::= { Test 1 } 

    
        -- 1.3.6.1.4.1.16535.1.1
        GetTime OBJECT-TYPE
            SYNTAX DisplayString (SIZE (0..100))
            MAX-ACCESS read-only
            STATUS current
            DESCRIPTION
                "Example : 2013/4/11"
            ::= { Time 1 }
    END

-- Test-MIB.my

很简单,该MIB库只有一个OID,即:1.3.6.1.4.1.16535.1.1,写完后我们测一个MIB库有没有问题,在linux机器上用snmptranslate -Tp -IR Test-MIB::Test显示结果如下:

  1. +--Test(16535)  
  2.    |  
  3.    +--Time(1)  
  4.       |  
  5.       +-- -R-- String    GetTime(1)  
  6.                Textual Convention: DisplayString  
  7.                Size: 0..100  
+--Test(16535)
   |
   +--Time(1)
      |
      +-- -R-- String    GetTime(1)
               Textual Convention: DisplayString
               Size: 0..100

OK, snmp自动发现了这个MIB库,  有了自定义的OID,但是还没有处理程序(子代理)

三、生成源代码

mib2c可以根据mib库生成对应的源代码,有多种模板,这里我们要生成子代理的代码,所以选择是固定的,执行env MIBS="+/usr/local/share/snmp/mibs/Test-MIB.my" mib2c Test,会引导你逐渐生成Test.h和Test.c, 先选2再选1,过程如下:

  1. [root@localhost mibs]# env MIBS="+/etc/snmp/mibs/Test-MIB.my" mib2c Test  
  2. writing to -  
  3. mib2c has multiple configuration files depending on the type of  
  4. code you need to write.  You must pick one depending on your need.  
  5.   
  6. You requested mib2c to be run on the following part of the MIB tree:  
  7.   OID:                              Test  
  8.   numeric translation:              .1.3.6.1.4.1.16535  
  9.   number of scalars within:         1     
  10.   number of tables within:          0     
  11.   number of notifications within:   0     
  12.   
  13. First, do you want to generate code that is compatible with the   
  14. ucd-snmp 4.X line of code, or code for the newer Net-SNMP 5.X code  
  15. base (which provides a much greater choice of APIs to pick from):  
  16.   
  17.   1) ucd-snmp style code  
  18.   2) Net-SNMP style code  
  19.   
  20. Select your choice : 2   
  21.   
  22. **********************************************************************  
  23.          GENERATING CODE FOR SCALAR OBJECTS:  
  24. **********************************************************************  
  25.   
  26.   It looks like you have some scalars in the mib you requested, so I  
  27.   will now generate code for them if you wish.  You have two choices  
  28.   for scalar API styles currently.  Pick between them, or choose not   
  29.   to generate any code for the scalars:  
  30.   
  31.   1) If you're writing code for some generic scalars  
  32.      (by hand use: "mib2c -c mib2c.scalar.conf Test")  
  33.   
  34.   2) If you want to magically "tie" integer variables to integer  
  35.      scalars  
  36.      (by hand use: "mib2c -c mib2c.int_watch.conf Test")  
  37.   
  38.   3) Don't generate any code for the scalars  
  39.   
  40. Select your choice: 1  
  41.     using the mib2c.scalar.conf configuration file to generate your code.  
  42. writing to Test.h  
  43. writing to Test.c  
  44.   
  45.   
  46.   
  47. **********************************************************************  
  48. * NOTE WELL: The code generated by mib2c is only a template.  *YOU*  *  
  49. * must fill in the code before it'll work most of the time.  In many *  
  50. * cases, spots that MUST be edited within the files are marked with  *  
  51. * /* XXX */ or /* TODO */ comments.                                  *  
  52. **********************************************************************  
  53. running indent on Test.h  
  54. running indent on Test.c  
[root@localhost mibs]# env MIBS="+/etc/snmp/mibs/Test-MIB.my" mib2c Test
writing to -
mib2c has multiple configuration files depending on the type of
code you need to write.  You must pick one depending on your need.

You requested mib2c to be run on the following part of the MIB tree:
  OID:                              Test
  numeric translation:              .1.3.6.1.4.1.16535
  number of scalars within:         1   
  number of tables within:          0   
  number of notifications within:   0   

First, do you want to generate code that is compatible with the 
ucd-snmp 4.X line of code, or code for the newer Net-SNMP 5.X code
base (which provides a much greater choice of APIs to pick from):

  1) ucd-snmp style code
  2) Net-SNMP style code

Select your choice : 2 

**********************************************************************
         GENERATING CODE FOR SCALAR OBJECTS:
**********************************************************************

  It looks like you have some scalars in the mib you requested, so I
  will now generate code for them if you wish.  You have two choices
  for scalar API styles currently.  Pick between them, or choose not 
  to generate any code for the scalars:

  1) If you're writing code for some generic scalars
     (by hand use: "mib2c -c mib2c.scalar.conf Test")

  2) If you want to magically "tie" integer variables to integer
     scalars
     (by hand use: "mib2c -c mib2c.int_watch.conf Test")

  3) Don't generate any code for the scalars

Select your choice: 1
    using the mib2c.scalar.conf configuration file to generate your code.
writing to Test.h
writing to Test.c



**********************************************************************
* NOTE WELL: The code generated by mib2c is only a template.  *YOU*  *
* must fill in the code before it'll work most of the time.  In many *
* cases, spots that MUST be edited within the files are marked with  *
* /* XXX */ or /* TODO */ comments.                                  *
**********************************************************************
running indent on Test.h
running indent on Test.c
mib2c已经统计出我们的mib库包含1个scalar变量,0个table变量,0个通知变量,Scalar就是包含我们常用的整型,字符串,时间等等数据类型。table就是scalar的一种集合,有一个和多个列组成,类似于数据库中的表。它必须具有索引项,用来按一定顺序检索表项,当然我们只写了一个标量的OID,不是表结构也不是通知结构

生成的Test.h如下:

  1. /* 
  2.  * Note: this file originally auto-generated by mib2c using 
  3.  *        $ 
  4.  */  
  5. #ifndef TEST_H  
  6. #define TEST_H  
  7.   
  8. /* 
  9.  * function declarations  
  10.  */  
  11. void            init_Test(void);  
  12. Netsnmp_Node_Handler handle_GetTime;  
  13.   
  14. #endif                          /* TEST_H */  
/*
 * Note: this file originally auto-generated by mib2c using
 *        $
 */
#ifndef TEST_H
#define TEST_H

/*
 * function declarations 
 */
void            init_Test(void);
Netsnmp_Node_Handler handle_GetTime;

#endif                          /* TEST_H */

生成的Test.c文件如下:

  1. /* 
  2.  * Note: this file originally auto-generated by mib2c using 
  3.  *        $ 
  4.  */  
  5.   
  6. #include <net-snmp/net-snmp-config.h>  
  7. #include <net-snmp/net-snmp-includes.h>  
  8. #include <net-snmp/agent/net-snmp-agent-includes.h>  
  9. #include "Test.h"  
  10.   
  11. /** Initializes the Test module */  
  12. void  
  13. init_Test(void)  
  14. {  
  15.     const oid       GetTime_oid[] = { 1, 3, 6, 1, 4, 1, 16535, 1, 1 };  
  16.   
  17.     DEBUGMSGTL(("Test""Initializing\n"));  
  18.   
  19.     netsnmp_register_scalar(netsnmp_create_handler_registration  
  20.                             ("GetTime", handle_GetTime, GetTime_oid,  
  21.                              OID_LENGTH(GetTime_oid), HANDLER_CAN_RONLY));  
  22. }  
  23.   
  24. int  
  25. handle_GetTime(netsnmp_mib_handler *handler,  
  26.                netsnmp_handler_registration *reginfo,  
  27.                netsnmp_agent_request_info *reqinfo,  
  28.                netsnmp_request_info *requests)  
  29. {  
  30.     /*   
  31.      * We are never called for a GETNEXT if it's registered as a 
  32.      * "instance", as it's "magically" handled for us.   
  33.      */  
  34.   
  35.     /*   
  36.      * a instance handler also only hands us one request at a time, so 
  37.      * we don't need to loop over a list of requests; we'll only get one.  
  38.      */  
  39.   
  40.     switch (reqinfo->mode) {  
  41.   
  42.     case MODE_GET:  
  43.         snmp_set_var_typed_value(requests->requestvb, ASN_OCTET_STR,  
  44.                                  /* 
  45.                                   * XXX: a pointer to the scalar's data  
  46.                                   */ ,  
  47.                                  /* 
  48.                                   * XXX: the length of the data in bytes  
  49.                                   */ );  
  50.         break;  
  51.   
  52.   
  53.     default:  
  54.         /* 
  55.          * we should never get here, so this is a really bad error  
  56.          */  
  57.         snmp_log(LOG_ERR, "unknown mode (%d) in handle_GetTime\n",  
  58.                  reqinfo->mode);  
  59.         return SNMP_ERR_GENERR;  
  60.     }  
  61.   
  62.     return SNMP_ERR_NOERROR;  
  63. }  
/*
 * 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       GetTime_oid[] = { 1, 3, 6, 1, 4, 1, 16535, 1, 1 };

    DEBUGMSGTL(("Test", "Initializing\n"));

    netsnmp_register_scalar(netsnmp_create_handler_registration
                            ("GetTime", handle_GetTime, GetTime_oid,
                             OID_LENGTH(GetTime_oid), HANDLER_CAN_RONLY));
}

int
handle_GetTime(netsnmp_mib_handler *handler,
               netsnmp_handler_registration *reginfo,
               netsnmp_agent_request_info *reqinfo,
               netsnmp_request_info *requests)
{
    /*  
     * We are never called for a GETNEXT if it's registered as a
     * "instance", as it's "magically" handled for us.  
     */

    /*  
     * a instance handler also only hands us one request at a time, so
     * we don't need to loop over a list of requests; we'll only get one. 
     */

    switch (reqinfo->mode) {

    case MODE_GET:
        snmp_set_var_typed_value(requests->requestvb, ASN_OCTET_STR,
                                 /*
                                  * XXX: a pointer to the scalar's data 
                                  */ ,
                                 /*
                                  * XXX: the length of the data in bytes 
                                  */ );
        break;


    default:
        /*
         * we should never get here, so this is a really bad error 
         */
        snmp_log(LOG_ERR, "unknown mode (%d) in handle_GetTime\n",
                 reqinfo->mode);
        return SNMP_ERR_GENERR;
    }

    return SNMP_ERR_NOERROR;
}

以上的代码都是自动生成的,我们没有写一行代码,到了这一步,我们需要把Test.c里面的 XXX改成自己的值,也就两行,修改后Test.c文件代码如下:

  1. /* 
  2.  * Note: this file originally auto-generated by mib2c using 
  3.  *        $ 
  4.  */  
  5.   
  6. #include <net-snmp/net-snmp-config.h>  
  7. #include <net-snmp/net-snmp-includes.h>  
  8. #include <net-snmp/agent/net-snmp-agent-includes.h>  
  9. #include "Test.h"  
  10. #include <time.h>  
  11.   
  12. /** Initializes the Test module */  
  13. void  
  14. init_Test(void)  
  15. {  
  16.     const oid       GetTime_oid[] = { 1, 3, 6, 1, 4, 1, 16535, 1, 1 };  
  17.   
  18.     DEBUGMSGTL(("Test""Initializing\n"));  
  19.   
  20.     netsnmp_register_scalar(netsnmp_create_handler_registration  
  21.                             ("GetTime", handle_GetTime, GetTime_oid,  
  22.                              OID_LENGTH(GetTime_oid), HANDLER_CAN_RONLY));  
  23. }  
  24.   
  25. int  
  26. handle_GetTime(netsnmp_mib_handler *handler,  
  27.                netsnmp_handler_registration *reginfo,  
  28.                netsnmp_agent_request_info *reqinfo,  
  29.                netsnmp_request_info *requests)  
  30. {  
  31.     /*   
  32.      * We are never called for a GETNEXT if it's registered as a 
  33.      * "instance", as it's "magically" handled for us.   
  34.      */  
  35.      /* 
  36.      * a instance handler also only hands us one request at a time, so 
  37.      * we don't need to loop over a list of requests; we'll only get one.  
  38.      */  
  39.   
  40.     time_t t;  
  41.     switch (reqinfo->mode) {  
  42.     case MODE_GET:  
  43.         time(&t);  
  44.         char szTime[100];  
  45.         snprintf(szTime,100,"%s",ctime(&t));  
  46.         snmp_set_var_typed_value(requests->requestvb, ASN_OCTET_STR,  
  47.                                  /* 
  48.                                   * XXX: a pointer to the scalar's data  
  49.                                   */ szTime,  
  50.                                  /* 
  51.                                   * XXX: the length of the data in bytes  
  52.                                   */ strlen(szTime));  
  53.         break;  
  54.   
  55.   
  56.     default:  
  57.         /* 
  58.          * we should never get here, so this is a really bad error  
  59.          */  
  60.         snmp_log(LOG_ERR, "unknown mode (%d) in handle_GetTime\n",  
  61.                  reqinfo->mode);  
  62.         return SNMP_ERR_GENERR;  
  63.     }  
  64.   
  65.     return SNMP_ERR_NOERROR;  
  66. }  
/*
 * 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"
#include <time.h>

/** Initializes the Test module */
void
init_Test(void)
{
    const oid       GetTime_oid[] = { 1, 3, 6, 1, 4, 1, 16535, 1, 1 };

    DEBUGMSGTL(("Test", "Initializing\n"));

    netsnmp_register_scalar(netsnmp_create_handler_registration
                            ("GetTime", handle_GetTime, GetTime_oid,
                             OID_LENGTH(GetTime_oid), HANDLER_CAN_RONLY));
}

int
handle_GetTime(netsnmp_mib_handler *handler,
               netsnmp_handler_registration *reginfo,
               netsnmp_agent_request_info *reqinfo,
               netsnmp_request_info *requests)
{
    /*  
     * We are never called for a GETNEXT if it's registered as a
     * "instance", as it's "magically" handled for us.  
     */
     /*
     * a instance handler also only hands us one request at a time, so
     * we don't need to loop over a list of requests; we'll only get one. 
     */

    time_t t;
    switch (reqinfo->mode) {
    case MODE_GET:
        time(&t);
        char szTime[100];
        snprintf(szTime,100,"%s",ctime(&t));
        snmp_set_var_typed_value(requests->requestvb, ASN_OCTET_STR,
                                 /*
                                  * XXX: a pointer to the scalar's data 
                                  */ szTime,
                                 /*
                                  * XXX: the length of the data in bytes 
                                  */ strlen(szTime));
        break;


    default:
        /*
         * we should never get here, so this is a really bad error 
         */
        snmp_log(LOG_ERR, "unknown mode (%d) in handle_GetTime\n",
                 reqinfo->mode);
        return SNMP_ERR_GENERR;
    }

    return SNMP_ERR_NOERROR;
}
简单吧,现在子代理程序基本就写完了,我们执行命令让我们的子代理生成可执行程序,执行

net-snmp-config --compile-subagent Test Test.c,生成了Test程序, 执行过程如下:



可以看出来,net-snmp-config程序生成了一个临时的C文件,netsnmptmp.12373.c 并与Test.c一起编译,生成了Test程序后又删除了该临时文件。我们会在最后研究netsnmptmp.12373.c文件。


四、测试一下

现在Test程序已经生成了,我们先执行主代理(service snmpd start),再执行子代理./Test,再ps -ef | grep Test,看一下,可以看到Test程序自动在后台启动了,如下:

  1. [root@localhost hepeng]# ps -ef| grep Test   
  2. root     27526     1  0 13:29 ?        00:00:00 ./Test  
  3. root     27531 27448  0 13:29 pts/2    00:00:00 grep Test  
[root@localhost hepeng]# ps -ef| grep Test 
root     27526     1  0 13:29 ?        00:00:00 ./Test
root     27531 27448  0 13:29 pts/2    00:00:00 grep Test
到这里我们可以想象到,Test.c文件中是没有main函数的,那么main函数肯定是在netsnmptmp.12373.c 中,net-snmp-config让Test程序变成了守护进程在后台运行。

到此我们的service端就已经布置完成了,子代理理论上可以回复自定义的GetTime OID的请求。

本文在一台装有snmpd和子代理的linux server机器上直接测试。执行命令如下:

  1. [root@localhost hepeng]# snmpget -v2c -c public localhost 1.3.6.1.4.1.16535.1.1.0  
  2. SNMPv2-SMI::enterprises.16535.1.1.0 = STRING: "Thu Apr 11 13:40:20 2013  
  3. "  
[root@localhost hepeng]# snmpget -v2c -c public localhost 1.3.6.1.4.1.16535.1.1.0
SNMPv2-SMI::enterprises.16535.1.1.0 = STRING: "Thu Apr 11 13:40:20 2013
"
可以看到,我们开发的子代理成功工作了。如果自定义的MIB库已经加入到snmpd指定的目录中,我们可以执行

  1. [hepeng@localhost ~]$ snmpget -v2c -c public localhost Test-MIB:GetTime.0  
  2. Test-MIB::GetTime.0 = STRING: Thu Apr 11 13:46:42 2013  
[hepeng@localhost ~]$ snmpget -v2c -c public localhost Test-MIB:GetTime.0
Test-MIB::GetTime.0 = STRING: Thu Apr 11 13:46:42 2013
snmpget会自动在所有的MIB库中查找Test-MIB库,并把Test-MIB:GetTime.0转换成1.3.6.1.4.1.16535.1.1.0并发送get请求。

现在子代理是已经开发成功了,我们实际上只写了两三行代码就开发了net-snmp子代理,是不是很简单呢?

现在有个问题,怎么加入到我们自己的项目中呢?因为Test.c中并没有main函数,在一个项目中直接调用init_Test()能不能让子代理work呢?那就要研究一下netsnmptmp.12373.c。


五、加入项目中

我们再次执行net-snmp-config --compile-subagent Test Test.c,然后立刻Ctrl+c,时间要控制好,让net-snmp-config程序产生了临时的C文件,却没有删除它。打开netsnmptmp.12373.c,我们来看一下代码,200多行,本文只贴上main函数的重要部分,代码中都添加了注释,不难理解:

  1. int main (int argc, char **argv)  
  2. {  
  3.   int arg;  
  4.   char* cp = NULL;  
  5.   int dont_fork = 0, do_help = 0;  
  6.   
  7.   while ((arg = getopt(argc, argv, "dD:fhHL:"  
  8. #ifndef DISABLE_MIB_LOADING  
  9.                        "m:M:"  
  10. #endif /* DISABLE_MIB_LOADING */  
  11.                        "n:"  
  12. #ifndef DISABLE_MIB_LOADING  
  13.                        "P:"  
  14. #endif /* DISABLE_MIB_LOADING */  
  15.                        "vx:")) != EOF) {  
  16.     switch (arg) {  
  17.     case 'D':/*这里省略多个case break,只是一些选项,没必要研究/ 
  18.       break; 
  19.  
  20.     default: 
  21.       fprintf(stderr, "invalid option: -%c\n", arg); 
  22.       usage(argv[0]); 
  23.       break; 
  24.     } 
  25.   } 
  26.  
  27.   if (do_help) { 
  28.     netsnmp_ds_set_boolean(NETSNMP_DS_APPLICATION_ID, 
  29.                            NETSNMP_DS_AGENT_NO_ROOT_ACCESS, 1); 
  30.   } else { 
  31.     /* we are a subagent  第一步:这里是告诉snmpd我们这个Test作为子代理*/  
  32.     netsnmp_ds_set_boolean(NETSNMP_DS_APPLICATION_ID,  
  33.                            NETSNMP_DS_AGENT_ROLE, 1);  
  34.   
  35.     if (!dont_fork) {/* 这里是让Test程序变成守护进程,执行Test后可以在后台运行不会退出 */  
  36.       if (netsnmp_daemonize(1, snmp_stderrlog_status()) != 0)  
  37.         exit(1);  
  38.     }  
  39.   
  40.     /* initialize tcpip, if necessary */  
  41.     SOCK_STARTUP;  
  42.   }  
  43.   
  44.   /* initialize the agent library 第二步*/  
  45.   init_agent(app_name);  
  46.   
  47.   /* initialize your mib code here 第三步*/  
  48.   init_Test();  
  49.   
  50.   /* Test will be used to read Test.conf files. 第四步 */  
  51.   init_snmp("Test");  
  52.   
  53.   if (do_help) {  
  54.     fprintf(stderr, "Configuration directives understood:\n");  
  55.     read_config_print_usage("  ");  
  56.     exit(0);  
  57.   }  
  58.   
  59.   /* In case we received a request to stop (kill -TERM or kill -INT) */  
  60.   netsnmp_running = 1;  
  61. #ifdef SIGTERM  
  62.   signal(SIGTERM, stop_server);  
  63. #endif  
  64. #ifdef SIGINT  
  65.   signal(SIGINT, stop_server);  
  66. #endif  
  67. #ifdef SIGHUP  
  68.   signal(SIGHUP, hup_handler);  
  69. #endif  
  70.   
  71.   /* main loop here... */  
  72.   while(netsnmp_running) {  
  73.     if (reconfig) {  
  74.       free_config();  
  75.       read_configs();  
  76.       reconfig = 0;  
  77.     }  
  78.     agent_check_and_process(1);  
  79.   }  
  80.   
  81.   /* at shutdown time */  
  82.   snmp_shutdown(app_name);  
  83.   
  84.   /* deinitialize your mib code here */  
  85.   
  86.   /* shutdown the agent library */  
  87.   shutdown_agent();  
  88.   SOCK_CLEANUP;  
  89.   exit(0);  
  90. }  
int main (int argc, char **argv)
{
  int arg;
  char* cp = NULL;
  int dont_fork = 0, do_help = 0;

  while ((arg = getopt(argc, argv, "dD:fhHL:"
#ifndef DISABLE_MIB_LOADING
                       "m:M:"
#endif /* DISABLE_MIB_LOADING */
                       "n:"
#ifndef DISABLE_MIB_LOADING
                       "P:"
#endif /* DISABLE_MIB_LOADING */
                       "vx:")) != EOF) {
    switch (arg) {
    case 'D':/*这里省略多个case break,只是一些选项,没必要研究/
      break;

    default:
      fprintf(stderr, "invalid option: -%c\n", arg);
      usage(argv[0]);
      break;
    }
  }

  if (do_help) {
    netsnmp_ds_set_boolean(NETSNMP_DS_APPLICATION_ID,
                           NETSNMP_DS_AGENT_NO_ROOT_ACCESS, 1);
  } else {
    /* we are a subagent  第一步:这里是告诉snmpd我们这个Test作为子代理*/
    netsnmp_ds_set_boolean(NETSNMP_DS_APPLICATION_ID,
                           NETSNMP_DS_AGENT_ROLE, 1);

    if (!dont_fork) {/* 这里是让Test程序变成守护进程,执行Test后可以在后台运行不会退出 */
      if (netsnmp_daemonize(1, snmp_stderrlog_status()) != 0)
        exit(1);
    }

    /* initialize tcpip, if necessary */
    SOCK_STARTUP;
  }

  /* initialize the agent library 第二步*/
  init_agent(app_name);

  /* initialize your mib code here 第三步*/
  init_Test();

  /* Test will be used to read Test.conf files. 第四步 */
  init_snmp("Test");

  if (do_help) {
    fprintf(stderr, "Configuration directives understood:\n");
    read_config_print_usage("  ");
    exit(0);
  }

  /* In case we received a request to stop (kill -TERM or kill -INT) */
  netsnmp_running = 1;
#ifdef SIGTERM
  signal(SIGTERM, stop_server);
#endif
#ifdef SIGINT
  signal(SIGINT, stop_server);
#endif
#ifdef SIGHUP
  signal(SIGHUP, hup_handler);
#endif

  /* main loop here... */
  while(netsnmp_running) {
    if (reconfig) {
      free_config();
      read_configs();
      reconfig = 0;
    }
    agent_check_and_process(1);
  }

  /* at shutdown time */
  snmp_shutdown(app_name);

  /* deinitialize your mib code here */

  /* shutdown the agent library */
  shutdown_agent();
  SOCK_CLEANUP;
  exit(0);
}

我们不管上面代码那些看不懂的函数,知道大概意思就行,现在我们来加入到自己的项目中,找到项目中的main函数,在main函数中添加初始化Test子代理的代码,本文为了方便理解,就在Test.c中添加main函数,就地取材,写个超简单函数,不考虑传入参数。修改后的Test.c文件如下:

  1. /* 
  2.  * Note: this file originally auto-generated by mib2c using 
  3.  *        $ 
  4.  */  
  5.   
  6. #include <net-snmp/net-snmp-config.h>  
  7. #include <net-snmp/net-snmp-includes.h>  
  8. #include <net-snmp/agent/net-snmp-agent-includes.h>  
  9. #include "Test.h"  
  10. #include <time.h>  
  11.   
  12. /** Initializes the Test module */  
  13. void  
  14. init_Test(void)  
  15. {  
  16.     const oid       GetTime_oid[] = { 1, 3, 6, 1, 4, 1, 16535, 1, 1 };  
  17.   
  18.     DEBUGMSGTL(("Test""Initializing\n"));  
  19.   
  20.     netsnmp_register_scalar(netsnmp_create_handler_registration  
  21.                             ("GetTime", handle_GetTime, GetTime_oid,  
  22.                              OID_LENGTH(GetTime_oid), HANDLER_CAN_RONLY));  
  23. }  
  24.   
  25. int  
  26. handle_GetTime(netsnmp_mib_handler *handler,  
  27.                netsnmp_handler_registration *reginfo,  
  28.                netsnmp_agent_request_info *reqinfo,  
  29.                netsnmp_request_info *requests)  
  30. {  
  31.     /* 
  32.      * We are never called for a GETNEXT if it's registered as a 
  33.      * "instance", as it's "magically" handled for us.   
  34.      */  
  35.     /* 
  36.      * a instance handler also only hands us one request at a time, so 
  37.      * we don't need to loop over a list of requests; we'll only get one.  
  38.      */  
  39.   
  40.     time_t t;  
  41.     switch (reqinfo->mode) {  
  42.     case MODE_GET:  
  43.     time(&t);  
  44.     char szTime[100];  
  45.     snprintf(szTime,100,"%s",ctime(&t));  
  46.         snmp_set_var_typed_value(requests->requestvb, ASN_OCTET_STR,  
  47.                                  /* 
  48.                                   * XXX: a pointer to the scalar's data  
  49.                                   */ szTime,  
  50.                                  /* 
  51.                                   * XXX: the length of the data in bytes  
  52.                                   */ strlen(szTime));  
  53.         break;  
  54.   
  55.   
  56.     default:  
  57.         /* 
  58.          * we should never get here, so this is a really bad error  
  59.          */  
  60.         snmp_log(LOG_ERR, "unknown mode (%d) in handle_GetTime\n",  
  61.                  reqinfo->mode);  
  62.         return SNMP_ERR_GENERR;  
  63.     }  
  64.   
  65.     return SNMP_ERR_NOERROR;  
  66. }  
  67.   
  68. static int keep_running;  
  69. RETSIGTYPE stop_server(int __attribute__((unused)) a) {  
  70.         keep_running = 0;  
  71. }  
  72.   
  73. int main()  
  74. {  
  75.    const char *app_name = "Test";  
  76.    /* we are a subagent */  
  77.    netsnmp_ds_set_boolean(NETSNMP_DS_APPLICATION_ID, NETSNMP_DS_AGENT_ROLE, 1);  
  78.   
  79.    /* initialize the agent library */  
  80.    init_agent(app_name);  
  81.   
  82.    /* initialize your mib code here */  
  83.    init_Test();  
  84.   
  85.    /* Test will be used to read Test.conf files. */  
  86.    init_snmp("Test");  
  87.    keep_running = 1;  
  88.    while(keep_running)  
  89.    {  
  90.         agent_check_and_process(1);/* block every 1 second */  
  91.    }  
  92.    /* at shutdown time */  
  93.    snmp_shutdown(app_name);  
  94.   
  95.    /* deinitialize your mib code here */  
  96.   
  97.    /* shutdown the agent library */  
  98.    shutdown_agent();  
  99.    return 0;  
  100. }  
/*
 * 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"
#include <time.h>

/** Initializes the Test module */
void
init_Test(void)
{
    const oid       GetTime_oid[] = { 1, 3, 6, 1, 4, 1, 16535, 1, 1 };

    DEBUGMSGTL(("Test", "Initializing\n"));

    netsnmp_register_scalar(netsnmp_create_handler_registration
                            ("GetTime", handle_GetTime, GetTime_oid,
                             OID_LENGTH(GetTime_oid), HANDLER_CAN_RONLY));
}

int
handle_GetTime(netsnmp_mib_handler *handler,
               netsnmp_handler_registration *reginfo,
               netsnmp_agent_request_info *reqinfo,
               netsnmp_request_info *requests)
{
    /*
     * We are never called for a GETNEXT if it's registered as a
     * "instance", as it's "magically" handled for us.  
     */
    /*
     * a instance handler also only hands us one request at a time, so
     * we don't need to loop over a list of requests; we'll only get one. 
     */

    time_t t;
    switch (reqinfo->mode) {
    case MODE_GET:
    time(&t);
    char szTime[100];
    snprintf(szTime,100,"%s",ctime(&t));
        snmp_set_var_typed_value(requests->requestvb, ASN_OCTET_STR,
                                 /*
                                  * XXX: a pointer to the scalar's data 
                                  */ szTime,
                                 /*
                                  * XXX: the length of the data in bytes 
                                  */ strlen(szTime));
        break;


    default:
        /*
         * we should never get here, so this is a really bad error 
         */
        snmp_log(LOG_ERR, "unknown mode (%d) in handle_GetTime\n",
                 reqinfo->mode);
        return SNMP_ERR_GENERR;
    }

    return SNMP_ERR_NOERROR;
}

static int keep_running;
RETSIGTYPE stop_server(int __attribute__((unused)) a) {
        keep_running = 0;
}

int main()
{
   const char *app_name = "Test";
   /* we are a subagent */
   netsnmp_ds_set_boolean(NETSNMP_DS_APPLICATION_ID, NETSNMP_DS_AGENT_ROLE, 1);

   /* initialize the agent library */
   init_agent(app_name);

   /* initialize your mib code here */
   init_Test();

   /* Test will be used to read Test.conf files. */
   init_snmp("Test");
   keep_running = 1;
   while(keep_running)
   {
        agent_check_and_process(1);/* block every 1 second */
   }
   /* at shutdown time */
   snmp_shutdown(app_name);

   /* deinitialize your mib code here */

   /* shutdown the agent library */
   shutdown_agent();
   return 0;
}

这个main函数是不是很简单呢?当snmpd stop的时候会调用stop_server,也就会注销我们的子代理。编译一下,这里我们不用net-snmp-config编译,因为是要加入到自己的项目中,所以推荐写入到Makefile中,本文就不写Makefile了,直接调用gcc命令生成(直接用net-snmp-config的参数就可以),如下:

  1. [root@localhost hepeng]# gcc  -fno-strict-aliasing -g -O2 -Ulinux -Dlinux=linux  -D_REENTRANT -D_GNU_SOURCE -fno-strict-aliasing -pipe -Wdeclaration-after-statement -I/usr/local/include -D_LARGEFILE_SOURCE -D_FILE_OFFSET_BITS=64 -I/usr/include/gdbm  -I/usr/lib64/perl5/5.8.8/x86_64-linux-thread-multi/CORE   -I. -I/usr/local/include -o Test Test.c  -L/usr/local/lib -lnetsnmpmibs -lnetsnmpagent -lnetsnmp -lnetsnmpmibs -lpci -lrpm -lrpmdb -lrpmio   -lnetsnmpagent  -Wl,-E -Wl,-rpath,/usr/lib64/perl5/5.8.8/x86_64-linux-thread-multi/CORE -lnetsnmp  -lcrypto  
  2. Test.c: In function ‘handle_GetTime’:  
  3. Test.c:45: warning: ISO C90 forbids mixed declarations and code  
[root@localhost hepeng]# gcc  -fno-strict-aliasing -g -O2 -Ulinux -Dlinux=linux  -D_REENTRANT -D_GNU_SOURCE -fno-strict-aliasing -pipe -Wdeclaration-after-statement -I/usr/local/include -D_LARGEFILE_SOURCE -D_FILE_OFFSET_BITS=64 -I/usr/include/gdbm  -I/usr/lib64/perl5/5.8.8/x86_64-linux-thread-multi/CORE   -I. -I/usr/local/include -o Test Test.c  -L/usr/local/lib -lnetsnmpmibs -lnetsnmpagent -lnetsnmp -lnetsnmpmibs -lpci -lrpm -lrpmdb -lrpmio   -lnetsnmpagent  -Wl,-E -Wl,-rpath,/usr/lib64/perl5/5.8.8/x86_64-linux-thread-multi/CORE -lnetsnmp  -lcrypto
Test.c: In function ‘handle_GetTime’:
Test.c:45: warning: ISO C90 forbids mixed declarations and code

然后我们启动snmpd,再执行Test程序,程序会block住,因为不是守护进程嘛,而且main有循环,如下:

  1. [root@video6 ~]# ./Test   
  2. NET-SNMP version 5.7.2 AgentX subagent connected  
[root@video6 ~]# ./Test 
NET-SNMP version 5.7.2 AgentX subagent connected
我们再调用snmpget来测试结果:

  1. [root@localhost hepeng]# snmpget -v2c -c public localhost 1.3.6.1.4.1.16535.1.1.0  
  2. SNMPv2-SMI::enterprises.16535.1.1.0 = STRING: "Thu Apr 11 02:39:17 2013  
  3. "  
[root@localhost hepeng]# snmpget -v2c -c public localhost 1.3.6.1.4.1.16535.1.1.0
SNMPv2-SMI::enterprises.16535.1.1.0 = STRING: "Thu Apr 11 02:39:17 2013
"

六、总结

子代理看起来非常好写,我们实际上就写了两三行,其它的函数看不懂也没关系, 不用深入了解,拷贝过来直接用,能编译过就行,一般看函数名字就知道怎么用。

子代理官方参考:http://www.net-snmp.org/wiki/index.php/TUT:Writing_a_Subagent

动态库扩展MIB库方法:http://www.net-snmp.org/wiki/index.php/TUT:Writing_a_Dynamically_Loadable_Object

shell脚本扩展MIB库方法:http://www.net-snmp.org/wiki/index.php/Tut:Extending_snmpd_using_shell_scripts


  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
基于 net-snmp 的项目开发可以按照以下步骤进行: 1. 确定需求:明确项目的目标和功能要求,例如监控网络设备的状态信息、采集性能数据等。 2. 安装和配置 net-snmp:下载并安装 net-snmp 软件包,根据需要进行相应的配置,如设置 SNMP 代理、定义 MIB 文件等。 3. 编写代码:使用适合的编程语言(如 C、Python、Java)编写代码来与 SNMP 代理进行交互。net-snmp 提供了一系列的 API 接口,可以方便地实现 SNMP 操作,如获取设备信息、设置参数、发送 Trap 等。 4. 连接和通信:使用 SNMP 协议与网络设备建立连接,并通过 SNMP 消息进行通信。可以使用 SNMP 的 Get、Set、Walk 等操作来获取或修改设备的数据。 5. 数据处理和分析:对从设备获取到的数据进行处理和分析,根据需求进行相应的计算、统计或报表生成等操作。 6. 错误处理和异常处理:考虑到网络环境的复杂性,需要合理地处理错误和异常情况,如设备断连、超时等。 7. 测试和调试:编写测试用例来验证代码的正确性和功能完整性,并进行调试和优化。 8. 文档编写:编写项目文档,包括项目需求、设计、实现等内容,以方便后续的维护和扩展。 9. 部署和运行:将项目部署到目标环境中,并进行验证和运行。 需要注意的是,net-snmp 是一个功能强大的 SNMP 实现,提供了丰富的功能和接口,但同时也需要对 SNMP 协议有一定的了解。在开发过程中,可以参考 net-snmp 官方文档和示例代码,以及相关的 SNMP 标准。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值