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\n"));

/******************************************************/
    //這里是我修改的,以便於驗證其有效
    readObject = 12345;

/******************************************************/
    DEBUGMSGTL(("readObject",
                "Initializing readObject scalar integer.  Default value = %ld\n",
                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\n"));
}

 

編譯一下

編譯的時候需要使用到另一個工具 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\n"));

    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\n", 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

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM