• Qt 使用 net-snmp 包的过程记录


    使用 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] = '';
    #ifdef QT_DEBUG
            printf("value #%d is a string: %s
    ", count++, response);
    #endif //QT_DEBUG
            resultTextEdit->setText(QString(response));
            free(response);
          } else {
    #ifdef QT_DEBUG
            printf("value #%d is NOT a string! Ack!
    ", count++);
    #endif //QT_DEBUG
          }
        }
      } else {
        /* FAILURE: print what goes wrong! */
    #ifdef QT_DEBUG
        if(snmpStatus == STAT_SUCCESS) {
          fprintf(stderr, "Error in packet
    Reason: %s
    ", snmp_errstring(responsePdu->errstat));
        } else if(snmpStatus == STAT_TIMEOUT) {
          fprintf(stderr, "Timeout: No response from %s.
    ", sessionToPeer.peername);
        } else {
          snmp_sess_perror("snmp get", sessionReturnedByLibrary);
        }
    #endif //QT_DEBUG
      }
    
      /*
       * Clean up:
       *  1) free the response.
       *  2) close the session.
       */
      if(responsePdu) {
        snmp_free_pdu(responsePdu);
      }
      snmp_close(sessionReturnedByLibrary);
    
      SOCK_CLEANUP;
    }

     大致步骤:

    1)  对 snmp 协议栈进行初始化,init_snmp();

    2) 新建 snmp 会话,对 session 进行初始化, 并对 session 进行基本的设置,比如 session 使用的协议、session 的重试次数以及等待时间等;

    3) 使用 snmp_create_pdu( MSG_TYPE) 来组装 request_pdu。查看这个版本的协议,支持的 pdu 类型有:

        /*
         * PDU types in SNMPv1, SNMPsec, SNMPv2p, SNMPv2c, SNMPv2u, SNMPv2*, and SNMPv3 
         */
    #define SNMP_MSG_GET        (ASN_CONTEXT | ASN_CONSTRUCTOR | 0x0) /* a0=160 */
    #define SNMP_MSG_GETNEXT    (ASN_CONTEXT | ASN_CONSTRUCTOR | 0x1) /* a1=161 */
    #define SNMP_MSG_RESPONSE   (ASN_CONTEXT | ASN_CONSTRUCTOR | 0x2) /* a2=162 */
    #define SNMP_MSG_SET        (ASN_CONTEXT | ASN_CONSTRUCTOR | 0x3) /* a3=163 */
    
        /*
         * PDU types in SNMPv1 and SNMPsec 
         */
    #define SNMP_MSG_TRAP       (ASN_CONTEXT | ASN_CONSTRUCTOR | 0x4) /* a4=164 */
    
        /     * PDU types in SNMPv2p, SNMPv2c, SNMPv2u, SNMPv2*, and SNMPv3 */
    #define SNMP_MSG_GETBULK    (ASN_CONTEXT | ASN_CONSTRUCTOR | 0x5) /* a5=165 */
    #define SNMP_MSG_INFORM     (ASN_CONTEXT | ASN_CONSTRUCTOR | 0x6) /* a6=166 */
    #define SNMP_MSG_TRAP2      (ASN_CONTEXT | ASN_CONSTRUCTOR | 0x7) /* a7=167 */
    
        /*
         * PDU types in SNMPv2u, SNMPv2*, and SNMPv3 
         */
    #define SNMP_MSG_REPORT     (ASN_CONTEXT | ASN_CONSTRUCTOR | 0x8) /* a8=168 */

    为 request_pdu 指定 oid(这里指定固定的 oid,mib 文件并没有使用)。

    4) 使用 nmp_synch_response(sessionReturnedByLibrary, requestPdu, &responsePdu) 来出发请求,并获取 resopose_pdu.

    5) 当正确的获取 response_pdu 之后,在一个 for 循环中历遍 response_pdu 中的 netsnmp_variable_list,将所有 variables 的 value 都按照字符串打印出来。

    6) 到所有的最后,关闭 session,删除 pdu。

    以上,完成了一个基本的 snmp_get 请求。

  • 相关阅读:
    HttpModule学习总结实例应用读书笔记
    SEO入门教程之入门相关
    HttpHandler学习总结实例应用读书笔记
    服务器安全设置总结(Win2003)
    网站建设合同书
    HTML标签解释大全
    敏捷之痒
    一个google浏览器很意思的东东
    C#访问非托管DLL
    随着DzNT的开源,我将投入到.NET的开发当中
  • 原文地址:https://www.cnblogs.com/pied/p/7501248.html
Copyright © 2020-2023  润新知