利用net-snmp 的mib2c,由一個MIB檔產出一個可執行的AgentX 程式

如何利用net-snmp 提供之工具,由一個MIB檔產出一個可執行的AgentX 程式

以下是用來示範的資料結構:

ExampleAgentX 節點 {

ExScalar int = //
一個純量變數節點 , OID 4960

ExTable //OID 5155
MyColA(索引欄位)(OID 91)
MyColB(OID 92)
MyColC(OID 93)
1
7
8
2
77
88

}

可以看到,ExampleAgentX 包含了一個純量變數 ExScalar,跟一個 ExTable表格.在此範例中,表格的內容雖然設計成固定.不過在實務上,Table 的內容是隨著Agent的執行而會動態更新.像是MIBII 中的 ifTable(顯示系統的網路介面OID table),或是UCD-SNMP-MIB 中的dskTable(系統中所有的磁碟列表)

以下是ExampleAgentX 資料結構的 MIB 檔內容
Table-Example-MIB DEFINITIONS ::= BEGIN

IMPORTS
mgmt, enterprises, NetworkAddress, IpAddress, Counter, Gauge, TimeTicks FROM RFC1155-SMI

DisplayString FROM RFC1213-MIB
TRAP-TYPE FROM RFC-1215
OBJECT-TYPE FROM RFC-1212;


-- ExampleAgentX 節點
ExampleAgentX OBJECT IDENTIFIER ::= { enterprises 2999 }

--ExScalar 純量變數節點
ExScalar OBJECT-TYPE
--資料型態是int
SYNTAX INTEGER
ACCESS read-only
STATUS optional
DESCRIPTION ""
::= { ExampleAgentX 4960 }
--OID 4960

--ExTable 表格
ExTable OBJECT-TYPE
--表格為ExRow資料結構的序列,換具話說,ExRow 定義了表格的欄位長相
SYNTAX SEQUENCE OF ExRow
ACCESS not-accessible
STATUS optional
DESCRIPTION ""
::= { ExampleAgentX 5155 }
--OID5155

--ExRow資料結構,在這個範例中,表格有3個欄位,MyColA , MyColB , MyColC
ExRow ::=
SEQUENCE {
MyColA INTEGER,
MyColB INTEGER,
MyColC INTEGER,
}

--ExColumn,定義表格的索引欄位.
ExColumn OBJECT-TYPE
SYNTAX ExRow
ACCESS not-accessible
STATUS optional
DESCRIPTION ""
--索引欄位是 MyColA
INDEX { MyColA }
::= { ExTable 7777 }

--MyColA 欄位的資料結構定義
MyColA OBJECT-TYPE
--是一個int
SYNTAX INTEGER
ACCESS read-only
STATUS optional
DESCRIPTION ""
::= { ExColumn 91 }
--OID91

--MyColB 欄位的資料結構定義
MyColB OBJECT-TYPE
--是一個int
SYNTAX INTEGER
ACCESS read-only
STATUS optional
DESCRIPTION ""
::= { ExColumn 92 }
--OID92

--MyColC 欄位的資料結構定義
MyColC OBJECT-TYPE
--是一個int
SYNTAX INTEGER
ACCESS read-only
STATUS optional
DESCRIPTION ""
::= { ExColumn 93 }
--OID93

END


mib2c

mib2c net-snmp 套件所提供,mib2c可以轉換mib 檔案,輸出一個C語言的樣板.mib2c 的用法複雜,而且很不直覺.所以除了參考本文件之外,也非常建議先看一下mib2c 官方說明文件.
使用mib2c的概念是:指定一個"輸出格式的設定檔",然後轉換一個"節點".ex:

