net-snmp擴展有多種方式,在此只介紹兩種——動態庫擴展,靜態庫擴展。
在做net-snmp開發之前,首先確定net-snmp相關的軟件是否安裝。
rpm -qa | grep snmp net-snmp-5.3.1-19.el5 net-snmp-perl-5.3.1-19.el5 net-snmp-libs-5.3.1-19.el5 net-snmp-utils-5.3.1-19.el5 net-snmp-devel-5.3.1-19.el5
動態庫擴展
使用動態庫擴展net-snmp有4個步驟,分別是:
1:編寫MIB庫。
2:根據MIB庫生成源代碼。可以使用mib2c工具。
3:編譯。
4:修改配置文件。
編寫MIB庫
假設我們現在需要使用snmp來獲取某個服務器上的硬盤使用情況——df命令顯示的數據。首先我們需要一個字段來表示HostName,然后我們需要一個表來存放磁盤的信息DiskInfoTable。

1 DISK-SNMP-MIB DEFINITIONS ::= BEGIN 2 IMPORTS 3 MODULE-IDENTITY, enterprises, OBJECT-TYPE, Integer32, 4 NOTIFICATION-TYPE FROM SNMPv2-SMI 5 SnmpAdminString FROM SNMP-FRAMEWORK-MIB 6 netSnmp FROM NET-SNMP-MIB 7 RowStatus, StorageType ,DisplayString FROM SNMPv2-TC 8 InetAddressType, InetAddress FROM INET-ADDRESS-MIB 9 ; 10 DiskCheck MODULE-IDENTITY 11 LAST-UPDATED "201602170000Z" 12 ORGANIZATION "www.cnblogs.com/ngnetboy" 13 CONTACT-INFO 14 "postal: none 15 email: ngnetboy@163.com 16 " 17 DESCRIPTION 18 "check the disk" 19 REVISION "201602170000Z" 20 DESCRIPTION "First draft" 21 ::= { enterprises 888888 } 22 23 root OBJECT IDENTIFIER ::= {DiskCheck 1} 24 25 HostName OBJECT IDENTIFIER ::= {root 1} 26 DiskInfoTable OBJECT IDENTIFIER ::= {root 2} 27 28 HostName OBJECT-TYPE 29 SYNTAX DisplayString 30 ACCESS read-only 31 STATUS current 32 DESCRIPTION 33 "PC is name" 34 ::= {root 1} 35 DiskInfoTable OBJECT-TYPE 36 SYNTAX SEQUENCE OF DiskInfoEntry 37 ACCESS not-accessible 38 STATUS current 39 DESCRIPTION "The disk's info include size used avail capacity and mounted on" 40 ::= {root 2} 41 42 diskInfoEntry OBJECT-TYPE 43 SYNTAX DiskInfoEntry 44 ACCESS not-accessible 45 STATUS current 46 DESCRIPTION "A row describing a given working group" 47 INDEX { Filesystem } 48 ::= {DiskInfoTable 1} 49 50 DiskInfoEntry ::= SEQUENCE { 51 Filesystem DisplayString 52 size DisplayString 53 used DisplayString 54 avail DisplayString 55 capacity DisplayString 56 mountedOn DisplayString 57 } 58 59 Filesystem OBJECT-TYPE 60 SYNTAX DisplayString 61 ACCESS read-only 62 STATUS current 63 DESCRIPTION "name of the disks" 64 ::= {diskInfoEntry 1} 65 66 size OBJECT-TYPE 67 SYNTAX DisplayString 68 ACCESS read-only 69 STATUS current 70 DESCRIPTION "size of the disks" 71 ::= {diskInfoEntry 2} 72 73 used OBJECT-TYPE 74 SYNTAX DisplayString 75 ACCESS read-only 76 STATUS current 77 DESCRIPTION "used of the disks" 78 ::= {diskInfoEntry 3} 79 80 avail OBJECT-TYPE 81 SYNTAX DisplayString 82 ACCESS read-only 83 STATUS current 84 DESCRIPTION "avail of the disks" 85 ::= {diskInfoEntry 4} 86 87 capacity OBJECT-TYPE 88 SYNTAX DisplayString 89 ACCESS read-only 90 STATUS current 91 DESCRIPTION "capacity of the disks" 92 ::= {diskInfoEntry 5} 93 94 mountedOn OBJECT-TYPE 95 SYNTAX DisplayString 96 ACCESS read-only 97 STATUS current 98 DESCRIPTION "mounted_on of the disks" 99 ::= {diskInfoEntry 6} 100 END
使用snmptranslate -Tp -IR DISK-SNMP-MIB::DiskCheck 命令查看mib庫

