SNMP Agent添加私有MIB

环境:

系统:Ubuntu 14.04 LTS
snmp版本:net-snmp-5.7.3

step1.编写自己的MIB文件

这部分内容,网上的资料还是比较多的,自己在挖坑过程中没有遇到太多问题,不啰嗦了。给出一个自己写好的MIB文件,里面包含了一些注释:

QOS-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;

--enterprises就是.1.3.4.1.4.1
-- ::={}格式里定义的就是当前节点的访问地址
--.1.3.6.1.4.1.73691
QosMIB MODULE-IDENTITY
    LAST-UPDATED "201803021450Z"
    ORGANIZATION
        ""
    CONTACT-INFO
        "sedwt-zjzhu"
    DESCRIPTION
        "XXX's QOS List MIB."
    ::= { enterprises 73691 }

--定义一个自己的根节点,访问地址为 QosMIB.1,也就是
--.1.3.6.1.4.1.73691.1
--这个根节点下面又定义了3个子节点
--SYNTAX是该节点数据类型,可以自己定义,也可以用标准里定义好的
--MAX-Access是该节点的读写属性,有not-accessible,read-only,read-write,read-create,根据自己需求选择合适的属性,read-create比较特殊,会在下一篇博客中单独讲。
Qos OBJECT IDENTIFIER ::= { QosMIB 1 }
    WANDevice OBJECT-TYPE
        SYNTAX Integer32 
        MAX-ACCESS read-write
        STATUS current
        DESCRIPTION
            "参数解释:广域网侧设备。取值范围:4 - 4 ,默认值:4"
        ::= { Qos 1 }

    WANConnectionDevice OBJECT-TYPE
        SYNTAX Integer32
        MAX-ACCESS read-write
        STATUS current
        DESCRIPTION
            "参数解释:广域网侧连接设备。,取值范围:1 - 1,默认值:1"
        ::= { Qos 2 }

    CID OBJECT-TYPE
        SYNTAX Integer32
        MAX-ACCESS read-write
        STATUS current
        DESCRIPTION
            "参数解释:CID。取值范围:2 - 20,默认值:2"
        ::= { Qos 3 }
END 

step2.使用mib2c工具生成c文件

写好的MIB文件我们放在用户根目录/.snmp/MIBS/文件夹中,例如我的目录为:
/home/zzj/.snmp/MIBS/QOS-MIB.txt

前一篇博客中,我们已经是搭好了snmp环境的。但是在我过坑的过程中发现,环境的事还没完,比如这里的.snmp目录就是没有的,需要我们手动创建。
如果你问为啥非要是这个目录?其实也一定,哈哈。不过当你后面运行mib2c提示Cannot find module xxx异常时,命令行会输出扫描过的目录,其中就包含.snmp这个目录,你把MIB文件放在它扫描的其他目录也是阔以滴!

ok,跳过了第一个小坑,咱们接着跳。

按照一般攻略,咱们就要执行mib2c生成c文件了。执行前先which mib2c看看环境变量设置好了没:

zzj@sed:/home$ which mib2c
/usr/local/snmp/bin/mib2c

good,看来没问题,接下来就是关键的mib2c。执行如下命令:

zzj@sed:/home$ env MIBS="+/home/zzj/.snmp/mibs/QOS-MIB.txt" mib2c Qos
Can't find a configuration file called mib2c.conf
I looked in:
  .
  /usr/local/snmp/
  /usr/local/snmp/share/snmp/mib2c-data
  ./mib2c-conf.d

嗯?看来不太对?没看人提起过还有mib2c.conf这个文件啊!各种百度、Google,想着可能是和snmpd.conf类似,然而并不是啊。这里折腾了不少时间,一直没法转成c文件。某天我看net-snmp的安装目录的local文件,发现这里面就有mib2c.conf,而且这里也有mib2c.pl,抱着死马当活马医的心态,在这个目录下试了一下。终于和网上教程对上了有木有!!如下:

