Net-snmp学习笔记-agent子代理简单开发-示例

一、写在前面

        本章将给出具体示例,包括自定义MIB库的编写、mib2c自动生成代码的改写、桌面工具MIB Browser软件的监控使用等,具体细节部分有ASN.1的数据类型简单介绍、get和set命令如何使用、snmpd.conf文件如何配置。

二、准备工作

        本示例以Linux系统环境编写,net-snmp库需自行下载,我这里使用的是5.9.4版本。

snmpd --version                (终端输入命令)

                                          (显示版本号)

NET-SNMP version:  5.9.4.pre2
Web:               http://www.net-snmp.org/
Email:             net-snmp-coders@lists.sourceforge.net

        在下载好net-snmp库后,snmp.conf文件应该是有的,我没找到我的snmpd,conf文件,可以自己在/usr/local/etc/snmp/路径下新建一个文件,如果这个路径没有就把路径也新建好。

snmpd.conf文件内容如下:

rocommunity public

rocommunity private

master agentx

 三、自定义mib文件

        假如,要新增MIB文件MYMIB-MIB.txt

-- MYMIB-MIB.txt

    MYMIB-MIB DEFINITIONS ::= BEGIN
    
    IMPORTS
        MODULE-IDENTITY,OBJECT-TYPE,enterprises,Gauge32
            FROM SNMPv2-SMI
        DisplayString
            FROM SNMPv2-TC;

-- start MODULE-IDENTITY

    Mymib MODULE-IDENTITY
        LAST-UPDATED "202409231400Z"
        ORGANIZATION "XXX"
        CONTACT-INFO "email: <123123123@qq.com>"
        DESCRIPTION "i"
        ::= { enterprises 54321 }
     
-- end MODULE-IDENTITY
    
-- start OBJECT-IDENTITY

Mid OBJECT-IDENTITY 
    STATUS  current
    DESCRIPTION
            "The object identity used to test"
    ::= { Mymib 1 }

-- end OBJECT-IDENTITY

-- start OBJECT IDENTIFIER

UAV OBJECT IDENTIFIER ::= { Mid 1 }

-- end OBJECT IDENTIFIER

-- start OBJECT-TYPE

-- 1.3.6.1.4.1.54321.1.1.1
    dataOneUAV OBJECT-TYPE
        SYNTAX      DisplayString
        MAX-ACCESS  read-write
        STATUS      current
        DESCRIPTION "The first data"
        ::= { UAV 1 }

--1.3.6.1.4.1.54321.1.1.2
    dataTwoUAV OBJECT-TYPE
        SYNTAX      Gauge32
        MAX-ACCESS  read-write
        STATUS      current
        DESCRIPTION "The second data"
        ::= { UAV 2 }

-- end OBJECT-TYPE
END
-- MYMIB-MIB.txt

使用snmptranslate -Tp -IR MYMIB-MIB::Mymib获取文件结构

+--Mymib(54321)
   +--Mid(1)
      |
      +--UAV(1)
         |
         +-- -RW- String    dataOneUAV(1)
         |        Textual Convention: DisplayString
         |        Size: 0..255
         +-- -RW- Gauge     dataTwoUAV(2)

四、mib2c自动生成代码 

使用mib2c自动生成.c和.h代码,这里需要root权限下执行

env MIBS="+/usr/local/share/snmp/mibs/MYMIB-MIB.txt" mib2c Mymib

选2,再选1。这种自动生成代码的.c可以让我们自定义注册处理程序。(PS:选2再选2,适用于简单的int类型的变量监测,这种会直接都写好,不用自己再改了)。

自动生成后的Mymib.c代码如下:

/*
 * Note: this file originally auto-generated by mib2c
 * using mib2c.scalar.conf
 */

#include <net-snmp/net-snmp-config.h>
#include <net-snmp/net-snmp-includes.h>
#include <net-snmp/agent/net-snmp-agent-includes.h>
#include "Mymib.h"

/** Initializes the Mymib module */
void
init_Mymib(void)
{
    const oid dataOneUAV_oid[] = { 1,3,6,1,4,1,54321,1,1,1 };
    const oid dataTwoUAV_oid[] = { 1,3,6,1,4,1,54321,1,1,2 };

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

    netsnmp_register_scalar(
        netsnmp_create_handler_registration("dataOneUAV", handle_dataOneUAV,
                               dataOneUAV_oid, OID_LENGTH(dataOneUAV_oid),
                               HANDLER_CAN_RWRITE
        ));
    netsnmp_register_scalar(
        netsnmp_create_handler_registration("dataTwoUAV", handle_dataTwoUAV,
                               dataTwoUAV_oid, OID_LENGTH(dataTwoUAV_oid),
                               HANDLER_CAN_RWRITE
        ));
}

