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] = '\0';
#ifdef QT_DEBUG
        printf("value #%d is a string: %s\n", count++, response);
#endif //QT_DEBUG
        resultTextEdit->setText(QString(response));
        free(response);
      } else {
#ifdef QT_DEBUG
        printf("value #%d is NOT a string! Ack!\n", count++);
#endif //QT_DEBUG
      }
    }
  } else {
    /* FAILURE: print what goes wrong! */
#ifdef QT_DEBUG
    if(snmpStatus == STAT_SUCCESS) {
      fprintf(stderr, "Error in packet\nReason: %s\n", snmp_errstring(responsePdu->errstat));
    } else if(snmpStatus == STAT_TIMEOUT) {
      fprintf(stderr, "Timeout: No response from %s.\n", 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 請求。

 


免責聲明!

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



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