net-snmp配置開發及注意事項

 

一.安裝及配置SNMPwin32環境)

1.下載

www.sourceforge.net下載到最新的net-snmp(目前最新版本5.4.1net-snmp-5.4.1.zip

2.解壓編譯

解壓後,可以看到有一個win32目錄,裏面存放的是和win32環境相關的文件,有3dsw

       libsdll.dsw             編譯lib文件和dll文件的工程

       win32.dsw            編譯lib文件和工具文件如snmpget,snmpset的工程

       win32sdk.dsw              類似於win32.dsw,區別在於:需要安裝Platform SDK。如果需要agent能支持 interfaces等一些高級功能,必須用此工程編譯。XPSP2 Platform SDK的下載地址

http://www.microsoft.com/msdownload/platformsdk/sdkupdate/XPSP2FULLInstall.htm

       只需要安裝Core SDK就可以了,安裝完後需要從開始菜單中Register一下。

       注意編譯的順序,最好先編譯libsdll.dsw,把netsnmp.libnetsnmpagent.libnetsnmphelpers.libnetsnmpmibs.libnetsnmptrapd.lib文件先編譯好,再編譯win32sdk.dsw中的項目。

3.安裝

運行win32目錄下的install-net-snmp.bat批處理文件,會把上一步編譯生成的文件及相關的頭文件等拷貝到c:/usr目錄。

4.配置

       c:/usr/etc/snmp目錄添加配置文件snmpd.conf,添加如下內容:

rocommunity  public

rwcommunity  private

       它表示的含義是,啓動agent服務後,通過public共同體是隻讀的,private共同體可讀也可寫。

       在命令行運行如下命令,將snmp註冊爲windows的服務:

              cmd>”C:/usr/bin/snmpd.exe” –register -Lf "C:/usr/log/snmpd.log"

       註冊成功後可以在【控制面板】->【管理工具】->【服務】中看到剛註冊的服務,服務名是:net-snmp agent

5.運行

       cmd>net start “net-snmp agent”

       如果正常,會得到啓動服務成功的提示

6.驗證

       cmd>snmpget –v2c –c public localhost 1.3.6.1.2.1.1.5.0

       cmd> snmpset -v2c -c private localhost sysContact.0 = piyeyong

       如果正常,會的到取得和設置成功的提示,出錯會給出錯誤提示。

二.MIB文件編寫

       MIB文件會存放於C:/usr/share/snmp/mibs/目錄下,是*.txt,純文本文件,可以直接打開查看和更改。RFC1213中定義的MIB節點信息的定義存放與RFC1213-MIB.txt,這些節點是比較重要的,會經常用到。

       如果要擴展MIB,應該定義在1.3.6.1.4.1(.iso.org.dod.internet.private.enterprises)子樹下。自定義MIB的節點,只需要描述該節點的SYNTAXACCESSSTATUSDESCRIPTION等屬性及它屬於父節點的第幾個子節點即可。如下所示,爲擴展MIB的一個簡單例子:

PROBA-MIB DEFINITIONS::=BEGIN

       IMPORTS     

              enterprises,OBJECT-TYPE,Integer32,TimeTicks

                    FROM SNMPv2-SMI

       TEXTUAL-CONVENTION,  DisplayString FROM SNMPv2-TC;

 

-- proba node

       proba OBJECT IDENTIFIER::={enterprises 8888}

 

baseinfo     OBJECT IDENTIFIER ::= { proba 1 }

 

-- company name

       probaCompName OBJECT-TYPE

              SYNTAX DisplayString (SIZE (0..255))

              ACCESS read-only

              STATUS mandatory

              DESCRIPTION "The Name of company"

              ::={baseinfo 1}

 

-- company location

       probaLocation OBJECT-TYPE

              SYNTAX DisplayString (SIZE (0..255))

              ACCESS read-write

              STATUS mandatory

              DESCRIPTION "The Location of company"

              ::={baseinfo 2}

             

-- employee number

       probaEmployeeNumber OBJECT-TYPE

              SYNTAX INTEGER

              ACCESS read-only

              STATUS mandatory

              DESCRIPTION "The number of employee"

              ::={baseinfo 3}

END

三.Agent端開發

       在上一步中定義好MIB的結構後,現在就開始編碼實現定義好的節點。net-snmp提供了一個MIB2C工具,利用它可以根據MIB的定義和配置文件自動生成*.c*.h模板文件,然後只需要在相應位置添加對節點數據的處理就可以了。

1.配置net-snmpperl模塊

       用使用mib2c工具,需要perl模塊的支持,可以從http://www.ActiveState.com/ActivePerl下載,目前最新版是5.8.8

       net-snmp源文件的perl目錄下,運行以下命令:

       cmd>perl makefile.pl

如果成功,會生成makefile文件

cmd>nmake

cmd>nmake install

       這時,會將net-snmp相關的perl模塊編譯好並安裝到c:/perl/site/lib目錄下。

       注:有時候運行nmake會失敗,把其它機器上安裝好的c:/perl/site/lib目錄下的文件拷貝過來,也可以運行。

2.mib2c生成模板源代碼

       運行以下命令:

       cmd>mib2c -c mib2c.scalar.conf baseinfo

       會按照模板配置文件mib2c.scalar.conf生成baseinfo.hbaseinfo.c文件。注意:baseinfo是上一步在MIB中定義的proba下的一個節點。在baseinfo.c中有很多/* XXX 註釋*/的地方,這些地方是需要我們修改,填上我們對節點數據的處理代碼。

3.read-only節點的代碼修改

       probaCompName節點爲例:

int

handle_probaCompName(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. */

   

    switch(reqinfo->mode) {

 

        case MODE_GET:

            snmp_set_var_typed_value(requests->requestvb, ASN_OCTET_STR,

                                     (u_char *)"proba" /* XXX: a pointer to the scalar's data */,

                                     strlen("proba")/* XXX: the length of the data in bytes */);

            break;

 

 

        default:

            /* we should never get here, so this is a really bad error */

            snmp_log(LOG_ERR, "unknown mode (%d) in handle_probaCompName/n", reqinfo->mode );

            return SNMP_ERR_GENERR;

    }

 

    return SNMP_ERR_NOERROR;

}

       從上面的代碼看出,只需在兩處/* XXX 註釋 */的代碼處填上這個節點的數據即可,管理站在執行get命令時這個值會返回給管理站。

4.read-write節點的代碼修改

       probaLocation節點爲例:

 

static char location[256];

void

init_baseinfo(void)

{

       memset(location, '/0', sizeof location);

       memcpy(location, "beijing", sizeof "beijing");

       。。。。。。

}

int

handle_probaLocation(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,

                                     (u_char *)location /* XXX: a pointer to the scalar's data */,

                                     strlen(location)/* 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 */

            if (0/* XXX: error? */) {

                netsnmp_set_request_error(reqinfo, requests, SNMP_ERR_COMMITFAILED/* some error */);

            }

            break;

 

        case MODE_SET_COMMIT:

            /* XXX: delete temporary storage */

                     memcpy(location, requests->requestvb->buf, requests->requestvb->val_len);

                     location[requests->requestvb->val_len] = '/0';

            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_probaLocation/n", reqinfo->mode );

            return SNMP_ERR_GENERR;

    }

 

    return SNMP_ERR_NOERROR;

}

