用NET-SNMP軟件包開發簡單客戶端代理(上)

用NET-SNMP軟件包開發簡單客戶端代理
寫在前面的話:
對於net-snmp我也是一個初學者,開始學習時也碰到了很多低級的問題。在很多論壇上(事實上比較少^_^, 建議大家直接去sourcefoge社區看關於net-snmp的mail-list),都沒有比較初級入門的文章,本着開源學習的精神,把自己的一點收穫,共享給大家。通過參考一些前輩的文章和幫助文檔,本文實現了一個簡單的mib,並編寫了文檔。本文主要面向初級學習者(我也是個菜鳥),歡迎大家留言討論。
1     SNMP協議簡介. 3
1.1          網絡管理協議結構... 3
1.2          管理信息庫... 4
1.3          SNMP的版本... 4
2     SNMP開發軟件包. 5
2.1          NET-SNMP簡介和安裝... 5
2.2          NET-SNMP代理的配置... 5
2.3          NET-SNMP工具的使用... 6
3     擴展開發——代理. 7
3.1          NET-SNMP中的scalar對象和table對象... 7
3.2          NET-SNMP擴展代理的兩種方式... 7
3.3          自定義MIB. 8
3.4          自定義MIB——簡單變量的實現... 9
3.5          自定義MIB——表對象的實現... 11
3.5.1       mib.iterator.conf模版的實現... 11
3.5.2       mib.iterator_access.conf模版的實現... 16
3.6          代碼的合併... 16
3.7          配置和運行... 16
4     開發中的問題與解決. 17
5     總結. 17
6     附錄. 18
6.1          主函數foxmail_new.c. 18
6.2          簡單變量實現代碼... 20
6.2.1       display_time.c. 20
6.2.2       display_time.h. 22
6.3          表的實現... 22
6.3.1       ExampleTable.c. 22
6.3.2       ExampleTable.h. 31
6.3.3       ExampleTable_access.c. 31
6.3.4       ExampleTable_access.h. 40
6.3.5       ExampleTable_checkfns.c. 40
6.3.6       ExampleTable_checkfns.h. 41
6.3.7       ExampleTable_checkfns_local.c. 42
6.3.8       ExampleTable_checkfns_local.h. 43
6.3.9       ExampleTable_columns.h. 43
6.3.10     ExampleTable_enums.h. 44
6.4 自定義mib文件MyMib.txt 44

用NET-SNMP軟件包開發簡單客戶端代理
1           SNMP協議簡介
作爲一個完備的系統,必須有一套反饋機制來調整系統的運行。簡單網絡管理協議產生的目的,就是爲了使鬆散的網絡更加有效地運行。它廣泛的應用於監測網絡的狀態、網絡設備的運行情況、各種電腦設備以及一些輔助的外圍設備,使得網絡管理員通過對節點的查詢和設置,發現並定位故障,進而採取相應措施維護網絡。網絡管理的研究已經發展了許多年,對於日益紛繁的需求,簡捷性和擴展性仍是研究的主題。本文檔的目的是關於客戶端代理的開發,不是對協議發展的探討。本文中協議相關資料可以參考RFC文檔:      
RFC1155:Structure and Identification of Management Information for TCP/IP-based
Internets         
RFC1157: SNMP                  
RFC1212: Concise MIB Definitions
RFC1215: A Convention for Defining Traps
RFC1905: Protocol Operations for SNMPv2        
RFC2011: SNMPv2 Management Information Base for the Internet Protocol using SMIv2
RFC2578: Structure of Management Information
RFC2579: Textual Conventions
RFC2580: Conformance Statements
1.1         網絡管理協議結構
SNMP的網絡管理模型包括以下關鍵元素:管理端、代理端、管理信息庫、網絡管理協議。它基於tcp/ip協議,屬於應用層協議,通過udp協議通信。管理端與代理端的通信原語包括:Get,Getnext,Set,Trap。對應這些命令相應的SNMP結構框架實現如圖1所示
圖1.    SNMP實現結構圖
從上圖我們可以看到協議,消息傳遞方式等。另外,在udp數據包中,發送信息是按ASN.1自解釋方式編碼的。但對於許多小型被監管設備,可能會運行不同協議,或者運行完整代理花費很大,於是產生了代管設備,主代理和子代理的概念。在小型設備上運行子代理,把數據發給主代理來完成snmp協議的通信。
1.2          管理信息庫
SNMP以MIB(管理信息結構)爲基礎來描述被監管資源,由此建立的數據集和稱之爲MIB
庫。它是一種樹型結構的數據庫,被監管的對象都處於葉子節點上。每個被監管對象都由一個唯一的對象標識符來識別。對象信息的存儲結構由MIB定義的簡單變量和表來構造,它一般包含描述名(對象標識符)、數據類型、讀寫規則、功能描述、狀態。MIB的定義可以查詢RFC1155,它定義了四種基本數據類型:INTEGER,OCTET STRING,OBJECT IDENTIFIER和NULL。由這四種基本類型通過SEQUENCE構造列和表,以及新類型如:NetworkAddress、IpAddress、Counter、Gauge、TimeTicks、Opaque等,以及宏定義。當然,根據需要還可以構造自己的數據類型。
1.3         SNMP的版本
目前SNMP有三個版本snmpV1、snmpV2、snmpV3。針對原始的V1版,93版的v2加入了安全機制,但用戶對其並不感興趣,在96版的v2中又刪除了安全機制,99年開始醞釀的v3版開始提出一個snmp的統一架構,採用User-based安全模型和View-based訪問控制模型提供SNMP網絡管理的安全性。安全機制是SNMPv3的最具特色的內容。
2           SNMP開發軟件包
目前,開發SNMP的軟件包有許多可以選擇如SNMP++、AGENT++、NET-SNMP等。這裏我們選用的是NET-SNMP。首先它是一個開源軟件,其次基於C語言開發,便於移植。ucd-snmp源自於卡耐基.梅隆大學的SNMP軟件包CMU snmp 2.1.2.1, 由加州大學Davis分校(University of California at Davis)開發與維護, 所以命名爲ucd-snmp。2000年11月ucd-snmp項目轉到由SourceForge(www.sourceforge.net)管理, 並更名爲net-snmp。
2.1         NET-SNMP簡介和安裝
net-snmp早先是在Unix平臺下開發的。現可以移植到:
* HP-UX (10.20 to 9.01 and 11.0)
       * Ultrix (4.5 to 4.2)
       * Solaris SPARC/ULTRA (2.8 to 2.3), Intel (2.9) and SunOS (4.1.4 to 4.1.2)
       * OSF (4.0, 3.2)
       * NetBSD (1.5alpha to 1.0)
       * FreeBSD (4.1 to 2.2)
       * BSDi (4.0.1 to 2.1)
       * Linux (kernels 2.4 to 1.3)
       * AIX (4.1.5, 3.2.5)
       * OpenBSD (2.8, 2.6)
       * Irix (6.5 to 5.1)
       * OS X (10.1.1 and 10.1.2)
       * Dynix/PTX 4.4
       * QNX 6.2.1A
