SNMP(Simple Network Management Protocol,簡易網路管理通訊協定)的前身是簡單網關監控協議(SGMP),用來對通訊線路進行管理。隨後,人們對SGMP進行了很大的修改,特別是加入了符合Internet定義的SMI和MIB:體繫結構,改進後的協議就是著名的SNMP。SNMP的目標是管理互連網Internet上眾多廠家生產的軟硬體平台,因此SNMP受Internet標準網路管理架構的影響也很大。現在SNMP已經出到第三個版本的協議,其功能較以前已經大大地加強和改進了.
MIB,Management Information Base:管理資訊庫,由網路管理協議訪問的管理對象資料庫,它包括SNMP可以通過網路裝置的SNMP管理代理程式進行設定的變數。SMI,Structure of Management Information:管理資訊結構,用於定義通過網路管理協議可訪問的對象的規則。
SMI定義在MIB中使用的資料類型及網路資源在MIB中的名稱或表示。
使用SNMP進行網路管理需要下面幾個重要部分:管理基站,管理代理程式,管理資訊庫和網路管理工具。管理基站通常是一個獨立的裝置,它用作網路管理者進行網路管理的使用者介面。基站上必須裝備有管理軟體,管理員可以使用的使用者介面和從MIB取得資訊的資料庫,同時為了進行網路管理它應該具備將管理命令發出基站的能力。
管理代理程式是一種網路裝置,如主機,橋接器,路由器和集線器等,這些裝置都必須能夠接收管理基站發來的資訊,它們的狀態也必須可以由管理基站監視。管理代理程式響應基站的請求進行相應的操作,也可以在沒有請求的情況下向基站發送資訊。
MIB是對象的集合,它代表網路中可以管理的資源和裝置。每個對象基本上是一個資料變數,它代表被管理的對象的一方面的資訊。
最後一個方面是管理協議,也就是SNMP,SNMP的準系統是:取得,設定和接收代理髮送的意外資訊。取得指的是基站發送請求,代理根據這個請求回送相應的資料,設定是基站設定管理對象(也就是代理)的值,接收代理髮送的意外資訊是指代理可以在基站未請求的狀態下向基站報告發生的意外情況。
SNMP為應用程式層協議,是TCP/IP協議族的一部分。它通過使用者資料包通訊協定(UDP)來操作。在分立的管理站中,管理者進程對位於管理站中心的MIB的訪問進行控制,並提供網路系統管理員介面。管理者進程通過SNMP完成網路管理。SNMP在UDP、IP及有關的特殊網路通訊協定(如,Ethernet, FDDI, X.25)之上實現。
SNMPv2標準的核心就是通訊協定———它是一個請求/應答式的協議。
這個協議提供了在manager與agent、manager與manager之間交換管理資訊的直觀、基本的方法。
每條SNMPv2的報文都由一些域構成:
如果發送方、接收方的兩個Party都採用了驗證(authentication)機制,它就包含與驗證有關的資訊;否則它為空白(取NULL)。
驗證的過程如下:發送方和接收方的Party都分別有一個驗證用的密鑰(secretkey)和一個驗證用的演算法。
報文發送前,發送方先將密鑰值填入圖中digest域,作為報文的首碼。然後根據驗證演算法,對報文中digest域以後(包括digest域)的報文資料進行計算,計算出一個摘要值(digest),再用摘要值取代密鑰,填入報文中的digest域。接收方收到報文後,先將報文中的摘要值取出來,暫存在一個位置,然後用發送方的密鑰放入報文中的digest。將這兩個摘要值進行比較,如果一樣,就證明發送方確實是srcParty域中所指明的那個Party,報文是合法的;如果不一樣,接收方斷定發送方非法。驗證機制可以防止非法使用者"冒充"某個合法Party來進行破壞。
authInfo域中還包含兩個時間戳記(timestamp),用於發送方與接收方之間的同步,以防止報文被截獲和重發。
SNMPv2的另一大改進是可以對通訊報文進行加密,以防止監聽者竊取報文內容。除了privDst域外,報文的其餘部分可以被加密。發送方與接收方採用同樣的密碼編譯演算法(如DES)。 通訊報文可以不加任何安全保護,或只進行驗證,也可以二者都進行。
我們下面來實現SNMP的編程實現網路資料的抓取
#include <stdio.h>#include <malloc.h>#include <snmp.h>#include <mgmtapi.h>#pragma comment(lib,"Mgmtapi.lib")#pragma comment(lib,"Snmpapi.lib")//利用 SNMP API時需要以上標頭檔和庫檔案#define GET 1 //get,就理解成擷取一個資訊。#define GETNEXT 2 //getnext,就理解成擷取下一個資訊。#define WALK 3 //walk,就理解成擷取一堆資訊,即所有資料庫子樹/子目錄的資訊#define TIMEOUT 6000 /* milliseconds */#define RETRIES 3//一些有用的oidchar *SnmpOid[5]={".1.3.6.1.2.1.25.4.2.1.2",//進程列表 ".1.3.6.1.4.1.77.1.2.25.1.1",//系統使用者".1.3.6.1.4.1.77.1.4.1.0",//網域名稱".1.3.6.1.2.1.25.6.3.1.2",//列出安裝的軟體".1.3.6.1.2.1.1"};// 列出系統資訊void usage(char *name){printf("=================SNMP tool================\n");printf("=======gxisone@hotmail.com 2004/8/10====\n");printf("\nusage: %s [remoteip] [sysprocess|sysuser|domainname|sysinf|software]\n",name);printf("Exameple: %s 192.168.1.1 sysuser\n",name);}int main(int argc,char *argv[]) { int operation; LPSTR agent; LPSTR community; RFC1157VarBindList variableBindings; LPSNMP_MGR_SESSION session; int timeout = TIMEOUT; int retries = RETRIES; int i; BYTE requestType; AsnInteger errorStatus; AsnInteger errorIndex; char *chkPtr = NULL; operation = WALK; //這個程式使用WALK來擷取資訊 if (argc != 3) { usage(argv[0]); return 0; } else {AsnObjectIdentifier reqObject; // 取得IP地址agent = (LPSTR)SNMP_malloc(strlen(*argv) + 1);strcpy(agent, argv[1]); community="public";//設定查詢密碼 variableBindings.list = NULL; variableBindings.len = 0;// 設定 oidif(!strcmp(argv[2],"sysprocess"))i=0;else if(!strcmp(argv[2],"sysuser"))i=1;else if(!strcmp(argv[2],"domainname"))i=2;else if(!strcmp(argv[2],"software"))i=3;else if(!strcmp(argv[2],"sysinf"))i=4;else{usage(argv[0]);return 0;}printf("%s\n",SnmpOid[i]); // 把字串轉換成標準oid if (!SnmpMgrStrToOid(SnmpOid[i], &reqObject)) { printf("Error: Invalid oid, %s, specified.\n", *argv); return 1; } else { variableBindings.len++; if ((variableBindings.list = (RFC1157VarBind *)SNMP_realloc( variableBindings.list, sizeof(RFC1157VarBind) * variableBindings.len)) == NULL) { printf("Error: Error allocating oid, %s.\n",*argv); return 1; } variableBindings.list[variableBindings.len - 1].name=reqObject; variableBindings.list[variableBindings.len - 1].value.asnType=ASN_NULL; } // Make sure only one variable binding was specified if operation // is WALK. if (operation == WALK && variableBindings.len != 1) { printf("Error: Multiple oids specified for WALK.\n"); return 1; } // Establish a SNMP session to communicate with the remote agent. The // community, communications timeout, and communications retry count // for the session are also required.if ((session = SnmpMgrOpen(agent, community, timeout, retries)) == NULL) { printf("error on SnmpMgrOpen %d\n", GetLastError()); return 1; } } // end if{ AsnObjectIdentifier root; AsnObjectIdentifier tempOid; SnmpUtilOidCpy(&root, &variableBindings.list[0].name); requestType = ASN_RFC1157_GETNEXTREQUEST; for(;;) { if (!SnmpMgrRequest(session, requestType, &variableBindings, &errorStatus, &errorIndex)) { printf("error on SnmpMgrRequest %d\n", GetLastError()); break; } else { if (errorStatus == SNMP_ERRORSTATUS_NOSUCHNAME || SnmpUtilOidNCmp(&variableBindings.list[0].name, &root, root.idLength)) { printf("End of MIB subtree.\n\n"); break; } if (errorStatus > 0) { printf("Error: errorStatus=%d, errorIndex=%d \n", errorStatus, errorIndex); break; } else { // 列印查詢的結果 char *string = NULL; SnmpMgrOidToStr(&variableBindings.list[0].name, &string); printf("Variable = %s\n", string); if (string) SNMP_free(string); printf("Value = "); SnmpUtilPrintAsnAny(&variableBindings.list[0].value); printf("\n"); }} // end if()// 準備下一次查詢 SnmpUtilOidCpy(&tempOid, &variableBindings.list[0].name); SnmpUtilVarBindFree(&variableBindings.list[0]); SnmpUtilOidCpy(&variableBindings.list[0].name, &tempOid); variableBindings.list[0].value.asnType = ASN_NULL; SnmpUtilOidFree(&tempOid);} // end while() // 釋放資源 SnmpUtilVarBindListFree(&variableBindings); SnmpUtilOidFree(&root);} // 關閉 SNMP session if (!SnmpMgrClose(session))//清理退出 { printf("error on SnmpMgrClose %d\n", GetLastError()); return 1; } return 0; }