(轉)淺談 Linux 系統中的 SNMP Trap


原文:https://www.ibm.com/developerworks/cn/linux/l-cn-snmp/index.html

簡介

本文講解 SNMP Trap,在介紹 Trap 概念之前,首先認識一下 SNMP 吧。

簡單網絡管理協議(Simple Network Management Protocol)是一種應用層協議,是TCP/IP協議族的一部分。它使網絡設備之間能夠方便地交換管理信息。能夠讓網絡管理員管理網絡的性能,發現和解決網絡問題及進行網絡的擴充。

目前SNMP已成為網絡管理領域中事實上的工業標准,並被廣泛支持和應用,大多數網絡管理系統和平台都是基於SNMP的。

SNMP 管理控制框架

簡而言之,SNMP 協議是用來管理設備的協議,何謂管理呢?我斗膽將其歸納為兩個基本點:監控(get)和配置(set)。也就是說:人們管理一個設備的基本手段可以歸納為 get 和 set 兩種操作。如圖 1 所示。

圖 1. SNMP 基本管理控制框架

圖 1. SNMP 基本管理控制框架

如果 NMS( 網管系統 ) 需要查詢被管理設備的狀態,則需要通過 SNMP 的 get 操作獲得設備的狀態信息;同樣,如果 NMS 需要修改或者配置被管理設備的參數,則需要通過 SNMP 的 set 操作來完成。

什么是 MIB?

MIB 是描述被管理設備上的參數的數據結構。如前所述,管理一個設備,就是利用 SNMP 協議,通過網絡對被管理設備上的參數進行 get 和 set 操作。

那么如何組織被管理設備上的參數呢?多數情況下,可以 get 和 set 的參數實在多得驚人,假如僅僅簡單地線性羅列它們,操作會十分不便。想象一下把 1000 個參數列成一張表,需要使用的時候查詢這樣一張表會有多么困難啊?比如您打算在地球上找一個城市,”Ithaca”,如果沒有歸類和分級,則需要查找一張巨大的表格。但如果告訴您城市” Ithaca”是:南美洲國家圭亞那的北部城市"Ithaca",那么就容易些了吧?

被管理的設備相當復雜,擁有很多可以被管理的參數,需要對它們進行歸類,分級。管理信息庫 (MIB) 是一個具有分層特性的信息的集合,我們可以通過 SNMP 去存取它。MIB 的成員是一些被管理的對象 (Managed Object),以對象標示符 (Object Identifiers) 來區分它們。被管理的對象由一個或多個對象實例 (Object Instances) 組成,本質上,這些對象實例就是變量。

在 MIB 的層次結構中,一個對象標示符唯一標識了被管理對象。MIB 的層次結構可以被描述成無根名的樹,樹的級別被不同的組織所划分。如下圖所示:

圖 2. MIB 樹

圖 2. MIB 樹

很多能夠被 SNMP 管理的對象都是由標准組織定義好的。比如系統磁盤的信息,用 OID ”1.3.6.1.4.1.2021.9” 表示。這串數字是國際標准化組織協商定義好的,大家都要去遵循它。當然,國際組織不可能預知未來,如果您要開發的設備有一些管理需求沒有任何 RFC 定義過,那么您也可以編寫自己的 MIB 文件來定義私有的 MIB 對象。

什么是 SNMP Trap

前面介紹的 get/set 操作都是從 NMS 發送到被管理設備的。但有時候,能夠從被管理設備主動發送信息到 NMS 也是非常必要的。

SNMP Trap 是 SNMP 的一部分,當被監控段出現特定事件,可能是性能問題,甚至是網絡設備接口宕掉等,代理端會給管理站發告警事件。假如在特定事件出現的時刻,不是由 Agent 主動通知 NMS,那么 NMS 必須不斷地對 Agent 進行輪詢。這是非常浪費計算資源的方法,正如人們用中斷通知 CPU 數據的到達,而不是讓 CPU 進行輪詢一樣。Trap 通知是更加合理的選擇。

用一句話來說的話,SNMP Trap 就是被管理設備主動發送消息給 NMS 的一種機制。這是本文主要關注的話題。

NET-SNMP 簡介

在 Linux 系統中,我們可以選擇 net-snmp 來處理絕大多數和 SNMP 相關的工作。

NET-SNMP 是一種開放源代碼的 SNMP 協議實現。它支持 SNMP v1, SNMP v2c 與 SNMP v3,並可以使用 IPV4 及 IPV6 。也包含 SNMP Trap 的所有相關實現。Net-snmp 包含了 snmp 實用程序集和完整的 snmp 開發庫。

用戶使用 net-snmp 提供的工具,可以完成很多關於 SNMP 的操作,具體說來,包括以下一些命令行應用程序:

一些應用程序可以用來從支持 SNMP 的設備獲得數據。其中 snmpget, snmpgetnext 可以支持獨立請求,比如:

1
2
% snmpget -v 1 -c demopublic test.net-snmp.org system.sysUpTime.0
system.sysUpTime.0 = Timeticks: (586731977) 67 days, 21:48:39.77

該命令獲得單個獨立的 MIB 對象 system.sysUpTime.0 的值。

而 snmpwalk, snmptable, snmpdelta 則用來支持重復請求。

1
2
3
4
5
6
% snmpwalk -v 2c -c demopublic test.net-snmp.org system
SNMPv2-MIB::sysDescr.0 = HP-UX net-snmp B.10.20 A 9000/715
SNMPv2-MIB::sysObjectID.0 = OID: enterprises.ucdavis.ucdSnmpAgent.hpux10
SNMPv2-MIB::sysUpTime.0 = Timeticks: (586998396) 67 days, 22:33:03.96
SNMPv2-MIB::sysContact.0 = Wes Hardaker wjhardaker@ucdavis.edu
SNMPv2-MIB::sysName.0 = net-snmp

上面的命令返回所有 system 節點以下的 MIB 對象的值。

命令 snmpset 對支持 SNMP 的設備配置屬性。如下例所示:

1
2
3
4
5
6
$ snmpget -v 1 -c demopublic test.net-snmp.org ucdDemoPublicString.0
  UCD-DEMO-MIB::ucdDemoPublicString.0 = "hi there!"
$ snmpset -v 1 -c demopublic test.net-snmp.org ucdDemoPublicString.0 s "Hello, world!"
  UCD-DEMO-MIB::ucdDemoPublicString.0 = "Hello, world!"
$ snmpget -v 1 -c demopublic test.net-snmp.org ucdDemoPublicString.0
  UCD-DEMO-MIB::ucdDemoPublicString.0 = "Hello, world!"

命令 snmpdf, snmpnetstat, snmpstatus 可以從支持 SNMP 的設備獲取特定的信息。比如下面的命令從目標系統上獲得類似 netstat 的信息:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
% snmpnetstat -v 2c -c public -a testhost
 
Active Internet (tcp) Connections (including servers)
Proto Local Address Foreign Address           (state)
tcp   *.echo         *.*                      LISTEN
tcp   *.discard      *.*                      LISTEN
tcp   *.daytime      *.*                      LISTEN
tcp   *.chargen      *.*                      LISTEN
tcp   *.ftp          *.*                      LISTEN
tcp   *.telnet       *.*                      LISTEN
tcp   *.smtp         *.*                      LISTEN
Active Internet (udp) Connections
Proto Local Address
udp    *.echo
udp    *.discard
udp    *.daytime
udp    *.chargen
udp    *.time

snmptranslate 命令將 MIB OIDs 的兩種表現形式 ( 數字及文字 ) 相互轉換。並顯示 MIB 的內容與結構,如下所示:

1
2
3
4
% snmptranslate .1.3.6.1.2.1.1.3.0
     SNMPv2-MIB::sysUpTime.0
% snmptranslate -On SNMPv2-MIB::sysUpTime.0
     .1.3.6.1.2.1.1.3.0

Net-snmp 還提供了一個基於 Tk/perl 的,圖形化的 MIB 瀏覽器 tkmib。

圖 2. Tkmib 界面

圖 2. Tkmib 界面

Net-snmp 還提供了接收 SNMP traps 的守護程序 snmptrapd。可以將選定的 SNMP 消息記錄到系統日志 syslog,NT 事件日志,或者文本文件中。或是轉發到其它的 SNMP 管理程序 , 也可以傳給外部的應用程序。本文的后面我們將用它來演示 SNMP Trap 的收發。

Net-snmp 還提供了一個回應 SNMP 查詢的客戶端 snmpd. 它集成了大量 SNMP 的模塊 . 並可通過動態鏈接庫 , 外部腳本與命令 , 多路 SNMP 技術 (SMUX), 以及可擴客戶端協議 (AgentX) 進行擴展 .

此外,net-snmp 還包含了用來開發 SNMP 應用程序的程序庫。支持 C 與 perl 的 APIs。因此您可以使用 net-snmp 的工具集完成一些關於 SNMP 的工作,也可以依賴 net-snmp 提供的開發包自己寫程序開發您所需要的 snmp 應用。

NET-SNMP 的安裝

不同的 Linux 發行版有不同的包管理機制,因此在這里,我打算寫下從源代碼安裝 net-snmp 的過程,適用於各種 Linux 發行版。

到 sourceforge網站下載適合的 net-snmp 版本,解壓。

執行文件目錄下的 configure 可執行文件,如果想指定程序包的安裝路徑,那么您首先建立相應的文件夾來存放安裝信息,您可以寫成 ./configure --prefix= 您指定的路徑名。參數 --prefix 用來告訴系統安裝信息存放的路徑,如果您沒有指定路徑,直接執行 ./configure,那么程序包都會安裝在系統默認的目錄下,通常為:/usr/local 下。例如:

1
./configure --prefix=/usr/local/snmp // 配置指定安裝目錄,

在配置過程中需要進行一些簡單的選擇:

1
2
3
4
5
6
7
8
9
default version of-snmp-version: 2
 
Systemcontact information(配置該設備的聯系人): yourname
 
System location ( 該設備的位置 ): china
 
Location to write logfile ( 日志文件位置 ): /var/log/snmpd.log
 
Location to Write persistent( 數據存儲目錄 ): /var/net-snmp

之后編譯並且安裝:

1
#make && make install // 編譯並且安裝

Snmp Trap 的發送和接收演示

實驗環境如圖 4 所示。在測試機 M1 上啟動 snmptrapd 進程,並且在 UDP 1162 端口上監聽 SNMP Trap 信息,一旦收到 Trap,snmptrapd 將所接收到的 Trap 信息內容打印到一個本地的文本文件中。 機器 M2 模擬發送 SNMP Trap 的設備,將調用 net-snmp 的命令行程序 snmptrap 發送一個 Trap。

圖 4. SNMP Trap 實驗環境

圖 4. SNMP Trap 實驗環境

設計一個 Trap 消息

Trap 消息也是用 MIB 來定義的。在下面的例子中,我們定義了一個 Trap 消息:nodeDown

清單 1. Trap nodeDown 的 MIB 定義
1
2
3
4
nodeDown NOTIFICATION-TYPE
    STATUS          current
    DESCRIPTION     "node down notification"
::= { notification 1 }

nodeDown 被定義為 Notification 類型,即 SNMPv2 類型的 Trap。

受空間所限,這里無法貼出完整的 MIB 文件。感興趣的讀者可以參考本文的附件。圖 5 給出了實驗所用的完整 MIB 樹:

圖 5. 包含 Trap nodedown 的完整 MIB 樹

圖 5. 包含 Trap nodedown 的完整 MIB 樹

NodeDown 對象位於 notification 節點下,子 OID 為 1。該 Trap 消息中我們打算包含兩個數據對象:Host 名字和 IP 地址。Host 和 ip 位於 enterprises.sampleTest.data 分支下。

搭建接受 Trap 的 snmptrapd 進程

可以利用 Net-snmp 提供的 snmptrapd 應用程序作為后台 SNMP Trap 服務器,負責接收被管理設備發送過來的 Trap 消息。

在圖 4 實驗機 M1 的命令行窗口中輸入以下命令:

1
snmptrapd – c mysnmptrad.conf udp:1622

該命令啟動了一個 snmptrapd 進程,守候在 UDP 端口 1622 上,偵聽 SNMP Trap 消息。-c 命令行指定了名為 mysnmptrapd.conf 的配置文件。文件內容如下:

1
2
3
$cat mysnmptrapd.conf
traphandle default lognotify IBM-DW-SAMPLE::nodeDown
authCommunity log,execute,net public

其中,以 traphandle 開頭的一行定義了 snmptrapd 進程接收到 Trap 消息后應該執行的動作。在本例中,lognotify 是一個 shell 腳本,功能是將接收到的 Trap 信息寫入文件 checkfile。

以 authCommunity 開頭的一行配置了 snmptrapd 的安全設置,表示可以接收 community 為”public”的 SNMP Trap,並且本進程可以有 log,net 和 execute 的權限。

Log 權限表明收到 Trap 之后 snmptrapd 可以記錄日志;execute 表明收到 Trap 之后可以執行 traphandle 中所指定的操作。Net 表示 snmptrapd 可以將接收到的 Trap 信息轉發到其他的 Receiver 去。(假如需要轉發,還需要對給定的 OID 指定以 forward 為開始的處理細節:forward OID|default DESTINATION)

腳本 lognotify 代碼如下:

清單 2. Lognotify 腳本
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
$cat lognotify
#!/bin/sh
 
read host
read ip
vars=
 
while read oid val
do
   if [ "$vars" = "" ]
   then
     vars="$oid = $val"
   else
     vars="$vars, $oid = $val"
   fi
done
 
echo trap: $1 $host $ip $vars >checkfile

使用 snmptrap 發送 SNMP Trap

可以用命令 snmptrap 在測試機 M2 上發送一個 SNMP Trap。假定 Trap 定義在文件 sample-trap.mib 中。在測試機 M2 上輸入以下命令:

1
2
snmptrap -m ./sample- trap.mib  -v 2c -c public 16.157.76.227:1622 \
"" IBM-DW-SAMPLE::nodeDown     IBM-DW-SAMPLE::nodeDown.1 s "M1"

您在試驗的時候,將 IP 地址替換為 M1 的真實 IP 地址即可。在上面的例子命令中我們只設置了 Trap nodeDown 消息中的 host 信息。其 OID 為 IBM-DW-SAMPLE::nodeDown.1。字母 s 表示該 OID 的類型為 string。“M1”為該 OID 變量的值。

打開文件 checkfile,應該能夠看到如下信息:

1
2
3
4
5
Trap: IBM-DW-SAMPLE::nodeDown < UNKNOWN > UDP: [16.157.76.221]:
  54329->[16.157.76.227]:1622 DISMAN-EVENT-MIB::sysUpTimeInstance=
  5:2:22:26.99, SNMPv2-MIB::snmpTrapOID.0 = SNMPv2-SMI::
  enterprises.10234.100.1 SNMPv2-SMI::enterprises.10234.100.1.1 =
“M1”

還不錯,這表示我們已經能夠用 snmptrap 發送 Trap;並且能夠使用 snmptrapd 來接收 Trap 消息了。

此例雖然簡單,但卻是 SNMP Trap 的典型應用。實際生產環境中的 SNMP Trap 應用的基本模型和本例是類似的。

此外,有的時候,我們可能不僅需要 net-snmp 所提供的現成工具,還需要在自己開發的應用程序中編碼發送 Trap。Net-snmp 不僅提供了現成的工具,也提供了開發庫,下面我們來看看如何在 C 程序中調用 net-snmp 的庫函數進行 SNMP Trap 的開發吧。

C 語言直接調用 API 進行 SNMP Trap 處理

C/C++ 依舊是很多系統必須的編程語言,當您的 C/C++ 程序需要發送 SNMP Trap 時,便可以考慮使用 net-snmp 提供的 SNMP 庫函數來實現。

所需要的頭文件

為了使用 netsnmp 的 API,必須 include 以下這些頭文件:

1
2
#include < net-snmp /net-snmp-config.h>
#include < net-snmp /net-snmp-includes.h>

初始化

在使用 netsnmp 庫之前,先要做一些必要的初始化工作。 函數 init_snmp 初始化 SNMP Library。 假如在調用 init_snmp 函數時指定了文件名,init_snmp 函數將讀取配置文件,設置諸如 Access Control 等具體配置。否則會使用默認的 /etc 下面的配置文件。

初始化 SNMP 庫之后,我們就可以打開一個會話 session。此后所有和 NMS 的信息交互都在該 session 內進行,因為可能在同一台機器上運行多個 SNMP 進程,每個進程都需要自己獨立的 session 來和 NMS 進行信息交互。

調用函數 snmp_sess_init 成功后將返回一個 session 數據結構。我們用該數據結構來設置 session 的屬性,比如 peer 的 IP 信息。

這里 peer 就是圖 2 中的 M2,在我本人的試驗環境下,該機器的 IP 地址為 16.157.76.227。因此在下面的代碼中,我將 peername 設置為 16.157.76.227。

還可以設置其它的必要信息,比如 Community,即用於 SNMP 安全的社區設置,前面 snmptrapd 設置為 public,因此這里也設置為 public。這類似於通行密碼,不過安全性的確比較弱。

Session 屬性設置好之后便可以使用函數 snmp_open 打開 session。示例代碼如下。

清單 3. 代碼最大長度示例
1
2
3
4
5
6
7
8
9
10
init_snmp("myexample");
struct snmp_session session;
snmp_sess_init(&session);
session.version = SNMP_VERSION_2c;
strcpy(peername,"16.157.76.227:1622");
strcpy(commu,"public");
session.peername = peername;
session.community = (unsigned char*)commu;
session.community_len = strlen(commu);
ss = snmp_open(&session);

打開一個會話之后,程序可以通過該會話發送 Trap 給 NMS,也可以從 NMS 接受 SNMP get/set 操作。

創建 pdu,發送 Trap

每個 Trap 都由 PDU 承載,PDU 有固定的格式。 為了簡化描述,本文只描述 SNMPv2 的 Trap 發送方法。SNMP v2c 和 v1 的 Trap 有所不同。請閱讀參考文獻了解這些不同的細節,此外本文的附件代碼中也實現了 v1 的支持代碼。SNMPv2 的 Trap PDU 定義如下:

圖 3. SNMP Trap PDU 格式

圖 3. SNMP Trap PDU 格式

其中各個組成部分的解釋如下: sysUpTime– 被管理設備上一次初始化網絡到本 Trap 發送以來的累積時間。 snmpTrapOID– 表示本 PDU 是一個 Trap,有固定的值。對於一般的 Trap,RFC1907 給出了通用的定義。用戶自定義的 Trap 通常是由以下幾個部分連接而成:SNMPv1 Enterprise parameter + '0' + SNMPv1 Specific trap code。 VarBindList– 變量列表,所謂變量就是 Trap 消息中所攜帶的信息單元。

下面的代碼片斷用來填充如圖三所示的一個 PDU。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
oid objid_sysuptime[] = { 1, 3, 6, 1, 2, 1, 1, 3, 0 };
  oid objid_snmptrap[] = { 1, 3, 6, 1, 6, 3, 1, 1, 4, 1, 0 };
  netsnmp_pdu * pdu = NULL;
  oid tmpOID[MAX_OID_LEN];
  size_t tmpOID_len;
  in_addr_t addr;
 
  pdu = snmp_pdu_create(SNMP_MSG_TRAP2);
  long sysuptime;
  char tempbuf[128];
  memset(tempbuf,128,0);
  sprintf(tempbuf,"%ld",sysuptime);
  sysuptime = get_uptime();
  oid tmpOID[MAX_OID_LEN];
  size_t tmpOID_len;
  tmpOID_len = MAX_OID_LEN;
  if(!snmp_parse_oid(TRAP_NAME_1, tmpOID, &tmpOID_len))
