Search code examples
c++qtnetworkingnet-snmp

net-snmp v2c working well but v3 return STAT_ERROR


I'm currently trying to develop snmpManager in my embedded device(linux OS). This device should communicate with another device's snmpAgent. Well, my code working good for snmpV2c also my snmp packets show in wireshark pcap. But when i try snmpV3 packets snmp_synch_response(session, requestPdu, &responsePdu) func return STAT_ERROR Btw snmpV2c was returning STAT_SUCCESS My code is shown in below, i created snmp session for v2 and v3.

    snmp_session *snmptypes::fCreateSession(QString IP)
    {
        struct snmp_session session;
        snmp_sess_init(&session);
        session.peername = strdup(IP.toAscii());
        static const char* v3_passphrase = "blablabl";
        session.retries = 2;
        session.timeout = 1000000;
        session.engineBoots = 0;
        session.engineTime = 0;
        QString engineID = "0000000c000000007f000001";
        session.contextEngineID = reinterpret_cast<uchar*>(strdup(engineID.toAscii()));
        qDebug()<<"contextEngineID:"<<session.contextEngineID;
        /*memory allocated by strdup() will be freed by calling snmp_close() */
        if(SnmpMainWidget::activeWaveForm == SnmpMainWidget::snmpv2c)
        {
            session.version = SNMP_VERSION_2c;
            const char *str;
            QString path = "user";
            QByteArray ba;
            ba = path.toLatin1();
            str = ba.data();
            session.community =  const_cast<unsigned char*>(reinterpret_cast<const unsigned char *>(str));
            session.community_len = path.length();
            session.retries = 2;
            session.timeout = 1000000;
        }
        else if (SnmpMainWidget::activeWaveForm == SnmpMainWidget::snmpv3)
        {
            session.version = SNMP_VERSION_3;
            session.securityName = strdup("blabl");
            session.securityNameLen = strlen(session.securityName);
            session.securityModel = USM_SEC_MODEL_NUMBER;
            session.securityLevel = SNMP_SEC_LEVEL_AUTHNOPRIV;
            session.securityAuthProto = usmHMACMD5AuthProtocol;
//          session.securityAuthProtoLen = sizeof (usmHMACMD5AuthProtocol) / sizeof (oid);
            session.securityAuthProtoLen = USM_AUTH_PROTO_MD5_LEN;
            session.securityPrivProto = usmNoPrivProtocol;
            session.securityPrivProtoLen = USM_PRIV_PROTO_NOPRIV_LEN;
            session.securityAuthKeyLen =USM_AUTH_KU_LEN;
            
            if (generate_Ku(session.securityAuthProto,session.securityAuthProtoLen,(u_char *) (v3_passphrase),
                            strlen(v3_passphrase),session.securityAuthKey,
                            &session.securityAuthKeyLen) != SNMPERR_SUCCESS)
            {
                qWarning()<<"ERROR KODU"<<generate_Ku(session.securityAuthProto,session.securityAuthProtoLen,(u_char *) (v3_passphrase),
                                                      strlen(v3_passphrase),session.securityAuthKey,
                                                      &session.securityAuthKeyLen);
                snmp_log(LOG_ERR,"Error generating Ku from authentication pass phrase. \n");
                exit(1);
            }
        }
        struct snmp_session* openedSession = snmp_open(&session);
        if(openedSession == nullptr)
        {
            snmp_sess_perror(reinterpret_cast<const char*>("No Ack!"), openedSession);
            qWarning()<<"Error while Session opening! FILE:"<<__FILE__<<"LINE:"<<__LINE__;
        }
        else
            qDebug()<<"creating session is succeed!";
        return openedSession;
    }

Session is creating successfully. no problem so far. Let's continue, then i create pdu like shown below,

    snmp_pdu *snmptypes::fCreatePDU(tsPDUvariables PDUvariables)
    {
        //    qDebug()<<"nrepeaters"<<PDUvariables.nrepeaters;
        //    qDebug()<<"mrepeaters"<<PDUvariables.mrepetitions;
        qDebug()<<"setParam"<<PDUvariables.setParam;
        qDebug()<<"dataType"<<PDUvariables.dataType;
        
        struct snmp_pdu* requestPdu;
        oid requestOid[MAX_OID_LEN];
        size_t requestOidLength = MAX_OID_LEN;
        snmp_parse_oid(static_cast<const char*>(PDUvariables.OID.toAscii()), requestOid, &requestOidLength);
        
        switch (PDUvariables.msgType) {
        case snmpGet:
            requestPdu = snmp_pdu_create(SNMP_MSG_GET);
            if (SnmpMainWidget::activeWaveForm == SnmpMainWidget::snmpv3){
                    requestPdu->version = SNMP_VERSION_3;
                    requestPdu->securityName = strdup("user");
                    requestPdu->securityNameLen = strlen(requestPdu->securityName);
                    requestPdu->securityLevel = SNMP_SEC_LEVEL_AUTHNOPRIV;
                    requestPdu->securityModel = USM_SEC_MODEL_NUMBER;
            }
            snmp_add_null_var(requestPdu, requestOid, requestOidLength);
            break;
//there are some other case like set,getbulk but don't need to focus these.get enough for now.
        }
        qDebug()<<"creating PDU is succeed!";
        
        return requestPdu;
    }

Finally I'm using snmpget func. for send snmp packet to dedicated IP; Problem occure right here, snmpv2 snmpStatus return success but snmpv3 reutn error. There for snmpv3 packet doesn't appear on wireshark and also doesn't send. I cant understand what am i wrong in V3 while V2 was working well.

bool snmptypes::fSnmpGet(QString IP,QString OID)
{
    const char PACKET_HEADER_BYTE = 4;//4Byte = SOH + len/256 + len%256 + EOH
    struct snmp_session* session = fCreateSession(IP);
    tsPDUvariables PDUvariables;
    PDUvariables.OID = OID;
    PDUvariables.msgType = snmpGet;
    struct snmp_pdu* requestPdu = fCreatePDU(PDUvariables);
    qDebug()<<"PDUvariables.msgType"<<PDUvariables.msgType;
    qDebug()<<"PDUvariables.dataType"<<PDUvariables.dataType;
    qDebug()<<"PDUvariables.OID"<<PDUvariables.OID;
    qDebug()<<"PDUvariables.setParam"<<PDUvariables.setParam;
    struct snmp_pdu* responsePdu = nullptr;
    if(requestPdu != nullptr && session != nullptr)
    {
        int snmpStatus = snmp_synch_response(session, requestPdu, &responsePdu);
        qDebug()<<"snmpStatus"<<snmpStatus;
        qDebug()<<"requestStatus::errStat"<<requestPdu->errstat;
        qDebug()<<"session->s_errno"<<session->s_errno;
        
        if(snmpStatus == STAT_SUCCESS)
        {
            if( responsePdu->errstat == SNMP_ERR_NOERROR and responsePdu->errstat == SNMP_ERR_NOERROR)
            {
                /* SUCCESS: Print the result variables */
                struct variable_list *snmpVariables;
                for(snmpVariables = responsePdu->variables; snmpVariables; snmpVariables = snmpVariables->next_variable)
                {            qDebug()<<"fSnmpGet"<<__LINE__;
                    print_variable(snmpVariables->name, snmpVariables->name_length, snmpVariables);
                }
                
                for(snmpVariables = responsePdu->variables; snmpVariables != nullptr; snmpVariables = snmpVariables->next_variable)
                {
                    
                    QString strOID;
                    for(size_t i =0; i<snmpVariables->name_length;i++)
                    {
                        qDebug()<<"snmpVariables->name[i]"<<snmpVariables->name[i];
                        
                        strOID.append(".");
                        strOID.append(QString::number( snmpVariables->name[i]));
                    }
                    qDebug()<<"strOID"<<strOID;
                    
                    unsigned int OIDLen =  static_cast<unsigned int> (strOID.length());
                    qDebug()<<"OID_Length:"<< static_cast<unsigned int> (OIDLen);
                    unsigned int paramLen = static_cast<unsigned int> (snmpVariables->val_len);
                    qDebug()<<"paramLen"<<paramLen;
                    
                    unsigned int    txDataLen = 5+OIDLen + paramLen;
                    qDebug()<<"txDataLen"<<txDataLen;
                    
                    int    totalTxDataLen = txDataLen+PACKET_HEADER_BYTE;
                    unsigned char   *outMessBuff = static_cast<unsigned char *>(malloc(totalTxDataLen));
                    unsigned char   *dataBuff = static_cast<unsigned char *>(malloc(txDataLen));
                    dataBuff[0] = snmpReqResponse;
                    dataBuff[1] = static_cast<unsigned char>(OIDLen/256 );
                    dataBuff[2] = static_cast<unsigned char>(OIDLen%256) ;
                    dataBuff[3] = static_cast<unsigned char>(paramLen/256) ;
                    dataBuff[4] = static_cast<unsigned char>(paramLen%256 );
                    
                    if(OIDLen > 0)
                    {
                        memcpy(dataBuff+5,strOID.toAscii(),OIDLen);
                        
                    }
                    if(paramLen > 0)
                    {
                        memcpy(dataBuff+5+OIDLen,snmpVariables->val.string,paramLen);
                    }
                    
                    totalTxDataLen = SnmpMainWidget::IPC.prepareIPCmessage(outMessBuff, dataBuff, static_cast<int>(txDataLen));
                    SnmpMainWidget::IPC.sendIpcMessageToQt(reinterpret_cast<const char *>(outMessBuff),static_cast<unsigned int>(totalTxDataLen));  // reinterpret_cast bit of dangerous should be good test !!
                    SnmpMainWidget::IPC.PrintPacketInHex("SNMP-->QT :",outMessBuff, static_cast<unsigned int>(totalTxDataLen));
                    free(dataBuff);
                    free(outMessBuff);
                }
            }
        }
        else
        {
            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", session->peername);

            }
            else
            {
                printf("snmp get requestPdu->command: %d\n", requestPdu->command);
                printf("snmp get errstat: %s\n", snmp_errstring(requestPdu->errstat));
                printf("snmp get errindex: %s\n", snmp_errstring(requestPdu->errindex));
                fprintf(stderr, "session->s_errno %d,session->s_snmp_errno %d \n", session->s_errno, session->s_snmp_errno);
                
            }
            
            if(responsePdu)
            {
                snmp_free_pdu(responsePdu);
            }
            snmp_close(session);
            
        }
    }
    else
        return false;
    return true;
}

Solution

  • I'm just solving the problem. When I checked the internet I realize missing init_snmp("snmpapp"); function in my code. After adding init snmp, v3 set get message is send perfectly.

        snmp_session *snmptypes::fCreateSession(QString IP)
    {
        struct snmp_session session;
        init_snmp("snmpapp");
        snmp_sess_init(&session);
        session.peername = strdup(IP.toAscii());
    .....
    }
    

    Maybe it would be a useful answer for people who will have the same problem.