利用net-snmp 的mib2c,由一個MIB檔產出一個可執行的AgentX 程式
如何利用net-snmp 提供之工具,由一個MIB檔產出一個可執行的AgentX 程式
以下是用來示範的資料結構:
ExampleAgentX 節點 {
ExScalar int = // 一個純量變數節點 , OID 4960
ExTable //OID 5155
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
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 }
--OID是5155
--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 }
--OID是91
--MyColB 欄位的資料結構定義
MyColB OBJECT-TYPE
--是一個int
SYNTAX INTEGER
ACCESS read-only
STATUS optional
DESCRIPTION ""
::= { ExColumn 92 }
--OID是92
--MyColC 欄位的資料結構定義
MyColC OBJECT-TYPE
--是一個int
SYNTAX INTEGER
ACCESS read-only
STATUS optional
DESCRIPTION ""
::= { ExColumn 93 }
--OID是93
END
mib2c
mib2c 是net-snmp 套件所提供,mib2c可以轉換mib 檔案,輸出一個C語言的樣板.mib2c 的用法複雜,而且很不直覺.所以除了參考本文件之外,也非常建議先看一下mib2c 的官方說明文件.
使用mib2c的概念是:指定一個"輸出格式的設定檔",然後轉換一個"節點".ex:
$mib2c -c mib2c.scalar.conf ifTable
這是告訴mib2c用 mib2c.scalar.conf 這個設定檔,來轉換ifTable 節點.
$mib2c -c mib2c.scalar.conf ifTable
這是告訴mib2c用 mib2c.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.沒錯,ExScalar跟 ExTable也是一個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
這樣就完成了.
$cp ExampleAgentX.mib.txt /usr/share/snmp/mibs/
or
$cp ExampleAgentX.mib.txt (net-snmp-prefix)/share/mibs/
接下來,設定MIBS環境變數(以bash為例)
$export MIBS=all
這樣就完成了.
整理後步驟如下:
- 將描述檔複製或連結到內定的MIB描述檔目錄
- 設定MIBS環境變數
- 使用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*/
一個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
$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的欄位*/
/*此結構定義了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 自動生成這兩個函式讓我們方便呼叫*/
的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的一筆資料然後再填入欄位的值*/
/*向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 就可以自己找到正確
的資料*/
/*此含是不太需要新增程式碼.在這裡也不必擔心需要開發自行解析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 有提供哪些命令列參數
留言
我使用你的方式實作,但是不知道如何跟主代理做連結。 麻煩給我點意見 謝謝。
sub-agent一般不是要跟主代理連結,才能跑嘛?
麻煩給我些意見喔,我啟動這隻agnetx了,但是我用另一台電腦沒有辦法做get,walk的一些動作