1 [root@localhost net-snmp]# snmptranslate -Tp -IR DISK-SNMP-MIB::DiskCheck 2 +--DiskCheck(888888) 3 | 4 +--root(1) 5 | 6 +-- -R-- String HostName(1) 7 | Textual Convention: DisplayString 8 | Size: 0..255 9 | 10 +--DiskInfoTable(2) 11 | 12 +--diskInfoEntry(1) 13 | Index: Filesystem 14 | 15 +-- -R-- String Filesystem(1) 16 | Textual Convention: DisplayString 17 | Size: 0..255 18 +-- -R-- String size(2) 19 | Textual Convention: DisplayString 20 | Size: 0..255 21 +-- -R-- String used(3) 22 | Textual Convention: DisplayString 23 | Size: 0..255 24 +-- -R-- String avail(4) 25 | Textual Convention: DisplayString 26 | Size: 0..255 27 +-- -R-- String capacity(5) 28 | Textual Convention: DisplayString 29 | Size: 0..255 30 +-- -R-- String mountedOn(6) 31 Textual Convention: DisplayString 32 Size: 0..255
需要注意的是,我們是在1.3.6.1.4.1下的enterprises節點下擴展,因此需要在IMPORTS中添加enterprises。如果需要其他類型,也需要在IMPORTS中添加,比如DisplayString, Integer32 ...
根據MIB庫生成源代碼
一種比較懶的方式就是使用mib2c工具,根據不同的模板生成代碼。本文中使用兩個模板——mib2c.scalar.conf,mib2c.iterate.conf。前者是專門生成一個簡單的變量,后者是生成一個table。
以上的MIB中HostName是一個簡單變量,DiskInfoTable是一個表。因此可以使用以下命令:
mib2c -c mib2c.scalar.conf HostName
mib2c -c mib2c.iterate.conf DiskInfoTable
以上命令會分別生成一個.c 和 .h 的文件。只需要修改生成的.c .h文件即可。生成簡單變量的代碼比較簡單,只需要修改一個地方即可,下面是一個diff的截圖:
只需要在XXX的地方添加即可。有注釋,不再贅述。
相比而言生成表的代碼會復雜一些。需要改動的地方也相當的多。表:顧名思義就是可以存儲多個相同結構的結構體數組,這個結構體數組並不是一個順序的存儲方式,而是一個鏈式的。在剛開始通過DiskInfoTable_createEntry創建鏈表節點,然后通過遍歷鏈表的方式,通過索引為每一組數據進行賦值。
首先在DiskInfoTable_get_first_data_point函數的開始,創建鏈表節點,並為節點賦值。我們定義一個函數為 void read_data(void); 此函數主要用來創建節點,賦值節點。做完這些,主要的功能代碼就完成了。接下來就是修復編譯的一些bug。由於這些代碼都是使用腳本生成的,在很多地方都不規范,我們需要手動改一些東西:
1:在initialize_table_DiskInfoTable函數中,
table_info->min_column = XXX;
table_info->max_column = YYY;
XXX和YYY表示輸出的最小/大列。在相對應的.h文件中有定義的宏,選取最小的和最大的賦值即可。
2:修改struct DiskInfoTable_entry{}; 由於我們在mib庫中定義了Index——Filesystem,在生成結構體的時候你會發現有兩個Filesystem字段,刪除一個即可,同樣你會發現所有的string類型的字段都變成了char型,再此需要把char型數據改為char數組。
3:在DiskInfoTable_createEntry函數中,會有一個對Index賦值的過程,代碼中使用的是 a=b 的形式,由於我們的Filesystem是string類型的,此時需要改為 strncpy/strcpy等字符串賦值的函數。
4:DiskInfoTable_get_first_data_point函數中少了一個賦值,加上即可:
*my_data_context = DiskInfoTable_head;
5:DiskInfoTable_get_next_data_point函數中少了一個返回值,加上即可:
return put_index_data;
6:DiskInfoTable_handler函數中需要修改的地方有兩類,第一類是使用snmp_set_var_typed_value設置節點value的時候,需要把字段改成u_char * 類型。第二類是在每個switch分支后,判斷table_entry是否為空,如果為空則調用netsnmp_set_request_error返回出錯信息。防止用戶在訪問snmp時導致snmp無法相應的問題。
以下是修改后的全部代碼:

