net-snmp agent开发(用net-snmp扩展MIB库)

注:本文经过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上开发和测试:

1.环境搭建

对于安装snmpd过程本文暂不做详解网上资料很多,本文所述开发与测试基于net-snmp-5.7.3版本

2.建立自己的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库,如下:

-- 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显示结果如下:

+--Test(16535)
   |
   +--Time(1)
      |
      +-- -R-- String    GetTime(1)
               Textual Convention: DisplayString
               Size: 0..100

3 生成源代码

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

[r[email protected] 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.c里面的
XXX改成自己的值,也就两行,修改后Test.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 "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;
}

3
静态库方法:

(1)将.c
和.h文件移动到源码目录下,我的是/opt/net-snmp-5.7.3/agent/mibgroup。

(2)在/opt/net-snmp-5.7.3下运行的configure命令,并且添加参数—with-mib-modules=Test,进行MIB库文件的配置。

(3)在/opt/net-snmp-5.7.3下运行make&makeinstall编译安装

(4)执行命令snmpget
-v2c -c public localhost Test-MIB:GetTime.0测试

结果:Test-MIB::GetTime.0 = STRING: Mon Jun  6 21:17:54 2016

4 生成共享库的方法:

(1) 在Test.c增加main函数,修改后如下:

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

(2)编译和部署基于root用户,具体编译命令为:

gcc -I `net-snmp-config --cflags` -fPIC -shared -g -O0 -o Test.so Test.c  `net-snmp-config --libs`

(3)修改/etc/snmp/snmpd.conf,添加一行:

dlmod Test /home/net-snmp/sbin/Test.so

其中/home/net-snmp/sbin/test.so为test.so的具体路径,测试完成后可以注掉这行即可

并添加:

view    systemview    included  .1.3.6.1.4.1.16535.1.1

这样我们自定义的OID才可以访问,接下来执行:

service snmpd start

若snmpd已启动则执行restart,服务正确启动后在本机输入:

snmpget -v2c -c public localhost .1.3.6.1.4.1.16535.1.1

输出结果应为:

SNMPv2-SMI::enterprises.16535.1.1.0
= STRING: "Mon Jun  6 20:56:13 2016

"

5
扩展一个子代理的方法:

(1)执行命令让我们的子代理生成可执行程序,

net-snmp-config --compile-subagent Test Test.c,生成了Test程序

(2)现在Test程序已经生成了,我们先执行主代理(service snmpd start),再执行子代理./Test,再ps -ef | grep Test,看一下,可以看到Test程序自动在后台启动了,本文在一台装有snmpd和子代理的linux
server机器上直接测试。执行命令如下:

snmpget -v2c -c public localhost 1.3.6.1.4.1.16535.1.1.0

运行结果:SNMPv2-SMI::enterprises.16535.1.1.0 = STRING: "Mon Jun  6 21:07:06 2016

"

可以看到,我们开发的子代理成功工作了。如果自定义的MIB库已经加入到snmpd指定的目录中,我们可以执行

snmpget -v2c -c public localhost Test-MIB:GetTime.0

结果:Test-MIB::GetTime.0
= STRING: Mon Jun  6 21:08:24 2016

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。

(3)我们再次执行net-snmp-config
--compile-subagent Test Test.c,然后立刻Ctrl+c,时间要控制好,让net-snmp-config程序产生了临时的C文件,却没有删除它。打开netsnmptmp.12373.c,我们来看一下代码,200多行,我们不管上面代码那些看不懂的函数,知道大概意思就行,现在我们来加入到自己的项目中,找到项目中的main函数,在main函数中添加初始化Test子代理的代码,本文为了方便理解,就在Test.c中添加main函数,就地取材,写个超简单函数,不考虑传入参数。修改后的Test.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 "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;
}

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

[[email protected] 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  -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有循环,如下:

[[email protected] ~]# ./Test
NET-SNMP version 5.7.2 AgentX subagent connected

我们再调用snmpget来测试结果:

[[email protected] hepeng]# snmpget -v2c -c public localhost 1.3.6.1.4.1.16535.1.1.0
SNMPv2-SMI::enterprises.16535.1.1.0 = STRING: "Mon Jun  6 21:07:06 2016
"

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

时间: 2024-10-09 13:03:39

net-snmp agent开发(用net-snmp扩展MIB库)的相关文章

SNMP AGENT函数介绍

http://wenku.baidu.com/view/6a7903a9d1f34693daef3e9f.html 一.  SNMP AGENT在SNMP框架中的位置 1.1 SNMP是被广泛接受并投入使用的工业标准,它采用轮询机制,便于网络管 理员检索任何节点的信息,进行修改,寻找故障:完成故障诊断,容量规划和报告生成. SNMP的结构分为NMS和AGENT两部分(如图1.1.1),NMS(Network Management Station,目前常用的网管平台有SunNetManager和I

SNMP协议开发概念理解-1

SNMP协议入门 1.引言 基于TCP/IP的网络管理包含3个组成部分: 1) 一个管理信息库MIB(Management Information Base).管理信息库包含所有代理进程的所有可被查询和修改的参数.RFC1213[McCloghrie and Rose 1991]定义了第二版的MIB,叫做MIB-II. 2)  关于MIB的一套公用的结构和表示符号.叫做管理信息结构SMI(Structure of Management Information).这个在RFC 1155 [Rose

10个Laravel4开发人员必用扩展包

Laravel是一个新的基于最新PHP版本语法,支持IoC等设计模式的快速开发框架.目前最新版本为4.2,推荐安装PHP版本5.5+. 本文列举10个基本软件包,都是开发人员使用Laravel框架来构建WEB应用过程中应该要用到的,无需自己重复构建,如认证.调试.网站优化. 代码生成器(Laravel Generators) 使用简单的命令行就可以自动根据代码模板生成Model/View/Controller代码以及模块(Module). IDE帮手(Laravel IDE Helper) 可以

让你提前认识软件开发(36):如何扩展数据表字段?

第2部分 数据库SQL语言 如何扩展数据表字段? [文章摘要] 在通信类软件中,经常会与数据库打交道.由于需求变化,或者是程序优化升级等原因,对数据表字段进行扩展是常有的事情.这就要求开发人员必须熟练掌握对数据表字段进行扩展的操作流程. 本文基于作者的数据库方面的工作经验,以实际的SQL程序为例,详细介绍了如何对对数据表字段进行扩展,为相关的开发工作提供了参考. [关键词] 数据库  数据表  扩展  SQL  开发 一.前言 在实际的软件开发项目中,对数据表字段的扩展包括如下两个方面: 第一,

Boost程序库完全开发指南——深入C++“准”标准库(第3版)

内容简介  · · · · · · Boost 是一个功能强大.构造精巧.跨平台.开源并且完全免费的C++程序库,有着“C++‘准’标准库”的美誉. Boost 由C++标准委员会部分成员所设立的Boost 社区开发并维护,使用了许多现代C++编程技术,内容涵盖字符串处理.正则表达式.容器与数据结构.并发编程.函数式编程.泛型编程.设计模式实现等许多领域,极大地丰富了C++的功能和表现力,能够使C++软件开发更加简捷.优雅.灵活和高效. <Boost程序库完全开发指南——深入C++“准”标准库(

Python 开发安卓Android及IOS应用库Kivy安装尝试

Python 开发安卓Android及IOS应用库Kivy安装尝试: 先来看看这货可以用来制作什么应用: Create a package for Windows Create a package for Android Creating packages for OS X Create a package for IOS 逆天的节奏啊,虽然600+页的英文文档,python3+还在on the way ,觉得这货潜力无穷啊. 当然,缺点就是要安装各种环境依赖,尤其是在win7平台下,估计会比较

一个让业务开发效率提高10倍的golang库

一个让业务开发效率提高10倍的golang库 此文除了是标题党,没有什么其他问题. 这篇文章推荐一个库,https://github.com/jianfengye/collection. 这个库是我在开发业务过程中 Slice 的频繁导致业务开发效率低,就产生了要做一个 Collection 包的想法.本文说说我开发这个库的前因后果 Golang 适不适合写业务? 最近一个逻辑非常复杂的业务,我用 Golang 来开发.开发过程不断在问一个问题,Golang 适不适合写业务? 业务说到底,是一大

Java开发区块链的三大sdk库

如果你想将区块链合并到一个Java项目中,现在我们来看看就是这个细分领域中三个最大的OSS玩家. 好的伙计们,我们都听说过比特币,以太坊或其他加密货币,其中有一些时髦的名字围绕着我们常见的新闻,但我们作为Java开发人员知道如何轻松地与这些区块链技术进行交互吗?以下是可用于利用区块链趋势的前三大Java项目.该列表基于GitHub存储库星级排序.非常感谢评论和意见. BitcoinJ 这个名字很具描述性,你不觉得吗?如果你想知道如何创建比特币钱包并管理节点之间的交易,那么你应该尝试使用Bitco

IOS开发笔记-百度地图(第三方库)

最近做了百度地图,在导入SDK后遇到了一些问题 编译错误: linker command failed with exit code 1 (use -v to see invocation) 想了很多办法,查了很多资料,最后终于解决. 可能原因: 1. 有重复的.m文件,或者未导入 解决办法:有重复的删除即可 在工作左边导航栏Target-->Build Phases-->compile Sources中,第三库库的所有.m文件都添加到里面 2.Valid Architectures 的值 在