int
handle_dataOneUAV(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_OCTET_STR,
                                     /* 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_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 (/* 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_dataOneUAV\n", reqinfo->mode );
            return SNMP_ERR_GENERR;
    }

    return SNMP_ERR_NOERROR;
}
int
handle_dataTwoUAV(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_GAUGE,
                                     /* 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_GAUGE);
            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_dataTwoUAV\n", reqinfo->mode );
            return SNMP_ERR_GENERR;
    }

    return SNMP_ERR_NOERROR;
}

 五、分析自动生成代码

1、头文件

#include <net-snmp/net-snmp-config.h>

#include <net-snmp/net-snmp-includes.h>

#include <net-snmp/agent/net-snmp-agent-includes.h>

#include "Mymib.h"

 这四个include包含了net-snmp库的相关头文件,以便于使用下面用到的函数。

2、init_Mymib自定义模块

在这里,会注册SNMP变量,并让代理知道它能够处理哪些请求。

DEBUGMSGTL函数

作用:输出一条调试信息,用于表示XXX模块正在初始化

         下面是netsnmp_register_scalar函数和netsnmp_create_handler_registration函数。我们所定义的子代理,就是靠这俩函数,来完成自定义的SET和GET操作处理的,可以先看netsnmp_create_handler_registration函数的使用,再看netsnmp_register_scalar函数的使用。

 netsnmp_register_scalar函数

作用:将处理程序和变量信息注册到SNMP代理中,在此过程中,它还创建一个netsnmp_variable_list节点,使得这个新的SNMP变量能够参与到SNMP请求和响应中

 netsnmp_create_handler_registration函数

作用:主要用于创建和准备一个处理程序注册对象,它并不直接将变量注册到SNMP代理中。而是返回一个包含所有相关信息的对象

         这个两个函数的存在意义要有一个简单理解,一个是创建好处理程序和注册对象,一个是将创建好的处理程序和注册对象,这些东西注册到SNMP代理中。我们所创建的信息,最终会放到子代理的netsnmp_variable_list链表中,这个链表是由netsnmp_register_scalar函数给新的变量,注册一个新的节点,而不是由netsnmp_create_handler_registration函数在链表中注册新的节点。

       为什么要对变量的存放赘述,因为SNMP协议的通信数据以pdu传输,要想解析从子代理中的数据,需要找到pdu中存放变量信息的地方在哪。 有关结构体存储的信息,我放在文章的最后。当然,你也可以在代码中按住ctrl找到相应的结构体,分析存放的内容。

3、补全.c文件

这里我直接给出补全后的文件代码

 

/*
 * Note: this file originally auto-generated by mib2c
 * using mib2c.scalar.conf
 */

#include <net-snmp/net-snmp-config.h>
#include <net-snmp/net-snmp-includes.h>
#include <net-snmp/agent/net-snmp-agent-includes.h>
#include "Mymib.h"
static unsigned char dataOneUAV[10]="hello";
static int dataTwoUAV=1;
/** Initializes the Mymib module */
void
init_Mymib(void)
{
    const oid dataOneUAV_oid[] = { 1,3,6,1,4,1,54321,1,1,1 };
    const oid dataTwoUAV_oid[] = { 1,3,6,1,4,1,54321,1,1,2 };

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

    netsnmp_register_scalar(
        netsnmp_create_handler_registration("dataOneUAV", handle_dataOneUAV,
                               dataOneUAV_oid, OID_LENGTH(dataOneUAV_oid),
                               HANDLER_CAN_RWRITE
        ));
    netsnmp_register_scalar(
        netsnmp_create_handler_registration("dataTwoUAV", handle_dataTwoUAV,
                               dataTwoUAV_oid, OID_LENGTH(dataTwoUAV_oid),
                               HANDLER_CAN_RWRITE
        ));
}

int
handle_dataOneUAV(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_OCTET_STR,
            dataOneUAV                         /* XXX: a pointer to the scalar's data */,
            strlen(dataOneUAV)                         /* 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_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 */
            memcpy(dataOneUAV, 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_dataOneUAV\n", reqinfo->mode );
            return SNMP_ERR_GENERR;
    }

    return SNMP_ERR_NOERROR;
}
int
handle_dataTwoUAV(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_GAUGE,
            &dataTwoUAV                         /* XXX: a pointer to the scalar's data */,
            sizeof(dataTwoUAV)                         /* 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_GAUGE);
            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 */
            dataTwoUAV = *requests->requestvb->val.integer;
            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_dataTwoUAV\n", reqinfo->mode );
            return SNMP_ERR_GENERR;
    }

    return SNMP_ERR_NOERROR;
}

 对比一下,自己应该基本上能看明白改了哪些地方,不再赘述。

4、自动生成可执行文件

