使用 C/C++ 进行 SNMP 开发,网上比较流行的主要是用 net-snmp 和 snmp++ 。在 sourceforge 上以 Qt 和 snmp 为关键词进行搜索,搜到的项目 net-snmp 相关的占了多数,推测,net-snmp 的使用人数可能更多一点。遂决定采用 net-snmp。
仍然是从 sourceforge 开始,随便找了一个规模不大的项目,开始对 net-snmp 进行熟悉。
1. 尝试代码编译
从项目把代码拉过来: git clone https://git.code.sf.net/p/qt-snmp/code qt-snmp-code
文件里没有 project 文件,使用 qt -project 生成 source.pro,qmake,make。
遇到没有 net-snmp-config.h 头文件的问题,很明显,是因为 net-snmp 库没有安装。
2. 安装 libnetsnmp
首先,因为这个代码库看起来比较早,所以选择了一个比较早期的 net-snmp 版本(2011)。snmp 已经是非常成熟的协议,所以,并不担心比较早的版本协议实现不完整。
从 sourceforge 下载代码包 net-snmp-5.4.4.tar.gz,并解压,进入代码根目录。
因为 net-snmp 的部分实现严重依赖 perl,所以,需要先安装 perl 的开发包:
然后进行 configure , make , make install.
另外,net-snmp 还依赖 openssl。在这里花了比较长的时间。因为我的交叉编译器只有 openssl 头文件,并没有库文件,而且我编译 net-snmp 又是用的静态库,所以,链接的时候老是提示没有一些加密函数。下面是我重新编译 openssl 的 config 配置(用从 ubuntu 下 的 openssl098_0.9.8o.orig.tar.gz):
CC=arm-linux-gcc ./config no-asm --prefix=/tmp/openssl
为 arm 编译 net-snmp 库使用的 configure 选项:
../configure --host=arm-linux --target=arm-linux --build=i686-linux --disable-shared --disable-scripts -enable-mini-agent --disable-ipv6 --disable-manuals --disable-ucd-snmp-compatibility --enable-as-needed --with-endianness=little --prefix=/tmp/snmp/
3. 继续编译 demo 的代码
因为已经安装了 libnetsnmp,而且代码要用到这个包,所以需要修改 project 文件,添加:
LIBS +=-lnetsnmp
make,成功。
暂时没有测试的环境,不知道程序是否有效,暂时先阅读一下代码。
4. 阅读项目代码
读完代码发现,真的只是写了一个最基本的 demo,界面倒是看起来做了一堆。甚至怀疑他这个最基本功能有没有实现,暂时没法测试,学习一下他的过程。
最主要业务代码,是在 snmpGet() 函数里,全文如下:
void MainWindow::SnmpGet() { init_snmp("snmp get"); struct snmp_session sessionToPeer; snmp_sess_init(&sessionToPeer); sessionToPeer.peername = strdup(agentDeviceAddressLineEdit->text().toStdString().c_str()); /*memory allocated by strdup() will be freed by calling snmp_close() */ if(snmpVersion1RadioButton->isChecked()) {/* snmp version 1 is obsolete, do nothing about it. */ } if(snmpVersion2RadioButton->isChecked()) {/* only version 2 community is implemented here */ sessionToPeer.version = SNMP_VERSION_2c; sessionToPeer.community = (u_char*) (strdup(communityLineEdit->text().toStdString().c_str())); sessionToPeer.community_len = strlen((const char*) sessionToPeer.community); } if(snmpVersion3RadioButton->isChecked()) { //TODO: implement SNMP version 3 options. more item may be needed to add to combo box. } sessionToPeer.retries = retriesSpinBox->value(); sessionToPeer.timeout = timeoutSpinBox->value(); SOCK_STARTUP; struct snmp_session* sessionReturnedByLibrary = snmp_open(&sessionToPeer); if(sessionReturnedByLibrary == NULL) { #ifdef QT_DEBUG snmp_sess_perror((const char*) "No Ack!", sessionReturnedByLibrary); #endif //QT_DEBUG SOCK_CLEANUP; return; } struct snmp_pdu* requestPdu = snmp_pdu_create(SNMP_MSG_GET); oid requestOid[MAX_OID_LEN]; size_t requestOidLength = MAX_OID_LEN; snmp_parse_oid(".1.3.6.1.2.1.1.1.0", requestOid, &requestOidLength); snmp_add_null_var(requestPdu, requestOid, requestOidLength); struct snmp_pdu* responsePdu = NULL; int snmpStatus = snmp_synch_response(sessionReturnedByLibrary, requestPdu, &responsePdu); if(snmpStatus == STAT_SUCCESS and responsePdu->errstat == SNMP_ERR_NOERROR) { /* SUCCESS: Print the result variables */ struct variable_list *snmpVariables; #ifdef QT_DEBUG for(snmpVariables = responsePdu->variables; snmpVariables; snmpVariables = snmpVariables->next_variable) { print_variable(snmpVariables->name, snmpVariables->name_length, snmpVariables); } #endif //QT_DEBUG /* retrieve response that we're interested. */ #ifdef QT_DEBUG int count = 1; #endif //QT_DEBUG for(snmpVariables = responsePdu->variables; snmpVariables != NULL; snmpVariables = snmpVariables->next_variable) { if(snmpVariables->type == ASN_OCTET_STR) { char* response = (char *) malloc(1 + snmpVariables->val_len); memcpy(response, snmpVariables->val.string, snmpVariables->val_len); response[snmpVariables->val_len] = '