1 /* 2 * Note: this file originally auto-generated by mib2c using 3 * : mib2c.iterate.conf,v 5.17 2005/05/09 08:13:45 dts12 Exp $ 4 */ 5 6 #include <net-snmp/net-snmp-config.h> 7 #include <net-snmp/net-snmp-includes.h> 8 #include <net-snmp/agent/net-snmp-agent-includes.h> 9 #include "DiskInfoTable.h" 10 typedef struct _disk_info { 11 char filesystem[64]; 12 char size[64]; 13 char used[64]; 14 char avail[64]; 15 char capacity[64]; 16 char mountedOn[64]; 17 }disk_info_t; 18 19 disk_info_t disk_info[MAX_DISK] = { 20 {"/dev/mapper", "19G", "2.6G", "15G", "15%", "/"}, 21 {"/dev/sda1", "99M", "12M", "83M", "13%", "/boot"}, 22 {"tmpfs", "252M", "0", "252M", "0%", "/dev/shm"}, 23 {"/dev/scd1", "2.8G", "2.8G", "0", "100%", "/media/"}, 24 }; 25 26 /** Initializes the DiskInfoTable module */ 27 void 28 init_DiskInfoTable(void) 29 { 30 /* 31 * here we initialize all the tables we're planning on supporting 32 */ 33 initialize_table_DiskInfoTable(); 34 } 35 36 /** Initialize the DiskInfoTable table by defining its contents and how it's structured */ 37 void 38 initialize_table_DiskInfoTable(void) 39 { 40 static oid DiskInfoTable_oid[] = 41 { 1, 3, 6, 1, 4, 1, 888888, 1, 2 }; 42 size_t DiskInfoTable_oid_len = OID_LENGTH(DiskInfoTable_oid); 43 netsnmp_handler_registration *reg; 44 netsnmp_iterator_info *iinfo; 45 netsnmp_table_registration_info *table_info; 46 47 reg = 48 netsnmp_create_handler_registration("DiskInfoTable", 49 DiskInfoTable_handler, 50 DiskInfoTable_oid, 51 DiskInfoTable_oid_len, 52 HANDLER_CAN_RONLY); 53 54 table_info = SNMP_MALLOC_TYPEDEF(netsnmp_table_registration_info); 55 netsnmp_table_helper_add_indexes(table_info, ASN_OCTET_STR, /* index: Filesystem */ 56 0); 57 table_info->min_column = COLUMN_FILESYSTEM; 58 table_info->max_column = COLUMN_MOUNTEDON; 59 60 iinfo = SNMP_MALLOC_TYPEDEF(netsnmp_iterator_info); 61 iinfo->get_first_data_point = DiskInfoTable_get_first_data_point; 62 iinfo->get_next_data_point = DiskInfoTable_get_next_data_point; 63 iinfo->table_reginfo = table_info; 64 65 netsnmp_register_table_iterator(reg, iinfo); 66 67 /* 68 * Initialise the contents of the table here 69 */ 70 } 71 72 /* 73 * Typical data structure for a row entry 74 */ 75 struct DiskInfoTable_entry { 76 /* 77 * Column values 78 */ 79 char Filesystem[16]; 80 char size[8]; 81 char used[8]; 82 char avail[8]; 83 char capacity[8]; 84 char mountedOn[16]; 85 /* 86 * Illustrate using a simple linked list 87 */ 88 int valid; 89 struct DiskInfoTable_entry *next; 90 }; 91 92 struct DiskInfoTable_entry *DiskInfoTable_head; 93 94 /* 95 * create a new row in the (unsorted) table 96 */ 97 struct DiskInfoTable_entry * 98 DiskInfoTable_createEntry(char *Filesystem) 99 { 100 struct DiskInfoTable_entry *entry; 101 102 entry = SNMP_MALLOC_TYPEDEF(struct DiskInfoTable_entry); 103 if (!entry) 104 return NULL; 105 106 //entry->Filesystem = Filesystem; 107 strncpy(entry->Filesystem, Filesystem, strlen(Filesystem)); 108 entry->next = DiskInfoTable_head; 109 DiskInfoTable_head = entry; 110 return entry; 111 } 112 /* 113 * remove a row from the table 114 */ 115 void 116 DiskInfoTable_removeEntry(struct DiskInfoTable_entry *entry) 117 { 118 struct DiskInfoTable_entry *ptr, *prev; 119 120 if (!entry) 121 return; /* Nothing to remove */ 122 123 for (ptr = DiskInfoTable_head, prev = NULL; 124 ptr != NULL; prev = ptr, ptr = ptr->next) { 125 if (ptr == entry) 126 break; 127 } 128 if (!ptr) 129 return; /* Can't find it */ 130 131 if (prev == NULL) 132 DiskInfoTable_head = ptr->next; 133 else 134 prev->next = ptr->next; 135 136 SNMP_FREE(entry); /* XXX - release any other internal resources */ 137 } 138 139 static void fill_diskinfotable_entry(struct DiskInfoTable_entry *entry) { 140 int i; 141 142 for (i = 0; i < MAX_DISK; i++) { 143 if (!strcmp(disk_info[i].filesystem, entry->Filesystem)) { 144 strncpy(entry->size, disk_info[i].size, strlen(disk_info[i].size)); 145 //strcpy(entry->size, "444G"); 146 strncpy(entry->used, disk_info[i].used, strlen(disk_info[i].used)); 147 strncpy(entry->avail, disk_info[i].avail, strlen(disk_info[i].avail)); 148 strncpy(entry->capacity, disk_info[i].capacity, strlen(disk_info[i].capacity)); 149 strncpy(entry->mountedOn, disk_info[i].mountedOn, strlen(disk_info[i].mountedOn)); 150 } 151 } 152 153 } 154 155 static void read_data(void) { 156 int i = 0; 157 struct DiskInfoTable_entry *entry = NULL; 158 159 if(DiskInfoTable_head == NULL){ 160 for (i = 0; i < MAX_DISK; i++) { 161 DiskInfoTable_createEntry(disk_info[i].filesystem); 162 } 163 } 164 165 entry = DiskInfoTable_head; 166 167 while (entry) { 168 fill_diskinfotable_entry(entry); 169 entry = entry->next; 170 } 171 172 173 } 174 175 /* 176 * Example iterator hook routines - using 'get_next' to do most of the work 177 */ 178 netsnmp_variable_list * 179 DiskInfoTable_get_first_data_point(void **my_loop_context, 180 void **my_data_context, 181 netsnmp_variable_list * put_index_data, 182 netsnmp_iterator_info *mydata) 183 { 184 read_data(); 185 *my_loop_context = DiskInfoTable_head; 186 *my_data_context = DiskInfoTable_head; 187 return DiskInfoTable_get_next_data_point(my_loop_context, 188 my_data_context, 189 put_index_data, mydata); 190 } 191 192 netsnmp_variable_list * 193 DiskInfoTable_get_next_data_point(void **my_loop_context, 194 void **my_data_context, 195 netsnmp_variable_list * put_index_data, 196 netsnmp_iterator_info *mydata) 197 { 198 struct DiskInfoTable_entry *entry = 199 (struct DiskInfoTable_entry *) *my_loop_context; 200 netsnmp_variable_list *idx = put_index_data; 201 202 if (entry) { 203 snmp_set_var_value(idx, (u_char *)entry->Filesystem, 204 sizeof(entry->Filesystem)); 205 idx = idx->next_variable; 206 *my_data_context = (void *) entry; 207 *my_loop_context = (void *) entry->next; 208 } else { 209 return NULL; 210 } 211 return put_index_data; 212 } 213 214 215 /** handles requests for the DiskInfoTable table */ 216 int 217 DiskInfoTable_handler(netsnmp_mib_handler *handler, 218 netsnmp_handler_registration *reginfo, 219 netsnmp_agent_request_info *reqinfo, 220 netsnmp_request_info *requests) 221 { 222 223 netsnmp_request_info *request; 224 netsnmp_table_request_info *table_info; 225 struct DiskInfoTable_entry *table_entry; 226 227 switch (reqinfo->mode) { 228 /* 229 * Read-support (also covers GetNext requests) 230 */ 231 case MODE_GET: 232 for (request = requests; request; request = request->next) { 233 table_entry = (struct DiskInfoTable_entry *) 234 netsnmp_extract_iterator_context(request); 235 table_info = netsnmp_extract_table_info(request); 236 237 switch (table_info->colnum) { 238 case COLUMN_FILESYSTEM: 239 if (table_entry == NULL) { 240 netsnmp_set_request_error(reqinfo, request, 241 SNMP_NOSUCHINSTANCE); 242 continue; 243 } 244 snmp_set_var_typed_value(request->requestvb, ASN_OCTET_STR, 245 (u_char *)table_entry->Filesystem, 246 sizeof(table_entry->Filesystem)); 247 break; 248 case COLUMN_SIZE: 249 if (table_entry == NULL) { 250 netsnmp_set_request_error(reqinfo, request, 251 SNMP_NOSUCHINSTANCE); 252 continue; 253 } 254 snmp_set_var_typed_value(request->requestvb, ASN_OCTET_STR, 255 (u_char *)table_entry->size, 256 sizeof(table_entry->size)); 257 break; 258 case COLUMN_USED: 259 if (table_entry == NULL) { 260 netsnmp_set_request_error(reqinfo, request, 261 SNMP_NOSUCHINSTANCE); 262 continue; 263 } 264 snmp_set_var_typed_value(request->requestvb, ASN_OCTET_STR, 265 (u_char *)table_entry->used, 266 sizeof(table_entry->used)); 267 break; 268 case COLUMN_AVAIL: 269 if (table_entry == NULL) { 270 netsnmp_set_request_error(reqinfo, request, 271 SNMP_NOSUCHINSTANCE); 272 continue; 273 } 274 snmp_set_var_typed_value(request->requestvb, ASN_OCTET_STR, 275 (u_char *)table_entry->avail, 276 sizeof(table_entry->avail)); 277 break; 278 case COLUMN_CAPACITY: 279 if (table_entry == NULL) { 280 netsnmp_set_request_error(reqinfo, request, 281 SNMP_NOSUCHINSTANCE); 282 continue; 283 } 284 snmp_set_var_typed_value(request->requestvb, ASN_OCTET_STR, 285 (u_char *)table_entry->capacity, 286 sizeof(table_entry->capacity)); 287 break; 288 case COLUMN_MOUNTEDON: 289 if (table_entry == NULL) { 290 netsnmp_set_request_error(reqinfo, request, 291 SNMP_NOSUCHINSTANCE); 292 continue; 293 } 294 snmp_set_var_typed_value(request->requestvb, ASN_OCTET_STR, 295 (u_char *)table_entry->mountedOn, 296 sizeof(table_entry->mountedOn)); 297 break; 298 default: 299 continue; 300 //netsnmp_set_request_error(reqinfo, request, SNMP_NOSUCHINSTANCE); 301 } 302 } 303 break; 304 305 } 306 return SNMP_ERR_NOERROR; 307 }
除了這些還需要有一個common.c把所有的代碼連接到一起。
1 #include "HostName.h" 2 #include "DiskInfoTable.h" 3 void init_diskcheck(void) { 4 init_HostName(); 5 init_DiskInfoTable(); 6 }
注:init_動態庫名 和生成的動態庫名字有聯系,如果動態庫名字不為 diskcheck.so net-snmp有可能無法識別。
編譯
編譯這塊主要是利用net-snmp給的命令,直接給出Makefile好了。
1 CC = gcc 2 3 DIR = HostName DiskInfoTable 4 #DIR = HostName 5 6 vpath %.c ./ 7 vpath %.c $(DIR) 8 9 INCLUDE = -I./ 10 INCLUDE += $(foreach x, $(DIR), -I./$(x)) 11 12 CFLAGS = -fPIC -shared -O3 13 CFLAGS += $(shell net-snmp-config --cflags) 14 15 LDFLAGS = $(shell net-snmp-config --libs) 16 17 SRC = common.c 18 SRC += $(foreach x, $(DIR), $(x).c) 19 OBJS = $(SRC:.c=.o) 20 21 TARGET = diskcheck.so 22 23 all:$(TARGET) 24 25 $(TARGET):$(OBJS) 26 $(CC) $^ -o $@ $(CFLAGS) $(LDFLAGS) 27 28 $(OBJS):%.o:%.c 29 $(CC) -c $^ -o $@ $(CFLAGS) $(INCLUDE) 30 31 .PHONY:clean 32 clean: 33 rm *.o $(TARGET)
把編譯好的diskcheck.so拷貝到 /usr/lib (32位系統) /usr/lib64 (64位系統)中.
修改配置文件
1:/etc/snmp/snmp.conf 中加上 mibs +MIB文件的名字(不帶后綴)
2:/etc/snmp/snmpd.conf 中加上
dlmod 動態庫名 動態庫絕對路徑 —— dlmod diskcheck /usr/lib/diskcheck.so
view systemview .1
Ps: 在/etc/snmp/snmpd.conf 中添加
view systemview included .1.3.6.1.4.1
有些系統在訪問節點的時候有錯誤,例如在redhat as5.8 中有問題,而CentOS6.4中沒問題【有可能是我的配置問題】。需要改成步驟二中的配置。