* Windows
等多個平臺。Net-snmp是一個代理端軟件,但也提供管理端的查詢工具。安裝有兩種方式:一是直接安裝的二進制包,二是需要編譯的源代碼。我們在windows平臺上安裝的二進制包,在虛擬Unix平臺CygWin上編譯安裝的源代碼。在CygWin中,按照常規的configure, make ,make install三個步驟就可成功編譯安裝源代碼。在windows上的二進制包的安裝就非常簡單了,只需按提示就可完成。源代碼和二進制包可從www.net-snmp.org網站下載,本文中所用的是net-snmp5.2.1.2的版本。之所以要先安裝一個可運行的net-snmp系統,是因爲我們開發程序運行環境的配置文件,是按照默認安裝路徑內部設定搜索的;另外,還可以利用其提供的配置工具來生成配置文件,利用提供的查詢工具來測試程序。
2.2         NET-SNMP代理的配置
運行net-snmp之前先要進行環境設置,否則無法查詢到結果。環境配置文件由snmpconf命令交互生成。運行snmpconf後,提示有三個配置文件:snmpd.conf,snmptraps.conf,snmp.conf。其中,snmpd.conf用來配置代理和管理端通信時的參數,只需設置兩個參數就可正常運行程序了,一是community name,有隻讀rocommunity和讀寫rwcommunity之分,相當於訪問賬號,這裏設rocommunity爲public;另一個是訪問端口,設爲snmp協議默認的161端口。 Snmp.conf是與mib庫設置相關的配置文件。Snmptraps.conf用來設置代理陷阱,本文沒有討論陷阱。配置文件可以放在三個地方,一是盤符根目錄下,二是~\usr\etc\snmp目錄下,三是~\usr\snmp\persist,按標準路徑最好是第二種方式。
另外,snmpconf和mib2c工具都是基於perl腳本的,在windows下需要安裝perl才能運行。按照幫助文檔的提示,下載ActivePerl安裝。並按照幫助文檔中perl的安裝要求,下載在win32環境下所需的其他組件,配置並測試perl模塊,使snmpconf和mib2c能正常運行。
2.3         NET-SNMP工具的使用
當環境設置好後,運行snmpd.exe,即snmp代理進程,就可以使用管理工具查詢其中的信息了。Net-snmp提供的查詢工具有很多,這裏只介紹常用的幾個,而且大部分查詢命令的格式都大同小異。這裏以.iso.org.dod.internet.mgmt.mib-2.system爲例,其Oid爲:.1.3.6.1.2.1.1。結構如下:
   ………system                  .1.3.6.1.2.1.1
            |——sysDescr        .1.3.6.1.2.1.1.1
            |——sysObjectID     .1.3.6.1.2.1.1.2
            ……
1)  snmpget.exe——snmpget [OPTIONS] AGENT OID [OID]...用來查詢葉子節點
實例:snmpget –v2c –c public localhost .1.3.6.1.2.1.1.5.0
-v2c:     使用的是2c的snmp版本,可選1|2c|3
  -c public:community 名爲public
  localhost: 代理的地址,這裏因爲代理運行在本機上,所以可用localhost
  .1.3…….0:這裏查詢的是.iso.org.dod.internet.mgmt.mib-2.system.sysName,其Oid爲.1.3.6.1.2.1.1.5,使用這個命令使葉子節點要在後面加.0。
2) snmpgetnext.exe——snmpgetnext [OPTIONS] AGENT OID [OID]...通過父節點查詢葉子節點
實例:snmpgetnext –v2c –c public localhost .1.3.6.1.2.1.1
這個命令假設不知道葉子節點,但知道父節點,則可遍歷到第一個葉子節點。此例結果等同於上一個例子。Oid也可輸入.1.3.6.1.2,因爲它是按字典順序遍歷的。
3) snmptable.exe——snmptable [OPTIONS] AGENT TABLE-OID 用來查詢表對象
實例:snmptable –v2c –c public localhost .1.3.6.1.2.1.4.20
這個命令查詢表對象,本例中查詢的是.iso.org.dod.internet.mgmt.mib-2.ip.ipAddrTable
4)snmpset.exe——snmpset [OPTIONS] AGENT OID TYPE VALUE [OID TYPE VALUE]...修改數據
   實例:snmpset –v2c –c public localhost .1.3.6.1.2.1.4.21.1.3.x i 99
        x:在這裏是索引值,表示表項中某一列的第幾個數據,根據要求設定   
        i: 這裏是列數據類型,包括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
5) mib2c 用來把mib庫文件編譯成.c和.h模版。具體使用在下面章節的應用中介紹
3           擴展開發——代理
當系統加入了新設備,或設備配置發生了變化等,需要實現新的mib模塊,這時就需要擴展代理端了。在利用net-snmp做擴展之前的準備工作是,一下載源代碼net-snmp5.2.1.2,二編譯出庫文件:netsnmpagent.lib、netsnmphelpers.lib、netsnmpmibs.lib、netsnmp.lib。這四個庫的工程文件都位於~\net-snmp-5.2.1.2\win32\下對應的文件夾中。編譯庫文件時注意把netsnmp.lib放到最後編譯,它可能會參考一下前面的編譯文件。對於其他進一步的應用和開發請參考幫助文檔編譯和使用其他的工具;本文檔中實現的代理程序,在源代碼包中只需要引用這四個庫文件。
開發工具VC6.0的環境設置也需要注意,參考幫助文檔readme.win32,設置好include目錄,library目錄。在project\configure的選項link的相關欄目中添加上四個庫文件,還要加上wsock32.lib庫。另外,在程序編譯過程中會碰到與VC的默認庫相沖突的地方,按提示在上述添加庫的地方加上/NODEFAULTLIB:XXX.LIB來除去默認庫。
代理的開發過程基本上遵循:mib模塊à轉換成C文件à編譯進代理中。
3.1         NET-SNMP中的scalar對象和table對象
mib模塊一般都由變量和表組成。因此Net-snmp把SMI中的對象分爲兩大類:scalar和table。Scalar就包含我們常用的整型,字符串,時間等等數據類型。table就是scalar的一種集合,有一個和多個列組成,類似於數據庫中的表。它必須具有索引項,用來按一定順序檢索表項。
Mib2工具通過模版把mib文件解析成.c和.h文件,這些文件僅僅是半成品,還需要手工在相應地方添加相應代碼。Mib2c有很多模版,可以根據相應需要來調用不同的模版。但mib2c目前不支持同時解析scalar和table 對象,對於具有這兩種對象的mib模塊,需要分別生成代碼文件,然後再合併成整體。
3.2         NET-SNMP擴展代理的兩種方式
用net-snmp擴展代理,實現方式可歸結爲兩種:一是靜態庫方式,通過修改配置頭文件,在相應地方包含新引入的mib模塊的.c和.h文件,然後重新編譯庫文件和代理程序;二是編譯動態共享庫,只需把新引入的mib模塊的.c和.h文件編譯成動態庫,通過設置能夠讓代理程序載入。
對於第二種方式,一需要編譯成.so動態共享庫,二需要原代理程序是否包含dlmod或load命令,三還要看系統是否支持。一般情況下僅支持Unix平臺。我們在CygWin下試驗過Unix平臺編譯。在VC下的編譯環境,基本上都是參考其makefile,configure等文件。
因爲是在windows平臺下開發,本文檔採用的是第一種方式。這種方式允許我們在原有mib庫上添加新的mib模塊,也可以只針對需要的mib模塊編譯單獨的程序。
源代碼包中的代理程序工程文件位於~\ net-snmp-5.2.1.2\win32\snmpd下,因爲它可移植到多個平臺,主程序代碼中有許多平臺開關,和各種選項,非常龐大。爲了簡化開發和調試,我們使用了一個幫助文檔中介紹的簡化的代理端程序框架。這個框架非常精簡,在性能上可能比原版差很多,但用來測試和開發mib庫就比較高效了。程序的運行機制:程序啓動,載入初始化mib模塊,然後進入一個等待呼叫的無限循環,代碼片段如下:
… …
init_MyMib();
… …
while(keep_running)
{
                  /* if you use select(), see snmp_select_info() in snmp_api(3) */
                  /*     --- OR ---  */
                  agent_check_and_process(1); /* 0 == don't block */
        }
