基于visual c++之windows核心编程代码分析(31)SNMP协议编程
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 //一些有用的oid char *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; // 设置 oid if(!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; }