snmp_error(“snmp_parse_oid”);
  snmp_add_var(pdu, objid_sysuptime, sizeof(objid_sysuptime)/sizeof(oid), 't', tempbuf);
  snmp_add_var(pdu, objid_snmptrap, sizeof(objid_snmptrap)/sizeof(oid),'o',
               “IBM-DW-SAMPLE::nodeDown”);

首先調用函數 snmp_pdu_create 創建一個 SNMPv2 的 Trap PDU。然后調用 snmp_add_var 向該 PDU 中添加圖三所示的三個部分。 sysUpTime 在 SNMPv2-MIB 中定義,其 OID 為”1.3.6.1.2.1.1.3.0”。我們只需要通過 get_uptime() 函數獲得該值,然后調用 snmp_add_var 將該變量加入剛才創建的 PDU 中。

同理,snmpTrapOID 也是固定的 :” 1.3.6.1.6.3.1.1.4.1.0”。同樣利用 snmp_add_var 函數將我們定義的 OID 為“IBM-DW-SAMPLE::nodeDown”的 Trap 加入該 PDU。 稍稍留意一下,您應該可以發現,sysUpTime 的類型為't',即 timestamp;而 snmpTrapOID 的類型為’ o ’,即 OID 類型。PDU 內的每一個元素都擁有自己的類型。 在該 Trap 中,我們還打算攜帶兩個變量:IBM-DW-SAMPLE::host 和 IBM-DW-SAMPLE::ip。表示一些真正有趣的信息。 添加這兩個變量的代碼如下所示:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
HP-DW-SAMPLE::ip",varId,&varIdLen))
     {
       snmp_perror("ip");
     }
     else
     {
        printf("Success snmp_parse_oid\n");
        snmp_add_var(pdu, varId, sizeof(varId)/sizeof(oid),'s',"2.2.2.2");
     }
     if(!snmp_parse_oid("HP-DW-SAMPLE::host",varId,&varIdLen))
     {
       snmp_perror("host");
     }
     else
     {
        retv =  snmp_add_var(pdu, varId, sizeof(varId)/sizeof(oid),'s',”M1”);
  }

用 snmp_parse_oid 解析相應變量的 OID,然后就可以調用 snmp_add_var 將您想設置的值加入 PDU 中了。假如您直接在代碼中使用”HP-DW-SAMPLE::ip”的 OID,(即 1.3.6.1.4.1.10234.10.2),那么可以不需要調用 snmp_parse_oid。該函數只是把一個好記的字符串翻譯為一串數字的 OID。不過這就好比用 16.157.1.2 而不使用 www.ibm.com 一樣。

至此,一個 SNMP Trap PDU 就創建成功了。將該 PDU 發送出去即可:

1
2
3
4
if( !snmp_send(sptr, pdu) )
{
   snmp_error("Send pdu error \n");
}

結束清理

程序結束之前需要做清理工作,代碼如下:

1
2
3
snmp_close(sptr);
snmp_shutdown( "myexample" );
SOCK_CLEANUP;

一個基本的 SNMP Trap 發送程序就這樣完成了。我們在 M1 上編譯運行它:

1
2
-bash-3.2$ gcc -o t1 snmptrap.c – lnetsnmp
-bash-3.2$ ./t1

在 M2 上查看 checkfile,可以驗證所收到的 Trap 消息的內容。假如您能看到該文件中正確的內容,那么恭喜您,您已經擁有了編程發送 SNMP Trap 的能力了。

結束語

雖然 SNMP 叫做簡單網絡管理協議,但似乎並不簡單,甚至可以說十分復雜,本文只關注 Trap 的相關概念和編程方法,卻也只能走馬觀花地說個皮毛,還是完全的實用主義。於我而言,這點兒皮毛便可以讓我交代手頭的工作任務了,但一個有理想的程序員或許會僅將本文當作一個起點,堅定不移地繼續深入地了解更多 SNMP 的知識吧。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM