天天看點

snmp 擷取裝置類型_SNMP開發系列(三)SNMP Agent的實作1 (三)SNMP Agent的實作

SNMP在IT營運、網絡裝置管理、通信網元管理、物聯網上應用廣泛。以下章節将分析Linux/pSos等嵌入式環境下SNMP Agent流程特點及使用嵌入式設計SNMP Agent的技術細節(其中涉及到資訊模型設計、C/C++語言等内容請各位看官提前學習哈)。本章也可以作為嵌入式下SNMP Agent的軟體開發開發指導書。

(一)SNMP協定體系及技術要點

(二)SNMP Agent的設計

(三)SNMP Agent的實作

snmp 擷取裝置類型_SNMP開發系列(三)SNMP Agent的實作1 (三)SNMP Agent的實作

1 (三)SNMP Agent的實作

1.1 MIB的建立

建立MIB主要需要完成本裝置的MIB庫的編寫以及利用mibcompiler工具來建立MIB庫控制樹代碼。;

利用 mibcompiler 生成控制檔案架構,需要生成的檔案有:

以sample.mib為例

sample.ctl //控制架構檔案 mibcomp –partial –o sample.ctl sample.mibmib.c //MIB樹 mibcomp –mib.c –o mib.c sample.mib sample.ctlleaf.h //葉子節點OID定義 mibcomp –leaf –o leaf.h sample.mib sample.ctlsample.c //通路函數架構 mibcomp –skel –o sample.c sample.mib sample.ctl mibcomp –stub –o sample.c sample.mib sample.ctlsample.h //通路函數頭檔案 mibcomp –skel.h –o sample.h sample.mib sample.ctl
           

現在分别描述各部分的開發特點;

1.1.1 設計MIB庫

MIB庫是SNMP Agent 的基礎,可以以從網上擷取一個公共的target.mib為基礎設計自己的MIB庫。

例:sample.mib

-- sample.mib SAMPLE-MIB DEFINITIONS ::= BEGIN --從已有的MIB中引入一些需要的現成資料IMPORTS OBJECT-TYPE, MODULE-IDENTITY, enterprises FROM SNMPv2-SMI DisplayString FROM SNMPv2-TC;-- asbTndTeam-- FROM ASB-MIB; asbRoot MODULE-IDENTITY LAST-UPDATED "9406072245Z" --"20020322" ORGANIZATION  "ASB, Inc." CONTACT-INFO  "ASB in ChenDu. Created by Yangyutong  email: [email protected] phone: "DESCRIPTION  "This MIB is the root MIB for ASB, Inc. enterprise."::= { enterprises 5555 } –-定位ASB在MIB中的節點位置  asbTndTeam OBJECT IDENTIFIER ::= { asbRoot 1 } asbExtend OBJECT IDENTIFIER ::= { asbRoot 2 }  -------------------------- dwdmProduction OBJECT IDENTIFIER ::= { asbTndTeam 1 }-- ochPort OBJECT IDENTIFIER ::= { dwdmProduction 1 }-- omsPort OBJECT IDENTIFIER ::= { dwdmProduction 2 }----ochPort葉子節點定義(簡單變量)iID OBJECT-TYPESYNTAX DisplayStringMAX-ACCESS read-onlySTATUS currentDESCRIPTION "ochPort iId."::= { ochPort 1 } iOperStatus OBJECT-TYPESYNTAX INTEGER {no(0),yes(1)}MAX-ACCESS read-writeSTATUS currentDESCRIPTION " iOperStatus."::= { ochPort 2 } ---- omsPort configurable parameters--iNeId OBJECT-TYPESYNTAX INTEGERMAX-ACCESS read-writeSTATUS currentDESCRIPTION " iNeId."::= { omsPort 1 } --定義表omsCfgTable OBJECT-TYPESYNTAX SEQUENCE OF OmsCfgEntryMAX-ACCESS not-accessibleSTATUS currentDESCRIPTION "Table."::= { omsPort 2 } --表類型申明omsCfgEntry OBJECT-TYPESYNTAX OmsCfgEntryMAX-ACCESS not-accessibleSTATUS currentDESCRIPTION "An interface entry."INDEX { omsCfgIndex }::= { omsCfgTable 1 } --新的資料類型申明PhyAddr ::=INTEGER{ one(1), two(2), three(3)}  OmsCfgEntry ::= SEQUENCE { omsCfgIndexINTEGER, omsPortTypeINTEGER, mPhyAddrPhyAddr }  omsCfgIndex OBJECT-TYPESYNTAX INTEGERMAX-ACCESS read-onlySTATUS currentDESCRIPTION "Index of this entry."::= { omsCfgEntry 1 } omsPortType OBJECT-TYPESYNTAX INTEGERMAX-ACCESS read-onlySTATUS currentDESCRIPTION "Type of this entry."::= { omsCfgEntry 2 } mPhyAddr OBJECT-TYPESYNTAX PhyAddrMAX-ACCESS read-onlySTATUS currentDESCRIPTION "IpAddress of this interface."::= { omsCfgEntry 3 } END
           

1.1.2 由MIB檔案生成MIB控制檔案

MIB控制檔案是系統用于來關聯變量處理接口的一個中間檔案,它的主要工作是為每種變量組申明處理函數;

方法是:利用 mibcompiler 生成控制檔案架構,然後手工添加處理函數申明。

例:

mibcomp –partial –o sample.ctl sample.mib

-- This file was automatically generated by Epilogue Technology's-- Emissary SNMP MIB Compiler, version 9.0.-- This file was generated using the -partial switch.-- -- YOU MAY MODIFY THIS FILE BUT BEWARE ACCIDENTALLY OVERWRITING IT-- BY REGENERATING IT WITH THE MIB COMPILER.-- -- Last build date: Fri Mar 22 13:20:53 2002-- from file:-- sample.mib  sample-ctl-A -- put your FORCE-INCLUDE's here  FORCE-INCLUDE  FORCE-INCLUDE  FORCE-INCLUDE  FORCE-INCLUDE  FORCE-INCLUDE  FORCE-INCLUDE  FORCE-INCLUDE  FORCE-INCLUDE "sm_types.h" FORCE-INCLUDE "snmpvars.h" FORCE-INCLUDE "samplmib.h" ---- put your global DEFAULT's here--  DEFAULT set-function-async null_set_async DEFAULT get-function-async get_%n_async DEFAULT next-function-async std_next_async DEFAULT test-function-async it_exists_async DEFAULT cookie (char *)0  -- put your EXCLUDE's here-- DEFINITIONS ::= BEGIN IMPORTS OBJECT-TYPE FROM RFC-1212; -- Below is an OBJECT-TYPE entry for every object in the input MIBs.-- You may position DEFAULT statements at any point in the MIB tree-- by putting them inside the appropriate OBJECT-TYPE declaration.-- You need not modify any to which you don't want to add DEFAULTs.-- (Or, you may remove any OBJECT-TYPE declaration to which you-- don't add DEFAULT statements, as long as you don't add DEFAULT-- statements to any of its descendants that reference its-- descriptor, directly or indirectly.) zeroDotZero OBJECT-TYPE ::= { ccitt 0 } org OBJECT-TYPE ::= { iso 3 } dod OBJECT-TYPE ::= { org 6 } internet OBJECT-TYPE ::= { dod 1 } directory OBJECT-TYPE ::= { internet 1 } mgmt OBJECT-TYPE ::= { internet 2 } mib-2 OBJECT-TYPE ::= { mgmt 1 } transmission OBJECT-TYPE ::= { mib-2 10 } experimental OBJECT-TYPE ::= { internet 3 } private OBJECT-TYPE ::= { internet 4 } enterprises OBJECT-TYPE ::= { private 1 } asbRoot OBJECT-TYPE ::= { enterprises 5555 } asbTndTeam OBJECT-TYPE ::= { asbRoot 1 } dwdmProduction OBJECT-TYPE ::= { asbTndTeam 1 } ochPort OBJECT-TYPE-- DEFAULT set-function-async ochPort_set_async DEFAULT get-function-async ochPort_get_async DEFAULT test-function-async ochPort_test_async ::= { dwdmProduction 1 } iID OBJECT-TYPE ::= { ochPort 1 } iOperStatus OBJECT-TYPE ::= { ochPort 2 } omsPort OBJECT-TYPE-- DEFAULT set-function-async omsPort_set_async DEFAULT get-function-async omsPort_get_async DEFAULT test-function-async omsPort_test_async ::= { dwdmProduction 2 } iNeId OBJECT-TYPE ::= { omsPort 1 } omsCfgTable OBJECT-TYPE-- DEFAULT set-function-async null_set_async DEFAULT get-function-async omsCfgTbl_get_async DEFAULT test-function-async omsCfgTbl_test_async ::= { omsPort 2 } omsCfgEntry OBJECT-TYPE ::= { omsCfgTable 1 } omsCfgIndex OBJECT-TYPE ::= { omsCfgEntry 1 } omsPortType OBJECT-TYPE ::= { omsCfgEntry 2 } mPhyAddr OBJECT-TYPE ::= { omsCfgEntry 3 } asbExtend OBJECT-TYPE ::= { asbRoot 2 } security OBJECT-TYPE ::= { internet 5 } snmpV2 OBJECT-TYPE ::= { internet 6 } snmpDomains OBJECT-TYPE ::= { snmpV2 1 } snmpUDPDomain OBJECT-TYPE ::= { snmpDomains 1 } snmpCLNSDomain OBJECT-TYPE ::= { snmpDomains 2 } snmpCONSDomain OBJECT-TYPE ::= { snmpDomains 3 } snmpDDPDomain OBJECT-TYPE ::= { snmpDomains 4 } snmpIPXDomain OBJECT-TYPE ::= { snmpDomains 5 } snmpProxys OBJECT-TYPE ::= { snmpV2 2 } rfc1157Proxy OBJECT-TYPE ::= { snmpProxys 1 } rfc1157Domain OBJECT-TYPE ::= { rfc1157Proxy 1 } snmpModules OBJECT-TYPE ::= { snmpV2 3 } end
           

1.1.3 利用MIB生成MIB樹及函數接口

利用上一步生成的控制檔案可以生成MIB樹檔案和變量接口架構。

方法是:

MIB樹: mib.c mibcomp –mib.c –o mib.c sample.mib sample.ctl葉子節點OID定義:leaf.h mibcomp –leaf –o leaf.h sample.mib sample.ctl通路函數架構: sample.c mibcomp –skel –o sample.c sample.mib sample.ctl mibcomp –stub –o sample.c sample.mib sample.ctl通路函數頭檔案: sample.h mibcomp –skel.h –o sample.h sample.mib sample.ctl
           

這幾個檔案将作為系統 SNMP Agent代碼的核心;以下的工作将是按我們具體的需求修改這些檔案。

1.1.4 為變量定義資料結構

對于簡單變量隻需在xxx_get_async()中将該值擷取并填入VB_T參數中即可;

對于表形變量必須定義一個資料結構,包括該表的索引和成員變量,以友善周遊MIB樹和擷取、設定表變量。

規則:

對表變量 xxx;

strcut xxxtablereq{type0 xxxindex; //索引union{ type1 element1; //表中的第一個成員 type2 element2; //表中的第二個成員 type3 element3; //表中的第三個成員 。。。}xxx_union;};#define element1 xxx_union.element1#define element2 xxx_union.element2#define element3 xxx_union.element3。。。 
           

這樣一個結構對應着一個VarBind,它的index是為了索引各個執行個體,union的成員就是需要操作的表變量值;

這兒還可以想象實際操作的兩種情況:

首先我們通過一個系統庫函數group_by_getproc_and_instance(),将Manager操作請求中的要操作表節點和變量歸類綁定(VarBind)的第一個VB_T找到。

· Manager要擷取一個葉子的執行個體;Agent通過對象辨別定位到該節點,再通過MIBLOC_T得到葉子的資訊,再根據該資訊和表的index定位具體的執行個體值;之後可以通過VB_T中的vb_link指針周遊目前表該變量的所有執行個體。

· Manager周遊的是一個表節點,Agent通過對象辨別定位到該節點的第一個變量(葉子),然後Agent可以通過index定位該表的目前執行個體,并通過VB_T中的vb_link指針周遊目前表該變量的所有執行個體。

注意:因為表形變量可能會有多個執行個體,是以必須為這些表配置設定空間,一般是在Agent初始化的時候進行;

例:

int xxx_init(){if(NULL == (p_gxxxtable = (struct xxxtablereq*)malloc(MAX_INST *  sizeof(struct xxxtablereq)) ) )return –1; memset(p_gxxxtable, 0, (MAX_INST * sizeof(struct xxxtablereq)) ); return 0;}
           

1.1.5 編寫變量操作接口函數

變量操作接口函數夫人架構是利用mibcompiler生成的;按類型可以分為test、set、get、next幾類。

Agent程式的開發實際主要集中在這些接口函數的編寫上。

1.1.5.1 test 函數的編寫

test函數在通路變量前出現,它檢查被通路變量的執行個體是否存在。

它的函數聲明類似于:

void xxx_test_async( OIDC_T lastmatch, int compc, OIDC_T* compl, SNMP_PKT_T* pktp, VB_T* vbp){}
           

1.OID_T lastmatch

MIB搜尋樹中消耗的object identifier 的最後一個單元。

例如:一個變量辨別為:1.2.3.4.5.6.7,它的被管理對象是1.2.3.4.5,那麼lastmatch為5,而6.7為執行個體辨別。

該參數在表形變量的進行中特别有用;考慮有一個表對象的辨別為:1.2.3.4;對它的成員變量,假設有三個變量它們被辨別為:1.2.3.4.1、1.2.3.4.2、1.2.3.4.3;lasrmatch分别是1、2、3;這樣就能很容易得區分和操作它們了。

2.int compc、OID_T* compl

描述變量辨別在MIB樹中未消耗的部分。compl為指向剩餘部分的指針,compc為該部分的個數;在大多數時候它們指向的是執行個體,注意前面講的“lastmatch”,結合起來就将對象和執行個體分别表現出來了。

仍舉上例:compl應指向6.7,compc值為2。

3.SNMP_PKT_T* pktp,

接收到的包結構指針。

4.VB_T* vbp

目前要操作的VarBind

傳回值:

snmp 擷取裝置類型_SNMP開發系列(三)SNMP Agent的實作1 (三)SNMP Agent的實作

注意:

1. 表形變量,一張表中的變量使用同一test函數;

2. 簡單變量,一般使用庫函數it_exists_async();

test函數樣例:

ip_test_async( OIDC_T lastmatch, int compc, OIDC_T *compl, SNMP_PKT_T *pktp, VB_T *vbp){ VB_T *group_vbp;  // 對簡單變量檢查它得結尾是不是.0 if (!((compc == 1) && (*compl == 0))) { testproc_error(pktp, vbp, NO_SUCH_NAME); return; }  // 将要操作得VarBind的頭指針找到 group_by_getproc_and_instance(pktp, vbp, compc, compl);  // 檢查每一個變量 for (group_vbp = vbp; group_vbp; group_vbp = group_vbp->vb_link)  {switch (group_vbp->vb_ml.ml_last_match) { case LEAF_ipForwarding: switch (VB_GET_INT32(group_vbp)) { case VAL_ipForwarding_forwarding: case VAL_ipForwarding_not_forwarding: break; default: // 該表變量不是規定的值則出錯 testproc_error(pktp, group_vbp, WRONG_VALUE); continue; } testproc_good(pktp, group_vbp); break; case LEAF_ipDefaultTTL: testproc_good(pktp, group_vbp); break; default: // 該表變量不是規定的值則出錯 testproc_error(pktp, group_vbp, GEN_ERR); return; } }}
           

1.1.5.2 next 函數的編寫

next函數用于處理get-next封包,獲得下一個執行個體的變量值,在get函數前調用。

它的函數聲明一般為:

void xxx_next_async( OIDC_T lastmatch, int compc, OIDC_T *compl, SNMP_PKT_T *pktp, VB_T *vbp){}
           

參數的說明同test函數。

Next的工作原理我們用一個例子說明:

假設要周遊路由表中的子網路遮罩,索引是ipAdEntAddr;

snmp 擷取裝置類型_SNMP開發系列(三)SNMP Agent的實作1 (三)SNMP Agent的實作

有三個執行個體,Manager需要進行三次next操作才能完成周遊;

snmp 擷取裝置類型_SNMP開發系列(三)SNMP Agent的實作1 (三)SNMP Agent的實作

注意:

1. 表形變量,一張表中的變量使用同一next函數;

2. 簡單變量,一般使用庫函數std_next_async();

Next函數示例:

Void ifEntry_next_async( OIDC_T lastmatch, int compc, OIDC_T *compl, SNMP_PKT_T *pktp, VB_T *vbp){#define ifEntry_INSTANCE_LEN 1 struct mib_ifreq *data, *best; OIDC_T tmp_inst[ifEntry_INSTANCE_LEN]; OIDC_T best_inst[ifEntry_INSTANCE_LEN]; int i; int error, no; unsigned inst_len; unsigned best_inst_len;  // 将要操作得VarBind的頭指針找到 group_by_getproc_and_instance(pktp, vbp, compc, compl);  // 将表中的所有執行個體中比compc/compl 大的最小的找出來 best = 0; /* This loop needs to iterate over each entry in your table */ for (data = iftab, no = ifnumber; no; data++,no--) { inst_len = 1; tmp_inst[0] = data->ie_iindex;  if ((oidcmp2(inst_len, tmp_inst, compc, compl) > 0) && ((!best || (oidcmp2(inst_len, tmp_inst, inst_len, best_inst) < 0)))) { best = data; for (i = 0; i < inst_len; i++) best_inst[i] = tmp_inst[i]; best_inst_len = inst_len; } } if (best) {// 找到next one ,現在需要得到它的值同時綁定該執行個體的VarBind for ( ; vbp ; vbp = vbp->vb_link) { if ((error = ifEntry_get_value(vbp->vb_ml.ml_last_match, pktp,  vbp, best)) == NO_ERROR) nextproc_next_instance(pktp, vbp, best_inst_len, best_inst); else nextproc_error(pktp, vbp, error); } } else //沒找到next one for ( ; vbp ; vbp = vbp->vb_link ) nextproc_no_next(pktp, vbp);}
           

1.1.5.3 get函數的編寫

處理get封包,取得變量值。

Get函數可以分為簡單變量和表形變量兩種不同情況。

它們一般的聲明形式如下:

1. 簡單變量:

void xxx_get_async( OIDC_T lastmatch, int compc, OIDC_T *compl, SNMP_PKT_T *pktp, VB_T *vbp){} 
           

參數說明同test函數。

示例說明:

void interfaces_get_async(OIDC_T lastmatch, int compc, OIDC_T *compl, SNMP_PKT_T *pktp, VB_T *vbp){ int error;  // 将要操作得VarBind的頭指針找到 group_by_getproc_and_instance(pktp, vbp, compc, compl);  // 檢查是單一葉子還是節點,是葉子則檢查結尾是否.0 if (!((compc == 1) && (*compl == 0))) for ( ; vbp; vbp = vbp->vb_link) getproc_nosuchins(pktp, vbp); else { //非葉子則周遊之 for ( ; vbp; vbp = vbp->vb_link) { if ((error = interfaces_get_value(vbp->vb_ml.ml_last_match, pktp,  vbp)) != NO_ERROR) getproc_error(pktp, vbp, error); } }}//-----------------------------------//獲得目前last_match的值static int interfaces_get_value(OIDC_T lastmatch, SNMP_PKT_T *pktp, VB_T *vbp){ switch(lastmatch) { case LEAF_ifNumber: // 獲得該變量值 getproc_got_int32(pktp, vbp, (INT_32_T)ifnumber); break; default: return GEN_ERR; } return NO_ERROR;}
           

2. 表形變量:

void xxxEntry_get_async( OIDC_T lastmatch, Int compc, OIDC_T *compl, SNMP_PKT_T *pktp,VB_T *vbp){}
           

參數說明見test函數;

示例說明:

void ifEntry_get_async(OIDC_T lastmatch, int compc, OIDC_T *compl, SNMP_PKT_T *pktp, VB_T *vbp){ struct mib_ifreq *data; int error;  // 将要操作得VarBind的頭指針找到 group_by_getproc_and_instance(pktp, vbp, compc, compl);  // 按照(compc and compl) 找到該節點在表中的位置 if (ifEntry_lookup(compc, compl, &data) != 0) for ( ; vbp; vbp = vbp->vb_link) getproc_nosuchins(pktp, vbp); else { //周遊所有變量 for ( ; vbp; vbp = vbp->vb_link) { if ((error = ifEntry_get_value(vbp->vb_ml.ml_last_match, pktp,  vbp, data)) != NO_ERROR) getproc_error(pktp, vbp, error); } }}
           

1.1.5.4 set函數的編寫

處理set封包。

它的一般函數聲明形式為:

void xxx_set_async( OIDC_T lastmatch, int compc, OIDC_T *compl, SNMP_PKT_T *pktp,VB_T *vbp){}
           

參數說明見test函數。

示例說明:

voidifEntry_set_async(OIDC_T lastmatch, int compc, OIDC_T *compl, SNMP_PKT_T *pktp, VB_T *vbp){ struct mib_ifreq ifr; long rc; struct mib_ifreq *data = vbp->vb_priv;  for ( ; vbp; vbp = vbp->vb_link) { switch (vbp->vb_ml.ml_last_match) { case LEAF_ifAdminStatus: // 具體管理變量 memset(&ifr, 0, sizeof(ifr)); ifr.ie_iindex = data->ie_iindex;ifr.ie_adminstatus = VB_GET_INT32(vbp);// 設定具體值 if (rc = ioctl(mib_sock, SIOCSIFADMINSTATUS, (char *)&ifr)) {  perror("ioctl error"); setproc_error(pktp, vbp, COMMIT_FAILED); return; } setproc_good(pktp, vbp); break; default: setproc_error(pktp, vbp, COMMIT_FAILED); return; } }} 
           

接口函數的說明可以看出, SNMP Agent開發實際是比較簡單的;這兒細心的讀者可能已經看到在test、get、set、next流程之間有一個隐患;即,在test于get、set以及next、get之間必須保證執行個體不發生改變,實際上需要控制系統核心在這期間關閉任務搶占位。

1.2 測試建議

建議的調試配置見下圖:

snmp 擷取裝置類型_SNMP開發系列(三)SNMP Agent的實作1 (三)SNMP Agent的實作

SNMP Agent單元調試環境

(全文完)