zzj@sed:~/net-snmp/net-snmp-5.7.3/local$ env MIBS="+/home/zzj/.snmp/mibs/QOS-MIB.txt" mib2c Qos
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:                              Qos
  numeric translation:              .1.3.6.1.4.1.73691.1
  number of scalars within:         9
  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

然后就比较简单了,如果你英文不太差的话,看看提示,跟着选就行了。这里第一个选2,后续的就不列了。

(敲黑板)注意啦,划重点了:
执行mib2c是需要一些其他文件的,例如mib2c.conf。最简单的执行方法就是在net-snmp解压目录/local这个目录下直接执行命令。

命令是啥?自己去上面翻代码吧!

终于生成了c文件了,生成的文件也是在local目录下,怀着激动的心情,我们进入下一阶段,修改c文件。打开文件

/*
 * 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 "Qos.h"

/** Initializes the Qos module */
void
init_Qos(void)
{
    const oid WANDevice_oid[] = { 1,3,6,1,4,1,73691,1,1 };
    const oid WANConnectionDevice_oid[] = { 1,3,6,1,4,1,73691,1,2 };
    const oid CID_oid[] = { 1,3,6,1,4,1,73691,1,3 };

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

    netsnmp_register_scalar(
        netsnmp_create_handler_registration("WANDevice", handle_WANDevice,
                               WANDevice_oid, OID_LENGTH(WANDevice_oid),
                               HANDLER_CAN_RWRITE
        ));
    netsnmp_register_scalar(
        netsnmp_create_handler_registration("WANConnectionDevice", handle_WANConnectionDevice,
                               WANConnectionDevice_oid, OID_LENGTH(WANConnectionDevice_oid),
                               HANDLER_CAN_RWRITE
        ));
    netsnmp_register_scalar(
        netsnmp_create_handler_registration("CID", handle_CID,
                               CID_oid, OID_LENGTH(CID_oid),
                               HANDLER_CAN_RWRITE
        ));
}

int
handle_WANDevice(netsnmp_mib_handler *handler,
                          netsnmp_handler_registration *reginfo,
                          netsnmp_agent_request_info   *reqinfo,
                          netsnmp_request_info         *requests)
{
    int ret;
    /* 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_INTEGER,
                                     /* XXX: a pointer to the scalar's data */,
                                     /* 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
         */
        case MODE_SET_RESERVE1:
                /* or you could use netsnmp_check_vb_type_and_size instead */
            ret = netsnmp_check_vb_type(requests->requestvb, ASN_INTEGER);
            if ( ret != SNMP_ERR_NOERROR ) {
                netsnmp_set_request_error(reqinfo, requests, ret );
            }
            break;

        case MODE_SET_RESERVE2:
            /* XXX malloc "undo" storage buffer */
            if (/* 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 */
            if (/* XXX: error? */) {
                netsnmp_set_request_error(reqinfo, requests, /* some error */);
            }
            break;

        case MODE_SET_COMMIT:
            /* XXX: delete temporary storage */
            if (/* 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 (/* 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_WANDevice\n", reqinfo->mode );
            return SNMP_ERR_GENERR;
    }

    return SNMP_ERR_NOERROR;
}
int
handle_WANConnectionDevice(netsnmp_mib_handler *handler,
                          netsnmp_handler_registration *reginfo,
                          netsnmp_agent_request_info   *reqinfo,
                          netsnmp_request_info         *requests)
{
    int ret;
    /* 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_INTEGER,
                                     /* XXX: a pointer to the scalar's data */,
                                     /* 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
         */
        case MODE_SET_RESERVE1:
                /* or you could use netsnmp_check_vb_type_and_size instead */
            ret = netsnmp_check_vb_type(requests->requestvb, ASN_INTEGER);
            if ( ret != SNMP_ERR_NOERROR ) {
                netsnmp_set_request_error(reqinfo, requests, ret );
            }
            break;

        case MODE_SET_RESERVE2:
            /* XXX malloc "undo" storage buffer */
            if (/* 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 */
            if (/* XXX: error? */) {
                netsnmp_set_request_error(reqinfo, requests, /* some error */);
            }
            break;

        case MODE_SET_COMMIT:
            /* XXX: delete temporary storage */
            if (/* 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 (/* 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_WANConnectionDevice\n", reqinfo->mode );
            return SNMP_ERR_GENERR;
    }

    return SNMP_ERR_NOERROR;
}
int
handle_CID(netsnmp_mib_handler *handler,
                          netsnmp_handler_registration *reginfo,
                          netsnmp_agent_request_info   *reqinfo,
                          netsnmp_request_info         *requests)
{
    int ret;
    /* 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_INTEGER,
                                     /* XXX: a pointer to the scalar's data */,
                                     /* 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
         */
        case MODE_SET_RESERVE1:
                /* or you could use netsnmp_check_vb_type_and_size instead */
            ret = netsnmp_check_vb_type(requests->requestvb, ASN_INTEGER);
            if ( ret != SNMP_ERR_NOERROR ) {
                netsnmp_set_request_error(reqinfo, requests, ret );
            }
            break;

        case MODE_SET_RESERVE2:
            /* XXX malloc "undo" storage buffer */
            if (/* 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 */
            if (/* XXX: error? */) {
                netsnmp_set_request_error(reqinfo, requests, /* some error */);
            }
            break;

        case MODE_SET_COMMIT:
            /* XXX: delete temporary storage */
            if (/* 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 (/* 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_CID\n", reqinfo->mode );
            return SNMP_ERR_GENERR;
    }

    return SNMP_ERR_NOERROR;
}

艾玛,好长啊,这么一大堆,怎么看啊。
其实不难,看到代码中那些红色的XXX没,填空啊!怎么填?好吧,我还是说一下吧。
以 handle_WANDevice 为例:
算了还是不说了,注释那么清楚了。。。
一般我们对于snmpset的处理都是放在MODE_SET_ACTION中来做的,其他几个MODE_SET_XXX好像涉及到对象内存申请、释放啥的,不是很复杂的开发,没必要动那里了,都if(0)吧。修改好的c文件就不放了吧,太长了。。

step3.生成动态库

离成功只有一小步了。c文件也写好了,现在该想想怎么让snmp能调用它。这个网上讲解比较多:走到这一步,就只有静态调用和动态调用两条路了:
静态调用就是像安装net-snmp那样,重新make,然后make install,这样的话每次修改都需要给agent重新安装新变异的snmp,优点是不用修改snmpd.conf。
动态调用就是编译成动态库so文件,在snmpd.conf中动态加载。灵活不少。

当前项目中用不到静态调用,我没有实验。一下为动态调用经验:
编译动态库我也是小白,找到一段可以编译so库的MakeFile文件,修改下可用,不解释:

CC=gcc

FLAGS=-I.`net-snmp-config --cflags` -g
DLFLAGS=-shared -fPIC -g

NAME=Qos
$(NAME).so:$(NAME).c
.PHONY: clean
$(NAME).so: $(NAME).c
    $(CC)$(CFLAGS) $(DLFLAGS) -c -o $(NAME).o $(NAME).c
    $(CC)$(CFLAGS) $(DLFLAGS) -o $(NAME).so $(NAME).o
clean:
    rm-rf *.o *.so

命名makefile文件为MakeFile_so,放在哪里随你自己指定吧,我们就是用make命令来生成so而已。
使用这个makefile的话,c文件,makefile文件,so文件都会在一个目录下,在这个目录下执行命令:

make -f MakeFile_so

如果你的c文件有毛病的话,对着命令行的提示改好吧,修改到没毛病,so就生成了。

step4.配置snmpd.conf加载so文件

打开snmpd.conf文件,在末尾加上

dlmod QosMIB /home/zzj/net-snmp/net-snmp-5.7.3/local/zzj/QosMIB.so

注意:这里需要Agent设备上支持dlmod,提前查清楚,不要做了无用功。

step5.验证

打开cmd,执行:

snmpwalk -v2c -c public localhost .1.3.6.1.4.1.73691.1

测试的这个so文件已经没了,没法贴结果了,如果你走到这一步,我相信应该是没有问题的。

全文完

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章