我們所要做的就是實現init_MyMib()函數,即自定義的mib模塊。關於無限循環中的阻塞與否,根據mib模塊而定。如果你的應用程序需要以非阻塞方式處理SNMP數據流,就使用一步接口(例如GUI、線程、forking等)。否則,只需要使用同步接口就可以了。在阻塞模式下,程序會佔用大量cpu資源。具體實現,參見附錄5.1。下面從mib庫開始。
3.3         自定義MIB
這裏我們定義了一個私人節點的MIB,位於節點.iso.org.dod.internet. private.enterprises.foxmail下,數字Oid爲:.1.3.6.1.4.1.310。樹型結構如下:
                     foxmail                                    .1.3.6.1.4.1.310
               |----SecondCounter                        .1.3.6.1.4.1.310.1
               |----WeekTime                            .1.3.6.1.4.1.310.2
               +----ExampleTable                        .1.3.6.1.4.1.310.3
                         +----ExampleEntry              .1.3.6.1.4.1.310.3.1
                                  |----UserIndex         .1.3.6.1.4.1.310.3.1.1
                                  |----UserStatus         .1.3.6.1.4.1.310.3.1.2
                                  |----CheckTime        .1.3.6.1.4.1.310.3.1.3
                                  |----MonSet          .1.3.6.1.4.1.310.3.1.4
在這個MIB中,定義了兩個簡單變量SecondCounter:整型,WeekTime:時間類型;還定義了一個表對象ExampleTable,其中UserIndex爲索引:整型,UserStatus:字符串,CheckTime:時間類型,以上三個都是隻讀,MonSet爲讀寫變量:整型。參見附錄5.4
3.4         自定義MIB——簡單變量的實現
把自定義的MIB命名爲MyMib.txt,參照上面章節,放到net-snmp二進制安裝路徑~\usr\share\snmp\mibs下。配置snmp.conf文件,加入新的庫MyMib.txt,語法爲MIBS=+MyMib,這樣才能讓系統搜索到。然後在CMD提示符下輸入:mib2c SecondCounter選擇生成net-snmp版的代碼。就會生成SecondCounter.c和SecondCounter.h文件,同樣可以產生WeekTime.c和WeekTime.h文件。
由模版生成的文件,不論是簡單變量還是表對象,其整體結構都是固定模式。在模版頭文件中對節點進行宏定義,函數聲明。在模版實現文件中,分爲兩大塊:一是初始化函數,主要用來對變量註冊;二是響應函數,用來響應管理端的查詢命令,響應函數的返回值,就是我們要手工實現的。我們所要做的工作就是把數據以一種合理的方式導入到其中。
模版是針對單個變量來處理的:
1)  初始化:
init_SecondCounter(void)
{
    static oid SecondCounter_oid[] = { 1,3,6,1,4,1,310,1 };
   DEBUGMSGTL(("SecondCounter", "Initializing\n"));
   netsnmp_register_scalar(netsnmp_create_handler_registration("SecondCounter", handle_SecondCounter, SecondCounter_oid, OID_LENGTH(SecondCounter_oid),HANDLER_CAN_RWRITE
                               ));
}
如果有多個變量就要註冊多次。
2)  處理響應函數
   handle_SecondCounter(netsnmp_mib_handler *handler,
                          netsnmp_handler_registration *reginfo,
                          netsnmp_agent_request_info   *reqinfo,
                          netsnmp_request_info         *requests)
其實現主要爲:
  switch(reqinfo->mode) {
        case MODE_GET:
            snmp_set_var_typed_value(requests->requestvb, ASN_INTEGER,
                             (u_char *) /* XXX: a pointer to the scalar's data */,
/* XXX: the length of the data in bytes */);
如果有多個變量,就要簡單重複switch語句,因爲switch只提供了通信原語的選擇,變量的選擇依賴各自的處理句柄函數。
顯然這兩套代碼如果合併起來就顯得冗餘了,因爲很多地方可以共用一套代碼。在這裏參考源代碼包~\net-snmp-5.2.1.2\agent\mibgroup\examples下的example.c和example.h文件合併我們的MIB。
主要實現方式是:
1)通過一種變量數組,把MIB中的變量列舉出來,代碼片斷如下:
struct variable2 foxmail_variables[] =
{    
       {FoxmailINT,ASN_INTEGER,RONLY,var_foxmail,1,{1}},
       {FoxmailTIMETICKS, ASN_TIMETICKS, RONLY, var_foxmail, 1, {2}}
     /*… … …加入其他的變量… … … */
};
結構體variable2的定義如下:
struct variable2 {
    u_char          magic;      /* passed to function as a hint */
    u_char          type;       /* type of variable */
    u_short         acl;        /* access control list for variable */
    FindVarMethod  *findVar;    /* function that finds variable */
    u_char          namelen;    /* length of name below */
    oid             name[2];    /* object identifier of variable */
};
通過上面代碼,可以看出,數組對每個變量都一一說明了變量類型、節點位置、讀寫狀態、實現函數等。
2) 然後再對變量數組進行註冊初始化:
REGISTER_MIB("foxmail", foxmail_variables, variable2, foxmail_variables_oid);
3)從數組可知實現函數爲var_foxmail();其原型爲:
u_char * var_foxmail(struct variable *vp,
                           oid * name,
                            size_t * length,
                           int exact, size_t * var_len, WriteMethod ** write_method)
它通過一個switch語句來選擇響應變量,代碼片斷如下:
       switch (vp->magic) {
    case FoxmailINT:      /*這裏爲數組中的索引值*/
        long_ret=XXX;     /*XXX爲返回值,這裏可以用指針來引入內部或外部數據*/
/* *write_method=write_foxmailINT  如果是可以set的變量,需調用寫函數*/
        return (u_char *) & long_ret;
    case FoxmailTIMETICKS:
    … … …
}
總之,這種方法比較緊湊有效,完整代碼參見附錄5.2
3.5         自定義MIB——表對象的實現
爲了更加有效的描述對象,引入表來把一系列相關的信息進行綜合。因此表就由具有一個或多個變量的列組成。從實現方式和數據結構來看,表是一個結構體變量數組;從系統管理來看,就是數據庫中的表單。從管理數據庫的需求出發,就要求實現表單的插入、刪除、恢復等基本功能,更高級的功能還應實現多個表的依賴關係、觸發機制等。在底層實現上這些都要通過操縱結構體數組來完成。
Mib2c提供了表的生成模版。其目的之一是可以降低對SNMP知識的要求,二是分離請求中對數據的定位和響應中對數據的處理,使得實現上集中在對數據的處理上。因此它產生的模版主要由三個部分組成:一是數據結構,對請求數據的構造;二是數據檢索,對請求數據的定位;三是操縱數據,對請求數據進行GET或SET。
不同的需求有不同的實現方式,mib2c根據表中導入數據的來源提供了兩大類模版。
其分類按照運行mib2c時的交互信息:一類爲數據相對代理處於外部,適合於監視和操縱外部數據的MIB,如從操作系統或其他系統的接口提取信息;並且表中的行的創建銷燬,通常都獨立於SNMP代理。第二類是數據爲代理所有,代理對數據的操縱直接反映到數據源。
本文檔只針對第一種類型實現表,且稱之爲外部方式。在mib2c進入外部方式,仍有三個模版,分別爲:mib2c.iterate.conf,mib2c.iterate_access.conf,mib2c.mfd.conf;前兩種方式基本一樣,使用了helpers中一種叫iterator的API,特點是可以處理無序數據,但是效率較低,適合於小型數據存儲,對內存、響應時間要求不是很嚴格的應用;後一種就很複雜了,它提供了更加細緻的代碼框架,通過多層交互,可以區分實時、半實時、永久型的數據,可以選擇數據的存儲方式,可以做列或表之間的依賴關係,數據結構的自動綁定或手工編輯,以及其他複雜的選項。我們只實現前兩種較爲簡單的。
3.5.1       mib.iterator.conf模版的實現
首先,這個模版生成了Example.c和Example.h文件。在Example.c文件中包含下列函數和結構體,形參省略:
struct  ExampleTable_entry;
void  init_ExampleTable;
void  initialize_table_ExampleTable;
Netsnmp_Node_Handler  ExampleTable_handler;
Netsnmp_First_Data_Point   ExampleTable_get_first_data_point;
Netsnmp_Next_Data_Point   ExampleTable_get_next_data_point;
struct ExampleTable_entry  *ExampleTable_createEntry;
void  ExampleTable_removeEntry;
實現過程我們按數據結構,數據檢索,數據操縱來說明函數的調用和處理過程。
1)  結構體:
struct ExampleTable_entry {
    /* Index values */                     /*這個結構體把exampleTable中的*/
    long UserIndex;                      /*變量都列舉,並指明瞭索引變量 */
                                       /*並有一個生成鏈表的next指針  */
    /* Column values */
  /*  long UserIndex;*/                    /*去掉重複                     */
    char UserStatus;
    u_long CheckTime;
    long MonSet;
    long old_MonSet;
    /* Illustrate using a simple linked list */
/*    int   valid;*/                        /*沒有用到的變量             */
    struct ExampleTable_entry *next;
};
通過這個結構體,我們把從外部讀入的數據做成一個鏈表,並把鏈表頭地址傳遞給
ExampleTable_get_first_data_point;
ExampleTable_get_next_data_point;
用這兩個函數遍歷鏈表。
2)  數據的檢索:
數據的檢索由ExampleTable_handler函數來完成,其原型爲:
int  ExampleTable_handler(
    netsnmp_mib_handler               *handler,
    netsnmp_handler_registration      *reginfo,
    netsnmp_agent_request_info        *reqinfo,
        netsnmp_request_info              *requests)
檢索過程通過一個循環包含的兩個語句:
for (request=requests; request; request=request->next) {
   table_entry = (struct ExampleTable_entry *)              /*檢索匹配表中的行*/
              netsnmp_extract_iterator_context(request);
table_info = netsnmp_extract_table_info(request);         /*檢索匹配表中的列*/
這樣相當於通過行號和列號確定了數據。而上面的兩個函數需要調用
ExampleTable_get_first_data_point;
ExampleTable_get_next_data_point;
來遍歷鏈表。
3)  數據的操縱:
數據的操縱涉及到對請求命令的響應,其代碼片斷如下:
switch (reqinfo->mode) {
case MODE_GET:
switch (table_info->colnum) {
       case COLUMN_USERINDEX:
              snmp_set_var_typed_value( request->requestvb, ASN_INTEGER,
                                          table_entry->UserIndex,
                                          sizeof(table_entry->UserIndex));
       … … …             /*對於get命令,通過snmp_set_var_typed_value*/
}                        /*返回變量值                                 */
case MODE_SET_RESERVE1:          /*對於set命令,就涉及這六個步驟,這裏省略了 */
case MODE_SET_RESERVE2:          /*具體實現代碼,我們所做的程序中,沒有涉及表 */
case MODE_SET_FREE:              /*中行的創建和刪除,模版說明中認爲,set 一個 */
case MODE_SET_ACTION:            /*不存在的索引,就可以在表中爲其創建一行     */
case MODE_SET_UNDO:             
case MODE_SET_COMMIT:          
}                                /*代碼已合併到mib.iterator_access.conf模版中*/
關於表的set操作的流程圖,如下圖:
圖2.    SET的執行過程
3.5.2       mib.iterator_access.conf模版的實現
首先,這個模版生成了  ExampleTable.c
ExampleTable.h
ExampleTable_columns.h
ExampleTable_enums.h
ExampleTable_checkfns_local.h
ExampleTable_checkfns_local.c
ExampleTable_checkfns.h
ExampleTable_checkfns.c
ExampleTable_access.h
ExampleTable_access.c
這些文件實際上是對mib.iterator.conf模版功能的細化,提供更加完善方式的控制。只有部分需要修改,根據自定義的mib,只編輯了ExampleTable.c,ExampleTable_access.c等文件。
它提供了get_XXX()和set_XXX()函數來完成數據的獲取和設置,需要手工實現。數據的組織不再自動生成,可以自己以其他方式實現,但仍要能關聯到遍歷函數上。ExampleTable_checkfns_local.c,ExampleTable_checkfns.c文件都是對set的檢查操作。在主體結構和運行過程上仍和mib.iterator.conf模版一樣。參見附錄5.3。
3.6         代碼的合併
在實現整個mib時,只需把簡單變量的初始化函數和表實現的初始化函數合併。在這裏簡單變量的初始化函數爲:void init_foxmail。把表的初始化函數void initialize_table_ExampleTable放到init_foxmail內部 ,再將init_foxmail作爲主函數的初始化函數,修改相關頭文件的引用,代碼的簡單合併就完成了。最後以主函數建立工程編譯全部文件。
3.7         配置和運行
在主函數中,代理的初始化由三個語句順序完成:
init_agent("example-demon");  /*運行代理名*/
init_foxmail();
init_snmp("example-demon");  /*讀入的配置文件名*/
這裏需要寫一個名爲example-demon.conf的配置文件,配置方式同介紹的snmpd.conf配置一樣。然後放在安裝目錄~\usr\etc\snmp下。關閉防火牆,在命令提示符下運行該程序,然後用snmpget,snmpgetnext,snmptable,snmpset測試程序。
4           開發中的問題與解決
在開始拿到這個軟件包時,並不知道從哪裏下手,以前沒接觸過這麼大的代碼包,只是按照幫助中關於win32平臺下的說明往下做,但在編譯源代碼包時,編譯到代理程序時就進行不下去了,出現了很多全局變量的依賴錯誤,當時的判斷是編譯環境的設置問題。於是轉到CygWin下,利用自帶的makefile來編譯整個代碼,非常成功的編譯並安裝到CygWin中。我們試着在其中編譯了幾個例子以及自己寫的幾個程序,也成功的通過了。在例子中,提供了一個makefile文件,於是就觀察其環境設置。在其gcc的編譯中,用到了
CFLAGS=-I. `net-snmp-config --cflags`
BUILDLIBS=`net-snmp-config --libs`
BUILDAGENTLIBS=`net-snmp-config --agent-libs`
查找配置文件,發現這裏面涉及到四個關鍵庫(見前面環境設置章節),在VC中我都沒有加上去。在VC中添加上去後,變量依賴問題大大減少,發現遺留的錯誤是由socket引起的。加上wsock32.lib後,在VC下就可以通過先前的程序了。
但程序運行時有碰到了問題,查不到結果,或者程序崩潰。最後我們通過檢查環境設置,對比文件,發現問題出在netsnmpagent.lib這個庫上。在原有的基礎上重編譯這個庫,新編譯的庫比原來大了幾倍,也就是說庫的編譯存在一個先後順序關係,可能這個庫要引用到其他庫,可能VC沒那麼智能,需要手工設置。解決這個問題後,後面碰到的基本上是編寫程序本身的錯誤了。
5           總結
net-snmp是一個功能很強的軟件,這個開源項目仍在不斷前進中。我們所做的只使用了其中的一小部分功能,主要是學習了它的一個開發流程、簡單的結構框架,以及工具的使用;另外也學習了snmp協議,瞭解了開發包應爲用戶隔離底層細節,提供應用接口給用戶。Net-snmp的幫助文檔並不適合新手入門,它的幫助都是針對功能寫的,一般通過它的開發網站中的tutorials來編譯幾個例子逐漸瞭解其功能,再回頭查看幫助說明。Net-snmp項目目前放在www.sourceforge.net,可以加入net-snmp的郵件列表尋求幫助。
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章