mirror of
https://github.com/ntop/ntopng.git
synced 2026-05-14 16:12:10 +00:00
1020 lines
29 KiB
C++
1020 lines
29 KiB
C++
/*
|
|
*
|
|
* (C) 2013-21 - ntop.org
|
|
*
|
|
*
|
|
* This program is free software; you can redistribute it and/or modify
|
|
* it under the terms of the GNU General Public License as published by
|
|
* the Free Software Foundation; either version 3 of the License, or
|
|
* (at your option) any later version.
|
|
*
|
|
* This program is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with this program; if not, write to the Free Software Foundation,
|
|
* Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
|
*
|
|
*/
|
|
|
|
#include "ntop_includes.h"
|
|
|
|
/* Code needed by both implementations for batch mode */
|
|
extern "C" {
|
|
#include "../third-party/snmp/snmp.c"
|
|
#include "../third-party/snmp/asn1.c"
|
|
#include "../third-party/snmp/net.c"
|
|
};
|
|
|
|
#ifdef HAVE_LIBSNMP
|
|
|
|
/* ******************************* */
|
|
/* ******************************* */
|
|
|
|
/* ******************************* */
|
|
|
|
SNMPSession::SNMPSession() {
|
|
session_ptr = NULL;
|
|
}
|
|
|
|
/* ******************************* */
|
|
|
|
SNMPSession::~SNMPSession() {
|
|
if(session_ptr)
|
|
snmp_sess_close(session_ptr);
|
|
}
|
|
|
|
/* ******************************* */
|
|
|
|
SNMP::SNMP() {
|
|
batch_mode = false;
|
|
#ifdef HAVE_LIBSNMP
|
|
init_snmp("ntopng");
|
|
#endif
|
|
}
|
|
|
|
/* ******************************* */
|
|
|
|
SNMP::~SNMP() {
|
|
for(unsigned int i=0; i<sessions.size(); i++)
|
|
delete sessions.at(i);
|
|
}
|
|
|
|
/* ******************************* */
|
|
|
|
/*
|
|
http://www.net-snmp.org/docs/README.thread.html
|
|
https://github.com/bluecmd/python3-netsnmp/blob/master/netsnmp/client_intf.c
|
|
*/
|
|
|
|
void SNMP::handle_async_response(struct snmp_pdu *pdu, const char *agent_ip) {
|
|
netsnmp_variable_list *vp = pdu->variables;
|
|
bool table_added = false;
|
|
|
|
while(vp != NULL) {
|
|
/* OID */
|
|
char rsp_oid[128], buf[128];
|
|
int offset = 0;
|
|
|
|
switch(vp->type) {
|
|
case SNMP_NOSUCHOBJECT:
|
|
case SNMP_NOSUCHINSTANCE:
|
|
case SNMP_ENDOFMIBVIEW:
|
|
vp = vp->next_variable;
|
|
continue; /* Error found */
|
|
break;
|
|
}
|
|
|
|
if(batch_mode)
|
|
snprintf(rsp_oid, sizeof(rsp_oid), "%s", agent_ip);
|
|
else {
|
|
for(u_int i=0; i<vp->name_length; i++) {
|
|
int rc = snprintf(&rsp_oid[offset], sizeof(rsp_oid)-offset, "%s%d", (offset > 0) ? "." : "", (int)vp->name_loc[i]);
|
|
|
|
if(rc > 0) offset += rc; else break;
|
|
}
|
|
}
|
|
|
|
if(!table_added)
|
|
lua_newtable(vm), table_added = true;
|
|
|
|
|
|
switch(vp->type) {
|
|
case ASN_INTEGER:
|
|
/* case ASN_GAUGE: */ /* Alias of ASN_INTEGER */
|
|
#ifdef NATIVE_TYPE
|
|
lua_push_int32_table_entry(vm, rsp_oid, (long)*vp->val.integer);
|
|
#else
|
|
snprintf(buf, sizeof(buf), "%ld", (long)*vp->val.integer);
|
|
lua_push_str_table_entry(vm, rsp_oid, buf);
|
|
#endif
|
|
break;
|
|
|
|
case ASN_UNSIGNED:
|
|
case ASN_TIMETICKS:
|
|
case ASN_COUNTER:
|
|
// ntop->getTrace()->traceEvent(TRACE_WARNING, "%s = %d", rsp_oid, vp->val.integer);
|
|
#ifdef NATIVE_TYPE
|
|
lua_push_uint32_table_entry(vm, rsp_oid, (u_int32_t)*vp->val.integer);
|
|
#else
|
|
snprintf(buf, sizeof(buf), "%u", (u_int32_t)*vp->val.integer);
|
|
lua_push_str_table_entry(vm, rsp_oid, buf);
|
|
#endif
|
|
break;
|
|
|
|
case ASN_COUNTER64:
|
|
{
|
|
u_int64_t v = ((u_int64_t) vp->val.counter64->high << 32) + vp->val.counter64->low;
|
|
|
|
#ifdef NATIVE_TYPE
|
|
lua_push_uint32_table_entry(vm, rsp_oid, v);
|
|
#else
|
|
snprintf(buf, sizeof(buf), "%llu", (long long unsigned int)v);
|
|
lua_push_str_table_entry(vm, rsp_oid, buf);
|
|
#endif
|
|
}
|
|
break;
|
|
|
|
case ASN_OCTET_STR:
|
|
{
|
|
// ntop->getTrace()->traceEvent(TRACE_WARNING, "%s = %s", rsp_oid, vp->val.string);
|
|
char buf[512];
|
|
bool is_printable = true;
|
|
u_int i, len = min(sizeof(buf)-1, vp->val_len);
|
|
|
|
for(i=0; i<len; i++) {
|
|
if((!isprint(vp->val.string[i]))
|
|
&& (!isspace(vp->val.string[i]))) {
|
|
is_printable = false;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if(is_printable) {
|
|
strncpy(buf, (const char*)vp->val.string, len);
|
|
buf[len] = '\0';
|
|
} else {
|
|
u_int idx = 0;
|
|
|
|
for(i=0; i<len; i++) {
|
|
int left = sizeof(buf)-idx;
|
|
int rc;
|
|
|
|
if(left <= 2) break;
|
|
rc = snprintf(&buf[idx], sizeof(buf)-idx, "%s%02X",
|
|
(i == 0) ? "" : " ", vp->val.string[i] & 0xFF);
|
|
|
|
if(rc <= 0)
|
|
break;
|
|
else
|
|
idx += rc;
|
|
} /* for */
|
|
|
|
buf[idx] = '\0';
|
|
}
|
|
|
|
lua_push_str_table_entry(vm, rsp_oid, buf);
|
|
}
|
|
break;
|
|
|
|
case ASN_OBJECT_ID:
|
|
{
|
|
char response[128];
|
|
int rsp_offset = 0;
|
|
|
|
for(u_int i=0; i<vp->val_len/8; i++) {
|
|
int rc = snprintf(&response[rsp_offset], sizeof(response)-rsp_offset,
|
|
"%s%d", (rsp_offset > 0) ? "." : "", (int)vp->val.objid[i]);
|
|
|
|
if(rc > 0) rsp_offset += rc; else break;
|
|
}
|
|
|
|
lua_push_str_table_entry(vm, rsp_oid, response);
|
|
}
|
|
break;
|
|
|
|
case ASN_APPLICATION:
|
|
case ASN_NULL:
|
|
lua_push_nil_table_entry(vm, rsp_oid);
|
|
break;
|
|
|
|
default:
|
|
ntop->getTrace()->traceEvent(TRACE_WARNING, "Missing %d type handler [agent: %s]", vp->type, agent_ip);
|
|
lua_push_nil_table_entry(vm, rsp_oid);
|
|
break;
|
|
}
|
|
|
|
vp = vp->next_variable;
|
|
} /* while */
|
|
|
|
if(!table_added)
|
|
lua_pushnil(vm);
|
|
}
|
|
|
|
/* ******************************* */
|
|
|
|
int asynch_response(int operation, struct snmp_session *sp, int reqid,
|
|
struct snmp_pdu *pdu, void *magic) {
|
|
SNMP *s = (SNMP*)magic;
|
|
|
|
if(operation == NETSNMP_CALLBACK_OP_RECEIVED_MESSAGE) {
|
|
if(pdu->command == SNMP_MSG_RESPONSE) {
|
|
sockaddr_in *sa = (sockaddr_in*)pdu->transport_data;
|
|
char buf[32], *peer = sp->peername;
|
|
|
|
if(peer == NULL) {
|
|
if(sa->sin_family == 2) /* IPv4 */
|
|
peer = Utils::intoaV4(ntohl(sa->sin_addr.s_addr), buf, sizeof(buf));
|
|
else
|
|
ntop->getTrace()->traceEvent(TRACE_WARNING, "Missing IPv6 support");
|
|
}
|
|
|
|
s->handle_async_response(pdu, peer);
|
|
}
|
|
}
|
|
|
|
return(0);
|
|
}
|
|
|
|
/* ******************************* */
|
|
|
|
/* See https://raw.githubusercontent.com/winlibs/net-snmp/master/snmplib/snmp_client.c */
|
|
|
|
void SNMP::send_snmpv1v2c_request(char *agent_host, char *community,
|
|
snmp_pdu_primitive pduType,
|
|
u_int version,
|
|
char *_oid[SNMP_MAX_NUM_OIDS],
|
|
bool _batch_mode) {
|
|
int rc, pdu_type = SNMP_MSG_GET;
|
|
struct snmp_pdu *pdu;
|
|
SNMPSession *snmpSession;
|
|
bool initSession = false;
|
|
|
|
batch_mode = _batch_mode;
|
|
|
|
if(batch_mode) {
|
|
create_snmp_session:
|
|
try {
|
|
snmpSession = new SNMPSession;
|
|
sessions.push_back(snmpSession);
|
|
initSession = true;
|
|
} catch(std::bad_alloc& ba) {
|
|
ntop->getTrace()->traceEvent(TRACE_WARNING, "Unable to allocate SNMP session");
|
|
return;
|
|
}
|
|
} else {
|
|
if(sessions.size() == 0) {
|
|
goto create_snmp_session;
|
|
} else {
|
|
snmpSession = sessions.at(0);
|
|
}
|
|
}
|
|
|
|
/* Initialize the session */
|
|
if(initSession) {
|
|
snmp_sess_init(&snmpSession->session);
|
|
snmpSession->session.peername = agent_host;
|
|
|
|
/* set the SNMP version number */
|
|
snmpSession->session.version = (version == 0) ? SNMP_VERSION_1 : SNMP_VERSION_2c;
|
|
|
|
/* set the SNMP community name used for authentication */
|
|
snmpSession->session.community = (u_char*)community;
|
|
snmpSession->session.community_len = strlen(community);
|
|
snmpSession->session.callback = asynch_response;
|
|
snmpSession->session.callback_magic = this;
|
|
|
|
/* Open the session */
|
|
snmpSession->session_ptr = snmp_sess_open(&snmpSession->session);
|
|
}
|
|
|
|
/* Create the PDU */
|
|
switch(pduType) {
|
|
case snmp_get_pdu:
|
|
pdu_type = SNMP_MSG_GET;
|
|
break;
|
|
case snmp_get_next_pdu:
|
|
pdu_type = SNMP_MSG_GETNEXT;
|
|
break;
|
|
case snmp_get_bulk_pdu:
|
|
pdu_type = (version == 0 /* SNMPv1 */) ? SNMP_MSG_GETNEXT : SNMP_MSG_GETBULK;
|
|
break;
|
|
case snmp_set_pdu:
|
|
pdu_type = SNMP_MSG_SET;
|
|
break;
|
|
}
|
|
|
|
if((pdu = snmp_pdu_create(pdu_type)) == NULL) {
|
|
ntop->getTrace()->traceEvent(TRACE_WARNING, "SNMP PDU create error");
|
|
return;
|
|
}
|
|
|
|
if(pdu_type == SNMP_MSG_GETBULK) {
|
|
pdu->non_repeaters = 0; /* GET */
|
|
pdu->max_repetitions = 10; /* GET-NEXT */
|
|
}
|
|
|
|
for(u_int i=0; i<SNMP_MAX_NUM_OIDS; i++) {
|
|
if(_oid[i] != NULL) {
|
|
size_t name_length = MAX_OID_LEN;
|
|
oid name[MAX_OID_LEN];
|
|
|
|
if(snmp_parse_oid(_oid[i], name, &name_length))
|
|
snmp_add_null_var(pdu, name, name_length);
|
|
} else
|
|
break;
|
|
}
|
|
|
|
/* Send the request */
|
|
if((rc = snmp_sess_send(snmpSession->session_ptr, pdu)) == 0) {
|
|
snmp_free_pdu(pdu);
|
|
snmp_perror("snmp_sess_send");
|
|
ntop->getTrace()->traceEvent(TRACE_WARNING, "SNMP send error [rc: %d]", rc);
|
|
}
|
|
|
|
//snmp_free_pdu(pdu); /* TODO: this is apparently freed when we close the session */
|
|
}
|
|
|
|
/* ******************************************* */
|
|
|
|
void SNMP::send_snmpv3_request(char *agent_host,
|
|
char *level,
|
|
char *username,
|
|
char *auth_protocol,
|
|
char *auth_passphrase,
|
|
char *privacy_protocol,
|
|
char *privacy_passphrase,
|
|
snmp_pdu_primitive pduType,
|
|
char *oid[SNMP_MAX_NUM_OIDS],
|
|
char value_types[SNMP_MAX_NUM_OIDS],
|
|
char *values[SNMP_MAX_NUM_OIDS],
|
|
bool _batch_mode) {
|
|
send_snmp_request(agent_host,
|
|
2 /* SNMPv3 */,
|
|
NULL,
|
|
level,
|
|
username,
|
|
auth_protocol,
|
|
auth_passphrase,
|
|
privacy_protocol,
|
|
privacy_passphrase,
|
|
pduType,
|
|
oid, value_types, values,
|
|
_batch_mode);
|
|
}
|
|
|
|
/* ******************************************* */
|
|
|
|
void SNMP::send_snmp_request(char *agent_host,
|
|
u_int version,
|
|
char *community,
|
|
char *level,
|
|
char *username,
|
|
char *auth_protocol,
|
|
char *auth_passphrase,
|
|
char *privacy_protocol,
|
|
char *privacy_passphrase,
|
|
snmp_pdu_primitive pduType,
|
|
char *_oid[SNMP_MAX_NUM_OIDS],
|
|
char value_types[SNMP_MAX_NUM_OIDS],
|
|
char *values[SNMP_MAX_NUM_OIDS],
|
|
bool _batch_mode) {
|
|
int rc, pdu_type;
|
|
struct snmp_pdu *pdu;
|
|
SNMPSession *snmpSession;
|
|
bool initSession = false;
|
|
|
|
batch_mode = _batch_mode;
|
|
|
|
if(batch_mode) {
|
|
create_snmp_session:
|
|
try {
|
|
snmpSession = new SNMPSession;
|
|
sessions.push_back(snmpSession);
|
|
initSession = true;
|
|
} catch(std::bad_alloc& ba) {
|
|
ntop->getTrace()->traceEvent(TRACE_WARNING, "Unable to allocate SNMP session");
|
|
return;
|
|
}
|
|
} else {
|
|
if(sessions.size() == 0) {
|
|
goto create_snmp_session;
|
|
} else {
|
|
snmpSession = sessions.at(0);
|
|
}
|
|
}
|
|
|
|
/* Initialize the session */
|
|
if(initSession) {
|
|
snmp_sess_init(&snmpSession->session);
|
|
snmpSession->session.peername = agent_host;
|
|
|
|
if(version <= 1) {
|
|
/* SNMP v1/v2c */
|
|
snmpSession->session.version = (version == 0) ? SNMP_VERSION_1 : SNMP_VERSION_2c;
|
|
|
|
/* set the SNMP community name used for authentication */
|
|
snmpSession->session.community = (u_char*)community;
|
|
snmpSession->session.community_len = strlen(community);
|
|
} else {
|
|
/* SNMP v3 */
|
|
snmpSession->session.version = SNMP_VERSION_3;
|
|
|
|
if(!strcasecmp(level, "noAuthNoPriv")) {
|
|
snmpSession->session.securityLevel = SNMP_SEC_LEVEL_NOAUTH;
|
|
username = NULL;
|
|
auth_protocol = NULL;
|
|
privacy_protocol = NULL;
|
|
} else {
|
|
/* set the SNMPv3 user name */
|
|
if(username) {
|
|
snmpSession->session.securityName = strdup(username);
|
|
snmpSession->session.securityNameLen = strlen(snmpSession->session.securityName);
|
|
} else {
|
|
ntop->getTrace()->traceEvent(TRACE_WARNING, "SNMP PDU no username specified");
|
|
return;
|
|
}
|
|
|
|
if((!strcasecmp(level, "authNoPriv")) || (!strcasecmp(level, "authPriv"))) {
|
|
if(!strcasecmp(auth_protocol, "md5")) {
|
|
snmpSession->session.securityAuthProto = usmHMACMD5AuthProtocol;
|
|
snmpSession->session.securityAuthProtoLen = sizeof(usmHMACMD5AuthProtocol)/sizeof(oid);
|
|
snmpSession->session.securityAuthKeyLen = USM_AUTH_KU_LEN;
|
|
} else if(!strcasecmp(auth_protocol, "sha")) {
|
|
snmpSession->session.securityAuthProto = usmHMACSHA1AuthProtocol;
|
|
snmpSession->session.securityAuthProtoLen = sizeof(usmHMACSHA1AuthProtocol)/sizeof(oid);
|
|
snmpSession->session.securityAuthKeyLen = USM_AUTH_KU_LEN; /* CHECK */
|
|
} else {
|
|
ntop->getTrace()->traceEvent(TRACE_WARNING, "SNMP PDU invalid authentication protocol [%s]", auth_protocol);
|
|
return;
|
|
}
|
|
|
|
if(generate_Ku(snmpSession->session.securityAuthProto,
|
|
snmpSession->session.securityAuthProtoLen,
|
|
(u_char *)auth_passphrase, strlen(auth_passphrase),
|
|
snmpSession->session.securityAuthKey,
|
|
&snmpSession->session.securityAuthKeyLen) != SNMPERR_SUCCESS) {
|
|
ntop->getTrace()->traceEvent(TRACE_WARNING, "SNMP PDU authentication pass phrase error");
|
|
return;
|
|
}
|
|
|
|
if(!strcasecmp(level, "authPriv")) {
|
|
snmpSession->session.securityLevel = SNMP_SEC_LEVEL_AUTHPRIV;
|
|
|
|
if(!strcasecmp(privacy_protocol, "DES")) {
|
|
snmpSession->session.securityPrivProto = snmp_duplicate_objid(usmDESPrivProtocol, USM_PRIV_PROTO_DES_LEN);
|
|
snmpSession->session.securityPrivProtoLen = USM_PRIV_PROTO_DES_LEN;
|
|
} else if(!strncasecmp(privacy_protocol, "AES", 3)) {
|
|
snmpSession->session.securityPrivProto = snmp_duplicate_objid(usmAESPrivProtocol, USM_PRIV_PROTO_AES_LEN);
|
|
snmpSession->session.securityPrivProtoLen = USM_PRIV_PROTO_AES_LEN;
|
|
}
|
|
|
|
snmpSession->session.securityPrivKeyLen = USM_PRIV_KU_LEN;
|
|
if(generate_Ku(snmpSession->session.securityAuthProto,
|
|
snmpSession->session.securityAuthProtoLen,
|
|
(u_char *)privacy_passphrase, strlen(privacy_passphrase),
|
|
snmpSession->session.securityPrivKey,
|
|
&snmpSession->session.securityPrivKeyLen) != SNMPERR_SUCCESS) {
|
|
ntop->getTrace()->traceEvent(TRACE_WARNING, "SNMP PDU security privacy error");
|
|
return;
|
|
}
|
|
} else
|
|
snmpSession->session.securityLevel = SNMP_SEC_LEVEL_AUTHNOPRIV;
|
|
}
|
|
}
|
|
}
|
|
|
|
snmpSession->session.callback = asynch_response;
|
|
snmpSession->session.callback_magic = this;
|
|
|
|
/* Open the session */
|
|
snmpSession->session_ptr = snmp_sess_open(&snmpSession->session);
|
|
}
|
|
|
|
/* Create the PDU */
|
|
switch(pduType) {
|
|
case snmp_get_pdu:
|
|
pdu_type = SNMP_MSG_GET;
|
|
break;
|
|
case snmp_get_next_pdu:
|
|
pdu_type = SNMP_MSG_GETNEXT;
|
|
break;
|
|
case snmp_get_bulk_pdu:
|
|
pdu_type = (version == 0 /* SNMPv1 */) ? SNMP_MSG_GETNEXT : SNMP_MSG_GETBULK;
|
|
break;
|
|
case snmp_set_pdu:
|
|
pdu_type = SNMP_MSG_SET;
|
|
if(values == NULL) {
|
|
ntop->getTrace()->traceEvent(TRACE_WARNING, "SNMP PDU create error");
|
|
return;
|
|
}
|
|
break;
|
|
default:
|
|
ntop->getTrace()->traceEvent(TRACE_WARNING, "Unknown SNMP PDU type %u", pduType);
|
|
pdu_type = SNMP_MSG_GET;
|
|
break;
|
|
}
|
|
|
|
if((pdu = snmp_pdu_create(pdu_type)) == NULL) {
|
|
ntop->getTrace()->traceEvent(TRACE_WARNING, "SNMP PDU create error");
|
|
return;
|
|
}
|
|
|
|
if(pdu_type == SNMP_MSG_GETBULK) {
|
|
pdu->non_repeaters = 0; /* GET */
|
|
pdu->max_repetitions = 10; /* GET-NEXT */
|
|
}
|
|
|
|
for(u_int i=0; i<SNMP_MAX_NUM_OIDS; i++) {
|
|
if(_oid[i] != NULL) {
|
|
size_t name_length = MAX_OID_LEN;
|
|
oid name[MAX_OID_LEN];
|
|
|
|
if(snmp_parse_oid(_oid[i], name, &name_length)) {
|
|
if((values != NULL) && (values[i] != NULL))
|
|
snmp_add_var(pdu, name, name_length, value_types[i], values[i]);
|
|
else
|
|
snmp_add_null_var(pdu, name, name_length);
|
|
}
|
|
} else
|
|
break;
|
|
}
|
|
|
|
/* Send the request */
|
|
if((rc = snmp_sess_send(snmpSession->session_ptr, pdu)) == 0) {
|
|
snmp_free_pdu(pdu);
|
|
snmp_perror("snmp_sess_send");
|
|
ntop->getTrace()->traceEvent(TRACE_WARNING, "SNMP send error [rc: %d]", rc);
|
|
}
|
|
|
|
//snmp_free_pdu(pdu); /* TODO: this is apparently freed when we close the session */
|
|
}
|
|
|
|
/* ******************************************* */
|
|
|
|
void SNMP::send_snmp_set_request(char *agent_host, char *community,
|
|
snmp_pdu_primitive pduType,
|
|
u_int version,
|
|
char *_oid[SNMP_MAX_NUM_OIDS],
|
|
char value_types[SNMP_MAX_NUM_OIDS],
|
|
char *values[SNMP_MAX_NUM_OIDS]) {
|
|
int rc;
|
|
struct snmp_pdu *pdu;
|
|
SNMPSession *snmpSession;
|
|
bool initSession = false;
|
|
|
|
batch_mode = false;
|
|
|
|
if(batch_mode) {
|
|
create_snmp_session:
|
|
try {
|
|
snmpSession = new SNMPSession;
|
|
sessions.push_back(snmpSession);
|
|
initSession = true;
|
|
} catch(std::bad_alloc& ba) {
|
|
ntop->getTrace()->traceEvent(TRACE_WARNING, "Unable to allocate SNMP session");
|
|
return;
|
|
}
|
|
} else {
|
|
if(sessions.size() == 0) {
|
|
goto create_snmp_session;
|
|
} else {
|
|
snmpSession = sessions.at(0);
|
|
}
|
|
}
|
|
|
|
/* Initialize the session */
|
|
if(initSession) {
|
|
snmp_sess_init(&snmpSession->session);
|
|
snmpSession->session.peername = agent_host;
|
|
|
|
/* set the SNMP version number */
|
|
snmpSession->session.version = (version == 0) ? SNMP_VERSION_1 : SNMP_VERSION_2c;
|
|
|
|
/* set the SNMP community name used for authentication */
|
|
snmpSession->session.community = (u_char*)community;
|
|
snmpSession->session.community_len = strlen(community);
|
|
snmpSession->session.callback = asynch_response;
|
|
snmpSession->session.callback_magic = this;
|
|
|
|
/* Open the session */
|
|
snmpSession->session_ptr = snmp_sess_open(&snmpSession->session);
|
|
}
|
|
|
|
/* Create the PDU */
|
|
if((pdu = snmp_pdu_create(SNMP_MSG_SET)) == NULL) {
|
|
ntop->getTrace()->traceEvent(TRACE_WARNING, "SNMP PDU create error");
|
|
return;
|
|
}
|
|
|
|
for(u_int i=0; i<SNMP_MAX_NUM_OIDS; i++) {
|
|
if(_oid[i] != NULL) {
|
|
size_t name_length = MAX_OID_LEN;
|
|
oid name[MAX_OID_LEN];
|
|
|
|
if(snmp_parse_oid(_oid[i], name, &name_length))
|
|
snmp_add_var(pdu, name, name_length, value_types[i], values[i]);
|
|
} else
|
|
break;
|
|
}
|
|
|
|
/* Send the request */
|
|
if((rc = snmp_sess_send(snmpSession->session_ptr, pdu)) == 0) {
|
|
snmp_free_pdu(pdu);
|
|
snmp_perror("snmp_sess_send");
|
|
ntop->getTrace()->traceEvent(TRACE_WARNING, "SNMP send error [rc: %d]", rc);
|
|
}
|
|
}
|
|
|
|
/* ******************************************* */
|
|
|
|
void SNMP::snmp_fetch_responses(lua_State* _vm, u_int timeout) {
|
|
bool add_nil = true;
|
|
|
|
// ntop->getTrace()->traceEvent(TRACE_WARNING, "%s(%u)", __FUNCTION__, batch_mode ? 1 : 0);
|
|
|
|
for(unsigned int i=0; i<sessions.size(); i++) {
|
|
int numfds;
|
|
fd_set fdset;
|
|
struct timeval tvp;
|
|
int count, block;
|
|
SNMPSession *snmpSession = sessions.at(i);
|
|
|
|
numfds = 0;
|
|
FD_ZERO(&fdset);
|
|
tvp.tv_sec = timeout, tvp.tv_usec = 0;
|
|
|
|
snmp_sess_select_info(snmpSession->session_ptr, &numfds, &fdset, &tvp, &block);
|
|
count = select(numfds, &fdset, NULL, NULL, &tvp);
|
|
if(count > 0) {
|
|
vm = _vm;
|
|
snmp_sess_read(snmpSession->session_ptr, &fdset); /* Will trigger asynch_response() */
|
|
add_nil = false;
|
|
}
|
|
}
|
|
|
|
if(add_nil) lua_pushnil(_vm);
|
|
}
|
|
|
|
/* ******************************************* */
|
|
|
|
int SNMP::snmp_read_response(lua_State* _vm, u_int timeout) {
|
|
snmp_fetch_responses(_vm, timeout);
|
|
return(CONST_LUA_OK);
|
|
}
|
|
|
|
/* ******************************* */
|
|
/* ******************************* */
|
|
|
|
#else
|
|
|
|
/* ******************************* */
|
|
/* ******************************* */
|
|
|
|
/* Self-contained SNMP implementation */
|
|
|
|
/* ******************************************* */
|
|
|
|
int SNMP::snmp_read_response(lua_State* vm, u_int timeout) {
|
|
int i = 0;
|
|
|
|
if(ntop->getGlobals()->isShutdown()
|
|
|| input_timeout(udp_sock, timeout) == 0) {
|
|
/* Timeout or shutdown in progress */
|
|
lua_pushnil(vm);
|
|
} else {
|
|
char buf[BUFLEN];
|
|
SNMPMessage *message;
|
|
char *sender_host, *oid_str, *value_str;
|
|
int sender_port, added = 0, len;
|
|
|
|
/* This receive doesn't block */
|
|
len = receive_udp_datagram(buf, BUFLEN, udp_sock, &sender_host, &sender_port);
|
|
message = snmp_parse_message(buf, len);
|
|
|
|
i = 0;
|
|
while(snmp_get_varbind_as_string(message, i, &oid_str, NULL, &value_str)) {
|
|
if(!added) lua_newtable(vm), added = 1;
|
|
lua_push_str_table_entry(vm, oid_str, value_str);
|
|
if(value_str) free(value_str), value_str = NULL;
|
|
i++;
|
|
}
|
|
|
|
snmp_destroy_message(message);
|
|
free(message); /* malloc'd by snmp_parse_message */
|
|
|
|
if(!added)
|
|
lua_pushnil(vm);
|
|
}
|
|
|
|
return(CONST_LUA_OK);
|
|
}
|
|
|
|
/* ******************************************* */
|
|
|
|
void SNMP::send_snmpv1v2c_request(char *agent_host, char *community,
|
|
snmp_pdu_primitive pduType,
|
|
u_int version,
|
|
char *oid[SNMP_MAX_NUM_OIDS],
|
|
bool _batch_mode) {
|
|
u_int agent_port = 161;
|
|
int i = 0;
|
|
SNMPMessage *message;
|
|
int len;
|
|
u_char buf[1500];
|
|
int operation = (pduType == snmp_get_pdu) ? NTOP_SNMP_GET_REQUEST_TYPE : NTOP_SNMP_GETNEXT_REQUEST_TYPE;
|
|
|
|
batch_mode = _batch_mode;
|
|
|
|
if((message = snmp_create_message())) {
|
|
snmp_set_version(message, version);
|
|
snmp_set_community(message, community);
|
|
snmp_set_pdu_type(message, operation);
|
|
snmp_set_request_id(message, request_id++);
|
|
snmp_set_error(message, 0);
|
|
snmp_set_error_index(message, 0);
|
|
|
|
for(i=0; i<SNMP_MAX_NUM_OIDS; i++) {
|
|
if(oid[i] != NULL)
|
|
snmp_add_varbind_null(message, oid[i]);
|
|
else
|
|
break;
|
|
}
|
|
|
|
len = snmp_message_length(message);
|
|
snmp_render_message(message, buf);
|
|
snmp_destroy_message(message);
|
|
free(message); /* malloc'd by snmp_create_message */
|
|
|
|
send_udp_datagram(buf, len, udp_sock, agent_host, agent_port);
|
|
}
|
|
}
|
|
|
|
/* ******************************************* */
|
|
|
|
void SNMP::snmp_fetch_responses(lua_State* vm, u_int sec_timeout) {
|
|
int i = 0;
|
|
|
|
if(ntop->getGlobals()->isShutdown()
|
|
|| input_timeout(udp_sock, sec_timeout) == 0) {
|
|
/* Timeout or shutdown in progress */
|
|
} else {
|
|
char buf[BUFLEN];
|
|
SNMPMessage *message;
|
|
char *sender_host, *oid_str, *value_str = NULL;
|
|
int sender_port, len;
|
|
|
|
len = receive_udp_datagram(buf, BUFLEN, udp_sock, &sender_host, &sender_port);
|
|
|
|
if((message = snmp_parse_message(buf, len))) {
|
|
bool table_added = false;
|
|
|
|
i = 0;
|
|
|
|
while(snmp_get_varbind_as_string(message, i, &oid_str, NULL, &value_str)) {
|
|
if(value_str /* && (value_str[0] != '\0') */) {
|
|
if(!table_added)
|
|
lua_newtable(vm), table_added = true;
|
|
|
|
if(batch_mode /* Used in batch mode */) {
|
|
/*
|
|
The key is the IP address as this is used when contacting multiple
|
|
hosts so we need to know who has sent back the response
|
|
*/
|
|
lua_push_str_table_entry(vm, sender_host /* Sender IP */, value_str);
|
|
} else
|
|
lua_push_str_table_entry(vm, oid_str, value_str);
|
|
|
|
free(value_str), value_str = NULL; /* malloc'd by snmp_get_varbind_as_string */
|
|
}
|
|
|
|
i++;
|
|
} /* while */
|
|
|
|
snmp_destroy_message(message);
|
|
free(message); /* malloc'd by snmp_parse_message */
|
|
if(table_added)
|
|
return;
|
|
}
|
|
}
|
|
|
|
lua_pushnil(vm);
|
|
}
|
|
|
|
/* ******************************* */
|
|
|
|
SNMP::SNMP() {
|
|
char version[4] = { '\0' };
|
|
|
|
ntop->getRedis()->get((char*)CONST_RUNTIME_PREFS_SNMP_PROTO_VERSION, version, sizeof(version));
|
|
|
|
if((udp_sock = socket(AF_INET, SOCK_DGRAM, 0)) < 0)
|
|
throw("Unable to start network discovery");
|
|
|
|
Utils::maximizeSocketBuffer(udp_sock, true /* RX */, 2 /* MB */);
|
|
snmp_version = atoi(version);
|
|
if(snmp_version > 1 /* v2c */) snmp_version = 1;
|
|
request_id = rand(); // Avoid overlaps with coroutines
|
|
}
|
|
|
|
/* ******************************* */
|
|
|
|
SNMP::~SNMP() {
|
|
if(udp_sock != -1) closesocket(udp_sock);
|
|
}
|
|
|
|
/* ******************************* */
|
|
/* ******************************* */
|
|
|
|
#endif /* HAVE_LIBSNMP */
|
|
|
|
/* Common code */
|
|
|
|
/* ******************************************* */
|
|
|
|
int SNMP::get(lua_State* vm, bool skip_first_param) {
|
|
return(snmp_get_fctn(vm, snmp_get_pdu, skip_first_param, false));
|
|
}
|
|
|
|
/* ******************************************* */
|
|
|
|
int SNMP::getnext(lua_State* vm, bool skip_first_param) {
|
|
return(snmp_get_fctn(vm, snmp_get_next_pdu, skip_first_param, false));
|
|
}
|
|
|
|
/* ******************************************* */
|
|
|
|
int SNMP::getnextbulk(lua_State* vm, bool skip_first_param) {
|
|
return(snmp_get_fctn(vm,
|
|
#ifdef HAVE_LIBSNMP
|
|
snmp_get_bulk_pdu /* GET-BULK (next only) */,
|
|
#else
|
|
snmp_get_next_pdu /* GET-NEXT (no bulk) */,
|
|
#endif
|
|
skip_first_param, false));
|
|
}
|
|
|
|
/* ******************************************* */
|
|
|
|
int SNMP::set(lua_State* vm, bool skip_first_param) {
|
|
#ifdef HAVE_LIBSNMP
|
|
return(snmp_get_fctn(vm, snmp_set_pdu, skip_first_param, false));
|
|
#else
|
|
return(-1);
|
|
#endif
|
|
}
|
|
|
|
/* ******************************************* */
|
|
|
|
#ifdef HAVE_LIBSNMP
|
|
int SNMP::snmpv3_get_fctn(lua_State* vm, snmp_pdu_primitive pduType, bool skip_first_param, bool _batch_mode) {
|
|
char *agent_host, *level, *username, *auth_protocol, *auth_passphrase, *privacy_protocol, *privacy_passphrase;
|
|
u_int timeout = 5, oid_idx = 0, idx = skip_first_param ? 2 : 1;
|
|
char *oid[SNMP_MAX_NUM_OIDS] = { NULL }, value_types[SNMP_MAX_NUM_OIDS] = { '\0' }, *values[SNMP_MAX_NUM_OIDS] = { NULL };
|
|
|
|
if(ntop_lua_check(vm, __FUNCTION__, idx, LUA_TSTRING) != CONST_LUA_OK) return(CONST_LUA_ERROR);
|
|
agent_host = (char*)lua_tostring(vm, idx++);
|
|
|
|
if(ntop_lua_check(vm, __FUNCTION__, idx, LUA_TSTRING) != CONST_LUA_OK) return(CONST_LUA_ERROR);
|
|
level = (char*)lua_tostring(vm, idx++);
|
|
|
|
if(ntop_lua_check(vm, __FUNCTION__, idx, LUA_TSTRING) != CONST_LUA_OK) return(CONST_LUA_ERROR);
|
|
username = (char*)lua_tostring(vm, idx++);
|
|
|
|
if(ntop_lua_check(vm, __FUNCTION__, idx, LUA_TSTRING) != CONST_LUA_OK) return(CONST_LUA_ERROR);
|
|
auth_protocol = (char*)lua_tostring(vm, idx++);
|
|
|
|
if(ntop_lua_check(vm, __FUNCTION__, idx, LUA_TSTRING) != CONST_LUA_OK) return(CONST_LUA_ERROR);
|
|
auth_passphrase = (char*)lua_tostring(vm, idx++);
|
|
|
|
if(ntop_lua_check(vm, __FUNCTION__, idx, LUA_TSTRING) != CONST_LUA_OK) return(CONST_LUA_ERROR);
|
|
privacy_protocol = (char*)lua_tostring(vm, idx++);
|
|
|
|
if(ntop_lua_check(vm, __FUNCTION__, idx, LUA_TSTRING) != CONST_LUA_OK) return(CONST_LUA_ERROR);
|
|
privacy_passphrase = (char*)lua_tostring(vm, idx++);
|
|
|
|
if(ntop_lua_check(vm, __FUNCTION__, idx, LUA_TNUMBER) != CONST_LUA_OK) return(CONST_LUA_ERROR);
|
|
timeout = min(timeout, (u_int)lua_tointeger(vm, idx));
|
|
idx++; // Do not out idx++ above as min is a #define and on some platforms it will increase idx twice
|
|
|
|
/* Add OIDs */
|
|
while((oid_idx < SNMP_MAX_NUM_OIDS) && (lua_type(vm, idx) == LUA_TSTRING)) {
|
|
if(pduType == snmp_set_pdu) {
|
|
/* SET */
|
|
oid[oid_idx] = (char*)lua_tostring(vm, idx);
|
|
|
|
|
|
/* Types
|
|
i: INTEGER, u: unsigned INTEGER, t: TIMETICKS, a: IPADDRESS
|
|
o: OBJID, s: STRING, x: HEX STRING, d: DECIMAL STRING
|
|
U: unsigned int64, I: signed int64, F: float, D: double
|
|
*/
|
|
if(lua_type(vm, idx+1) != LUA_TSTRING) return(CONST_LUA_ERROR);
|
|
value_types[oid_idx] = ((char*)lua_tostring(vm, idx+1))[0];
|
|
|
|
if(lua_type(vm, idx+2) != LUA_TSTRING) return(CONST_LUA_ERROR);
|
|
values[oid_idx] = (char*)lua_tostring(vm, idx+2);
|
|
|
|
oid_idx += 3, idx += 3;
|
|
} else {
|
|
oid[oid_idx++] = (char*)lua_tostring(vm, idx);
|
|
idx++;
|
|
}
|
|
}
|
|
|
|
if(oid_idx == 0) {
|
|
/* Missing OIDs */
|
|
return(CONST_LUA_ERROR);
|
|
}
|
|
|
|
send_snmpv3_request(agent_host, level, username, auth_protocol, auth_passphrase, privacy_protocol, privacy_passphrase, pduType, oid, value_types, values, _batch_mode);
|
|
|
|
if(skip_first_param)
|
|
return(CONST_LUA_OK); /* This is an async call */
|
|
else
|
|
return(snmp_read_response(vm, timeout));
|
|
}
|
|
#endif
|
|
|
|
/* ******************************************* */
|
|
|
|
int SNMP::snmp_get_fctn(lua_State* vm, snmp_pdu_primitive pduType, bool skip_first_param, bool _batch_mode) {
|
|
#ifdef HAVE_LIBSNMP
|
|
if(lua_type(vm, skip_first_param ? 5 : 4) != LUA_TNUMBER)
|
|
return(snmpv3_get_fctn(vm, pduType, skip_first_param, _batch_mode));
|
|
else
|
|
#endif
|
|
{
|
|
char *agent_host, *community;
|
|
u_int timeout = 5, version = snmp_version, oid_idx = 0, idx = skip_first_param ? 2 : 1;
|
|
char *oid[SNMP_MAX_NUM_OIDS] = { NULL };
|
|
#ifdef HAVE_LIBSNMP
|
|
char value_types[SNMP_MAX_NUM_OIDS] = { '\0' }, *values[SNMP_MAX_NUM_OIDS] = { NULL };
|
|
#endif
|
|
|
|
if(ntop_lua_check(vm, __FUNCTION__, idx, LUA_TSTRING) != CONST_LUA_OK) return(CONST_LUA_ERROR);
|
|
agent_host = (char*)lua_tostring(vm, idx++);
|
|
|
|
if(ntop_lua_check(vm, __FUNCTION__, idx, LUA_TSTRING) != CONST_LUA_OK) return(CONST_LUA_ERROR);
|
|
community = (char*)lua_tostring(vm, idx++);
|
|
|
|
if(ntop_lua_check(vm, __FUNCTION__, idx, LUA_TNUMBER) != CONST_LUA_OK) return(CONST_LUA_ERROR);
|
|
timeout = min(timeout, (u_int)lua_tointeger(vm, idx));
|
|
idx++; // Do not out idx++ above as min is a #define and on some platforms it will increase idx twice
|
|
|
|
if(ntop_lua_check(vm, __FUNCTION__, idx, LUA_TNUMBER) != CONST_LUA_OK) return(CONST_LUA_ERROR);
|
|
version = (u_int)lua_tointeger(vm, idx++);
|
|
|
|
/* Add OIDs */
|
|
while((oid_idx < SNMP_MAX_NUM_OIDS) && (lua_type(vm, idx) == LUA_TSTRING)) {
|
|
if(pduType == snmp_set_pdu) {
|
|
/* SET */
|
|
oid[oid_idx] = (char*)lua_tostring(vm, idx);
|
|
|
|
/* Types
|
|
i: INTEGER, u: unsigned INTEGER, t: TIMETICKS, a: IPADDRESS
|
|
o: OBJID, s: STRING, x: HEX STRING, d: DECIMAL STRING
|
|
U: unsigned int64, I: signed int64, F: float, D: double
|
|
*/
|
|
if(lua_type(vm, idx+1) != LUA_TSTRING) return(CONST_LUA_ERROR);
|
|
#ifdef HAVE_LIBSNMP
|
|
value_types[oid_idx] = ((char*)lua_tostring(vm, idx+1))[0];
|
|
#endif
|
|
|
|
if(lua_type(vm, idx+2) != LUA_TSTRING) return(CONST_LUA_ERROR);
|
|
#ifdef HAVE_LIBSNMP
|
|
values[oid_idx] = (char*)lua_tostring(vm, idx+2);
|
|
#endif
|
|
|
|
oid_idx += 3, idx += 3;
|
|
} else {
|
|
oid[oid_idx++] = (char*)lua_tostring(vm, idx);
|
|
idx++;
|
|
}
|
|
}
|
|
|
|
if(oid_idx == 0) {
|
|
/* Missing OIDs */
|
|
return(CONST_LUA_ERROR);
|
|
}
|
|
|
|
if(pduType == snmp_set_pdu) {
|
|
/* SET */
|
|
#ifdef HAVE_LIBSNMP
|
|
send_snmp_set_request(agent_host, community, pduType, version, oid, value_types, values);
|
|
#else
|
|
return(CONST_LUA_ERROR); /* not supported */
|
|
#endif
|
|
} else {
|
|
send_snmpv1v2c_request(agent_host, community, pduType, version, oid, _batch_mode);
|
|
}
|
|
|
|
if(skip_first_param)
|
|
return(CONST_LUA_OK); /* This is an async call */
|
|
else
|
|
return(snmp_read_response(vm, timeout));
|
|
}
|
|
}
|
|
|