LoginSignup
2
2

More than 5 years have passed since last update.

net-snmp apiのsnmp_sess_open

Last updated at Posted at 2016-07-01

snmp_sess_openを使って、open、closeするサンプル。
メモリリークしないように、特に注意して書いたつもり。
自分でテストしたときは、mainの処理をforで10万回ループさせても、
とりあえずメモリリークしなかったので、大丈夫だと思う。思いたい。
(v3をopenする処理、1回だけならいいけど、10万回もループさせると、とても重い。)

a.cpp

#include <net-snmp/net-snmp-config.h>
#include <net-snmp/net-snmp-includes.h>
#include <net-snmp/session_api.h>

#include <string>
#include <map>

#define OIDSIZE(p) (sizeof(p)/sizeof(oid))

struct snmp_node_param_t {
    std::string host;
    std::string community;
    std::string port;
    std::string target;
    std::string timeout; // 秒指定
    std::string retry;
    std::string version; // 1 or v2c or 3のいずれか。
    std::string username;
    std::string level; // noAuthNoPriv or authNoPriv or authPrivのいずれか。
    std::string auth_protocol; // MD5 or SHAのいずれか。
    std::string auth_key;
    std::string priv_protocol; // AES or DESのいずれか。
    std::string priv_key;
};

struct snmp_sessp_t {
    struct snmp_session session;
    void* sess = nullptr;
};

int sess_open(const snmp_node_param_t &node_param, snmp_sessp_t &sessp);
void sess_close(snmp_sessp_t &sessp);

int sess_open(const snmp_node_param_t &node_param, snmp_sessp_t &sessp) {
    // 呼び出し元で、あらかじめ
    // init_snmp("snmpapp");
    // 呼び出しておくこと。
    // 呼び出さなかった場合、snmpv3の処理は失敗する。

    sess_close(sessp);
    snmp_sess_init(&sessp.session);

    char hostport[4096];
    char community[4096];
    char security_name[4096];
    char auth_key[4096];
    char priv_key[4096];

    sprintf(hostport, "%s:%s", node_param.host.c_str(), node_param.port.c_str());
    sprintf(community, "%s", node_param.community.c_str());
    sprintf(security_name, "%s", node_param.username.c_str());
    sprintf(auth_key, "%s", node_param.auth_key.c_str());
    sprintf(priv_key, "%s", node_param.priv_key.c_str());

    sessp.session.securityEngineID = 0;
    sessp.session.securityEngineIDLen = 0;

    sessp.session.securityName = 0;
    sessp.session.securityNameLen = 0;

    sessp.session.contextEngineID = 0;
    sessp.session.contextEngineIDLen = 0;

    sessp.session.contextName = 0;
    sessp.session.contextNameLen = 0;

    sessp.session.securityAuthProto = nullptr;
    sessp.session.securityPrivProto = nullptr;

    sessp.session.peername = hostport;
    sessp.session.remote_port = static_cast<u_short>(atoi(node_param.port.c_str()));
    sessp.session.timeout = atoi(node_param.timeout.c_str()) * 1000000;
    sessp.session.retries = atoi(node_param.retry.c_str());

    if (node_param.version == "3") {
        sessp.session.version = SNMP_VERSION_3;
        sessp.session.securityModel = USM_SEC_MODEL_NUMBER;
        sessp.session.securityName = security_name;
        sessp.session.securityNameLen = node_param.username.size();

        bool ap = false;
        bool pp = false;

        if (node_param.level == "authNoPriv") {
            sessp.session.securityLevel = SNMP_SEC_LEVEL_AUTHNOPRIV;
            ap = true;
        } else if (node_param.level == "authPriv") {
            sessp.session.securityLevel = SNMP_SEC_LEVEL_AUTHPRIV;
            ap = true;
            pp = true;
        } else {
            // 謎のレベルを指定してきたら、とりあえずnoAuthNoPriv。
            sessp.session.securityLevel = SNMP_SEC_LEVEL_NOAUTH;
        }

        if (node_param.auth_protocol == "SHA") {
            sessp.session.securityAuthProto = snmp_duplicate_objid(usmHMACSHA1AuthProtocol, USM_AUTH_PROTO_SHA_LEN);
            sessp.session.securityAuthProtoLen = USM_AUTH_PROTO_SHA_LEN;
        } else {
            // 謎のauthProtocolを指定してきたら、とりあえずMD5。
            sessp.session.securityAuthProto = snmp_duplicate_objid(usmHMACMD5AuthProtocol, USM_AUTH_PROTO_MD5_LEN);
            sessp.session.securityAuthProtoLen = USM_AUTH_PROTO_MD5_LEN;            
        }

        if (node_param.priv_protocol == "AES") {
            sessp.session.securityPrivProto = snmp_duplicate_objid(usmAESPrivProtocol, USM_PRIV_PROTO_AES_LEN);
            sessp.session.securityPrivProtoLen = USM_PRIV_PROTO_AES_LEN; 
        } else if (node_param.priv_protocol == "AES128") {
            sessp.session.securityPrivProto = snmp_duplicate_objid(usmAES128PrivProtocol, USM_PRIV_PROTO_AES128_LEN);
            sessp.session.securityPrivProtoLen = USM_PRIV_PROTO_AES128_LEN; 
        } else {
            // 謎のprivProtocolを指定してきたら、とりあえずDES。
            sessp.session.securityPrivProto = snmp_duplicate_objid(usmDESPrivProtocol, USM_PRIV_PROTO_DES_LEN);
            sessp.session.securityPrivProtoLen = USM_PRIV_PROTO_DES_LEN;
        }

        if (ap && !node_param.auth_key.empty()) {
            sessp.session.securityAuthKeyLen = USM_AUTH_KU_LEN;

            if (
                generate_Ku( 
                    sessp.session.securityAuthProto,
                    sessp.session.securityAuthProtoLen,
                    reinterpret_cast<unsigned char*>(auth_key),
                    node_param.auth_key.size(),
                    sessp.session.securityAuthKey,
                    &sessp.session.securityAuthKeyLen 
                ) != SNMPERR_SUCCESS 
            ) {
                printf("Error generating a key (Ku) from the supplied authentication pass phrase.\n");
            }
        }

        if (pp && !node_param.priv_key.empty()) {
            sessp.session.securityPrivKeyLen = USM_PRIV_KU_LEN;

            if ( 
                generate_Ku(
                    sessp.session.securityAuthProto,
                    sessp.session.securityAuthProtoLen,
                    reinterpret_cast<unsigned char*>(priv_key),
                    node_param.priv_key.size(),
                    sessp.session.securityPrivKey, 
                    &sessp.session.securityPrivKeyLen 
                ) != SNMPERR_SUCCESS 
            ) {
                printf("Error generating a key (Ku) from the supplied privacy pass phrase.\n");
            }
        } else {
            sessp.session.securityPrivProto = snmp_duplicate_objid(usmNoPrivProtocol, OIDSIZE(usmNoPrivProtocol));
            sessp.session.securityPrivProtoLen = OIDSIZE(usmNoPrivProtocol);
            sessp.session.securityPrivKeyLen = USM_PRIV_KU_LEN;
        }
    } else if (node_param.version == "2c") {
        sessp.session.version = SNMP_VERSION_2c;
        sessp.session.securityModel = SNMP_SEC_MODEL_SNMPv2c;
        sessp.session.community = reinterpret_cast<unsigned char*>(community);
        sessp.session.community_len = node_param.community.size();
    } else {
        sessp.session.version = SNMP_VERSION_1;
        sessp.session.securityModel = SNMP_SEC_MODEL_SNMPv1;
        sessp.session.community = reinterpret_cast<unsigned char*>(community);
        sessp.session.community_len = node_param.community.size();
    }

    sessp.sess = snmp_sess_open(&sessp.session);

    if (sessp.sess == nullptr) {
        int liberror;
        int syserror;
        char *buf;

        snmp_error(&sessp.session, &liberror, &syserror, &buf);

        printf("%s\n", buf);

        free(buf);

        return -1;
    }

    return 0;
}

void sess_close(snmp_sessp_t &sessp) {
    if (sessp.sess == nullptr) {
        return;
    }

    snmp_sess_close(sessp.sess);
    sessp.sess = nullptr;

    // snmp_duplicate_objidで確保した領域は、freeしないとダメっぽい。。。
    free(sessp.session.securityAuthProto);
    free(sessp.session.securityPrivProto);
}

int main(int, char**) {
    init_snmp("snmpapp");

    snmp_node_param_t node_param;
    node_param.host = "192.168.0.1";
    node_param.community = "public";
    node_param.port = "161";
    node_param.timeout = "2";
    node_param.retry = "1";
    node_param.version = "3";
    node_param.username = "test1";
    node_param.level = "authPriv";
    node_param.auth_protocol = "MD5";
    node_param.auth_key = "authp1234";
    node_param.priv_protocol = "DES";
    node_param.priv_key = "encp1234";

    snmp_sessp_t sessp;

    if (sess_open(node_param, sessp)) {
        printf("sess_open fail.\n");
        return 1;
    }

    // sess_openが成功したら、sessp.sess(snmp_sess_openの戻り値)を使って、
    // getやwalkの処理ができる。これはまた無駄に長い処理がいるので、
    // 別の機会にzzz
    // sess_closeを呼ばないと、もれなくメモリリークする。
    // また、ソケットも閉じないので、そのうちソケットが開けなくなる。
    // 繰り返し処理で使うときは忘れないように。

    sess_close(sessp);

    return 0;
}

2
2
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
2