Commit 8887cb79 authored by Micha Mueller's avatar Micha Mueller
Browse files

Add support for SNMPv3

parent 470d6777
......@@ -389,6 +389,7 @@ The existence of the perf_event_paranoid file is the official method for determi
## snmp
The SNMP plugin enables dcdbpusher to talk with devices which have an SNMP agent running and query requests from them. A SNMP sensor corresponds to a single value as identified by the unique OID. Sensors are aggregated by connections. See the exemplary snmp.conf file in the `config/` directory.
> NOTE     In the SNMP context the word privacy is used synonymously for encription.
| Value | Explanation |
|:----- |:----------- |
......@@ -396,10 +397,20 @@ The SNMP plugin enables dcdbpusher to talk with devices which have an SNMP agent
| Type | Type of the SNMP application which runs on the device queried by the connection. Currently only the type Agent is supported.
| Host | Host name of the device which is to be queried.
| Port | The SNMP port should be usually 161. No changes should be required here.
| Community | Which SNMP community to use.
| OIDPrefix | This OIDPrefix is used for all following sensors.
| |
| Version | Which SNMP version to use (either 2 (maps to 2c) or 3).
| Community | Which SNMP community to use (required only if version 2 is used).
| Username | Username to authenticate with (only required for version 3).
| SecLevel | The security level to be used (only required for version 3). Can be either `noAuthNoPriv` for no authentication and privacy ("privacy" is SNMPs synonym for encryption), `authNoPriv` for only authentication and `authPriv` for authentication and privacy.
| AuthProto | Which protocol to use for authentication (only required for version 3 and if SecLevel != noAuthNoPriv). Can be MD5 or SHA1.
| AuthKey | The passphrase for authentication (only required for version 3 and if SecLevel != noAuthNoPriv). Must be at least 8 characters long.
| PrivProto | Which protocol to use for privacy (only required for version 3 and if SecLevel = AuthPriv). Can be DES or AES.
| PrivKey | The passphrase for privacy encryption (only required for version 3 and if SecLevel = AuthPriv). Must be at least 8 characters long.
| mqttPart | Connection specific MQTT-part which is appended to the MQTT-prefix and succeded by the sensor specific suffix.
| |
| OID | OID suffix which together with the OIDPrefix forms the unique OID identifying a value to query.
| passphrase | has to be at least 8 characters long
## sysFS
......
......@@ -18,41 +18,47 @@ connections {
; name is only useful when we print error messages.
connection name1 {
Type Agent ; Type of connection
Host HostNameOfSNMPAgent ; Hostname of the SNMP agent
Host test.net-snmp.org ; Hostname of the SNMP agent
Port 161 ; Port of the SNMP agent
Community public ; SNMP community string
OIDPrefix 1.3.6.1.4.1.1000 ; When querying OIDs, we'll
OIDPrefix 1.3.6.1.2.1.1 ; When querying OIDs, we'll
; always use this prefix
Version 3
Community public ; SNMP community string
Username MD5DESUser
SecLevel AuthPriv
AuthProto MD5
AuthKey "The UCD Demo Password"
PrivProto DES
PrivKey "The UCD Demo Password"
mqttPart BB ; When generating MQTT topics,
; we'll append this part to
; the prefix
sensor name1 {
OID 100.1 ; OID of the sensor
sensor sysUpTime {
OID 3.0 ; OID of the sensor
mqttsuffix 0001 ; MQTT suffix of the sensor
interval 1000 ; Read sensor every 1000ms
interval 5000 ; Read sensor every 1000ms
}
sensor name2 {
OID 100.2
sensor SysORUpTime {
OID 9.1.4.1
mqttsuffix 0002
default temp1
}
}
connection name2
{
Type Agent
Host HostNameOfSNMPAgent2
Port 161
Community public
OIDPrefix 1.3.6.1.4.1.1000.15
mqttPart CC
sensor name1 {
OID 3
mqttsuffix 0017
interval 5000
}
}
; connection name2 {
; Type Agent
; Host HostNameOfSNMPAgent2
; Port 161
; Community public
; OIDPrefix 1.3.6.1.4.1.1000.15
; mqttPart CC
;
; sensor name1 {
; OID 3
; mqttsuffix 0017
; interval 5000
; }
; }
}
......@@ -70,17 +70,84 @@ bool SNMPConfigurator::readConfig(std::string cfgPath) {
// at the moment we just ignore it, as it can be only "Agent" anyways
} else if (boost::iequals(c.first, "Host")) {
conn.setHost(c.second.data());
LOG(debug) << " Host " << conn.getHost();
} else if (boost::iequals(c.first, "Port")) {
conn.setPort(stoi(c.second.data()));
LOG(debug) << " Port " << conn.getPort();
} else if (boost::iequals(c.first, "Community")) {
conn.setSNMPCommunity(c.second.data());
LOG(debug) << " Community " << conn.getSNMPCommunity();
} else if (boost::iequals(c.first, "OIDPrefix")) {
conn.setOIDPrefix(c.second.data());
LOG(debug) << " OID-prefix " << conn.getOIDPrefix();
} else if (boost::iequals(c.first, "Version")) {
int version = stoi(c.second.data());
switch (version) {
case 1:
conn.setVersion(SNMP_VERSION_1);
LOG(debug) << " Version 1";
break;
case 2:
conn.setVersion(SNMP_VERSION_2c);
LOG(debug) << " Version 2c";
break;
case 3:
conn.setVersion(SNMP_VERSION_3);
LOG(debug) << " Version 3";
break;
default:
conn.setVersion(-1);
LOG(warning) << "Invalid SNMP Version " << version << "!";
}
} else if (boost::iequals(c.first, "Username")) {
conn.setUsername(c.second.data());
LOG(debug) << " Username " << conn.getUsername();
} else if (boost::iequals(c.first, "SecLevel")) {
std::string secLvl = c.second.data();
if (boost::iequals(secLvl, "noAuthNoPriv")) {
conn.setSecurityLevel(SNMP_SEC_LEVEL_NOAUTH);
LOG(debug) << " Security Level NoAuthNoPriv";
} else if (boost::iequals(secLvl, "authNoPriv")) {
conn.setSecurityLevel(SNMP_SEC_LEVEL_AUTHNOPRIV);
LOG(debug) << " Security Level AuthNoPriv";
} else if (boost::iequals(secLvl, "authPriv")) {
conn.setSecurityLevel(SNMP_SEC_LEVEL_AUTHPRIV);
LOG(debug) << " Security Level AuthPriv";
}
} else if (boost::iequals(c.first, "AuthProto")) {
std::string authProto = c.second.data();
if (boost::iequals(authProto, "MD5")) {
conn.setAuthProto(usmHMACMD5AuthProtocol);
conn.setAuthProtoLen(USM_AUTH_PROTO_MD5_LEN);
LOG(debug) << " Authentication protocol MD5";
} else if (boost::iequals(authProto, "SHA1")) {
conn.setAuthProto(usmHMACSHA1AuthProtocol);
conn.setAuthProtoLen(USM_AUTH_PROTO_SHA_LEN);
LOG(debug) << " Authentication protocol SHA1";
}
} else if (boost::iequals(c.first, "PrivProto")) {
std::string privProto = c.second.data();
if (boost::iequals(privProto, "DES")) {
conn.setPrivProto(snmp_duplicate_objid(usmDESPrivProtocol, USM_PRIV_PROTO_DES_LEN));
conn.setPrivProtoLen(USM_PRIV_PROTO_DES_LEN);
LOG(debug) << " Privacy protocol DES";
} else if (boost::iequals(privProto, "AES")) {
conn.setPrivProto(snmp_duplicate_objid(usmAESPrivProtocol, USM_PRIV_PROTO_AES_LEN));
conn.setPrivProtoLen(USM_PRIV_PROTO_AES_LEN);
LOG(debug) << " Privacy protocol AES";
}
} else if (boost::iequals(c.first, "AuthKey")) {
conn.setAuthKey(c.second.data());
LOG(debug) << " Authkey " << conn.getAuthKey();
} else if (boost::iequals(c.first, "PrivKey")) {
conn.setPrivKey(c.second.data());
LOG(debug) << " Privkey " << conn.getPrivKey();
} else if (boost::iequals(c.first, "mqttPart")) {
mqttPartConnection = c.second.data();
if (mqttPartConnection[mqttPartConnection.length()-1] != '/') {
mqttPartConnection.append("/");
}
LOG(debug) << " MQTTPart " << mqttPartConnection;
} else if (boost::iequals(c.first, "sensor")) {
LOG(debug) << "Sensor \"" << c.second.data() << "\"";
if (!c.second.empty()) {
......
......@@ -12,18 +12,31 @@
SNMPConnection::SNMPConnection() {
_snmpCommunity = "";
_oidPrefix = "";
_username = "";
_authKey = "";
_privKey = "";
_host = "";
_authProto = NULL;
_privProto = NULL;
_authProtoLen = 0;
_privProtoLen = 0;
_port = 0;
_securityLevel = -1;
_version = -1;
_snmpSession.community_len = 0;
_snmpSession.securityNameLen = 0;
_strand = NULL;
}
SNMPConnection::~SNMPConnection() {
if (_snmpSession.community_len) {
free(_snmpSession.peername);
free(_snmpSession.community);
_snmpSession.community_len = 0;
free(_snmpSession.peername);
if (_version == SNMP_VERSION_2c) {
if (_snmpSession.community_len) {
free(_snmpSession.community);
_snmpSession.community_len = 0;
}
} else if (_version == SNMP_VERSION_3) {
free(_snmpSession.securityName);
}
if (_strand) {
......@@ -39,10 +52,65 @@ void SNMPConnection::initializeStrand(boost::asio::io_service& io) {
void SNMPConnection::init() {
snmp_sess_init(&_snmpSession);
_snmpSession.version = SNMP_VERSION_2c;
_snmpSession.version = _version;
_snmpSession.peername = strdup(_host.c_str());
_snmpSession.community = (u_char*) strdup(_snmpCommunity.c_str());
_snmpSession.community_len = _snmpCommunity.length();
if (_version == SNMP_VERSION_2c) {
_snmpSession.community = (u_char*) strdup(_snmpCommunity.c_str());
_snmpSession.community_len = _snmpCommunity.length();
} else if (_version == SNMP_VERSION_3) {
_snmpSession.community = NULL;
_snmpSession.community_len = 0;
/* set the SNMPv3 user name */
_snmpSession.securityName = strdup(_username.c_str());
_snmpSession.securityNameLen = _username.length();
/* set the security level */
_snmpSession.securityLevel = _securityLevel;
if(_snmpSession.securityLevel == SNMP_SEC_LEVEL_NOAUTH) {
// no authentication, no privacy
// nothing to do
} else if (_snmpSession.securityLevel == SNMP_SEC_LEVEL_AUTHNOPRIV || _snmpSession.securityLevel == SNMP_SEC_LEVEL_AUTHPRIV) {
// authentication...
_snmpSession.securityAuthKeyLen = USM_AUTH_KU_LEN;
//set the authentication method
_snmpSession.securityAuthProto = _authProto;
_snmpSession.securityAuthProtoLen = _authProtoLen;//sizeof(_authProto)/sizeof(oid);
/* set the authentication key to a hashed version of our
_authKey (which must be at least 8 characters long) */
if (generate_Ku(_snmpSession.securityAuthProto, _snmpSession.securityAuthProtoLen,
(u_char *) _authKey.c_str(), _authKey.length(), _snmpSession.securityAuthKey,
&_snmpSession.securityAuthKeyLen) != SNMPERR_SUCCESS) {
LOG(error) << "SNMP: Error generating Ku from authentication key";
}
if (_snmpSession.securityLevel == SNMP_SEC_LEVEL_AUTHPRIV) {
//...with privacy
_snmpSession.securityPrivKeyLen = USM_PRIV_KU_LEN;
//set the privacy method (aka encryption)
_snmpSession.securityPrivProto = _privProto;
_snmpSession.securityPrivProtoLen = _privProtoLen;
/* set the privacy key to a hashed version of our
_privKey (which must be at least 8 characters long) */
if (generate_Ku(_snmpSession.securityAuthProto, _snmpSession.securityAuthProtoLen,
(u_char *) _privKey.c_str(), _privKey.length(), _snmpSession.securityPrivKey,
&_snmpSession.securityPrivKeyLen) != SNMPERR_SUCCESS) {
LOG(error) << "SNMP: Error generating Ku from privacy key";
}
}
} else {
LOG(warning) << "SNMP security level unknown!";
}
} else {
LOG(warning) << "SNMP Version " << _version << " not supported!";
}
}
uint64_t SNMPConnection::issueGet(oid* OID, size_t OIDLen) {
......@@ -68,11 +136,9 @@ uint64_t SNMPConnection::issueGet(oid* OID, size_t OIDLen) {
vp = response->variables;
if (vp) {
if (vp->type == ASN_INTEGER) {
/* Trigger callback in SNMPPusher.cpp */
ret = *vp->val.integer;
}
else if (vp->type == 0x43) { /* Timeticks: No idea why there is no definition for this in the libray */
/* Trigger callback in SNMPPusher.cpp */
ret = *vp->val.integer;
}
else {
......@@ -86,6 +152,14 @@ uint64_t SNMPConnection::issueGet(oid* OID, size_t OIDLen) {
}
}
} else {
if (status == STAT_SUCCESS) {
LOG(error) << "Error in packet: " << snmp_errstring(response->errstat);
} else {
char* err;
snmp_error(ss, NULL, NULL, &err);
LOG(error) << "SNMP-get: " << err;
}
throw std::runtime_error("Request failed!");
}
......
......@@ -42,6 +42,54 @@ public:
return _oidPrefix;
}
void setUsername(const std::string& username) {
_username = username;
}
const std::string& getUsername() {
return _username;
}
void setAuthKey(const std::string& authKey) {
_authKey = authKey;
}
const std::string& getAuthKey() {
return _authKey;
}
void setPrivKey(const std::string& privKey) {
_privKey = privKey;
}
const std::string& getPrivKey() {
return _privKey;
}
void setAuthProto(oid* authProto) {
_authProto = authProto;
}
void setPrivProto(oid* privProto) {
_privProto = privProto;
}
void setAuthProtoLen(size_t authProtoLen) {
_authProtoLen = authProtoLen;
}
size_t getAuthProtoLen() {
return _authProtoLen;
}
void setPrivProtoLen(size_t privProtoLen) {
_privProtoLen = privProtoLen;
}
size_t getPrivProtoLen() {
return _privProtoLen;
}
void setHost(const std::string& host) {
_host = host;
}
......@@ -58,6 +106,22 @@ public:
return _port;
}
void setSecurityLevel(int securityLevel) {
_securityLevel = securityLevel;
}
int getSecurityLevel() {
return _securityLevel;
}
void setVersion(long int version) {
_version = version;
}
long int getVersion() {
return _version;
}
void initializeStrand(boost::asio::io_service& io);
boost::asio::io_service::strand* getStrand() const {
......@@ -83,7 +147,16 @@ private:
std::string _snmpCommunity;
std::string _oidPrefix;
std::string _host;
std::string _username;
std::string _authKey;
std::string _privKey;
oid* _authProto;
oid* _privProto;
size_t _authProtoLen;
size_t _privProtoLen;
int _port;
int _securityLevel;
long int _version;
struct snmp_session _snmpSession;
boost::asio::io_service::strand* _strand;
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment