zoukankan      html  css  js  c++  java
  • net-snmp的MIBs扩展(linux下)

    net-snmp的MIBs扩展

    • MIB的相关概念
      在SNMP网络管理中,管理信息库MIB(Management Information Base)是对于通过网络管理协议可以访问的信息。这些信息更具体的理解为网管中被管资源,而网络管理中的资源是以对象来表示,每一个对象表示被管资源某一方面的属性,这些对象的集合形成管理信息库。

    • 先说一下系统环境    

    o@o-pc:~$ uname -a
    Linux o-pc 3.19.0-21-generic #21-Ubuntu SMP Sun Jun 14 18:31:11 UTC 2015 x86_64 x86_64 x86_64 GNU/Linux

    1 编译安装net-snmp

    具体的就不说了,见此处: net-snmp-5.7.3配置编译安装 http://www.cnblogs.com/oloroso/p/4595123.html

    2 编写MIB文件

    MIB文件描述

    MIB文件是用 ASN.1 语法来描述的,所以为了精确定义MIB中各管理对象,用户不得不参考一些ASN.1语法的有关文档如RFC1155、RFC1212等等来定义设备自己的MIB。ASN.1是抽象句法表示法一 (Abstract Syntax Notation One) 的简称,对于每个管理对象它都用文本来描述,一般文件的后缀名都用“.mib”。

    关于MIB文件示例,可以见编译安装后的net-snmp目录,一般是 /usr/local/net-snmp/share/snmp/mibs/ 。  

    ls /usr/local/net-snmp/share/snmp/mibs/
    AGENTX-MIB.txt                       IPV6-TCP-MIB.txt                     SNMP-NOTIFICATION-MIB.txt
    ...
    IPV6-MIB.txt                         SNMP-MPD-MIB.txt                     UDP-MIB.txt

    一个简单的示例

    这里我们写一个MIB文件,含有两个节点,一个是只读权限的,一个是读写权限的。然后把这个文件命名为 myTest.mib 然后保存到 /usr/local/net-snmp/share/snmp/mibs/ 目录。

    --开始
    TEST-MIB DEFINITIONS ::= BEGIN
    
    --引入部分
    IMPORTS
        enterprises
            FROM RFC1155-SMI            
        Integer32,OBJECT-TYPE
            FROM SNMPv2-SMI            
        DisplayString
            FROM SNMPv2-TC
        TEXTUAL-CONVENTION
            FROM SNMPv2-TC; --引用结束,用分号
    
    
    --定义节点
    --enterprises的OID是1.3.6.1.4
     test OBJECT IDENTIFIER ::= {enterprises 77587}
    
    readObject  OBJECT IDENTIFIER ::= {test 1}  
    writeObject OBJECT IDENTIFIER ::= {test 2}  
    
        readobject  OBJECT-TYPE --对象名称
        SYNTAX      Integer32   --类型
        MAX-ACCESS read-only        --访问方式
        STATUS      current     --状态
        DESCRIPTION "test read" --描述
        ::= {test 1}                --父节点
    
        writeObject OBJECT-TYPE --对象名称
        SYNTAX      DisplayString   --类型
        MAX-ACCESS read-write       --访问方式
        STATUS      current     --状态
        DESCRIPTION "test write"    --描述
        ::= {test 2}                --父节点
    
    --结束定义
    END

    3 使自定义的MIB文件生效

    要使得这个自定义的MIB生效,最简单的办法就是把它的内容添加到已有的某个MIB文件中就是了。比如 cat myTest.mib >> 原有MIB文件 ,然后重启一下 snmpd 服务就是了。不过这是一种投机的做法。
    正常一点的做法是:

    • 将自定义的MIB文件的开始处定义的名称加入到环境变量 MIBS 中,譬如这里 TEST-MIB DEFINITIONS ::= BEGIN 中的 TEST-MIB
      还可以添加到 snmpd 的配置文件中。因为本机上是采取的添加到 bash 的环境变量中的,所以在 ~/.bashrc 文件中添加了一行
      export MIBS=+TEST-MIB
    • 杀死 snmpd 进程,然后重启它。
    • 使用 snmptranslate 查看自定义的 TEST-MIB 是否被加载了。
    o@o-pc:~/snmp/mibs$ /usr/local/net-snmp/bin/snmptranslate -Tp -IR test
    +--test(77587)
       |
       +-- -R-- Integer32 readObject(1)
       +-- -RW- String    writeObject(2)
                Textual Convention: DisplayString
                Size: 0..255

    4 实现agent代理程序

    我们可以先来获取一下前面定义的 readObject 节点的值试试。
    因为 enterprises 的OID是 1.3.6.1.4 ,而 test enterprises 的叶子(77585),而 readObject 又是 test 的叶子节点(1)。所以其OID为 1.3.6.1.4.77585.1
    下面使用snmpget来测试一下  

    o@o-pc:~/snmp/mibs$ /usr/local/net-snmp/bin/snmpget -c public -v 2c localhost 1.3.6.1.4.1.77587.1
    TEST-MIB::readObject = No Such Object available on this agent at this OID

    结果是No Such Object available on this agent at this OID,即

    使用mib2c程序来生成 .c .h 文件。

    使用以下命令来生成,两条命令的 -c 后跟的配置文件可以是不同的,因为节点的类型可能不一样。第一个是(只读的)数字(integer32,counter,time)的配置,第二个是其它类型的。

    mib2c -c mib2c.int_watch.conf readObject
    mib2c -c mib2c.scalar.conf writeObject

    只读节点readObject.c和readObject.h的修改。

    readObject.h文件就不改了,这里没什么必要。
    先来看一下readObject.c文件。(这里我把不重要的删掉了)
    所做的修改就是在 init_readObject 函数中添加了一句 readObject = 12345; 。这个是随性而为的。我们只需要知道,当使用 snmpget 来获取 readObject 节点值的时候,获取的就是这个变量的值。这里还要注意一点,这个值一旦确定就不会再更改了。如果需要可以更改的,前面生成代码模板的时候应该使用 mib2c -c mib2c.scalar.conf readObject   

    #include <net-snmp/net-snmp-config.h>
    #include <net-snmp/net-snmp-includes.h>
    #include <net-snmp/agent/net-snmp-agent-includes.h>
    #include "readObject.h"
    
    //这一句是mib2c生成的,默认值设为0
    long    readObject = 0;  /* XXX: set default value */
    
    void
    init_readObject(void)
    {
      netsnmp_handler_registration *reg;
    
        const oid readObject_oid[] = { 1,3,6,1,4,1,77587,1 };
      static netsnmp_watcher_info readObject_winfo;
    
      DEBUGMSGTL(("readObject", "Initializing the readObject module
    "));
    
    /******************************************************/
        //这里是我修改的,以便于验证其有效
        readObject = 12345;
    
    /******************************************************/
        DEBUGMSGTL(("readObject",
                    "Initializing readObject scalar integer.  Default value = %ld
    ",
                    readObject));
        reg = netsnmp_create_handler_registration(
                 "readObject", NULL,
                  readObject_oid, OID_LENGTH(readObject_oid),
                  HANDLER_CAN_RONLY);
        netsnmp_init_watcher_info(&readObject_winfo, &readObject, sizeof(long),
                      ASN_INTEGER, WATCHER_FIXED_SIZE);
    if (netsnmp_register_watched_scalar( reg, &readObject_winfo ) < 0 ) {
            snmp_log( LOG_ERR, "Failed to register watched readObject" );
        }
    
      DEBUGMSGTL(("readObject",
                  "Done initalizing readObject module
    "));
    }

    编译一下

    编译的时候需要使用到另一个工具 net-snmp-config 。这个工具用来做两件事,一个是生成中间代码,然后使用gcc来编译它。
    为什么要生成中间代码呢?你看上面的生存的 readObject.c 中没有main函数就知道了吧。
    具体使用如下

    net-snmp-config --compile-subagent readObject readObject.c
    
    • –compile-subagent的意思是编译为subagent程序。
    • readObject是编译后输出程序名
    • readObject.c是要编译的文件

    还可以加上 --norm 参数来阻止编译后删除生成的中间代码文件。我们可以试一下。

    o@o-pc:~/snmp/mibs$ net-snmp-config --compile-subagent --norm readObject readObject.c
    generating the tmporary code file: netsnmptmp.25506.c
    void init_readObject(void);
    checking for init_readObject in readObject.c
    init_readObject(void)
    checking for shutdown_readObject in readObject.c
    running: gcc  -fno-strict-aliasing -g -O2 -Ulinux -Dlinux=linux  -D_REENTRANT -D_GNU_SOURCE -DDEBIAN -fwrapv -fno-strict-aliasing -pipe -I/usr/local/include -D_LARGEFILE_SOURCE -D_FILE_OFFSET_BITS=64  -I/usr/lib/x86_64-linux-gnu/perl/5.20/CORE   -I. -I/usr/local/net-snmp/include -o readObject netsnmptmp.25506.c  readObject.c  -L/usr/local/net-snmp/lib -lnetsnmpmibs -lnetsnmpagent -lnetsnmp -lnetsnmpmibs -ldl  -lnetsnmpagent  -Wl,-E -lnetsnmp  
    leaving the tmporary code file: netsnmptmp.25506.c
    subagent program readObject created

    编译后我们可以看到,当前目录下出现了编译后的程序 readObject 和一个 netsnmptmp.25506.c 的文件。你把它打开,就可以看到这是一个具有 main 函数的.c源文件了。简化之后的代码如下了。

    #include "readObject.h"
    const char *app_name = "readObject";
    static int reconfig = 0;
    
    extern int netsnmp_running;
    
    
    int
    main (int argc, char **argv)
    {
        ...
      while ((arg = getopt(argc, argv, ...)) != EOF) {
        ...
      }
        ...
    
      /* 初始化agent库 *
      /*initialize the agent library */
      init_agent(app_name);
    
      /* i初始化你的mib代码 */
      /* initialize your mib code here */
      init_readObject();
    
      /* readObject will be used to read readObject.conf files. */
      /* readObject will be used to read readObject.conf files. */
      init_snmp("readObject");
      ...
      exit(0);
    }

    运行测试一下

    这里我们先启动这个 readObject 的程序,然后再去重新 snmpget 获取节点 readObeject 的值。
    注意,这个 readObject 默认是守护进程。
    获取结果如下,可以看到成功获取了。  

    o@o-pc:~/snmp/mibs$ /usr/local/net-snmp/bin/snmpget -c public -v 2c localhost 1.3.6.1.4.1.77587.1
    ...
    
    TEST-MIB::readObject.0 = INTEGER: 12345

    读写节点writeObject.c的修改

    下面是修改后的writeObject.c文件

    #include <net-snmp/net-snmp-config.h>
    #include <net-snmp/net-snmp-includes.h>
    #include <net-snmp/agent/net-snmp-agent-includes.h>
    #include "writeObject.h"
    
    //这里是添加的,buf用于保存控制端设置的值,也用于返回。
    #define BUFSIZE 1024
    static char buf[BUFSIZE] = "test Write";    //给一个默认值
    
    
    
    /** Initializes the writeObject module */
    void
    init_writeObject(void)
    {
        const oid writeObject_oid[] = { 1,3,6,1,4,1,77587,2 };
    
      DEBUGMSGTL(("writeObject", "Initializing
    "));
    
        netsnmp_register_scalar(
            netsnmp_create_handler_registration("writeObject", handle_writeObject,
                                   writeObject_oid, OID_LENGTH(writeObject_oid),
                                   HANDLER_CAN_RWRITE
            ));
    }
    
    int
    handle_writeObject(netsnmp_mib_handler *handler,
                              netsnmp_handler_registration *reginfo,
                              netsnmp_agent_request_info   *reqinfo,
                              netsnmp_request_info         *requests)
    {
        int ret;
    
        switch(reqinfo->mode) {
        //是获取操作
            case MODE_GET:
                snmp_set_var_typed_value(requests->requestvb, ASN_OCTET_STR,
                 /*这里填buf,用于返回数据给控制端*/      buf  /* XXX: a pointer to the scalar's data */,
                 /*这里是buf数据字节数,注意writeObject类型*/ strlen(buf)  /* XXX: the length of the data in bytes */);
                break;
    
            /*
             * SET REQUEST
             *
             * multiple states in the transaction.  See:
             * http://www.net-snmp.org/tutorial-5/toolkit/mib_module/set-actions.jpg
             */
        //下面是设置操作的,也就是snmpset
    
            case MODE_SET_RESERVE1: //这个不管它
                    /* or you could use netsnmp_check_vb_type_and_size instead */
                ret = netsnmp_check_vb_type(requests->requestvb, ASN_OCTET_STR);
                if ( ret != SNMP_ERR_NOERROR ) {
                    netsnmp_set_request_error(reqinfo, requests, ret );
                }
                break;
    
            case MODE_SET_RESERVE2: //这个也不管它
                /* XXX malloc "undo" storage buffer */
                //我们不需要动态申请内存,直接略过
                if ( 0 /* XXX if malloc, or whatever, failed: */) {
                    netsnmp_set_request_error(reqinfo, requests, SNMP_ERR_RESOURCEUNAVAILABLE);
                }
                break;
    
            case MODE_SET_FREE:
                /* XXX: free resources allocated in RESERVE1 and/or
                   RESERVE2.  Something failed somewhere, and the states
                   below won't be called. */
                break;
    
        /****************************************************************/
        //  这里是我们的重点,控制端传过来的数据就在这里获取
    
            case MODE_SET_ACTION:
                /* XXX: perform the value change here */
                /* 获取控制端使用snmpset传来的数据 */
                memcpy(buf,requests->requestvb->buf,requests->requestvb->val_len);
    
                if (0/* XXX: error? */) {   //这个先不管了
                    netsnmp_set_request_error(reqinfo, requests, 0 /* some error */);
                }
                break;
    //下面的都不管了
            case MODE_SET_COMMIT:
                /* XXX: delete temporary storage */
                if (0 /* XXX: error? */) {
                    /* try _really_really_ hard to never get to this point */
                    netsnmp_set_request_error(reqinfo, requests, SNMP_ERR_COMMITFAILED);
                }
                break;
    
            case MODE_SET_UNDO:
                /* XXX: UNDO and return to previous value for the object */
                if (0 /* XXX: error? */) {
                    /* try _really_really_ hard to never get to this point */
                    netsnmp_set_request_error(reqinfo, requests, SNMP_ERR_UNDOFAILED);
                }
                break;
    
            default:
                /* we should never get here, so this is a really bad error */
                snmp_log(LOG_ERR, "unknown mode (%d) in handle_writeObject
    ", reqinfo->mode );
                return SNMP_ERR_GENERR;
        }
    
        return SNMP_ERR_NOERROR;
    }

    编译运行测试一下

    编译还是和前面说的一样,就不详细说了。
    一样运行后进行获取一次试试。这里使用的snmpbulkget(批量获取)而不是snmpget。是因为任性而已。  

    o@o-pc:~/snmp/mibs$  snmpbulkget -c public -v 2c localhost writeObject.0
    MY-TEST-MIB::writeObject.0 = STRING: test Write
    
    o@o-pc:~/snmp/mibs$ snmpget -c public -v 2c localhost writeObject.0
    MY-TEST-MIB::writeObject.0 = STRING: test Write

    可以看大获取是没有问题的,那么设置一下试试。设置要使用到 snmpset 工具,使用方式和 snmpget 类似。只是需要在最后加上要设置的数据的类型和数据。

    snmpset -c public -v 2c localhost writeObject.0 s "nihao"
    

    数据类型参数可以使用 snmpset --help 来查看,其结果如下

      TYPE: one of i, u, t, a, o, s, x, d, b
        i: INTEGER, u: unsigned INTEGER, t: TIMETICKS, a: IPADDRESS
        o: OBJID, s: STRING, x: HEX STRING, d: DECIMAL STRING, b: BITS
        U: unsigned int64, I: signed int64, F: float, D: double

    设置完成之后再次获取试试。

    o@o-pc:~/snmp/mibs$ snmpget -c public -v 2c localhost writeObject.0
    MY-TEST-MIB::writeObject.0 = STRING: nihao

    如果测试的时候碰到设置不成功的情况,检查一下snmpd的配置文件(通常是snmpd.conf)中权限设置的问题。

    o@o-pc:~/snmp/mibs$ snmpset -c public -v 2c localhost writeObject.0 s "nihao"
    Error in packet.
    Reason: noAccess
    Failed object: MY-TEST-MIB::writeObject.0
  • 相关阅读:
    ABP理论学习之异常处理
    ABP理论学习之导航(Navigation)
    ABP理论学习之验证DTO
    C#程序实现窗体的最大化/最小化
    残缺棋盘的覆盖问题
    23:区间内的真素数
    最大质因子序列
    02:二分法求函数的零点
    01:查找最接近的元素
    最大连续和问题【四种不同的算法】
  • 原文地址:https://www.cnblogs.com/oloroso/p/4599501.html
Copyright © 2011-2022 走看看