From 78b7c24df1509ad9704c9a5a7330540ec5c5b340 Mon Sep 17 00:00:00 2001 From: CARMA Developer Date: Fri, 30 Jun 2023 09:36:04 -0700 Subject: [PATCH] Updated immediate forward and ERVcloudforwarding for SNMPv3 with client from Carma Streets --- .devcontainer/docker-compose-vscode.yml | 2 +- container/service.sh | 10 +- src/tmx/TmxUtils/src/SNMPClient.cpp | 691 +++++++++++++----- src/tmx/TmxUtils/src/SNMPClient.h | 287 ++++++-- src/tmx/TmxUtils/src/SNMPClientException.cpp | 8 + src/tmx/TmxUtils/src/SNMPClientException.h | 42 +- .../src/ERVCloudForwardingPlugin.cpp | 8 +- .../test/SNMPClientTest.cpp | 14 +- .../ImmediateForwardPlugin/manifest.json | 2 +- .../src/ImmediateForwardPlugin.cpp | 213 +++++- .../src/ImmediateForwardPlugin.h | 7 + 11 files changed, 999 insertions(+), 285 deletions(-) create mode 100644 src/tmx/TmxUtils/src/SNMPClientException.cpp diff --git a/.devcontainer/docker-compose-vscode.yml b/.devcontainer/docker-compose-vscode.yml index c3e19d9a8..09f119a6e 100755 --- a/.devcontainer/docker-compose-vscode.yml +++ b/.devcontainer/docker-compose-vscode.yml @@ -15,7 +15,7 @@ services: command: /bin/sh -c "while sleep 1000; do :; done" environment: - MYSQL_PASSWORD=/run/secrets/mysql_password - - SIMULATION_MODE=true + - SIMULATION_MODE=false - SIMULATION_IP=127.0.0.1 - SIMULATION_REGISTRATION_PORT=6767 - LOCAL_IP=127.0.0.1 diff --git a/container/service.sh b/container/service.sh index 7ce8dddb0..8013f73a4 100755 --- a/container/service.sh +++ b/container/service.sh @@ -11,9 +11,9 @@ done # command plugin must always be enabled tmxctl --plugin CommandPlugin --enable # If in simulation mode, enable SimulationAdapter -if [ "${SIMULATION_MODE^^}" = "TRUE" ] -then - echo "Enabling CDASim Adapter for Simulation Integration!" - tmxctl --plugin CDASimAdapter --enable -fi +# if [ "${SIMULATION_MODE^^}" = "TRUE" ] +# then +# echo "Enabling CDASim Adapter for Simulation Integration!" +# tmxctl --plugin CDASimAdapter --enable +# fi tmxcore \ No newline at end of file diff --git a/src/tmx/TmxUtils/src/SNMPClient.cpp b/src/tmx/TmxUtils/src/SNMPClient.cpp index a185a06c8..512ab5524 100644 --- a/src/tmx/TmxUtils/src/SNMPClient.cpp +++ b/src/tmx/TmxUtils/src/SNMPClient.cpp @@ -1,196 +1,537 @@ #include "SNMPClient.h" -namespace tmx { -namespace utils { - -SNMPClient::SNMPClient(const std::string &rsuIP, uint16_t snmp_port, const std::string &snmp_user, const std::string &securityLevel, const std::string &authPassPhrase) - : _snmp_port(snmp_port) - , _rsuIP(rsuIP) -{ - std::string ip_port_string = rsuIP + ":" + std::to_string(snmp_port); - char *ip_port = &ip_port_string[0]; - init_snmp("snmpclient"); - snmp_sess_init(&session); - session.peername = ip_port; - session.version = SNMP_VERSION_3; - session.securityName = (char *)snmp_user.c_str(); - session.securityNameLen = snmp_user.length(); - 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; - 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 (generate_Ku(session.securityAuthProto, - session.securityAuthProtoLen, - (u_char *)authPassPhrase.c_str(),authPassPhrase.length(), - session.securityAuthKey, - &session.securityAuthKeyLen) != SNMPERR_SUCCESS) - { - std::string errMsg = "Error generating Ku from authentication pass phrase. \n"; - throw SNMPClientException(errMsg); - } - ss = snmp_open(&session); - if (!ss) - { - std::string errMsg = "Cannot open SNMP session. \n"; - throw SNMPClientException(errMsg); - } - else - { - fprintf(stdout, "snmp session is open.\n"); - } -} +// add a way to default to V1 functionality +namespace tmx::utils { -std::string SNMPClient::SNMPGet(const std::string &req_oid) -{ - std::string result = ""; - auto pdu = snmp_pdu_create(SNMP_MSG_GET); - if (!snmp_parse_oid(req_oid.c_str(), anOID, &anOID_len)) +// SNMPClient::SNMPClient(const std::string &rsuIP, uint16_t snmp_port, const std::string &snmp_user, const std::string &securityLevel, const std::string &authPassPhrase) +// : _snmp_port(snmp_port) +// , _rsuIP(rsuIP) + + 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) { - snmp_perror(req_oid.c_str()); - std::string errMsg = "OID could not be created from input:" + req_oid; - throw SNMPClientException(errMsg); - SOCK_CLEANUP; + + // SPDLOG_DEBUG("Starting SNMP Client"); + // SPDLOG_DEBUG("Target device IP address: {0}", ip_); + // SPDLOG_INFO("Target device NTCIP port: {0}", port_); + PLOG(logDEBUG1) << "Starting SNMP Client"; + PLOG(logDEBUG1) << "Target device IP address: " << ip_; + PLOG(logINFO) << "Target device NTCIP 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]; + + 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(); + + // Establish the session parameters. + if(snmp_version_ != 3){ + char community_char[community_.length()]; + std::copy(community_.begin(), community_.end(), community_char); + unsigned char* comm = reinterpret_cast(community_char); + + session.community = comm; + session.community_len = community_.length(); + } + + + 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; + 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 (generate_Ku(session.securityAuthProto, + session.securityAuthProtoLen, + (u_char *)authPassPhrase.c_str(),authPassPhrase.length(), + session.securityAuthKey, + &session.securityAuthKeyLen) != SNMPERR_SUCCESS) + { + std::string errMsg = "Error generating Ku from authentication pass phrase. \n"; + throw snmp_client_exception(errMsg); + } + + + session.timeout = timeout_; + + ss = snmp_open(&session); + + if (ss == nullptr) + { + // SPDLOG_ERROR("Failed to establish session with target device"); + 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 + { + // SPDLOG_INFO("Established session with device at {0}", ip_); + PLOG(logINFO) << "Established session with device at ", ip_; + } + } - snmp_add_null_var(pdu, anOID, anOID_len); - auto status = snmp_synch_response(ss, pdu, &response); - if (!response) - { - throw SNMPClientException("No response for SNMP Get request!"); + + snmp_client::~snmp_client(){ + // SPDLOG_INFO("Closing snmp session"); + PLOG(logINFO) << "Closing SNMP session"; + snmp_close(ss); } - else if (status == STAT_SUCCESS && response->errstat == SNMP_ERR_NOERROR) - { - // SUCCESS: Return the response as result - for (auto vars = response->variables; vars; vars = vars->next_variable) + + + + + 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) { - if (vars->type == ASN_OCTET_STR) + // SPDLOG_DEBUG("Attemping to GET value for: {0}", input_oid); + PLOG(logDEBUG1) << "Attempting to GET value for: " << input_oid; + pdu = snmp_pdu_create(SNMP_MSG_GET); + } + else if (request_type == request_type::SET) + { + // SPDLOG_DEBUG("Attemping to SET value for {0}", input_oid, " to {1}", val.val_int); + PLOG(logDEBUG1) << "Attempting to SET value for " << input_oid << " to " << val.val_int; + pdu = snmp_pdu_create(SNMP_MSG_SET); + } + else{ + // SPDLOG_ERROR("Invalid request type, method accepts only GET and SET"); + PLOG(logERROR) << "Invalid request type, method accpets only GET and SET"; + } + + // 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 + // SPDLOG_ERROR("OID could not be created from input: {0}", input_oid); + PLOG(logERROR) << "OID could not be created from input: " << input_oid; + return false; + + } + else{ + + if(request_type == request_type::GET) { - result = reinterpret_cast(vars->val.string); + // Add OID to pdu for get request + snmp_add_null_var(pdu, OID, OID_len); } - else + else if(request_type == request_type::SET) { - throw SNMPClientException("Received respones type is not a string"); + 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()); + } + else if(val.type == snmp_response_obj::response_type::STRING){ + // SPDLOG_ERROR("Setting string value is currently not supported"); + PLOG(logERROR) << "Setting string value is currently not supported"; + return false; + } + } + + // SPDLOG_INFO("Created OID for input: {0}", input_oid); + PLOG(logERROR) << "Created OID for input: " << input_oid; + } + // Send the request + int status = snmp_synch_response(ss, pdu, &response); + + // Check response + if(status == STAT_SUCCESS && response->errstat == SNMP_ERR_NOERROR) { + + // SPDLOG_INFO("STAT_SUCCESS, received a response"); + PLOG(logINFO) << "STAT_SUCCESS, received a response"; + + if(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){ + if(vars->val.integer){ + val.val_int = *vars->val.integer; + // SPDLOG_DEBUG("Integer value in object: {0}", val.val_int); + PLOG(logDEBUG1) << "Integer value in object: ", val.val_int; + } + else{ + // SPDLOG_ERROR("Response specifies type integer, but no integer value found"); + PLOG(logERROR) << "Response specifies type integer, but no integer value found"; + return false; + } + + } + else if(vars->type == ASN_OCTET_STR){ + if(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]); + } + + } + else{ + // SPDLOG_ERROR("Response specifies type string, but no string value found"); + PLOG(logERROR) << "Response specifies type string, but no string value found"; + return false; + } + } + else{ + // SPDLOG_ERROR("Received a message type which isn't an integer or string"); + PLOG(logERROR) << "Received a message type which isn't an integer or string"; + return false; + } + } + } + else if(request_type == request_type::SET){ + + if(val.type == snmp_response_obj::response_type::INTEGER){ + // SPDLOG_DEBUG("Success in SET for OID: {0} Value: {1}", input_oid ,val.val_int); + PLOG(logDEBUG1) << "Success in SET for OID: " << input_oid << " Value: " << val.val_int; + } + + else if(val.type == snmp_response_obj::response_type::STRING){ + // SPDLOG_DEBUG("Success in SET for OID: {0} Value:", input_oid); + PLOG(logDEBUG1) << "Success in SET for OID: " << input_oid << " Value: "; + for(auto data : val.val_string){ + // SPDLOG_DEBUG("{0}", data); + PLOG(logDEBUG1) << data; + } + } } + + } + else + { + log_error(status, request_type, response); + return false; } + + if (response){ + snmp_free_pdu(response); + OID_len = MAX_OID_LEN; + } + + return true; } - else + + std::string snmp_client::SNMPGet(const std::string &req_oid) { - // FAILURE: Print what went wrong! - std::string errMsg = snmp_errstring(response->errstat); - throw SNMPClientException("Error in packet. Reason:" + errMsg); - } - if (response) - snmp_free_pdu(response); - return result; -} - -bool SNMPClient::SNMPSet(const std::string &oid, int32_t value) -{ - return SNMPClient::SNMPSet(oid, ASN_INTEGER, (const void *)&value, sizeof(value)); -} - -bool SNMPClient::SNMPSet(const std::string &oid, u_char type, const void *value, size_t len) -{ - bool rc = true; - static int quiet = 0; - int arg; - int count; - int current_name = 0; - int current_type = 0; - int current_value = 0; - char *names[SNMP_MAX_CMDLINE_OIDS]; - char types[SNMP_MAX_CMDLINE_OIDS]; - char *values[SNMP_MAX_CMDLINE_OIDS]; - int status; - int failures = 0; - int exitval = 0; - auto pdu = snmp_pdu_create(SNMP_MSG_SET); - - // if (!snmp_parse_oid(oid.c_str(), anOID, &anOID_len)) - // { - // snmp_perror(oid.c_str()); - // std::string errMsg = "OID could not be created from input:" + oid; - // throw SNMPClientException(errMsg); - // SOCK_CLEANUP; - // } - for (count = 0; count < current_name; count++) { - if (snmp_parse_oid(names[count], anOID, &anOID_len) == NULL) { - snmp_perror(names[count]); - failures++; - } else - if (snmp_add_var - (pdu, anOID, anOID_len, types[count], values[count])) { - snmp_perror(names[count]); - failures++; + snmp_pdu *response; + + std::string result = ""; + auto pdu = snmp_pdu_create(SNMP_MSG_GET); + + if (!snmp_parse_oid(req_oid.c_str(), OID, &OID_len)) + { + snmp_perror(req_oid.c_str()); + std::string errMsg = "OID could not be created from input:" + req_oid; + throw snmp_client_exception(errMsg); + SOCK_CLEANUP; } - } - if (failures) { - snmp_close(ss); - SOCK_CLEANUP; - exit(1); + snmp_add_null_var(pdu, OID, OID_len); + int status = snmp_synch_response(ss, pdu, &response); + + if (!response) + { + throw snmp_client_exception("No response for SNMP Get request!"); + } + else if (status == STAT_SUCCESS && response->errstat == SNMP_ERR_NOERROR) + { + // SUCCESS: Return the response as result + for (auto vars = response->variables; vars; vars = vars->next_variable) + { + if (vars->type == ASN_OCTET_STR) + { + result = reinterpret_cast(vars->val.string); + } + else + { + throw snmp_client_exception("Received respones type is not a string"); + } + } + } + else + { + // FAILURE: Print what went wrong! + std::string errMsg = snmp_errstring(response->errstat); + throw snmp_client_exception("Error in packet. Reason:" + errMsg); + } + if (response) + snmp_free_pdu(response); + return result; + } + + + int snmp_client::get_port() const + { + return port_; } - - // snmp_add_null_var(pdu, anOID, anOID_len); - // snmp_pdu_add_variable(pdu, anOID, anOID_len, type, value, len); - - auto status = snmp_synch_response(ss, pdu, &response); - if (status == STAT_SUCCESS) { - if (response->errstat == SNMP_ERR_NOERROR) { - if (!quiet) { - for (vars = response->variables; vars; - vars = vars->next_variable) - print_variable(vars->name, vars->name_length, vars); + + + void snmp_client::log_error(const int& status, const request_type& request_type, snmp_pdu *response) const + { + + if (status == STAT_SUCCESS) + { + // SPDLOG_ERROR("Variable type: {0}",response->variables->type); + // SPDLOG_ERROR("Error in packet.{0} ", static_cast(snmp_errstring(static_cast(response->errstat)))); + + PLOG(logERROR) << "Variable type: " << response->variables->type; + PLOG(logERROR) << "Error in packet " << static_cast(snmp_errstring(static_cast(response->errstat))); + } + else if (status == STAT_TIMEOUT){ + + // SPDLOG_ERROR("Timeout, no response from server"); + PLOG(logERROR) << "Timeout, no response from server"; + } + else{ + if(request_type == request_type::GET){ + // SPDLOG_ERROR("Unknown SNMP Error for {0}", "GET"); + PLOG(logERROR) << "Unknown SNMP Error for GET"; } - } else { - fprintf(stderr, "Error in packet.\nReason: %s\n", - snmp_errstring(response->errstat)); - if (response->errindex != 0) { - fprintf(stderr, "Failed object: "); - for (count = 1, vars = response->variables; - vars && (count != response->errindex); - vars = vars->next_variable, count++); - if (vars) - fprint_objid(stderr, vars->name, vars->name_length); - fprintf(stderr, "\n"); + else if(request_type == request_type::SET){ + // SPDLOG_ERROR("Unknown SNMP Error for {0}", "SET"); + PLOG(logERROR) << "Unknown SNMP Error for SET"; } - exitval = 2; - } - } else if (status == STAT_TIMEOUT) { - fprintf(stderr, "Timeout: No Response from %s\n", - session.peername); - exitval = 1; - } else { /* status == STAT_ERROR */ - snmp_sess_perror("snmpset", ss); - exitval = 1; + } + } - if (response) - snmp_free_pdu(response); - snmp_close(ss); - SOCK_CLEANUP; - return exitval; -} - -int SNMPClient::GetPort() const -{ - return _snmp_port; -} - -std::string SNMPClient::GetAddress() const -{ - return _rsuIP; -} - -SNMPClient::~SNMPClient() -{ - fprintf(stdout, "Closing snmp session\n"); - snmp_close(ss); -} - -}} // namespace tmx::utils +} // namespace + + + + + + + + + +// #include "SNMPClient.h" + +// namespace tmx { +// namespace utils { + +// SNMPClient::SNMPClient(const std::string &rsuIP, uint16_t snmp_port, const std::string &snmp_user, const std::string &securityLevel, const std::string &authPassPhrase) +// : _snmp_port(snmp_port) +// , _rsuIP(rsuIP) +// { +// std::string ip_port_string = rsuIP + ":" + std::to_string(snmp_port); +// char *ip_port = &ip_port_string[0]; +// init_snmp("snmpclient"); +// snmp_sess_init(&session); +// session.peername = ip_port; +// session.version = SNMP_VERSION_3; +// session.securityName = (char *)snmp_user.c_str(); +// session.securityNameLen = snmp_user.length(); + + +// 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; +// 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 (generate_Ku(session.securityAuthProto, +// session.securityAuthProtoLen, +// (u_char *)authPassPhrase.c_str(),authPassPhrase.length(), +// session.securityAuthKey, +// &session.securityAuthKeyLen) != SNMPERR_SUCCESS) +// { +// std::string errMsg = "Error generating Ku from authentication pass phrase. \n"; +// throw SNMPClientException(errMsg); +// } + +// ss = snmp_open(&session); + +// if (!ss) +// { +// std::string errMsg = "Cannot open SNMP session. \n"; +// throw SNMPClientException(errMsg); +// } +// else +// { +// fprintf(stdout, "snmp session is open.\n"); +// } +// } + +// std::string SNMPClient::SNMPGet(const std::string &req_oid) +// { +// std::string result = ""; +// auto pdu = snmp_pdu_create(SNMP_MSG_GET); + +// if (!snmp_parse_oid(req_oid.c_str(), anOID, &anOID_len)) +// { +// snmp_perror(req_oid.c_str()); +// std::string errMsg = "OID could not be created from input:" + req_oid; +// throw SNMPClientException(errMsg); +// SOCK_CLEANUP; +// } + +// snmp_add_null_var(pdu, anOID, anOID_len); +// int status = snmp_synch_response(ss, pdu, &response); + +// if (!response) +// { +// throw SNMPClientException("No response for SNMP Get request!"); +// } +// else if (status == STAT_SUCCESS && response->errstat == SNMP_ERR_NOERROR) +// { +// // SUCCESS: Return the response as result +// for (auto vars = response->variables; vars; vars = vars->next_variable) +// { +// if (vars->type == ASN_OCTET_STR) +// { +// result = reinterpret_cast(vars->val.string); +// } +// else +// { +// throw SNMPClientException("Received respones type is not a string"); +// } +// } +// } +// else +// { +// // FAILURE: Print what went wrong! +// std::string errMsg = snmp_errstring(response->errstat); +// throw SNMPClientException("Error in packet. Reason:" + errMsg); +// } +// if (response) +// snmp_free_pdu(response); +// return result; +// } + +// bool SNMPClient::SNMPSet(const std::string &oid, int32_t value) +// { +// // considering handling as a string here or creating a struct like in TSC +// return SNMPClient::SNMPSet(oid, ASN_INTEGER, (const void *)&value, sizeof(value)); +// } + +// bool SNMPClient::SNMPSet(const std::string &oid, u_char type, const void *value, size_t len) +// { +// bool rc = true; +// static int quiet = 0; +// int arg; +// int count; +// int current_name = 0; +// int current_type = 0; +// int current_value = 0; +// char *names[SNMP_MAX_CMDLINE_OIDS]; +// char types[SNMP_MAX_CMDLINE_OIDS]; +// char *values[SNMP_MAX_CMDLINE_OIDS]; +// int status; +// int failures = 0; +// int exitval = 0; +// auto pdu = snmp_pdu_create(SNMP_MSG_SET); + +// // if (!snmp_parse_oid(oid.c_str(), anOID, &anOID_len)) +// // { +// // snmp_perror(oid.c_str()); +// // std::string errMsg = "OID could not be created from input:" + oid; +// // throw SNMPClientException(errMsg); +// // SOCK_CLEANUP; +// // } +// for (count = 0; count < current_name; count++) { +// if (snmp_parse_oid(names[count], anOID, &anOID_len) == NULL) { +// snmp_perror(names[count]); +// failures++; +// } else +// if (snmp_add_var +// (pdu, anOID, anOID_len, types[count], values[count])) { +// snmp_perror(names[count]); +// failures++; +// } +// } + +// if (failures) { +// snmp_close(ss); +// SOCK_CLEANUP; +// exit(1); +// } + +// //snmp_add_null_var(pdu, anOID, anOID_len); +// //snmp_pdu_add_variable(pdu, anOID, anOID_len, type, value, len); +// //snmp_add_var(pdu, anOID, anOID_len, 'i', (std::to_string(current_value).c_str())); + +// int status = snmp_synch_response(ss, pdu, &response); + +// if (status == STAT_SUCCESS) { +// if (response->errstat == SNMP_ERR_NOERROR) { +// if (!quiet) { +// for (vars = response->variables; vars; +// vars = vars->next_variable) +// print_variable(vars->name, vars->name_length, vars); +// } +// } else { +// fprintf(stderr, "Error in packet.\nReason: %s\n", +// snmp_errstring(response->errstat)); +// if (response->errindex != 0) { +// fprintf(stderr, "Failed object: "); +// for (count = 1, vars = response->variables; +// vars && (count != response->errindex); +// vars = vars->next_variable, count++); +// if (vars) +// fprint_objid(stderr, vars->name, vars->name_length); +// fprintf(stderr, "\n"); +// } +// exitval = 2; +// } +// } else if (status == STAT_TIMEOUT) { +// fprintf(stderr, "Timeout: No Response from %s\n", +// session.peername); +// exitval = 1; +// } else { /* status == STAT_ERROR */ +// snmp_sess_perror("snmpset", ss); +// exitval = 1; +// } + +// if (response) +// snmp_free_pdu(response); +// snmp_close(ss); +// SOCK_CLEANUP; +// return exitval; +// } + +// int SNMPClient::GetPort() const +// { +// return _snmp_port; +// } + +// std::string SNMPClient::GetAddress() const +// { +// return _rsuIP; +// } + +// SNMPClient::~SNMPClient() +// { +// fprintf(stdout, "Closing snmp session\n"); +// snmp_close(ss); +// } + +// }} // namespace tmx::utils diff --git a/src/tmx/TmxUtils/src/SNMPClient.h b/src/tmx/TmxUtils/src/SNMPClient.h index 64dd0c617..e94cb96cf 100644 --- a/src/tmx/TmxUtils/src/SNMPClient.h +++ b/src/tmx/TmxUtils/src/SNMPClient.h @@ -1,63 +1,246 @@ - -#ifndef SNMPCLIENT_H_ -#define SNMPCLIENT_H_ +#pragma once #include -#include #include -#include -#include -#include -#include +#include +#include "PluginLog.h" +#include "SNMPClientException.h" namespace tmx::utils { -class SNMPClient +enum class request_type +{ + GET, + SET, + OTHER //Processing this request type is not a defined behavior, included for testing only +}; + + +/** @brief A struct to hold the value being sent to the TSC, can be integer or string. Type needs to be defined*/ +struct snmp_response_obj { -private: - netsnmp_session session; - netsnmp_session *ss; - netsnmp_pdu *pdu, *response = NULL; - netsnmp_variable_list *vars; - oid anOID[MAX_OID_LEN]; - size_t anOID_len = MAX_OID_LEN; - int _snmp_port; - std::string _rsuIP; - -public: - /** - * @brief Construct a new SNMPClient object - * @param ip RSU IP - * @param port SNMP port - */ - SNMPClient(const std::string &rsuIP, uint16_t snmp_port, const std::string &snmp_user, const std::string &securityLevel, const std::string &authPassPhrase); - /** - * @brief Send SNMP v3 Get request to an RSU to retrieve data - * @param oid OID (Object Identifier) uniquely identify managed objects in a MIB database. Concept refers to: https://en.wikipedia.org/wiki/Management_information_base - * @return std::string identified by the oid. If SNMP response is not string, exit with failure. - */ - std::string SNMPGet(const std::string &oid); - /** - * @brief Send SNMP v3 Set request to an RSU to write data - * @param oid OID (Object Identifier) uniquely identify managed objects in a MIB database. - * @return std::string identified by the oid. If SNMP response is not string, exit with failure. - */ - bool SNMPSet(const std::string &oid, int32_t value); - bool SNMPSet(const std::string &oid, u_char type, const void *value, size_t len); - /** - * @brief Retrieve the port used by this SNMP client as an integer. - * @return The port as expected in a host integer. - */ - virtual int GetPort() const; - /** - * @brief Retrieve a copy of the RSU IPv4 address. - * @return std::string with a copy of the constructor input address. - */ - virtual std::string GetAddress() const; - ~SNMPClient(); + /** @brief The type of value being requested or set, on the TSC */ + enum class response_type + { + INTEGER, + STRING + }; + + //snmp response values can be any asn.1 supported types. + //Integer and string values can be processed here + int64_t val_int = 0; + std::vector val_string; + response_type type; + + inline bool operator==(const snmp_response_obj& obj2) const + { + return val_int == obj2.val_int && val_string == obj2.val_string && type == obj2.type; + } }; -} // namespace tmx::utils +class snmp_client +{ + private: + + /*variables to store an snmp session*/ + // struct that holds information about who we're going to be talking to + // We need to declare 2 of these, one to fill info with and second which is + // a pointer returned by the library + snmp_session session; + snmp_session *ss; + + /*Structure to hold all of the information that we're going to send to the remote host*/ + snmp_pdu *pdu; + + + /*OID is going to hold the location of the information which we want to receive. It will need a size as well*/ + oid OID[MAX_OID_LEN]; + size_t OID_len = MAX_OID_LEN; + + // Values from config + /*Target device IP address*/ + std::string ip_; + /*Target device NTCIP port*/ + int port_ = 0; + /*Target community for establishing snmp communication*/ + std::string community_ = "public"; + /* net-snmp version definition: SNMP_VERSION_1:0 SNMP_VERSION_2c:1 SNMP_VERSION_2u:2 SNMP_VERSION_3:3 + https://github.com/net-snmp/net-snmp/blob/master/include/net-snmp/library/snmp.h */ + int snmp_version_ = 0; + /*Time after which the the snmp request times out*/ + int timeout_ = 10000; + + public: + /** @brief Constructor for Traffic Signal Controller Service client. + * Uses the arguments provided to establish an snmp connection + * @param ip The ip ,as a string, for the tsc_client_service to establish an snmp communication with. + * @param port Target port as integer on the host for snmp communication. + * @param community The community id as a string. Defaults to "public" if unassigned. + * @param snmp_version The snmp_version as defined in net-snmp.Default to 0 if unassigned. + * net-snmp version definition: SNMP_VERSION_1:0 SNMP_VERSION_2c:1 SNMP_VERSION_2u:2 SNMP_VERSION_3:3" + * @param timeout The time in microseconds after which an snmp session request expires. Defaults to 100 if unassigned + * **/ + 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 = 0, int timeout = 100); + + /* Disable default copy constructor*/ + snmp_client() = delete; + + + /** @brief Returns true or false depending on whether the request could be processed for given input OID at the Traffic Signal Controller. + * @param input_oid The OID to request information for. + * @param request_type The request type for which the error is being logged. Accepted values are "GET" and "SET" only. + * @param value_int The integer value for the object returned by reference. For "SET" it is the value to be set. + * For "GET", it is the value returned for the returned object by reference. + * This is an optional argument, if not provided, defaults to 0. + * @param value_str String value for the object, returned by reference. Optional argument, if not provided the value is set as an empty string + * @return Integer value at the oid, returns false if value cannot be set/requested or oid doesn't have an integer value to return.*/ + + virtual bool process_snmp_request(const std::string& input_oid, const request_type& request_type, snmp_response_obj& val); + /** @brief Finds error type from status and logs an error. + * @param status The integer value corresponding to net-snmp defined errors. macros considered are STAT_SUCCESS(0) and STAT_TIMEOUT(2) + * @param request_type The request type for which the error is being logged (GET/SET). + * @param response The snmp_pdu struct */ + + std::string SNMPGet(const std::string &oid); + + virtual int get_port() const; + + void log_error(const int& status, const request_type& request_type, snmp_pdu *response) const; + + /** @brief Destructor for client. Closes the snmp session**/ + virtual ~snmp_client(); + +}; + +} // namespace + + + + + + + + + + +// #ifndef SNMPCLIENT_H_ +// #define SNMPCLIENT_H_ + +// #include +// #include +// #include +// #include +// #include +// #include +// #include +// #include "PluginLog.h" + +// #include + + +// namespace tmx::utils { + + +// enum class RESPONSE_TYPE +// { +// INTEGER, +// STRING +// }; + + +// struct snmp_response_obj +// { + +// int64_t val_int = 0; +// std::vector val_string; +// RESPONSE_TYPE type; + +// inline bool operator==(const snmp_response_obj &obj2) const +// { + +// return val_int == obj2.val_int && val_string == obj2.val_string && type == obj2.type; + +// } +// }; + + +// class SNMPClient +// { +// private: +// netsnmp_session session; +// netsnmp_session *ss; + +// // netsnmp_pdu *pdu, *response = NULL; +// netsnmp_pdu *pdu; +// netsnmp_pdu *response; + +// netsnmp_variable_list *vars; // unknown + +// oid anOID[MAX_OID_LEN]; +// size_t anOID_len = MAX_OID_LEN; + +// int _snmp_port; +// std::string _rsuIP; + + +// // std::string community_ = "public"; + +// // int snmp_version_ = 0; + +// //int timeout_ = 10000; + + + + +// public: +// /** +// * @brief Construct a new SNMPClient object +// * @param ip RSU IP +// * @param port SNMP port +// */ + + +// SNMPClient(const std::string &rsuIP, uint16_t snmp_port, const std::string &snmp_user, const std::string &securityLevel, const std::string &authPassPhrase); +// /** +// * @brief Send SNMP v3 Get request to an RSU to retrieve data +// * @param oid OID (Object Identifier) uniquely identify managed objects in a MIB database. Concept refers to: https://en.wikipedia.org/wiki/Management_information_base +// * @return std::string identified by the oid. If SNMP response is not string, exit with failure. +// */ + + +// std::string SNMPGet(const std::string &oid); +// /** +// * @brief Send SNMP v3 Set request to an RSU to write data +// * @param oid OID (Object Identifier) uniquely identify managed objects in a MIB database. +// * @return std::string identified by the oid. If SNMP response is not string, exit with failure. +// */ + + +// bool SNMPSet(const std::string &oid, snmp_response_obj &val); + +// // bool SNMPSet(const std::string &oid, int32_t value); +// // bool SNMPSet(const std::string &oid, u_char type, const void *value, size_t len); +// /** +// * @brief Retrieve the port used by this SNMP client as an integer. +// * @return The port as expected in a host integer. +// */ + + +// virtual int GetPort() const; +// /** +// * @brief Retrieve a copy of the RSU IPv4 address. +// * @return std::string with a copy of the constructor input address. +// */ + + + +// virtual std::string GetAddress() const; + + +// ~SNMPClient(); +// }; + +// } // namespace tmx::utils -#endif /* SNMPCLIENT_H_ */ +// #endif /* SNMPCLIENT_H_ */ diff --git a/src/tmx/TmxUtils/src/SNMPClientException.cpp b/src/tmx/TmxUtils/src/SNMPClientException.cpp new file mode 100644 index 000000000..3311a6b96 --- /dev/null +++ b/src/tmx/TmxUtils/src/SNMPClientException.cpp @@ -0,0 +1,8 @@ +#include "SNMPClientException.h" + +namespace tmx::utils { + + snmp_client_exception::snmp_client_exception(const std::string &msg): std::runtime_error(msg){}; + + snmp_client_exception::~snmp_client_exception() = default; +} diff --git a/src/tmx/TmxUtils/src/SNMPClientException.h b/src/tmx/TmxUtils/src/SNMPClientException.h index c2effd0b7..501285de3 100644 --- a/src/tmx/TmxUtils/src/SNMPClientException.h +++ b/src/tmx/TmxUtils/src/SNMPClientException.h @@ -1,23 +1,23 @@ -#ifndef SNMPCLIENTEXCEPTION_H_ -#define SNMPCLIENTEXCEPTION_H_ -#include +#pragma once -namespace tmx::utils { - -class SNMPClientException : public std::exception -{ -private: - std::string message; - -public: - explicit SNMPClientException(const std::string& msg) : message(msg){}; - const char *what() const noexcept override - { - return message.c_str(); - } - ~SNMPClientException() override = default; -}; +#include -} // namespace tmx::utils - -#endif /* SNMPCLIENTEXCEPTION_H_ */ +namespace tmx::utils { + /** + * @brief Runtime error related to SNMP client used to communicate with Traffic Signal Controller (NTCIP). + * + * @author Paul Bourelly + */ + class snmp_client_exception : public std::runtime_error{ + public: + /** + * @brief Destructor. + */ + ~snmp_client_exception() override; + /** + * @brief Constructor. + * @param msg String exception message. + */ + explicit snmp_client_exception(const std::string &msg ); + }; +} diff --git a/src/v2i-hub/ERVCloudForwardingPlugin/src/ERVCloudForwardingPlugin.cpp b/src/v2i-hub/ERVCloudForwardingPlugin/src/ERVCloudForwardingPlugin.cpp index c69b9e5e4..718b96093 100644 --- a/src/v2i-hub/ERVCloudForwardingPlugin/src/ERVCloudForwardingPlugin.cpp +++ b/src/v2i-hub/ERVCloudForwardingPlugin/src/ERVCloudForwardingPlugin.cpp @@ -49,8 +49,11 @@ namespace ERVCloudForwardingPlugin try { PLOG(logINFO) << "Create SNMP Client to connect to RSU. RSU IP:" << _rsuIp << ",\tRSU Port:" << _snmpPort << ",\tSecurity Level:" << _securityLevel << "\tSNMP User: " << _snmpUser << ",\tAuthentication Passphrase: " << _authPassPhrase << endl; - auto snmpClient = std::make_shared(_rsuIp, _snmpPort, _snmpUser, _securityLevel, _authPassPhrase); + // auto snmpClient = std::make_shared(_rsuIp, _snmpPort, _snmpUser, _securityLevel, _authPassPhrase); + auto snmpClient = std::make_shared(_rsuIp, _snmpPort, "public", _snmpUser, _securityLevel, _authPassPhrase, 3, 1000); + //auto gps_sentence = snmpClient->SNMPGet(_GPSOID); auto gps_sentence = snmpClient->SNMPGet(_GPSOID); + //auto gps_sentence = snmpClient->process_snmp_request(_GPSOID, tmx::utils::request_type::GET); auto gps_map = ERVCloudForwardingWorker::ParseGPS(gps_sentence); long latitude = 0; long longitude = 0; @@ -77,7 +80,8 @@ namespace ERVCloudForwardingPlugin } isRegistered = true; } - catch (SNMPClientException &ex) + // catch (SNMPClientException &ex) + catch (snmp_client_exception &ex) { PLOG(logERROR) << "Cannot register RSU location. Reason: " << ex.what() << endl; } diff --git a/src/v2i-hub/ERVCloudForwardingPlugin/test/SNMPClientTest.cpp b/src/v2i-hub/ERVCloudForwardingPlugin/test/SNMPClientTest.cpp index 71de826dc..f96c6de58 100644 --- a/src/v2i-hub/ERVCloudForwardingPlugin/test/SNMPClientTest.cpp +++ b/src/v2i-hub/ERVCloudForwardingPlugin/test/SNMPClientTest.cpp @@ -16,9 +16,12 @@ namespace unit_test std::string snmp_user = "dummy"; std::string securityLevel = "authPriv"; std::string authPassPhrase = "dummy"; // Error: passphrase chosen is below the length requirements of the USM (min=8). - ASSERT_ANY_THROW(SNMPClient(rsu_ip, snmp_port, snmp_user, securityLevel, authPassPhrase)); + std::string communityTest = "public"; //remove after test + // ASSERT_ANY_THROW(SNMPClient(rsu_ip, snmp_port, snmp_user, securityLevel, authPassPhrase)); + ASSERT_ANY_THROW(snmp_client(rsu_ip, snmp_port, communityTest, snmp_user, securityLevel, authPassPhrase, 3, 1000)); authPassPhrase = "dummydummy"; - ASSERT_NO_THROW(SNMPClient(rsu_ip, snmp_port, snmp_user, securityLevel, authPassPhrase)); + // ASSERT_NO_THROW(SNMPClient(rsu_ip, snmp_port, snmp_user, securityLevel, authPassPhrase)); + ASSERT_NO_THROW(snmp_client(rsu_ip, snmp_port, communityTest, snmp_user, securityLevel, authPassPhrase, 3, 1000)); } TEST_F(SNMPClientTest, SNMPGet) @@ -28,7 +31,10 @@ namespace unit_test std::string snmp_user = "dummy"; std::string securityLevel = "authPriv"; std::string authPassPhrase = "dummydummy"; - auto snmpClient = SNMPClient(rsu_ip, snmp_port, snmp_user, securityLevel, authPassPhrase); - ASSERT_THROW(snmpClient.SNMPGet("1.0.15628.4.1.8.5.0"), SNMPClientException); + std::string communityTest = "public"; //remove after test + // auto snmpClient = SNMPClient(rsu_ip, snmp_port, snmp_user, securityLevel, authPassPhrase); + auto snmpClient = snmp_client(rsu_ip, snmp_port, communityTest, snmp_user, securityLevel, authPassPhrase, 3, 1000); + // ASSERT_THROW(snmpClient.SNMPGet("1.0.15628.4.1.8.5.0"), SNMPClientException); + ASSERT_THROW(snmpClient.SNMPGet("1.0.15628.4.1.8.5.0"), snmp_client_exception); } } \ No newline at end of file diff --git a/src/v2i-hub/ImmediateForwardPlugin/manifest.json b/src/v2i-hub/ImmediateForwardPlugin/manifest.json index 9acae12ff..fd171b94d 100644 --- a/src/v2i-hub/ImmediateForwardPlugin/manifest.json +++ b/src/v2i-hub/ImmediateForwardPlugin/manifest.json @@ -74,7 +74,7 @@ }, { "key":"AuthPassPhrase", - "default":"dummy", + "default":"dummyPass", "description":"SNMP v3 authentication passphrase." }, { diff --git a/src/v2i-hub/ImmediateForwardPlugin/src/ImmediateForwardPlugin.cpp b/src/v2i-hub/ImmediateForwardPlugin/src/ImmediateForwardPlugin.cpp index ba1381538..8491b460d 100644 --- a/src/v2i-hub/ImmediateForwardPlugin/src/ImmediateForwardPlugin.cpp +++ b/src/v2i-hub/ImmediateForwardPlugin/src/ImmediateForwardPlugin.cpp @@ -23,6 +23,9 @@ using namespace boost::property_tree; using namespace std; using namespace tmx; + +// RSU has the ability to "sign every nth message" for encrypted messages + namespace ImmediateForward { @@ -31,6 +34,7 @@ const char* Key_SkippedNoMessageRoute = "Messages Skipped (No route)"; const char* Key_SkippedSignError = "Message Skipped (Signature Error Response)"; const char* Key_SkippedInvalidUdpClient = "Messages Skipped (Invalid UDP Client)"; +// no change ImmediateForwardPlugin::ImmediateForwardPlugin(std::string name) : PluginClient(name), _configRead(false), _skippedNoDsrcMetadata(0), @@ -44,9 +48,7 @@ ImmediateForwardPlugin::ImmediateForwardPlugin(std::string name) : PluginClient( _muteDsrc = false; } - - - +// (reuse mutex) SNMPClientList (done?) ImmediateForwardPlugin::~ImmediateForwardPlugin() { lock_guard lock(_mutexUdpClient); @@ -59,11 +61,21 @@ ImmediateForwardPlugin::~ImmediateForwardPlugin() delete _udpClientList[i][j]; } } + + for (uint i = 0; i < _SNMPClientList.size(); i++) + { + for (uint j = 0; j < _SNMPClientList[i].size(); j++) + { + if (_SNMPClientList[i][i] != NULL) + delete _SNMPClientList[i][j]; + } + } + } // @SONAR_START@ - +// no change void ImmediateForwardPlugin::OnConfigChanged(const char *key, const char *value) { PluginClient::OnConfigChanged(key, value); @@ -71,6 +83,7 @@ void ImmediateForwardPlugin::OnConfigChanged(const char *key, const char *value) UpdateConfigSettings(); } +// no change void ImmediateForwardPlugin::OnMessageReceived(IvpMessage *msg) { // Uncomment this line to call the base method, which prints the message received to cout. @@ -94,10 +107,12 @@ void ImmediateForwardPlugin::OnMessageReceived(IvpMessage *msg) if(!_muteDsrc) { + PLOG(logDEBUG) << "Sending message to radio..."; SendMessageToRadio(msg); } } +// no change void ImmediateForwardPlugin::OnStateChange(IvpPluginState state) { PluginClient::OnStateChange(state); @@ -108,6 +123,7 @@ void ImmediateForwardPlugin::OnStateChange(IvpPluginState state) } } +// Update config for SNMP clients void ImmediateForwardPlugin::UpdateConfigSettings() { PLOG(logINFO) << "Updating configuration settings."; @@ -127,9 +143,19 @@ void ImmediateForwardPlugin::UpdateConfigSettings() SetStatus(Key_SkippedInvalidUdpClient, _skippedInvalidUdpClient); SetStatus(Key_SkippedSignError, _skippedSignErrorResponse); } - for (uint i = 0; i < _udpClientList.size(); i++) - { - UpdateUdpClientFromConfigSettings(i); + + PLOG(logINFO) << "Updating client lists"; + if(snmpState == 1){ //updates + for (uint i = 0; i < _SNMPClientList.size(); i++) + { + UpdateUdpClientFromConfigSettings(i); + } + } + else{ + for (uint i = 0; i < _udpClientList.size(); i++) + { + UpdateUdpClientFromConfigSettings(i); + } } // Get the signature setting. @@ -144,25 +170,43 @@ void ImmediateForwardPlugin::UpdateConfigSettings() _configRead = true; } + // Retrieve all settings for a UDP client, then create a UDP client using those settings. // Other settings related to the UDP client are also updated (i.e. msg id list, psid list). + +// Update for SNMPClientList, keep RSU IPs sending to both clients as need bool ImmediateForwardPlugin::UpdateUdpClientFromConfigSettings(uint clientIndex) { + // only check snmp if snmp is enabled if (_udpClientList.size() <= clientIndex) { - PLOG(logWARNING) << "Invalid client number. Only " << _udpClientList.size() << " clients available."; + PLOG(logWARNING) << "Invalid UDP client number. Only " << _udpClientList.size() << " clients available."; + return false; + } + else if (_SNMPClientList.size() <= clientIndex) // updated + { + PLOG(logWARNING) << "Invalid SNMP client number. Only " << _SNMPClientList.size() << " clients available."; return false; } int clientNum = clientIndex + 1; string messagesSetting((boost::format("Messages_Destination_%d") % clientNum).str()); string udpPortSetting((boost::format("Destination_%d") % clientNum).str()); + string SNMPPortSetting((boost::format("Destination_%d") % clientNum).str()); + + PLOG(logDEBUG) << "SNMPPortSetting: " << SNMPPortSetting; try { string destinations; string messages; - GetConfigValue(udpPortSetting, destinations); + if(snmpState == 1){ + GetConfigValue(SNMPPortSetting, destinations); + } + else{ + GetConfigValue(udpPortSetting, destinations); + } + GetConfigValue(messagesSetting, messages); GetConfigValue("EnableSNMP", snmpState); @@ -176,13 +220,22 @@ bool ImmediateForwardPlugin::UpdateUdpClientFromConfigSettings(uint clientIndex) ParseJsonMessageConfig(messages, clientIndex); + // only check snmp if enabled? for (uint i = 0; i < _udpClientList[clientIndex].size(); i++) { if (_udpClientList[clientIndex][i] != NULL) delete _udpClientList[clientIndex][i]; } + for (uint i = 0; i < _SNMPClientList[clientIndex].size(); i++) // updated + { + if (_SNMPClientList[clientIndex][i] != NULL) + delete _SNMPClientList[clientIndex][i]; + } + _udpClientList[clientIndex].clear(); + _SNMPClientList[clientIndex].clear(); // updated + if (destinations.length() > 0) { @@ -195,12 +248,16 @@ bool ImmediateForwardPlugin::UpdateUdpClientFromConfigSettings(uint clientIndex) boost::split(addr, srvs[i], boost::is_any_of(":")); if (addr.size() != 2) continue; + if (snmpState == 1) { _rsuIp = addr[0]; _snmpPort = stoul(addr[1]); PLOG(logINFO) << "Create SNMP Client to connect to RSU. RSU IP: " << _rsuIp << ",\tRSU Port: " << _snmpPort << "\tSNMP User: " << _snmpUser << ",\tSecurity Level: " << _securityLevel << ",\tAuthentication Passphrase: " << _authPassPhrase << endl; + // update SNMPClientList with the creation of a new SNMPClient with given params + _SNMPClientList[clientIndex].push_back(new snmp_client(_rsuIp, _snmpPort, "public", _snmpUser, _securityLevel, _authPassPhrase, 3, 1000)); // updated + PLOG(logDEBUG) << "Client added to list"; } else { @@ -220,6 +277,7 @@ bool ImmediateForwardPlugin::UpdateUdpClientFromConfigSettings(uint clientIndex) return true; } +// no change bool ImmediateForwardPlugin::ParseJsonMessageConfig(const std::string& json, uint clientIndex) { if (json.length() == 0) @@ -287,6 +345,7 @@ bool ImmediateForwardPlugin::ParseJsonMessageConfig(const std::string& json, uin return true; } +// No change to payload code? void ImmediateForwardPlugin::SendMessageToRadio(IvpMessage *msg) { bool foundMessageType = false; @@ -327,7 +386,7 @@ void ImmediateForwardPlugin::SendMessageToRadio(IvpMessage *msg) // Format the message using the protocol defined in the - // USDOT ROadside Unit Specifications Document v 4.0 Appendix C. + // USDOT Roadside Unit Specifications Document v 4.0 Appendix C. stringstream os; @@ -351,7 +410,6 @@ void ImmediateForwardPlugin::SendMessageToRadio(IvpMessage *msg) std::string req = "\'{\"type\":\""+mType+"\",\"message\":\""+base64str+"\"}\'"; - string cmd1="curl -X POST "+url+" -H \'Content-Type: application/json\' -d "+req; const char *cmd=cmd1.c_str(); char buffer[2048]; @@ -372,7 +430,7 @@ void ImmediateForwardPlugin::SendMessageToRadio(IvpMessage *msg) PLOG(logERROR) << "Error parsing Messages: " << ex.what(); return; } - PLOG(logDEBUG1) << "SCMS Contain response = " << result << std::endl; + PLOG(logDEBUG) << "SCMS Contain response = " << result << std::endl; cJSON *root = cJSON_Parse(result.c_str()); // Check if status is 200 (successful) cJSON *status = cJSON_GetObjectItem(root, "code"); @@ -390,18 +448,16 @@ void ImmediateForwardPlugin::SendMessageToRadio(IvpMessage *msg) base642hex(signedMsg,payloadbyte); // this allows sending hex of the signed message rather than base64 } - else + else { payloadbyte=msg->payload->valuestring; } // @SONAR_START@ - // Send the message using the configured SNMP client + // Send the message using the configured SNMP clients if (snmpState == 1) { - auto _snmpClient = std::make_shared(_rsuIp, _snmpPort, _snmpUser, _securityLevel, _authPassPhrase); - // os << "1.3.6.1.4.1.1206.4.2.18.4.2.1.2" << " x " << _messageConfigMap[configIndex].Psid; // if (_messageConfigMap[configIndex].Channel.empty()) // os << " 1.3.6.1.4.1.1206.4.2.18.4.2.1.3" << " i " << msg->dsrcMetadata->channel; @@ -411,15 +467,124 @@ void ImmediateForwardPlugin::SendMessageToRadio(IvpMessage *msg) // os << "1.3.6.1.4.1.1206.4.2.18.4.2.1.7" << " b 1100"; // else os << "1.3.6.1.4.1.1206.4.2.18.4.2.1.7" << " b 0000"; // os << " 1.3.6.1.4.1.1206.4.2.18.4.2.1.8" << " x " << payloadbyte; + // string message = os.str(); + + // Test for single snmp client instance + + _SNMPClientList[0].push_back(new snmp_client(_rsuIp, _snmpPort, "public", _snmpUser, _securityLevel, _authPassPhrase, 3, 1000)); + PLOG(logINFO) << "Test SNMP client pushed to list"; + + auto request = tmx::utils::request_type::SET; + auto type_INT = tmx::utils::snmp_response_obj::response_type::INTEGER; + auto type_STR = tmx::utils::snmp_response_obj::response_type::STRING; + + + for(int i = 0; i < _SNMPClientList[_messageConfigMap[configIndex].ClientIndex].size(); i++){ + PLOG(logINFO) << "Current client in list: " << i; + + + // Check to see if the client exists, and if so, set the message to it. Does the index of the forward table need incremented? + if(_SNMPClientList[_messageConfigMap[configIndex].ClientIndex][i] != NULL){ + + // Initial set to enable standby mode on a given RSU + tmx::utils::snmp_response_obj modeMessage; + modeMessage.type = type_INT; // may create shorthand for INTEGER/STRING + modeMessage.val_int = 2; + _SNMPClientList[_messageConfigMap[configIndex].ClientIndex][i]->process_snmp_request("iso.0.15628.4.1.99.0", request, modeMessage); + + // tmxtype and sendtype needed? + PLOG(logDEBUG2) << _logPrefix << "Sending - TmxType: " << _messageConfigMap[configIndex].TmxType << ", SendType: " << _messageConfigMap[configIndex].SendType + << ", PSID: " << _messageConfigMap[configIndex].Psid << ", Client: " << _messageConfigMap[configIndex].ClientIndex + << ", Channel: " << (_messageConfigMap[configIndex].Channel.empty() ? ::to_string( msg->dsrcMetadata->channel) : _messageConfigMap[configIndex].Channel) + << ", Port: " << _SNMPClientList[_messageConfigMap[configIndex].ClientIndex][i]->get_port(); + + // Get the max number of possible objects in the forward table + tmx::utils::snmp_response_obj maxIFMs; + maxIFMs.type = type_INT; + _SNMPClientList[_messageConfigMap[configIndex].ClientIndex][i]->process_snmp_request("1.3.6.1.4.1.1206.4.2.18.4.1", tmx::utils::request_type::GET, maxIFMs); + + // Loop through each client's immedate forward table until max objs reached (or NULL) + // How to iterate through the forward table? + // for(int j = 0; i < maxIFMs.val_int; i++){ + + // } + + tmx::utils::snmp_response_obj msgEnableObj; + msgEnableObj.type = type_INT; + msgEnableObj.val_int = 1; // should always be set to enable + _SNMPClientList[_messageConfigMap[configIndex].ClientIndex][i]->process_snmp_request("1.3.6.1.4.1.1206.4.2.18.4.2.1.4", request, msgEnableObj); + + tmx::utils::snmp_response_obj msgIndexObj; + msgIndexObj.type = type_INT; + msgIndexObj.val_int = 0; // needs updating + _SNMPClientList[_messageConfigMap[configIndex].ClientIndex][i]->process_snmp_request("1.3.6.1.4.1.1206.4.2.18.4.2.1.1", request, msgIndexObj); + + tmx::utils::snmp_response_obj psidObj; + psidObj.type = type_STR; + std::vector psidVector(_messageConfigMap[configIndex].Psid.begin(), _messageConfigMap[configIndex].Psid.end()); + psidObj.val_string = psidVector; + _SNMPClientList[_messageConfigMap[configIndex].ClientIndex][i]->process_snmp_request("1.3.6.1.4.1.1206.4.2.18.4.2.1.2", request, psidObj); + + // dsrc message id - int32 + + tmx::utils::snmp_response_obj channelObj; + channelObj.type = type_INT; + channelObj.val_int = stoi(_messageConfigMap[configIndex].Channel); + _SNMPClientList[_messageConfigMap[configIndex].ClientIndex][i]->process_snmp_request("1.3.6.1.4.1.1206.4.2.18.4.2.1.3", request, channelObj); + + tmx::utils::snmp_response_obj statusObj; + statusObj.type = type_INT; + statusObj.val_int = 1; + _SNMPClientList[_messageConfigMap[configIndex].ClientIndex][i]->process_snmp_request("1.3.6.1.4.1.1206.4.2.18.4.2.1.5", request, statusObj); + + tmx::utils::snmp_response_obj payloadObj; + payloadObj.type = type_STR; + std::vector payloadVector(payloadbyte.begin(), payloadbyte.end()); + payloadObj.val_string = payloadVector; + _SNMPClientList[_messageConfigMap[configIndex].ClientIndex][i]->process_snmp_request("1.3.6.1.4.1.1206.4.2.18.4.2.1.8", request, payloadObj); + + // tmx::utils::snmp_response_obj priorityObj; + // priorityObj.type = type_INT; + // priorityObj.val_int = 0; + // _SNMPClientList[_messageConfigMap[configIndex].ClientIndex][i]->process_snmp_request("1.3.6.1.4.1.1206.4.2.18.4.2.1.6", request, priorityObj); + + // tmx::utils::snmp_response_obj optionsObj; + // optionsObj.type = type_INT; // will need to be 4 bits, add a swtich case to support + // optionsObj.val_int = 0; + // _SNMPClientList[_messageConfigMap[configIndex].ClientIndex][i]->process_snmp_request("1.3.6.1.4.1.1206.4.2.18.4.2.1.7", request, optionsObj); + + + // DRSC Spec + // rsuIFMIndex RsuTableIndex + // rsuIFMPsid RsuPsidTC + // rsuIFMDsrcMsgId Int32 + // rsuIFMTxMode INT + // rsuIFMTxChannel Int32 + // rsuIFMEnable INT + // rsuIFMStatus RowStatus + + // NTCIP Spec + // Msg index - 1.3.6.1.4.1.1206.4.2.18.4.2.1.1 RsuTableIndex - int32 + // PSID - 1.3.6.1.4.1.1206.4.2.18.4.2.1.2 RsuPsidTC (J2735 hex) - octet string + // Channel - 1.3.6.1.4.1.1206.4.2.18.4.2.1.3 int32 + // Msg enable - 1.3.6.1.4.1.1206.4.2.18.4.2.1.4 INTEGER + // Status 1.3.6.1.4.1.1206.4.2.18.4.2.1.5 RowStatus + // Priority 1.3.6.1.4.1.1206.4.2.18.4.2.1.6 int32 + // Options 1.3.6.1.4.1.1206.4.2.18.4.2.1.7 (used to enabled signed or encrypted messages + protocol, 4 bit binary) bits bitset? + // Payload - 1.3.6.1.4.1.1206.4.2.18.4.2.1.8 octet string + + } + else{ + PLOG(logWARNING) << "SNMP client invalid"; + } + + } - // os << "i 2"; + // PLOG(logDEBUG2) << _logPrefix << "Sending: " << asnMessage + // << " to port: " << _snmpClient->GetPort(); - string message = "i 2"; - PLOG(logDEBUG2) << _logPrefix << "Sending: " << message - << " to port: " << _snmpClient->GetPort(); + // auto snmp_response = _snmpClient->SNMPSet("iso.0.15628.4.1.99.0", asnMessage); - auto snmp_response = _snmpClient->SNMPSet("iso.0.15628.4.1.99.0", message); - PLOG(logDEBUG) << "Received response: " << snmp_response; } // Send the message using the configured UDP client. @@ -435,7 +600,7 @@ void ImmediateForwardPlugin::SendMessageToRadio(IvpMessage *msg) os << "Signature="<< (signState == 1 ? "True" : "False") << "\n" << "Encryption=False\n"; os << "Payload=" << payloadbyte << "\n"; - string message = os.str(); + string message = os.str(); // finalized message to send to clients for (uint i = 0; i < _udpClientList[_messageConfigMap[configIndex].ClientIndex].size(); i++) { @@ -448,7 +613,7 @@ void ImmediateForwardPlugin::SendMessageToRadio(IvpMessage *msg) << ", Channel: " << (_messageConfigMap[configIndex].Channel.empty() ? ::to_string( msg->dsrcMetadata->channel) : _messageConfigMap[configIndex].Channel) << ", Port: " << _udpClientList[_messageConfigMap[configIndex].ClientIndex][i]->GetPort(); - _udpClientList[_messageConfigMap[configIndex].ClientIndex][i]->Send(message); + _udpClientList[_messageConfigMap[configIndex].ClientIndex][i]->Send(message); // sending message to client } else { diff --git a/src/v2i-hub/ImmediateForwardPlugin/src/ImmediateForwardPlugin.h b/src/v2i-hub/ImmediateForwardPlugin/src/ImmediateForwardPlugin.h index d06ed7db4..703315703 100644 --- a/src/v2i-hub/ImmediateForwardPlugin/src/ImmediateForwardPlugin.h +++ b/src/v2i-hub/ImmediateForwardPlugin/src/ImmediateForwardPlugin.h @@ -27,6 +27,7 @@ using namespace tmx::utils; namespace ImmediateForward { + struct MessageConfig { @@ -62,6 +63,12 @@ class ImmediateForwardPlugin : public tmx::utils::PluginClient std::mutex _mutexUdpClient; typedef std::vector svr_list; std::array _udpClientList; + + // Client list for snmp, mutex unnecessary? + // std::mutex _mutexSNMPClient; + typedef std::vector snmp_list; + std::array _SNMPClientList; + std::vector _messageConfigMap; std::map _messageCountMap; std::string _rsuIp;