$mib2c -c mib2c.scalar.conf ifTable
這是告訴mib2cmib2c.scalar.conf 這個設定檔,來轉換ifTable 節點.

  • -c參數:輸出格式的設定檔.此設定檔會紀錄著產生C語言樣板的方法.透過此設定檔,mib2c 可以轉換節點成多種C語言樣板.如果使用系統套件管理員安裝的net-snmp ,應該會是放在/etc/snmp 目錄下.或是透過系統的套件管理工具作查詢.如果是自行製造的net-snmp ,應該會放在(net-snmp-prefix)/etc/snmp/,若沒有,可以用find 工具找尋.但是!使用mib2c 時並不需要指定設定檔的目錄.在此範例中,我們專注說明mib2c.scalar.conf(只轉換節點下純量變數的設定檔),mib2c.table_data.conf(只轉換節點下表格的設定檔)
  • mib2c 的第二個參數 ,是一個snmp 的某一個節點.這部份是很奇怪,因為mib2c 並不以處裡一個MIB描述檔作為單位.而是以一個以定義好的snmp 節點作為處理單位.在此範例中,我們要轉換ExampleAgentX 節點.不過你事實上也可以將第二個參數替換成ExScalar ExTable.沒錯,ExScalarExTable也是一個snmp的合法節點.
一般狀況下,mib2c 會到一個固定的目錄找尋MIB描述檔.這個目錄應該是/usr/share/snmp/mibs/.如果是自行製造net-snmp ,那麼目錄可能是 (net-snmp-prefix)/share/mibs/.你可以編輯snmp.conf 中的 mibfile 參數來決定MIB描述檔放哪邊.這個目錄放了很多SMI RFC制定好的MIB描述檔.如果要讓mib2c 找到我們自行設計的MIB描述檔,可以先將我們的描述檔複製(或符號連結)/usr/share/snmp/mibs/ or (net-snmp-prefix)/share/mibs/ .
$cp ExampleAgentX.mib.txt /usr/share/snmp/mibs/
or
$cp ExampleAgentX.mib.txt (net-snmp-prefix)/share/mibs/

接下來,設定MIBS環境變數(bash為例)
$export MIBS=all
這樣就完成了.
整理後步驟如下:
  1. 將描述檔複製或連結到內定的MIB描述檔目錄
  2. 設定MIBS環境變數
  3. 使用mib2c 程式轉換節點


mib2c 轉換ExampleAgentX所有的純量

指令:
$mib2c -c mib2c.scalar.conf ExampleAgentX

藍色程式碼為轉換後的C樣板內容.(不過mib2c 轉換後的程式常常會有編譯不過的狀況,所以藍色字已經是處理過可以編譯的內容,可能跟你的輸出有所差異).紅色字為程式員需要自行加入的部份.綠色字為說明註解
/*
* Note: this file originally auto-generated by mib2c using
* : mib2c.scalar.conf 11805 2005-01-07 09:37:18Z dts12 $
*/
#include
#include
#include
#include "ExampleAgentX.h"

/** Initializes the ExampleAgentX module */
void
init_ExampleAgentX(void)
{
static oid ExScalar_oid[] = { 1,3,6,1,4,1,2999,4960 };

DEBUGMSGTL(("ExampleAgentX", "Initializing\n"));
/* netsnmp_register_scalar 是向net-snmp core 登錄一個scalar 物件*/
netsnmp_register_scalar(
/* netsnmp_create_handler_registration 是向net-snmp core 登錄
一個oid 的處理函式handle_ExScalar*/
netsnmp_create_handler_registration("ExScalar", handle_ExScalar,
ExScalar_oid, OID_LENGTH(ExScalar_oid),
HANDLER_CAN_RONLY
));
}

int
handle_ExScalar(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. */
/*OID被查詢時,此函數就會被net-snmp core 呼叫*/
int _v = 123;
switch(reqinfo->mode) {

case MODE_GET:
snmp_set_var_typed_value(requests->requestvb, ASN_INTEGER,
(u_char *)&_v,sizeof(int) );
break;


default:
/* we should never get here, so this is a really bad error */
snmp_log(LOG_ERR, "unknown mode (%d) in handle_ExScalar\n", reqinfo->mode );
return SNMP_ERR_GENERR;
}

return SNMP_ERR_NOERROR;
}


mib2c 轉換ExampleAgentX所有的表格

指令:

$mib2c -c mib2c.table_data.conf ExampleAgentX
方框內藍色程式碼為轉換後的C樣板內容.(不過mib2c 轉換後的程式常常會有編譯不過的狀況,所以藍色字已經是處理過可以編譯的內容,可能跟你的輸出有所差異).紅色字為程式員需要自行加入的部份.綠色字為說明註解
/*
* Note: this file originally auto-generated by mib2c using
* : mib2c.table_data.conf 15999 2007-03-25 22:32:02Z dts12 $
*/