對於read-write節點的處理要複雜一點,對每一次管理站的set請求,代理站的處理會經過如下圖所示的步驟:

       從圖中可以看出,通過這種機制,在處理出錯的時候,可以根據需要實現回滾操作。

5.重新編譯

       按照一下步驟重新編譯工程:

       1)把baseinfo.hbaseinfo.c文件拷貝到net-snmp源文件下agent/mibgroup目錄下;

2)打開win32sdk,將其添加到netsnmpmibssdk工程;

3)打開net-snmp源文件下win32目錄下的mib_module_includes.h,添加:

#include "mibgroup/proba/baseinfo.h"

       4)打開net-snmp源文件下win32目錄下的mib_module_inits.h,添加:

                if (should_init("baseinfo")) init_baseinfo();

       5)重新編譯netsnmpmibssdk工程和snmpdsdk工程,把生成的snmpd.exe拷貝到c:/usr/binnetsnmpmibs.lib拷貝到c:/usr/lib

四.管理站開發

以獲取sysName節點爲例:

   struct snmp_session session, *ss;

   struct snmp_pdu *pdu;

   struct snmp_pdu *response;

 

   oid anOID[MAX_OID_LEN];

   size_t anOID_len = MAX_OID_LEN;

  

   struct variable_list *vars;

   int status;

 

   /*

    * Initialize the SNMP library

    */

   init_snmp("snmpapp");

 

    /*

    * Initialize a "session" that defines who we're going to talk to

    */

   snmp_sess_init( &session );                   /* set up defaults */

   session.peername = "localhost";

  

   /* set up the authentication parameters for talking to the server */

  

   #ifdef DEMO_USE_SNMP_VERSION_3

  

   /* Use SNMPv3 to talk to the experimental server */

  

   /* set the SNMP version number */

   session.version=SNMP_VERSION_3;

       

   /* set the SNMPv3 user name */

   session.securityName = strdup("MD5User");

   session.securityNameLen = strlen(session.securityName);

  

   /* set the security level to authenticated, but not encrypted */

   session.securityLevel = SNMP_SEC_LEVEL_AUTHNOPRIV;

  

   /* set the authentication method to MD5 */

   session.securityAuthProto = usmHMACMD5AuthProtocol;

   session.securityAuthProtoLen = sizeof(usmHMACMD5AuthProtocol)/sizeof(oid);

   session.securityAuthKeyLen = USM_AUTH_KU_LEN;

   

   /* set the authentication key to a MD5 hashed version of our

      passphrase "The Net-SNMP Demo Password" (which must be at least 8

      characters long) */

   if (generate_Ku(session.securityAuthProto,

                   session.securityAuthProtoLen,

                   (u_char *) our_v3_passphrase, strlen(our_v3_passphrase),

                   session.securityAuthKey,

                   &session.securityAuthKeyLen) != SNMPERR_SUCCESS) {

       snmp_perror(argv[0]);

       snmp_log(LOG_ERR,

                "Error generating Ku from authentication pass phrase. /n");

       exit(1);

   }

  

   #else /* we'll use the insecure (but simplier) SNMPv1 */

  

   /* set the SNMP version number */

   session.version = SNMP_VERSION_1;

  

   /* set the SNMPv1 community name used for authentication */

   session.community = (u_char*)"public";

   session.community[6] = '/0';

   session.community_len = 6;

  

   #endif /* SNMPv1 */

 

   /* windows32 specific initialization (is a noop on unix) */

   SOCK_STARTUP;

  

   /*

    * Open the session

    */

   ss = snmp_open(&session);                     /* establish the session */

 

   if (!ss) {

       snmp_perror("ack");

       snmp_log(LOG_ERR, "something horrible happened!!!/n");

       exit(2);

   }

 

   /*

    * Create the PDU for the data for our request.

    *   1) We're going to GET the system.sysDescr.0 node.

    */

   pdu = snmp_pdu_create(SNMP_MSG_GET);

 

   read_objid(".1.3.6.1.2.1.1.5.0", anOID, &anOID_len);

   //get_node("sysDescr.0", anOID, &anOID_len);

 

   snmp_add_null_var(pdu, anOID, anOID_len);

 

   /*

    * Send the Request out.

    */

   status = snmp_synch_response(ss, pdu, &response);

 

   /*

    * Process the response.

    */

   if (status == STAT_SUCCESS && response->errstat == SNMP_ERR_NOERROR) {

     /*

      * SUCCESS: Print the result variables

      */

     for(vars = response->variables; vars; vars = vars->next_variable)

       print_variable(vars->name, vars->name_length, vars);

     /* manipuate the information ourselves */

     for(vars = response->variables; vars; vars = vars->next_variable) {

       int count=1;

       if (vars->type == ASN_OCTET_STR) {

         char *sp = (char *)malloc(1 + vars->val_len);

         memcpy(sp, vars->val.string, vars->val_len);

         sp[vars->val_len] = '/0';

         printf("value #%d is a string: %s/n", count++, sp);

         free(sp);

       }

       else

         printf("value #%d is NOT a string! Ack!/n", count++);

     }

   } else {

     /*

      * FAILURE: print what went wrong!

      */

   

     if (status == STAT_SUCCESS)

       fprintf(stderr, "Error in packet/nReason: %s/n",

               snmp_errstring(response->errstat));

     else

       snmp_sess_perror("snmpget", ss);

   

   }

 

   /*

    * Clean up:

    *  1) free the response.

    *  2) close the session.

    */

   if (response)

     snmp_free_pdu(response);

   snmp_close(ss);

   

   /* windows32 specific cleanup (is a noop on unix) */

   SOCK_CLEANUP;

  

注意:需要引用以下頭文件:

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

#include <net-snmp/utilities.h>

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

鏈接netsnmp.lib:

#pragma comment(lib, "netsnmp")

    使用的編譯環境爲VC++2005.net

需要把netsnmp.lib所在的目錄添加到【附加庫目錄】中,Release版的【代碼生成】->【運行時庫】選擇【多線程 DLL (/MD)】,Debug版選擇【多線程調試 DLL (/MDd)】;

Stdafx.h中添加:

#include <windows.h>

    MFC的使用】選擇【使用標準 Windows 庫】

在編譯的過程中發現,不僅要把net-snmp原文件夾下include目錄添加到【附加包含目錄】,還需要把win32目錄也添加進去,因爲win32/net-snmp/library下有一個snmpv3-security-includes.h文件。

五.其它注意事項

1.       如果是採用下載可執行文件安裝net-snmp時,Net-SNMP Agent Service項選擇Standard agent,否則無法從管理器上讀取到AgentSystem等節點下的信息。

使用SNMP++提供的一個C#組件。控制面板添加安裝組件中添加SNMP組件後,system32目錄下會增加許多*.mib文件,如dhcp.mib,這些文件是mib庫,存放的是OID與名字和描述的對應關係,相當於DNS。有了這些文件,在SNMP++.net開發包中,調用MIB類的loadDirectoryMib方法,就可以載入這些對應關係了。
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章