Send the trap to the configured SNMP managers
Get the snmp manager details from the D-bus service and send the
trap to all the configured snmp managers.
Resolves openbmc/openbmc#3058
Change-Id: I9c59ae866ab194e92a85254ca646ecc299803f26
Signed-off-by: Ratan Gupta <ratagupt@in.ibm.com>
diff --git a/Makefile.am b/Makefile.am
index 7167b1d..68c7ab7 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -9,7 +9,8 @@
noinst_HEADERS = \
snmp_client.hpp \
- snmp_conf_manager.hpp
+ snmp_conf_manager.hpp \
+ snmp_util.hpp
phosphor_network_snmpconf_SOURCES = \
snmp_main.cpp \
@@ -51,7 +52,8 @@
libsnmp_LTLIBRARIES = libsnmp.la
libsnmp_la_SOURCES = \
- snmp_notification.cpp
+ snmp_notification.cpp \
+ snmp_util.cpp
libsnmp_la_LDFLAGS = \
$(SDBUSPLUS_LIBS) \
diff --git a/snmp_main.cpp b/snmp_main.cpp
index 932b9f4..d11ed1e 100644
--- a/snmp_main.cpp
+++ b/snmp_main.cpp
@@ -10,7 +10,7 @@
/* Need a custom deleter for freeing up sd_event */
struct EventDeleter
{
- void operator()(sd_event* event) const
+ void operator()(sd_event *event) const
{
event = sd_event_unref(event);
}
@@ -18,13 +18,13 @@
using EventPtr = std::unique_ptr<sd_event, EventDeleter>;
-int main(int argc, char* argv[])
+int main(int argc, char *argv[])
{
using namespace phosphor::logging;
auto bus = sdbusplus::bus::new_default();
- sd_event* event = nullptr;
+ sd_event *event = nullptr;
auto r = sd_event_default(&event);
if (r < 0)
{
diff --git a/snmp_notification.cpp b/snmp_notification.cpp
index 1763602..b0df691 100644
--- a/snmp_notification.cpp
+++ b/snmp_notification.cpp
@@ -1,4 +1,5 @@
#include "snmp_notification.hpp"
+#include "snmp_util.hpp"
#include <phosphor-logging/elog-errors.hpp>
#include <phosphor-logging/log.hpp>
@@ -59,7 +60,6 @@
void Notification::sendTrap()
{
constexpr auto comm = "public";
- constexpr auto localHost = "127.0.0.1";
netsnmp_session session{0};
snmp_sess_init(&session);
@@ -73,63 +73,67 @@
session.callback = nullptr;
session.callback_magic = nullptr;
- // TODO:- get it from settings D-bus object.
- session.peername = const_cast<char*>(localHost);
+ auto mgrs = getManagers();
- // create the session
- auto ss = snmp_add(
- &session, netsnmp_transport_open_client("snmptrap", session.peername),
- nullptr, nullptr);
- if (!ss)
+ for (auto& mgr : mgrs)
{
- log<level::ERR>("Unable to get the snmp session.",
- entry("SNMPMANAGER=%s", session.peername));
- elog<InternalFailure>();
- }
-
- // Wrap the raw pointer in RAII
- snmpSessionPtr sessionPtr(ss, &::snmp_close);
-
- ss = nullptr;
-
- auto pdu = snmp_pdu_create(SNMP_MSG_TRAP2);
- if (!pdu)
- {
- log<level::ERR>("Failed to create notification PDU");
- elog<InternalFailure>();
- }
-
- pdu->trap_type = SNMP_TRAP_ENTERPRISESPECIFIC;
-
- auto trapInfo = getTrapOID();
-
- if (!snmp_pdu_add_variable(
- pdu, SNMPTrapOID, sizeof(SNMPTrapOID) / sizeof(oid), ASN_OBJECT_ID,
- trapInfo.first.data(), trapInfo.second * sizeof(oid)))
- {
- log<level::ERR>("Failed to add the SNMP var(trapID)");
- snmp_free_pdu(pdu);
- elog<InternalFailure>();
- }
-
- auto objectList = getFieldOIDList();
-
- for (const auto& object : objectList)
- {
- if (!addPDUVar(*pdu, std::get<0>(object), std::get<1>(object),
- std::get<2>(object), std::get<3>(object)))
+ session.peername = const_cast<char*>(mgr.c_str());
+ // create the session
+ auto ss = snmp_add(
+ &session,
+ netsnmp_transport_open_client("snmptrap", session.peername),
+ nullptr, nullptr);
+ if (!ss)
{
- log<level::ERR>("Failed to add the SNMP var");
+ log<level::ERR>("Unable to get the snmp session.",
+ entry("SNMPMANAGER=%s", mgr.c_str()));
+ elog<InternalFailure>();
+ }
+
+ // Wrap the raw pointer in RAII
+ snmpSessionPtr sessionPtr(ss, &::snmp_close);
+
+ ss = nullptr;
+
+ auto pdu = snmp_pdu_create(SNMP_MSG_TRAP2);
+ if (!pdu)
+ {
+ log<level::ERR>("Failed to create notification PDU");
+ elog<InternalFailure>();
+ }
+
+ pdu->trap_type = SNMP_TRAP_ENTERPRISESPECIFIC;
+
+ auto trapInfo = getTrapOID();
+
+ if (!snmp_pdu_add_variable(pdu, SNMPTrapOID,
+ sizeof(SNMPTrapOID) / sizeof(oid),
+ ASN_OBJECT_ID, trapInfo.first.data(),
+ trapInfo.second * sizeof(oid)))
+ {
+ log<level::ERR>("Failed to add the SNMP var(trapID)");
snmp_free_pdu(pdu);
elog<InternalFailure>();
}
- }
- // pdu is freed by snmp_send
- if (!snmp_send(sessionPtr.get(), pdu))
- {
- log<level::ERR>("Failed to send the snmp trap.");
- elog<InternalFailure>();
+ auto objectList = getFieldOIDList();
+
+ for (const auto& object : objectList)
+ {
+ if (!addPDUVar(*pdu, std::get<0>(object), std::get<1>(object),
+ std::get<2>(object), std::get<3>(object)))
+ {
+ log<level::ERR>("Failed to add the SNMP var");
+ snmp_free_pdu(pdu);
+ elog<InternalFailure>();
+ }
+ }
+ // pdu is freed by snmp_send
+ if (!snmp_send(sessionPtr.get(), pdu))
+ {
+ log<level::ERR>("Failed to send the snmp trap.");
+ elog<InternalFailure>();
+ }
}
log<level::DEBUG>("Sent SNMP Trap");
diff --git a/snmp_util.cpp b/snmp_util.cpp
new file mode 100644
index 0000000..a8a1ff8
--- /dev/null
+++ b/snmp_util.cpp
@@ -0,0 +1,124 @@
+#include "snmp_util.hpp"
+#include "xyz/openbmc_project/Common/error.hpp"
+
+#include <phosphor-logging/elog-errors.hpp>
+#include <phosphor-logging/log.hpp>
+
+#include <netdb.h>
+#include <arpa/inet.h>
+#include <string>
+
+namespace phosphor
+{
+
+using namespace phosphor::logging;
+using namespace sdbusplus::xyz::openbmc_project::Common::Error;
+
+ObjectValueTree getManagedObjects(sdbusplus::bus::bus& bus,
+ const std::string& service,
+ const std::string& objPath)
+{
+ ObjectValueTree interfaces;
+
+ auto method = bus.new_method_call(service.c_str(), objPath.c_str(),
+ "org.freedesktop.DBus.ObjectManager",
+ "GetManagedObjects");
+
+ auto reply = bus.call(method);
+
+ if (reply.is_method_error())
+ {
+ log<level::ERR>("Failed to get managed objects",
+ entry("PATH=%s", objPath.c_str()));
+ elog<InternalFailure>();
+ }
+
+ reply.read(interfaces);
+ return interfaces;
+}
+
+namespace network
+{
+
+std::string resolveAddress(const std::string& address)
+{
+ addrinfo hints{0};
+ addrinfo* addr = nullptr;
+
+ hints.ai_family = AF_UNSPEC;
+ hints.ai_socktype = SOCK_STREAM;
+ hints.ai_flags |= AI_CANONNAME;
+
+ auto result = getaddrinfo(address.c_str(), NULL, &hints, &addr);
+ if (result)
+ {
+ log<level::ERR>("getaddrinfo failed",
+ entry("ADDRESS=%s", address.c_str()));
+ elog<InternalFailure>();
+ }
+
+ AddrPtr addrPtr{addr};
+ addr = nullptr;
+
+ char ipaddress[INET6_ADDRSTRLEN]{0};
+ result = getnameinfo(addrPtr->ai_addr, addrPtr->ai_addrlen, ipaddress,
+ sizeof(ipaddress), NULL, 0, NI_NUMERICHOST);
+ if (result)
+ {
+ log<level::ERR>("getnameinfo failed",
+ entry("ADDRESS=%s", address.c_str()));
+ elog<InternalFailure>();
+ }
+
+ return ipaddress;
+}
+
+namespace snmp
+{
+
+static constexpr auto busName = "xyz.openbmc_project.Network.SNMP";
+static constexpr auto root = "/xyz/openbmc_project/network/snmp/manager";
+static constexpr auto clientIntf = "xyz.openbmc_project.Network.Client";
+
+/** @brief Gets the sdbus object for this process.
+ * @return the bus object.
+ */
+static auto& getBus()
+{
+ static auto bus = sdbusplus::bus::new_default();
+ return bus;
+}
+
+std::vector<std::string> getManagers()
+{
+ std::vector<std::string> managers;
+ auto& bus = getBus();
+ auto objTree = phosphor::getManagedObjects(bus, busName, root);
+ for (const auto& objIter : objTree)
+ {
+ try
+ {
+ auto& intfMap = objIter.second;
+ auto& snmpClientProps = intfMap.at(clientIntf);
+ auto& address = snmpClientProps.at("Address").get<std::string>();
+ auto& port = snmpClientProps.at("Port").get<uint16_t>();
+ auto ipaddress = phosphor::network::resolveAddress(address);
+ auto mgr = std::move(ipaddress);
+ if (port > 0)
+ {
+ mgr += ":";
+ mgr += std::to_string(port);
+ }
+ managers.push_back(mgr);
+ }
+ catch (const std::exception& e)
+ {
+ log<level::ERR>(e.what());
+ }
+ }
+ return managers;
+}
+
+} // namespace snmp
+} // namespace network
+} // namespace phosphor
diff --git a/snmp_util.hpp b/snmp_util.hpp
new file mode 100644
index 0000000..7a38745
--- /dev/null
+++ b/snmp_util.hpp
@@ -0,0 +1,75 @@
+#pragma once
+
+#include <stdint.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <netdb.h>
+
+#include <map>
+#include <string>
+
+#include <sdbusplus/server.hpp>
+
+namespace phosphor
+{
+
+/* Need a custom deleter for freeing up addrinfo */
+struct AddrDeleter
+{
+ void operator()(addrinfo* addrPtr) const
+ {
+ freeaddrinfo(addrPtr);
+ }
+};
+
+using AddrPtr = std::unique_ptr<addrinfo, AddrDeleter>;
+
+using DbusInterface = std::string;
+using DbusProperty = std::string;
+
+using Value =
+ sdbusplus::message::variant<bool, uint8_t, int16_t, uint16_t, int32_t,
+ uint32_t, int64_t, uint64_t, std::string>;
+
+using PropertyMap = std::map<DbusProperty, Value>;
+
+using DbusInterfaceMap = std::map<DbusInterface, PropertyMap>;
+
+using ObjectValueTree =
+ std::map<sdbusplus::message::object_path, DbusInterfaceMap>;
+
+/** @brief Gets all managed objects associated with the given object
+ * path and service.
+ * @param[in] bus - D-Bus Bus Object.
+ * @param[in] service - D-Bus service name.
+ * @param[in] objPath - D-Bus object path.
+ * @return On success returns the map of name value pair.
+ */
+ObjectValueTree getManagedObjects(sdbusplus::bus::bus& bus,
+ const std::string& service,
+ const std::string& objPath);
+
+namespace network
+{
+
+/** @brief Resolves the given address to IP address.
+ * Given address could be hostname or IP address.
+ * if given address is not valid then it throws an exception.
+ * @param[in] address - address which needs to be converted into IP address.
+ * @return the IP address.
+ */
+std::string resolveAddress(const std::string& address);
+
+namespace snmp
+{
+
+/** @brief Gets all the snmp manager info.
+ * @return the list of manager info in the format
+ * of ipaddress:port
+ */
+std::vector<std::string> getManagers();
+
+} // namespace snmp
+} // namespace network
+
+} // namespace phosphor