#include
#include
#include
#include "ExampleAgentX.h"

/* Typical data structure for a row entry */
/*
此結構定義了ExTable的欄位*/
struct ExTable_entry {
/* Index values */
long MyColA;

/* Column values */
long MyColB;
long MyColC;

int valid;
};

/*新加入一筆資料到Extable所使用的函式*/
netsnmp_tdata_row *
ExTable_createEntry (
netsnmp_tdata *table_data,
long MyColA
);
/*Extable移除一筆資料所使用的函式*/
void
ExTable_removeEntry(
netsnmp_tdata *table_data,
netsnmp_tdata_row *row
);
/*新加入或移除Extable 的一筆資料,都是需要讓net-snmp core知道.因為net-snmp core 負責OID的解譯作業,table的資料列個數跟OID長相有關係,如果不讓net-snmp core知道,net-snmp core 將不能正確解譯查詢
OID.所以mib2c 自動生成這兩個函式讓我們方便呼叫*/

netsnmp_tdata_row *row[2];


/** Initializes the ExampleAgentX module */
void
init_ExampleAgentX(void)
{
/* here we initialize all the tables we're planning on supporting */
initialize_table_ExTable();
}

//# Determine the first/last column names

/** Initialize the ExTable table by defining its contents and how it's structured */
void
initialize_table_ExTable(void)
{
static oid ExTable_oid[] = {1,3,6,1,4,1,2999,5155};
size_t ExTable_oid_len = OID_LENGTH(ExTable_oid);
netsnmp_handler_registration *reg;
netsnmp_tdata *table_data;
netsnmp_table_registration_info *table_info;
/*net-snmp core 登錄處理OID的函式*/
reg = netsnmp_create_handler_registration(
"ExTable", ExTable_handler,
ExTable_oid, ExTable_oid_len,
HANDLER_CAN_RONLY
);
/*生成一個table 物件*/
table_data = netsnmp_tdata_create_table( "ExTable", 0 );
table_info = SNMP_MALLOC_TYPEDEF( netsnmp_table_registration_info );
netsnmp_table_helper_add_indexes(table_info,
ASN_INTEGER, /* index: MyColA */
0);

table_info->min_column = COLUMN_MYCOLA;
table_info->max_column = COLUMN_MYCOLC;
/*net-snmp core 登錄table 物件*/
netsnmp_tdata_register( reg, table_data, table_info );

/* Initialise the contents of the table here */

/*net-snmp core 新增Extable的一筆資料然後再填入欄位的值*/
row[0] = ExTable_createEntry(table_data,0);
( (struct ExTable_entry*)(row[0]->data) )->MyColB=7;
( (struct ExTable_entry*)(row[0]->data) )->MyColC=8;
/*
net-snmp core 新增Extable的一筆資料然後再填入欄位的值*/
row[1] = ExTable_createEntry(table_data,1);
( (struct ExTable_entry*)(row[1]->data) )->MyColB=77;
( (struct ExTable_entry*)(row[1]->data) )->MyColC=88;

/*作業順序就如同你看到的,先呼叫函式新增一筆Extable資料,函式會傳回新增完成的資料的位置(指標),
但是內容還沒有填,取得指標之後程序員要負責將正確的資料內容填入
先新增,再填入資料,是比較容易搞混的作業方法*/
}


/* create a new row in the table */
netsnmp_tdata_row *
ExTable_createEntry(netsnmp_tdata *table_data
, long MyColA
) {
struct ExTable_entry *entry;
netsnmp_tdata_row *row;

entry = SNMP_MALLOC_TYPEDEF(struct ExTable_entry);
if (!entry)
return NULL;

row = netsnmp_tdata_create_row();
if (!row) {
SNMP_FREE(entry);
return NULL;
}
row->data = entry;
entry->MyColA = MyColA;
netsnmp_tdata_row_add_index( row, ASN_INTEGER,
&(entry->MyColA),
sizeof(entry->MyColA));
netsnmp_tdata_add_row( table_data, row );
return row;
}