在终端运行命令

net-snmp-config --compile-subagent Mymib Mymib.c

出现以下信息,证明成功生成可执行文件

generating the temporary code file: netsnmptmp.874626.c
void init_Mymib(void);
checking for init_Mymib in Mymib.c
init_Mymib(void)
checking for shutdown_Mymib in Mymib.c
running: gcc  -g -O2 -DNETSNMP_ENABLE_IPV6 -fno-strict-aliasing -DNETSNMP_REMOVE_U64 -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.30/CORE   -I. -I/usr/local/include -o Mymib netsnmptmp.874626.c  Mymib.c  -L/usr/local/lib -lnetsnmpmibs -lnetsnmpagent -lnetsnmp -lnetsnmpmibs -lnl-3 -ldl -lm  -lpcre -lnetsnmpagent -lpcre  -Wl,-E -lnetsnmp -lm   -lssl -lssl -lcrypto 
netsnmptmp.874626.c: In function ‘main’:
netsnmptmp.874626.c:228:7: warning: implicit declaration of function ‘unlink’ [-Wimplicit-function-declaration]
  228 |       unlink(pid_file);
      |       ^~~~~~
netsnmptmp.874626.c:238:65: warning: implicit declaration of function ‘getpid’; did you mean ‘getpt’? [-Wimplicit-function-declaration]
  238 |         int len = snprintf(buf, sizeof(buf), "%ld\n", (long int)getpid());
      |                                                                 ^~~~~~
      |                                                                 getpt
netsnmptmp.874626.c:239:9: warning: implicit declaration of function ‘write’; did you mean ‘fwrite’? [-Wimplicit-function-declaration]
  239 |         write(fd, buf, len);
      |         ^~~~~
      |         fwrite
netsnmptmp.874626.c:240:9: warning: implicit declaration of function ‘close’; did you mean ‘pclose’? [-Wimplicit-function-declaration]
  240 |         close(fd);
      |         ^~~~~
      |         pclose

removing the temporary code file: netsnmptmp.874626.c
subagent program Mymib created

六、运行子代理,实验GET和SET指令

在root权限下,执行命令。启动SNMP代理服务

snmpd -Le -f

运行可执行文件,启动子代理

./Mqri

退出root权限,使用snmpget和snmpset命令实验是否能正常运行

第一个是执行命令,第二个是打印显示

snmpget -v2c -c public localhost 1.3.6.1.4.1.54321.1.1.1.0
SNMPv2-SMI::enterprises.54321.1.1.1.0 = STRING: "hello"

snmpset -v2c -c private localhost 1.3.6.1.4.1.54321.1.1.1.0 s IamSNMP
SNMPv2-SMI::enterprises.54321.1.1.1.0 = STRING: "IamSNMP"

snmpget -v2c -c public localhost 1.3.6.1.4.1.54321.1.1.1.0
SNMPv2-SMI::enterprises.54321.1.1.1.0 = STRING: "IamSNMP"

snmpget -v2c -c public localhost 1.3.6.1.4.1.54321.1.1.2.0
SNMPv2-SMI::enterprises.54321.1.1.2.0 = Gauge32: 1

snmpset -v2c -c private localhost 1.3.6.1.4.1.54321.1.1.2.0 u 2
SNMPv2-SMI::enterprises.54321.1.1.2.0 = Gauge32: 2

snmpget -v2c -c public localhost 1.3.6.1.4.1.54321.1.1.2.0
SNMPv2-SMI::enterprises.54321.1.1.2.0 = Gauge32: 2

七、总结不足

        这基本上就是一个简单示例,其中还有好多没有说到的地方,比如说pdu的消息中包含哪些内容,在源码中如何体现、MIB文件的数据类型对应自动生成代码中的数据类型是什么,如何在.c文件中准确设置数据类型、snmpset命令中为什么要是有u,u代表什么,s代表什么,其他的数据类型用什么字母代表。上述只是一个简单例子了解net-snmp库如何运行起来,在实际使用中,需要根据需求,在GET和SET处理函数中添加自己需要的功能,比如说与监测设备之间的udp通信。一般来说不会使用net-snmp-config --compile-subagent Mymib Mymib.c自动生成可执行文件,自己在代码中补全main函数即可,SNMP代理的监听会使用agent_check_and_process函数。本来写的时候想一口气写完整,写一半的时候感觉码字太繁琐,第一篇博客,比较懒,理解一下。如果看的人多,可以后续写一下与设备监控的简单示例,我会用一个c++程序跑起来作为启动的设备,与子代理进行UDP通信交换共享内存数据,应用层做一个简单的自定义标志,只需要在子代理的GET和SET中加个UDP通信即可。废话到此结束,有什么问题大家及时指正,互相学习

评论 12
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值