-
Notifications
You must be signed in to change notification settings - Fork 69
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
<!-- Thanks for the contribution, this is awesome. --> # PR Details ## Description RSU health monitor plugin is a V2xHub plugin that interface with its connected RSU directly via SNMP protocol. The plugin is responsible for monitoring the connected RSU status by constantly ping RSU device. For detailed design of this plugin, refer to https://usdot-carma.atlassian.net/wiki/spaces/WFD2/pages/2640740360/RSU+Health+Monitor+Plugin+Design . <!--- Describe your changes in detail --> ## Related Issue NA <!--- This project only accepts pull requests related to open issues --> <!--- If suggesting a new feature or change, please discuss it in an issue first --> <!--- If fixing a bug, there should be an issue describing it with steps to reproduce --> <!--- Please link to the issue here: --> ## Motivation and Context Data visualization <!--- Why is this change required? What problem does it solve? --> ## How Has This Been Tested? Integration test <!--- Please describe in detail how you tested your changes. --> <!--- Include details of your testing environment, and the tests you ran to --> <!--- see how your change affects other areas of the code, etc. --> ## Types of changes <!--- What types of changes does your code introduce? Put an `x` in all the boxes that apply: --> - [ ] Defect fix (non-breaking change that fixes an issue) - [x] New feature (non-breaking change that adds functionality) - [ ] Breaking change (fix or feature that cause existing functionality to change) ## Checklist: <!--- Go over all the following points, and put an `x` in all the boxes that apply. --> <!--- If you're unsure about any of these, don't hesitate to ask. We're here to help! --> - [ ] I have added any new packages to the sonar-scanner.properties file - [ ] My change requires a change to the documentation. - [ ] I have updated the documentation accordingly. - [x] I have read the **CONTRIBUTING** document. [V2XHUB Contributing Guide](https://github.com/usdot-fhwa-OPS/V2X-Hub/blob/develop/Contributing.md) - [ ] I have added tests to cover my changes. - [ ] All new and existing tests passed.
- Loading branch information
1 parent
5328f89
commit a098635
Showing
19 changed files
with
1,246 additions
and
4 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,25 @@ | ||
#pragma once | ||
|
||
|
||
#include <tmx/messages/message.hpp> | ||
#include "MessageTypes.h" | ||
|
||
|
||
namespace tmx::messages { | ||
|
||
|
||
class RSUStatusMessage : public tmx::message | ||
{ | ||
public: | ||
RSUStatusMessage() {} | ||
|
||
/// Message type for routing this message through TMX core. | ||
static constexpr const char* MessageType = MSGTYPE_APPLICATION_STRING; | ||
|
||
/// Message sub type for routing this message through TMX core. | ||
static constexpr const char* MessageSubType = MSGSUBTYPE_RSU_STATUS_STRING; | ||
}; | ||
|
||
} /* namespace tmx::messages */ | ||
|
||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,56 @@ | ||
#pragma once | ||
namespace tmx::utils::rsu41::mib::oid | ||
{ | ||
/** | ||
* @brief This header file contains a subset of RSU MIB definition from https://github.com/certificationoperatingcouncil/COC_TestSpecs/blob/master/AppNotes/RSU/RSU-MIB.txt | ||
*/ | ||
// Contains the ID given to this RSU. | ||
static constexpr const char *RSU_ID_OID = "1.0.15628.4.1.17.4.0"; | ||
|
||
// Contains the version of this MIB supported by this RSU, e.g. rsuMIB 4.1 rev201812060000Z | ||
static constexpr const char *RSU_MIB_VERSION = "1.0.15628.4.1.17.1.0"; | ||
|
||
// Contains the version of firmware running on this RSU. | ||
static constexpr const char *RSU_FIRMWARE_VERSION = "1.0.15628.4.1.17.2.0"; | ||
|
||
// Contains the name of the manufacturer of this RSU. | ||
static constexpr const char *RSU_MANUFACTURER = "1.0.15628.4.1.17.5.0"; | ||
|
||
// Contains GPS NMEA GPGGA output string. | ||
static constexpr const char *RSU_GPS_OUTPUT_STRING = "1.0.15628.4.1.8.5.0"; | ||
|
||
// Immediate Forward Message Index | ||
static constexpr const char *RSU_IFM_INDEX = "1.0.15628.4.1.5.1.1.0"; | ||
|
||
// Immediate Forward Message PSID. | ||
static constexpr const char *RSU_IFM_PSID = "1.0.15628.4.1.5.1.2.0"; | ||
|
||
// Immediate Forward Message DSRC Message ID | ||
static constexpr const char *RSU_IFM_DSRC_MSG_ID = "1.0.15628.4.1.5.1.3.0"; | ||
|
||
// Immediate Forward Message Transmit Mode | ||
static constexpr const char *RSU_IFM_TX_MODE = "1.0.15628.4.1.5.1.4.0"; | ||
|
||
// DSRC channel set for Immediate Forward Message transmit | ||
static constexpr const char *RSU_IFM_TX_CHANNEL = "1.0.15628.4.1.5.1.5.0"; | ||
|
||
// Set this bit to enable transmission of the message 0=off, 1=on | ||
static constexpr const char *RSU_IFM_ENABLE = "1.0.15628.4.1.5.1.6.0"; | ||
|
||
// Create (4) or Destroy (6) row entry | ||
static constexpr const char *RSU_IFM_STATUS = "1.0.15628.4.1.5.1.7.0"; | ||
|
||
// Specifies the current mode of operation of the RSU and provides capability to transition the device into a new mode, e.g. from the current mode to off, etc | ||
static constexpr const char *RSU_MODE = "1.0.15628.4.1.99.0"; | ||
|
||
/* | ||
SYNTAX INTEGER { | ||
bothOp (0), --both Continuous and Alternating modes are operational | ||
altOp (1), --Alternating mode is operational, | ||
--Continuous mode is not operational | ||
contOp (2), --Continuous mode is operational, | ||
--Alternating mode is not operational | ||
noneOp (3) --neither Continuous nor Alternating mode is operational | ||
*/ | ||
static constexpr const char *RSU_CHAN_STATUS = "1.0.15628.4.1.19.1.0"; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,227 @@ | ||
#include "SNMPClient.h" | ||
|
||
namespace tmx::utils | ||
{ | ||
|
||
// Client defaults to SNMPv3 | ||
snmp_client::snmp_client(const std::string &ip, const int &port, const std::string &community, | ||
const std::string &snmp_user, const std::string &securityLevel, const std::string &authPassPhrase, int snmp_version, int timeout) | ||
|
||
: ip_(ip), port_(port), community_(community), snmp_version_(snmp_version), timeout_(timeout) | ||
{ | ||
|
||
PLOG(logDEBUG1) << "Starting SNMP Client. Target device IP address: " << ip_ << ", Target device SNMP port: " << port_; | ||
|
||
// Bring the IP address and port of the target SNMP device in the required form, which is "IPADDRESS:PORT": | ||
std::string ip_port_string = ip_ + ":" + std::to_string(port_); | ||
char *ip_port = &ip_port_string[0]; | ||
|
||
// Initialize SNMP session parameters | ||
init_snmp("carma_snmp"); | ||
snmp_sess_init(&session); | ||
session.peername = ip_port; | ||
session.version = snmp_version_; // SNMP_VERSION_3 | ||
session.securityName = (char *)snmp_user.c_str(); | ||
session.securityNameLen = snmp_user.length(); | ||
|
||
// Fallback behavior to setup a community for SNMP V1/V2 | ||
if (snmp_version_ != SNMP_VERSION_3) | ||
{ | ||
session.community = (unsigned char *)community.c_str(); | ||
session.community_len = community_.length(); | ||
} | ||
|
||
// SNMP authorization/privach config | ||
if (securityLevel == "authPriv") | ||
{ | ||
session.securityLevel = SNMP_SEC_LEVEL_AUTHPRIV; | ||
} | ||
|
||
else if (securityLevel == "authNoPriv") | ||
{ | ||
session.securityLevel = SNMP_SEC_LEVEL_AUTHNOPRIV; | ||
} | ||
|
||
else | ||
session.securityLevel = SNMP_SEC_LEVEL_NOAUTH; | ||
|
||
// Passphrase used for both authentication and privacy | ||
auto phrase_len = authPassPhrase.length(); | ||
auto phrase = (u_char *)authPassPhrase.c_str(); | ||
|
||
// Defining and generating auth config with SHA1 | ||
session.securityAuthProto = snmp_duplicate_objid(usmHMACSHA1AuthProtocol, USM_AUTH_PROTO_SHA_LEN); | ||
session.securityAuthProtoLen = USM_AUTH_PROTO_SHA_LEN; | ||
session.securityAuthKeyLen = USM_AUTH_KU_LEN; | ||
if (session.securityLevel != SNMP_SEC_LEVEL_NOAUTH && generate_Ku(session.securityAuthProto, | ||
session.securityAuthProtoLen, | ||
phrase, phrase_len, | ||
session.securityAuthKey, | ||
&session.securityAuthKeyLen) != SNMPERR_SUCCESS) | ||
{ | ||
std::string errMsg = "Error generating Ku from authentication pass phrase. \n"; | ||
throw snmp_client_exception(errMsg); | ||
} | ||
|
||
// Defining and generating priv config with AES (since using SHA1) | ||
session.securityPrivKeyLen = USM_PRIV_KU_LEN; | ||
session.securityPrivProto = | ||
snmp_duplicate_objid(usmAESPrivProtocol, | ||
OID_LENGTH(usmAESPrivProtocol)); | ||
session.securityPrivProtoLen = OID_LENGTH(usmAESPrivProtocol); | ||
|
||
if (session.securityLevel == SNMP_SEC_LEVEL_AUTHPRIV && generate_Ku(session.securityAuthProto, | ||
session.securityAuthProtoLen, | ||
phrase, phrase_len, | ||
session.securityPrivKey, | ||
&session.securityPrivKeyLen) != SNMPERR_SUCCESS) | ||
{ | ||
std::string errMsg = "Error generating Ku from privacy pass phrase. \n"; | ||
throw snmp_client_exception(errMsg); | ||
} | ||
|
||
session.timeout = timeout_; | ||
|
||
// Opens the snmp session if it exists | ||
ss = snmp_open(&session); | ||
|
||
if (ss == nullptr) | ||
{ | ||
PLOG(logERROR) << "Failed to establish session with target device"; | ||
snmp_sess_perror("snmpget", &session); | ||
throw snmp_client_exception("Failed to establish session with target device"); | ||
} | ||
else | ||
{ | ||
PLOG(logINFO) << "Established session with device at " << ip_; | ||
} | ||
} | ||
|
||
snmp_client::~snmp_client() | ||
{ | ||
PLOG(logINFO) << "Closing SNMP session"; | ||
snmp_close(ss); | ||
} | ||
|
||
// Original implementation used in Carma Streets https://github.com/usdot-fhwa-stol/snmp-client | ||
bool snmp_client::process_snmp_request(const std::string &input_oid, const request_type &request_type, snmp_response_obj &val) | ||
{ | ||
|
||
/*Structure to hold response from the remote host*/ | ||
snmp_pdu *response; | ||
|
||
// Create pdu for the data | ||
if (request_type == request_type::GET) | ||
{ | ||
PLOG(logDEBUG1) << "Attempting to GET value for: " << input_oid; | ||
pdu = snmp_pdu_create(SNMP_MSG_GET); | ||
} | ||
else if (request_type == request_type::SET) | ||
{ | ||
PLOG(logDEBUG1) << "Attempting to SET value for " << input_oid << " to " << val.val_int; | ||
pdu = snmp_pdu_create(SNMP_MSG_SET); | ||
} | ||
else | ||
{ | ||
PLOG(logERROR) << "Invalid request type, method accpets only GET and SET"; | ||
return false; | ||
} | ||
|
||
// Read input OID into an OID variable: | ||
// net-snmp has several methods for creating an OID object | ||
// their documentation suggests using get_node. read_objid seems like a simpler approach | ||
// TO DO: investigate update to get_node | ||
if (!read_objid(input_oid.c_str(), OID, &OID_len)) | ||
{ | ||
// If oid cannot be created | ||
PLOG(logERROR) << "OID could not be created from input: " << input_oid; | ||
return false; | ||
} | ||
else | ||
{ | ||
|
||
if (request_type == request_type::GET) | ||
{ | ||
// Add OID to pdu for get request | ||
snmp_add_null_var(pdu, OID, OID_len); | ||
} | ||
else if (request_type == request_type::SET) | ||
{ | ||
if (val.type == snmp_response_obj::response_type::INTEGER) | ||
{ | ||
snmp_add_var(pdu, OID, OID_len, 'i', (std::to_string(val.val_int)).c_str()); | ||
} | ||
// Needs to be finalized to support octet string use | ||
else if (val.type == snmp_response_obj::response_type::STRING) | ||
{ | ||
PLOG(logERROR) << "Setting string value is currently not supported"; | ||
} | ||
} | ||
|
||
PLOG(logINFO) << "Created OID for input: " << input_oid; | ||
} | ||
// Send the request | ||
int status = snmp_synch_response(ss, pdu, &response); | ||
PLOG(logINFO) << "Response request status: " << status << " (=" << (status == STAT_SUCCESS ? "SUCCESS" : "FAILED") << ")"; | ||
|
||
// Check GET response | ||
if (status == STAT_SUCCESS && response && response->errstat == SNMP_ERR_NOERROR && request_type == request_type::GET) | ||
{ | ||
for (auto vars = response->variables; vars; vars = vars->next_variable) | ||
{ | ||
// Get value of variable depending on ASN.1 type | ||
// Variable could be a integer, string, bitstring, ojbid, counter : defined here https://github.com/net-snmp/net-snmp/blob/master/include/net-snmp/types.h | ||
// get Integer value | ||
if (vars->type == ASN_INTEGER && vars->val.integer) | ||
{ | ||
val.type = snmp_response_obj::response_type::INTEGER; | ||
val.val_int = *vars->val.integer; | ||
} | ||
else if (vars->type == ASN_OCTET_STR && vars->val.string) | ||
{ | ||
size_t str_len = vars->val_len; | ||
for (size_t i = 0; i < str_len; ++i) | ||
{ | ||
val.val_string.push_back(vars->val.string[i]); | ||
} | ||
val.type = snmp_response_obj::response_type::STRING; | ||
} | ||
} | ||
} | ||
else | ||
{ | ||
log_error(status, request_type, response); | ||
return false; | ||
} | ||
|
||
if (response) | ||
{ | ||
snmp_free_pdu(response); | ||
OID_len = MAX_OID_LEN; | ||
} | ||
|
||
return true; | ||
} | ||
|
||
int snmp_client::get_port() const | ||
{ | ||
return port_; | ||
} | ||
|
||
void snmp_client::log_error(const int &status, const request_type &request_type, const snmp_pdu *response) const | ||
{ | ||
|
||
if (status == STAT_SUCCESS) | ||
{ | ||
PLOG(logERROR) << "Variable type: " << response->variables->type << ". Error in packet " << static_cast<std::string>(snmp_errstring(static_cast<int>(response->errstat))); | ||
} | ||
else if (status == STAT_TIMEOUT) | ||
{ | ||
PLOG(logERROR) << "Timeout, no response from server"; | ||
} | ||
else | ||
{ | ||
PLOG(logERROR) << "Unknown SNMP Error for " << (request_type == request_type::GET ? "GET" : "SET"); | ||
} | ||
} | ||
} // namespace |
Oops, something went wrong.