/* remove a row from the table */
void
ExTable_removeEntry(netsnmp_tdata *table_data,
netsnmp_tdata_row *row) {
struct ExTable_entry *entry;

if (!row)
return; /* Nothing to remove */
entry = (struct ExTable_entry *)
netsnmp_tdata_remove_and_delete_row( table_data, row );
if (entry)
SNMP_FREE( entry ); /* XXX - release any other internal resources */
}


/** handles requests for the ExTable table */
int
ExTable_handler(
netsnmp_mib_handler *handler,
netsnmp_handler_registration *reginfo,
netsnmp_agent_request_info *reqinfo,
netsnmp_request_info *requests) {

netsnmp_request_info *request;
netsnmp_table_request_info *table_info;
netsnmp_tdata *table_data;
netsnmp_tdata_row *table_row;
struct ExTable_entry *table_entry;
int ret;

/*net-snmp core 收到OID的查詢之後,會呼叫此函式*/
/*
此含是不太需要新增程式碼.在這裡也不必擔心需要開發自行解析OID的程式碼.因為net-snmp core 有登錄了
table
物件.所以net-snmp core 可以正確解析OID,而我們新增一筆Table資料也有向net-snmp core通知,
net-snmp core
知道資料的位置(指標)在哪裡,所以程式員完全不需要自行寫程式,mib2c 產生的code 就可以自己找到正確
的資料*/
switch (reqinfo->mode) {
/*
* Read-support (also covers GetNext requests)
*/
case MODE_GET:
for (request=requests; request; request=request->next) {
table_entry = (struct ExTable_entry *)
netsnmp_tdata_extract_entry(request);
table_info = netsnmp_extract_table_info( request);

switch (table_info->colnum) {
case COLUMN_MYCOLA:
if ( !table_entry ) {
netsnmp_set_request_error(reqinfo, request,
SNMP_NOSUCHINSTANCE);
continue;
}
snmp_set_var_typed_integer( request->requestvb, ASN_INTEGER,
table_entry->MyColA);
break;
case COLUMN_MYCOLB:
if ( !table_entry ) {
netsnmp_set_request_error(reqinfo, request,
SNMP_NOSUCHINSTANCE);
continue;
}
snmp_set_var_typed_integer( request->requestvb, ASN_INTEGER,
table_entry->MyColB);
break;
case COLUMN_MYCOLC:
if ( !table_entry ) {
netsnmp_set_request_error(reqinfo, request,
SNMP_NOSUCHINSTANCE);
continue;
}
snmp_set_var_typed_integer( request->requestvb, ASN_INTEGER,
table_entry->MyColC);
break;
default:
netsnmp_set_request_error(reqinfo, request,
SNMP_NOSUCHOBJECT);
break;
}
}
break;

}
return SNMP_ERR_NOERROR;
}





net-snmp-config 編譯C語言檔案

net-snmp 提供了一個工具程式net-snmp-config .這個工具其中一個選項 --compile-subagent 專門用來編譯AgentX模組.使用--compile-subagent 選項,net-snmp-config 會生成一個含有agentx相關功能的c 程式碼,然後跟我們指定的c語言程式碼一起編譯,產生的unix程式就是一個含有agnetx 功能的 subagent.

指令如下
net-snmp-config --comiple-subagent Output_name subagent_c_code

  • Output_name : 編譯產生的unix 程式的檔名
  • subagent_c_code:我們所開發完成的subagnet 程式碼

編譯完成產生的unix 程式,可以直接在shell 執行.或是可以用--help 參數看看這個subagent 有提供哪些命令列參數

留言

Jiro Luo寫道…
請問要如何執行???
我使用你的方式實作,但是不知道如何跟主代理做連結。 麻煩給我點意見 謝謝。
Jiro Luo寫道…
請問一下這個程式該怎麼讓他執行呢?

sub-agent一般不是要跟主代理連結,才能跑嘛?

麻煩給我些意見喔,我啟動這隻agnetx了,但是我用另一台電腦沒有辦法做get,walk的一些動作

這個網誌中的熱門文章

[C語言]宣告陣列的大小是0 ???

ubuntu 16.04開機遇到 flip_done timed out 的問題