diff options
author | Andrey V.Kosteltsev <AKosteltsev@IBS.RU> | 2022-07-17 19:37:13 +0300 |
---|---|---|
committer | Andrey V.Kosteltsev <AKosteltsev@IBS.RU> | 2022-07-17 19:37:13 +0300 |
commit | 0bdbefbfd41baf9edc9e52e741ac650caf119895 (patch) | |
tree | 15840fbb5fb546d9cc0561620561bf22d9d15eef /meta-ibs/meta-cp2-5422/recipes-phosphor/network | |
parent | ac5f52c0cdb8f98771cf8d48734fe5be235af2a4 (diff) | |
download | openbmc-0bdbefbfd41baf9edc9e52e741ac650caf119895.tar.xz |
IBS: SNMP Support (First Approx)
Diffstat (limited to 'meta-ibs/meta-cp2-5422/recipes-phosphor/network')
6 files changed, 1175 insertions, 0 deletions
diff --git a/meta-ibs/meta-cp2-5422/recipes-phosphor/network/obmc-sila-snmp_git.bb b/meta-ibs/meta-cp2-5422/recipes-phosphor/network/obmc-sila-snmp_git.bb new file mode 100644 index 0000000000..ef817d170c --- /dev/null +++ b/meta-ibs/meta-cp2-5422/recipes-phosphor/network/obmc-sila-snmp_git.bb @@ -0,0 +1,65 @@ +SUMMARY = "Sila SNMP agent for OpenBMC" +DESCRIPTION = "The project provides a snmp subagent and configuration manager." +HOMEPAGE = "http://git.sila.ru/openbmc/obmc-sila-snmp.git/" + +PR = "r1" +PV = "1.0+git${SRCPV}" +LICENSE = "Apache-2.0" +LIC_FILES_CHKSUM = "file://LICENSE;md5=e3fc50a88d0a364313df4b21ef20c29e" + +SRC_URI = "git://git@git.sila.ru/srv/pub/scm/git/openbmc/obmc-sila-snmp.git;branch=master;protocol=ssh" +SRCREV = "771d61bc4849de8c6af38a71dfa4e1740721fc12" + + +inherit autotools pkgconfig python3native +inherit obmc-phosphor-dbus-service + +S = "${WORKDIR}/git" + +DEPENDS += " autoconf-archive-native" +DEPENDS += " net-snmp" +DEPENDS += " systemd" +DEPENDS += " sdbusplus" +DEPENDS += " sdeventplus" +DEPENDS += " ${PYTHON_PN}-sdbus++-native" +DEPENDS += " phosphor-logging" +DEPENDS += " phosphor-dbus-interfaces" + + +# Package configuration +SNMP_PACKAGES = " \ + ${PN}-agent \ + ${PN}-cfg-manager \ + " + +ALLOW_EMPTY_${PN} = "1" +PACKAGE_BEFORE_PN += "${SNMP_PACKAGES}" +PACKAGECONFIG ?= "agent cfg-manager" +SYSTEMD_PACKAGES = "${PN}-agent" +DBUS_PACKAGES = "${PN}-cfg-manager" + +# -------------------------------------- +# ${PN}-agent specific configuration: +# -------------------------------------- +PACKAGECONFIG[agent] = "--enable-agent,--disable-agent,," +RDEPENDS_${PN}-agent += " sdbusplus net-snmp-libs net-snmp-server-snmpd" +FILES:${PN}-agent = " \ + ${bindir}/sila-snmp-agent \ + ${datadir}/snmp/mibs/SILA-MIB.txt \ + " + +SYSTEMD_SERVICE:${PN}-agent += "sila-snmp-agent.service" + +# -------------------------------------- +# ${PN}-cfg-manager specific configuration +PACKAGECONFIG[cfg-manager] = "--enable-cfg-manager,--disable-cfg-manager,," +RDEPENDS:${PN}-cfg-manager += " sdbusplus" +FILES:${PN}-cfg-manager = "${bindir}/sila-snmpcfg" +DBUS_SERVICE:${PN}-cfg-manager += "sila-snmp-cfg-manager.service" + +# Makes the MIB-file available over https by bmcweb +FILES:${PN}-agent += " ${datadir}/www/mibs/SILA-MIB.txt " +do_install:append () { + mkdir -p ${D}${datadir}/www/mibs + ln -s ${datadir}/snmp/mibs/SILA-MIB.txt ${D}${datadir}/www/mibs/SILA-MIB.txt +} diff --git a/meta-ibs/meta-cp2-5422/recipes-phosphor/network/phosphor-snmp/0001-main-Use-sdeventplus.patch b/meta-ibs/meta-cp2-5422/recipes-phosphor/network/phosphor-snmp/0001-main-Use-sdeventplus.patch new file mode 100644 index 0000000000..e38befa7d6 --- /dev/null +++ b/meta-ibs/meta-cp2-5422/recipes-phosphor/network/phosphor-snmp/0001-main-Use-sdeventplus.patch @@ -0,0 +1,123 @@ +From d07ee811792a8440fc0cc99d7f51aa4121e50928 Mon Sep 17 00:00:00 2001 +From: "Andrey V.Kosteltsev" <AKosteltsev@IBS.RU> +Date: Sun, 17 Jul 2022 12:25:14 +0300 +Subject: [PATCH 1/3] main: Use sdeventplus + +Makes use sdeventplus instead custom wrapper around the sd_event library calls. + +Signed-off-by: Andrey V.Kosteltsev <AKosteltsev@IBS.RU> +--- + meson.build | 8 +++++++- + snmp_main.cpp | 27 ++++----------------------- + subprojects/sdeventplus.wrap | 6 ++++++ + 3 files changed, 17 insertions(+), 24 deletions(-) + create mode 100644 subprojects/sdeventplus.wrap + +diff --git a/meson.build b/meson.build +index 73ca4a6..8a82752 100644 +--- a/meson.build ++++ b/meson.build +@@ -17,6 +17,7 @@ conf_data.set_quoted('SNMP_CONF_PERSIST_PATH', '/var/lib/phosphor-snmp/managers/ + conf_data.set('CLASS_VERSION', 1) + + sdbusplus_dep = dependency('sdbusplus') ++sdeventplus_dep = dependency('sdeventplus') + phosphor_dbus_interfaces_dep = dependency('phosphor-dbus-interfaces') + phosphor_logging_dep = dependency('phosphor-logging') + libsystemd_dep = dependency('libsystemd') +@@ -30,6 +31,7 @@ deps = [ + phosphor_dbus_interfaces_dep, + phosphor_logging_dep, + sdbusplus_dep, ++ sdeventplus_dep, + ] + + sources = [ +@@ -55,6 +57,7 @@ executable( + + libsnmp_deps = [ + sdbusplus_dep, ++ sdeventplus_dep, + phosphor_logging_dep, + phosphor_dbus_interfaces_dep, + netsnmp_dep, +@@ -85,7 +88,10 @@ import('pkgconfig').generate( + libsnmp_lib, + name: meson.project_name(), + version: meson.project_version(), +- requires: sdbusplus_dep, ++ requires: [ ++ sdbusplus_dep, ++ sdeventplus_dep, ++ ], + description: 'Phosphor snmp utilities', + ) + +diff --git a/snmp_main.cpp b/snmp_main.cpp +index be4bea1..ab3c4b6 100644 +--- a/snmp_main.cpp ++++ b/snmp_main.cpp +@@ -4,38 +4,19 @@ + + #include <phosphor-logging/lg2.hpp> + #include <sdbusplus/bus.hpp> ++#include <sdeventplus/event.hpp> + #include <sdbusplus/server/manager.hpp> + + #include <memory> + +-/* Need a custom deleter for freeing up sd_event */ +-struct EventDeleter +-{ +- void operator()(sd_event* event) const +- { +- sd_event_unref(event); +- } +-}; +- +-using EventPtr = std::unique_ptr<sd_event, EventDeleter>; +- + int main(int /*argc*/, char** /*argv[]*/) + { + auto bus = sdbusplus::bus::new_default(); + +- sd_event* event = nullptr; +- auto r = sd_event_default(&event); +- if (r < 0) +- { +- lg2::error("Error creating a default sd_event handler: {RC}", "RC", r); +- return r; +- } +- +- EventPtr eventPtr{event}; +- event = nullptr; ++ auto event = sdeventplus::Event::get_default(); + + // Attach the bus to sd_event to service user requests +- bus.attach_event(eventPtr.get(), SD_EVENT_PRIORITY_NORMAL); ++ bus.attach_event(event.get(), SD_EVENT_PRIORITY_NORMAL); + + // Add sdbusplus Object Manager for the 'root' path of the snmp. + sdbusplus::server::manager::manager objManager(bus, OBJ_NETWORK_SNMP); +@@ -46,5 +27,5 @@ int main(int /*argc*/, char** /*argv[]*/) + + manager->restoreClients(); + +- return sd_event_loop(eventPtr.get()); ++ return event.loop(); + } +diff --git a/subprojects/sdeventplus.wrap b/subprojects/sdeventplus.wrap +new file mode 100644 +index 0000000..f871ac0 +--- /dev/null ++++ b/subprojects/sdeventplus.wrap +@@ -0,0 +1,6 @@ ++[wrap-git] ++url = https://github.com/openbmc/sdeventplus.git ++revision = HEAD ++ ++[provide] ++sdeventplus = sdeventplus_dep +-- +2.35.1 + diff --git a/meta-ibs/meta-cp2-5422/recipes-phosphor/network/phosphor-snmp/0002-main-Add-UNIX-signals-handler.patch b/meta-ibs/meta-cp2-5422/recipes-phosphor/network/phosphor-snmp/0002-main-Add-UNIX-signals-handler.patch new file mode 100644 index 0000000000..9ff1e35897 --- /dev/null +++ b/meta-ibs/meta-cp2-5422/recipes-phosphor/network/phosphor-snmp/0002-main-Add-UNIX-signals-handler.patch @@ -0,0 +1,102 @@ +From 81daf0a592fdcb0df22476621e3202e9932a867e Mon Sep 17 00:00:00 2001 +From: "Andrey V.Kosteltsev" <AKosteltsev@IBS.RU> +Date: Sun, 17 Jul 2022 12:55:01 +0300 +Subject: [PATCH 2/3] main: Add UNIX signals handler + +This commit adds a handler for SIGTERM and SIGINT signals that is the normal service stop. + +Signed-off-by: Andrey V.Kosteltsev <AKosteltsev@IBS.RU> +--- + meson.build | 4 ++++ + snmp_main.cpp | 15 +++++++++++++++ + subprojects/stdplus.wrap | 6 ++++++ + 3 files changed, 25 insertions(+) + create mode 100644 subprojects/stdplus.wrap + +diff --git a/meson.build b/meson.build +index 8a82752..4ed6094 100644 +--- a/meson.build ++++ b/meson.build +@@ -18,6 +18,7 @@ conf_data.set('CLASS_VERSION', 1) + + sdbusplus_dep = dependency('sdbusplus') + sdeventplus_dep = dependency('sdeventplus') ++stdplus_dep = dependency('stdplus') + phosphor_dbus_interfaces_dep = dependency('phosphor-dbus-interfaces') + phosphor_logging_dep = dependency('phosphor-logging') + libsystemd_dep = dependency('libsystemd') +@@ -32,6 +33,7 @@ deps = [ + phosphor_logging_dep, + sdbusplus_dep, + sdeventplus_dep, ++ stdplus_dep, + ] + + sources = [ +@@ -58,6 +60,7 @@ executable( + libsnmp_deps = [ + sdbusplus_dep, + sdeventplus_dep, ++ stdplus_dep, + phosphor_logging_dep, + phosphor_dbus_interfaces_dep, + netsnmp_dep, +@@ -91,6 +94,7 @@ import('pkgconfig').generate( + requires: [ + sdbusplus_dep, + sdeventplus_dep, ++ stdplus_dep, + ], + description: 'Phosphor snmp utilities', + ) +diff --git a/snmp_main.cpp b/snmp_main.cpp +index ab3c4b6..df5850b 100644 +--- a/snmp_main.cpp ++++ b/snmp_main.cpp +@@ -5,10 +5,18 @@ + #include <phosphor-logging/lg2.hpp> + #include <sdbusplus/bus.hpp> + #include <sdeventplus/event.hpp> ++#include <sdeventplus/source/signal.hpp> + #include <sdbusplus/server/manager.hpp> ++#include <stdplus/signal.hpp> + + #include <memory> + ++static void cleanExit(sdeventplus::source::Signal& source, ++ const struct signalfd_siginfo*) ++{ ++ source.get_event().exit(EXIT_SUCCESS); ++} ++ + int main(int /*argc*/, char** /*argv[]*/) + { + auto bus = sdbusplus::bus::new_default(); +@@ -27,5 +35,12 @@ int main(int /*argc*/, char** /*argv[]*/) + + manager->restoreClients(); + ++ // Clean exit by unix signal ++ stdplus::signal::block(SIGTERM); ++ stdplus::signal::block(SIGINT); ++ ++ sdeventplus::source::Signal sigTerm(event, SIGTERM, cleanExit); ++ sdeventplus::source::Signal sigInt(event, SIGINT, cleanExit); ++ + return event.loop(); + } +diff --git a/subprojects/stdplus.wrap b/subprojects/stdplus.wrap +new file mode 100644 +index 0000000..2f8a5f4 +--- /dev/null ++++ b/subprojects/stdplus.wrap +@@ -0,0 +1,6 @@ ++[wrap-git] ++url = https://github.com/openbmc/stdplus.git ++revision = HEAD ++ ++[provide] ++stdplus = stdplus_dep +-- +2.35.1 + diff --git a/meta-ibs/meta-cp2-5422/recipes-phosphor/network/phosphor-snmp/0003-conf_manager-use-snmpd-as-a-clients-storage.patch b/meta-ibs/meta-cp2-5422/recipes-phosphor/network/phosphor-snmp/0003-conf_manager-use-snmpd-as-a-clients-storage.patch new file mode 100644 index 0000000000..e13de7ba74 --- /dev/null +++ b/meta-ibs/meta-cp2-5422/recipes-phosphor/network/phosphor-snmp/0003-conf_manager-use-snmpd-as-a-clients-storage.patch @@ -0,0 +1,859 @@ +From 22a0736aa2613f0821e762e76db0efac17a0b7b1 Mon Sep 17 00:00:00 2001 +From: "Andrey V.Kosteltsev" <AKosteltsev@IBS.RU> +Date: Sun, 17 Jul 2022 19:05:14 +0300 +Subject: [PATCH 3/3] conf_manager: use snmpd as a clients storage + +This commit brings the alternative of storage of clients. It's able now to communicate with snmpd and manage the built-in list of snmp trap receivers. + +Signed-off-by: Andrey V.Kosteltsev <AKosteltsev@IBS.RU> +--- + meson.build | 9 ++ + meson_options.txt | 19 ++++ + snmp/exceptions.hpp | 71 ++++++++++++++ + snmp/notify_tables.hpp | 58 ++++++++++++ + snmp/oid.hpp | 42 +++++++++ + snmp/pdu.hpp | 150 +++++++++++++++++++++++++++++ + snmp/session.hpp | 110 ++++++++++++++++++++++ + snmp_conf_manager.cpp | 207 +++++++++++++++++++++++++++++++++++++++++ + snmp_conf_manager.hpp | 8 ++ + snmp_main.cpp | 11 +++ + 10 files changed, 685 insertions(+) + create mode 100644 snmp/exceptions.hpp + create mode 100644 snmp/notify_tables.hpp + create mode 100644 snmp/oid.hpp + create mode 100644 snmp/pdu.hpp + create mode 100644 snmp/session.hpp + +diff --git a/meson.build b/meson.build +index 4ed6094..ad07caa 100644 +--- a/meson.build ++++ b/meson.build +@@ -44,6 +44,15 @@ sources = [ + 'snmp_serialize.cpp', + ] + ++if get_option('snmpd-support').enabled() ++ conf_data.set('USE_SNMPD', 1) ++ deps += [ ++ netsnmp_dep, ++ ] ++ conf_data.set_quoted('SNMPD_ADDRESS', get_option('snmpd-address')) ++ conf_data.set_quoted('SNMPD_COMMUNITY', get_option('snmpd-community')) ++endif ++ + configure_file(output: 'config.h', + configuration: conf_data + ) +diff --git a/meson_options.txt b/meson_options.txt +index f1c7bd8..8b26686 100644 +--- a/meson_options.txt ++++ b/meson_options.txt +@@ -1 +1,20 @@ ++ ++option('snmpd-support', ++ type: 'feature', ++ value: 'disabled', ++ description: 'Enable snmpd as a client storage.' ++) ++ ++option('snmpd-address', ++ type: 'string', ++ value: 'localhost', ++ description: 'Address of snmpd.' ++) ++ ++option('snmpd-community', ++ type: 'string', ++ value: 'private', ++ description: 'Community name to communicate with snmpd.' ++) ++ + option('tests', type : 'feature', description : 'Build tests') +\ No newline at end of file +diff --git a/snmp/exceptions.hpp b/snmp/exceptions.hpp +new file mode 100644 +index 0000000..9f1d858 +--- /dev/null ++++ b/snmp/exceptions.hpp +@@ -0,0 +1,71 @@ ++#pragma once ++ ++#include <net-snmp/net-snmp-config.h> ++#include <net-snmp/net-snmp-includes.h> ++ ++#include <stdexcept> ++ ++namespace snmp ++{ ++namespace exception ++{ ++ ++class SnmpError : public std::runtime_error ++{ ++ public: ++ using std::runtime_error::runtime_error; ++}; ++ ++class SnmpSessionError : public SnmpError ++{ ++ public: ++ using SnmpError::SnmpError; ++ ++ SnmpSessionError(netsnmp_session* sp, const std::string& name) : ++ SnmpError(makeError(sp, name)) ++ { ++ } ++ ++ protected: ++ static std::string makeError(netsnmp_session* sp, const std::string& name) ++ { ++ char* msg = nullptr; ++ snmp_error(sp, nullptr, nullptr, &msg); ++ std::string error = name + " failed, "; ++ error += msg; ++ SNMP_FREE(msg); ++ ++ return error; ++ } ++}; ++ ++class SnmpTimeout : public SnmpError ++{ ++ public: ++ using SnmpError::SnmpError; ++ ++ SnmpTimeout() : SnmpError("Connection timed out!") ++ { ++ } ++}; ++ ++class SnmpPacketError : public SnmpError ++{ ++ public: ++ using SnmpError::SnmpError; ++ ++ SnmpPacketError(int ec) : SnmpError(makeError(ec)) ++ { ++ } ++ ++ protected: ++ static std::string makeError(int ec) ++ { ++ std::string error("Error in packet, "); ++ error += snmp_errstring(ec); ++ return error; ++ } ++}; ++ ++} // namespace exception ++} // namespace snmp +diff --git a/snmp/notify_tables.hpp b/snmp/notify_tables.hpp +new file mode 100644 +index 0000000..7d23c0f +--- /dev/null ++++ b/snmp/notify_tables.hpp +@@ -0,0 +1,58 @@ ++/* ++ * SPDX-License-Identifier: Apache-2.0 ++ * Copyright (C) 2021 YADRO. ++ */ ++#include "oid.hpp" ++ ++namespace snmp ++{ ++namespace table ++{ ++ ++/** ++ * @brief SNMP-NOTIFICATION-MIB::snmpNotifyTable fields ++ */ ++namespace notify ++{ ++ // clang-format off ++static const snmp::OID tag = {1,3,6,1,6,3,13,1,1,1,2}; ++static const snmp::OID type = {1,3,6,1,6,3,13,1,1,1,3}; ++static const snmp::OID storageType = {1,3,6,1,6,3,13,1,1,1,4}; ++static const snmp::OID rowStatus = {1,3,6,1,6,3,13,1,1,1,5}; ++ // clang-format on ++} // namespace notify ++ ++/** ++ * @brief SNMP-TARGET-MIB::snmpTargetParamsTable fields ++ */ ++namespace targetParams ++{ ++ // clang-format off ++static const snmp::OID mpModel = {1,3,6,1,6,3,12,1,3,1,2}; ++static const snmp::OID securityModel = {1,3,6,1,6,3,12,1,3,1,3}; ++static const snmp::OID securityName = {1,3,6,1,6,3,12,1,3,1,4}; ++static const snmp::OID securityLevel = {1,3,6,1,6,3,12,1,3,1,5}; ++static const snmp::OID storageType = {1,3,6,1,6,3,12,1,3,1,6}; ++static const snmp::OID rowStatus = {1,3,6,1,6,3,12,1,3,1,7}; ++ // clang-format on ++} // namespace targetParams ++ ++/** ++ * @brief SNMP-TARGET-MIB::snmpTargetAddrTable fields ++ */ ++namespace targetAddr ++{ ++ // clang-format off ++static const snmp::OID tDomain = {1,3,6,1,6,3,12,1,2,1,2}; ++static const snmp::OID tAddress = {1,3,6,1,6,3,12,1,2,1,3}; ++static const snmp::OID timeout = {1,3,6,1,6,3,12,1,2,1,4}; ++static const snmp::OID retryCount = {1,3,6,1,6,3,12,1,2,1,5}; ++static const snmp::OID tagList = {1,3,6,1,6,3,12,1,2,1,6}; ++static const snmp::OID params = {1,3,6,1,6,3,12,1,2,1,7}; ++static const snmp::OID storageType = {1,3,6,1,6,3,12,1,2,1,8}; ++static const snmp::OID rowStatus = {1,3,6,1,6,3,12,1,2,1,9}; ++ // clang-format on ++} // namespace targetAddr ++ ++} // namespace table ++} // namespace snmp +diff --git a/snmp/oid.hpp b/snmp/oid.hpp +new file mode 100644 +index 0000000..5394873 +--- /dev/null ++++ b/snmp/oid.hpp +@@ -0,0 +1,42 @@ ++#pragma once ++ ++#include <net-snmp/net-snmp-config.h> ++#include <net-snmp/net-snmp-includes.h> ++ ++#include <string> ++#include <vector> ++ ++namespace snmp ++{ ++ ++struct OID : public std::vector<oid> ++{ ++ using std::vector<oid>::vector; ++ ++ static std::string dump(const oid* objid, const size_t length) ++ { ++ std::string ret(length * 11 + 1, '\0'); ++ size_t offset = 0; ++ for (size_t i = 0; i < length; ++i) ++ { ++ offset += snprintf(ret.data() + offset, ret.size() - offset, ".%lu", ++ objid[i]); ++ } ++ ret.resize(offset); ++ return std::forward<std::string>(ret); ++ } ++ ++ std::string dump() const ++ { ++ return std::forward<std::string>(dump(data(), size())); ++ } ++ ++ template <typename T> OID operator+(const T& tail) const ++ { ++ OID res = *this; ++ res.insert(res.end(), tail.begin(), tail.end()); ++ return std::forward<OID>(res); ++ } ++}; ++ ++} // namespace snmp +diff --git a/snmp/pdu.hpp b/snmp/pdu.hpp +new file mode 100644 +index 0000000..5487da0 +--- /dev/null ++++ b/snmp/pdu.hpp +@@ -0,0 +1,150 @@ ++#pragma once ++ ++#include "oid.hpp" ++ ++#include <net-snmp/net-snmp-config.h> ++#include <net-snmp/net-snmp-includes.h> ++ ++#include <iterator> ++#include <memory> ++ ++namespace snmp ++{ ++ ++class session; ++ ++namespace details ++{ ++ ++struct PduDeleter ++{ ++ void operator()(netsnmp_pdu* pduPtr) const ++ { ++ snmp_free_pdu(pduPtr); ++ } ++}; ++ ++using PduPtr = std::unique_ptr<netsnmp_pdu, PduDeleter>; ++} // namespace details ++ ++class pdu ++{ ++ public: ++ pdu() = delete; ++ pdu(const pdu&) = delete; ++ pdu& operator=(const pdu&) = delete; ++ ++ pdu(pdu&&) = default; ++ pdu& operator=(pdu&&) = default; ++ ~pdu() = default; ++ ++ netsnmp_pdu* get() const ++ { ++ return pduPtr.get(); ++ } ++ ++ static pdu create(const int command) ++ { ++ return std::forward<pdu>(pdu(snmp_pdu_create(command))); ++ } ++ ++ void addVar(const OID& oid) ++ { ++ snmp_add_null_var(pduPtr.get(), oid.data(), oid.size()); ++ } ++ ++ void addVar(const OID& oid, const int& value) ++ { ++ snmp_pdu_add_variable(pduPtr.get(), oid.data(), oid.size(), ASN_INTEGER, ++ &value, sizeof(value)); ++ } ++ ++ void addVar(const OID& oid, const std::string& value) ++ { ++ snmp_pdu_add_variable(pduPtr.get(), oid.data(), oid.size(), ++ ASN_OCTET_STR, value.c_str(), value.size()); ++ } ++ ++ void addVar(const OID& oid, const OID& value) ++ { ++ snmp_pdu_add_variable(pduPtr.get(), oid.data(), oid.size(), ++ ASN_OBJECT_ID, value.data(), ++ value.size() * sizeof(OID::value_type)); ++ } ++ ++ void addVar(const OID& oid, const std::vector<uint8_t>& value) ++ { ++ snmp_pdu_add_variable(pduPtr.get(), oid.data(), oid.size(), ++ ASN_OCTET_STR, value.data(), value.size()); ++ } ++ ++ netsnmp_pdu* operator->() const ++ { ++ return pduPtr.get(); ++ } ++ ++ struct iterator ++ { ++ using value_type = netsnmp_variable_list; ++ using reference = netsnmp_variable_list&; ++ using pointer = netsnmp_variable_list*; ++ using iterator_category = std::forward_iterator_tag; ++ ++ iterator(netsnmp_variable_list* vp) : vp(vp) ++ { ++ } ++ ++ reference operator*() ++ { ++ return *vp; ++ } ++ ++ pointer operator->() ++ { ++ return vp; ++ } ++ ++ iterator& operator++() ++ { ++ vp = vp->next_variable; ++ return *this; ++ } ++ ++ friend bool operator==(const iterator& a, const iterator& b) ++ { ++ return a.vp == b.vp; ++ }; ++ friend bool operator!=(const iterator& a, const iterator& b) ++ { ++ return a.vp != b.vp; ++ }; ++ ++ private: ++ pointer vp; ++ }; ++ ++ iterator begin() const ++ { ++ return iterator(pduPtr->variables); ++ } ++ iterator end() const ++ { ++ return iterator(nullptr); ++ } ++ ++ protected: ++ explicit pdu(netsnmp_pdu* rawPtr) : pduPtr(rawPtr) ++ { ++ if (pduPtr->command == SNMP_MSG_GETBULK) ++ { ++ pduPtr->non_repeaters = 0; ++ pduPtr->max_repetitions = 10; ++ } ++ } ++ ++ private: ++ friend class snmp::session; ++ details::PduPtr pduPtr; ++}; ++ ++} // namespace snmp +diff --git a/snmp/session.hpp b/snmp/session.hpp +new file mode 100644 +index 0000000..6665882 +--- /dev/null ++++ b/snmp/session.hpp +@@ -0,0 +1,110 @@ ++#pragma once ++ ++#include "exceptions.hpp" ++#include "pdu.hpp" ++ ++#include <net-snmp/net-snmp-config.h> ++#include <net-snmp/net-snmp-includes.h> ++ ++#include <memory> ++#include <string> ++ ++namespace snmp ++{ ++ ++namespace details ++{ ++ ++struct SessionDeleter ++{ ++ void operator()(netsnmp_session* sessionPtr) const ++ { ++ snmp_close(sessionPtr); ++ } ++}; ++ ++using SessionPtr = std::unique_ptr<netsnmp_session, SessionDeleter>; ++ ++} // namespace details ++ ++class session ++{ ++ public: ++ session() = delete; ++ session(const session&) = delete; ++ session& operator=(const session&) = delete; ++ ++ session(session&&) = default; ++ session& operator=(session&&) = default; ++ ~session() = default; ++ ++ netsnmp_session* get() const ++ { ++ return sessionPtr.get(); ++ } ++ ++ static session open(const std::string& peername, ++ const std::string& community, ++ const int version = SNMP_VERSION_2c, ++ const int retryAttempts = 3, ++ const int timeout = 1000000) ++ { ++ netsnmp_session ss{}; ++ ++ snmp_sess_init(&ss); ++ ss.version = version; ++ ss.retries = retryAttempts; ++ ss.timeout = timeout; ++ ss.peername = const_cast<char*>(peername.c_str()); ++ ss.community = const_cast<u_char*>( ++ reinterpret_cast<const u_char*>(community.c_str())); ++ ss.community_len = community.size(); ++ ++ netsnmp_session* sp = snmp_open(&ss); ++ ++ if (!sp) ++ { ++ throw snmp::exception::SnmpSessionError(&ss, "snmp_open()"); ++ } ++ ++ return std::forward<session>(session(sp)); ++ } ++ ++ void send(pdu& pdu) const ++ { ++ netsnmp_pdu* rsp = nullptr; ++ auto status = snmp_synch_response(sessionPtr.get(), pdu.get(), &rsp); ++ pdu.pduPtr.release(); ++ pdu.pduPtr.reset(rsp); ++ ++ switch (status) ++ { ++ case STAT_TIMEOUT: ++ throw snmp::exception::SnmpTimeout(); ++ break; ++ ++ case STAT_ERROR: ++ throw snmp::exception::SnmpSessionError( ++ sessionPtr.get(), "snmp_synch_response()"); ++ break; ++ ++ default: ++ break; ++ } ++ ++ if (pdu->errstat != SNMP_ERR_NOERROR) ++ { ++ throw snmp::exception::SnmpPacketError(pdu->errstat); ++ } ++ } ++ ++ protected: ++ explicit session(netsnmp_session* sp) : sessionPtr(sp) ++ { ++ } ++ ++ private: ++ details::SessionPtr sessionPtr; ++}; ++ ++} // namespace snmp +diff --git a/snmp_conf_manager.cpp b/snmp_conf_manager.cpp +index 4ddace1..2a631b7 100644 +--- a/snmp_conf_manager.cpp ++++ b/snmp_conf_manager.cpp +@@ -6,6 +6,11 @@ + #include "snmp_util.hpp" + #include "xyz/openbmc_project/Common/error.hpp" + ++#if defined( USE_SNMPD ) && ( USE_SNMPD == 1 ) ++#include "snmp/pdu.hpp" ++#include "snmp/notify_tables.hpp" ++#endif ++ + #include <arpa/inet.h> + + #include <phosphor-logging/elog-errors.hpp> +@@ -29,14 +34,97 @@ ConfManager::ConfManager(sdbusplus::bus::bus& bus, const char* objPath) : + details::CreateIface::action::defer_emit), + dbusPersistentLocation(SNMP_CONF_PERSIST_PATH), bus(bus), + objectPath(objPath) ++#if defined( USE_SNMPD ) && ( USE_SNMPD == 1 ) ++ , ++ session(::snmp::session::open(SNMPD_ADDRESS, SNMPD_COMMUNITY)) ++#endif + {} + ++#if defined( USE_SNMPD ) && ( USE_SNMPD == 1 ) ++static std::vector<uint8_t> encodeAddr(const std::string& addr, uint16_t port) ++{ ++ std::vector<uint8_t> data; ++ ++ addrinfo hints; ++ addrinfo* ptr = nullptr; ++ ++ memset(&hints, 0, sizeof hints); ++ hints.ai_family = AF_UNSPEC; ++ hints.ai_socktype = SOCK_STREAM; ++ hints.ai_flags |= AI_CANONNAME; ++ ++ auto result = ++ getaddrinfo(addr.c_str(), std::to_string(port).c_str(), &hints, &ptr); ++ if (result) ++ { ++ log<level::ERR>("getaddrinfo failed", ++ entry("ADDRESS=%s", addr.c_str())); ++ elog<InternalFailure>(); ++ } ++ ++ AddrPtr addrPtr{ptr}; ++ ptr = nullptr; ++ ++ auto sa = reinterpret_cast<const sockaddr_in*>(addrPtr->ai_addr); ++ auto begin = reinterpret_cast<const uint8_t*>(&(sa->sin_addr)); ++ data.insert(data.end(), begin, begin + sizeof(sa->sin_addr)); ++ ++ begin = reinterpret_cast<const uint8_t*>(&(sa->sin_port)); ++ data.insert(data.end(), begin, begin + sizeof(sa->sin_port)); ++ ++ return data; ++} ++#endif ++ + std::string ConfManager::client(std::string address, uint16_t port) + { + // will throw exception if it is already configured. + checkClientConfigured(address, port); + + lastClientId++; ++ ++#if defined( USE_SNMPD ) && ( USE_SNMPD == 1 ) ++ auto data = encodeAddr(address, port); ++ try ++ { ++ auto pdu = ::snmp::pdu::create(SNMP_MSG_SET); ++ auto index = std::to_string(lastClientId); ++ ++ constexpr int noAuthNoPriv = 1; ++ constexpr int createAndGo = 4; ++ ++ using namespace ::snmp::table; ++ ++ pdu.addVar(targetParams::mpModel + index, 1 /* SNMPv2c */); ++ pdu.addVar(targetParams::securityModel + index, 2 /* SNMPv2c */); ++ pdu.addVar(targetParams::securityName + index, "public"); ++ pdu.addVar(targetParams::securityLevel + index, noAuthNoPriv); ++ pdu.addVar(targetParams::rowStatus + index, createAndGo); ++ ++ const ::snmp::OID snmpUDPDomain = {1, 3, 6, 1, 6, 1, 1}; ++ ++ pdu.addVar(targetAddr::tDomain + index, snmpUDPDomain); ++ pdu.addVar(targetAddr::tAddress + index, data); ++ pdu.addVar(targetAddr::tagList + index, index); ++ pdu.addVar(targetAddr::params + index, index); ++ pdu.addVar(targetAddr::rowStatus + index, createAndGo); ++ ++ constexpr int trap = 1; ++ ++ pdu.addVar(notify::tag + index, index); ++ pdu.addVar(notify::type + index, trap); ++ pdu.addVar(notify::rowStatus + index, createAndGo); ++ ++ session.send(pdu); ++ } ++ catch (const ::snmp::exception::SnmpError& ex) ++ { ++ log<level::ERR>("Sending client data to snmpd failed", ++ entry("ERROR=%s", ex.what()), ++ entry("ADDRESS=%s", address.c_str())); ++ elog<InternalFailure>(); ++ } ++#else + try + { + // just to check whether given address is valid or not. +@@ -48,6 +136,7 @@ std::string ConfManager::client(std::string address, uint16_t port) + elog<InvalidArgument>(Argument::ARGUMENT_NAME("Address"), + Argument::ARGUMENT_VALUE(address.c_str())); + } ++#endif + + // create the D-Bus object + std::filesystem::path objPath; +@@ -57,8 +146,10 @@ std::string ConfManager::client(std::string address, uint16_t port) + auto client = std::make_unique<phosphor::network::snmp::Client>( + bus, objPath.string().c_str(), *this, address, port); + ++#if ! defined( USE_SNMPD ) || ( USE_SNMPD == 0 ) + // save the D-Bus object + serialize(lastClientId, *client, dbusPersistentLocation); ++#endif + + this->clients.emplace(lastClientId, std::move(client)); + return objPath.string(); +@@ -98,6 +189,27 @@ void ConfManager::deleteSNMPClient(Id id) + return; + } + ++#if defined( USE_SNMPD ) && ( USE_SNMPD == 1 ) ++ try ++ { ++ using namespace ::snmp::table; ++ ++ constexpr int destroy = 6; ++ auto pdu = ::snmp::pdu::create(SNMP_MSG_SET); ++ auto index = std::to_string(id); ++ pdu.addVar(notify::rowStatus + index, destroy); ++ pdu.addVar(targetAddr::rowStatus + index, destroy); ++ pdu.addVar(targetParams::rowStatus + index, destroy); ++ session.send(pdu); ++ } ++ catch (const ::snmp::exception::SnmpError& ex) ++ { ++ log<level::ERR>("Deleting client data from snmpd failed", ++ entry("ERROR=%s", ex.what()), ++ entry("CLIENT_UD=%zu", id)); ++ elog<InternalFailure>(); ++ } ++#else + std::error_code ec; + // remove the persistent file + fs::path fileName = dbusPersistentLocation; +@@ -115,12 +227,106 @@ void ConfManager::deleteSNMPClient(Id id) + { + lg2::error("{FILE} doesn't exist", "FILE", fileName); + } ++#endif ++ + // remove the D-Bus Object. + this->clients.erase(it); + } + + void ConfManager::restoreClients() + { ++#if defined( USE_SNMPD ) && ( USE_SNMPD == 1 ) ++ using SnmpClient = std::tuple<std::string, uint16_t>; ++ using SnmpClients = std::map<Id, SnmpClient>; ++ SnmpClients snmpClients; ++ ++ try ++ { ++ using namespace ::snmp::table; ++ auto root = targetAddr::tAddress; ++ ++ while (!root.empty()) ++ { ++ auto pdu = ::snmp::pdu::create(SNMP_MSG_GETBULK); ++ pdu.addVar(root); ++ session.send(pdu); ++ ++ for (const auto& var : pdu) ++ { ++ if (var.name_length <= targetAddr::tAddress.size() || ++ !std::equal(targetAddr::tAddress.begin(), ++ targetAddr::tAddress.end(), var.name)) ++ { ++ // End of table reached. ++ root.clear(); ++ break; ++ } ++ ++ std::string index(var.name + targetAddr::tAddress.size(), ++ var.name + var.name_length); ++ Id idNum = std::stol(index); ++ ++ char ipaddress[INET6_ADDRSTRLEN]; ++ if (inet_ntop(AF_INET, var.val.string, ipaddress, ++ sizeof(ipaddress))) ++ { ++ auto& snmpClient = snmpClients[idNum]; ++ std::get<0>(snmpClient) = ipaddress; ++ std::get<1>(snmpClient) = ++ ntohs(*(reinterpret_cast<uint16_t*>( ++ var.val.string + sizeof(in_addr_t)))); ++ } ++ ++ if (!var.next_variable) ++ { ++ // Following request should contain the last OID ++ root.assign(var.name, var.name + var.name_length); ++ } ++ } ++ } ++ } ++ catch (const ::snmp::exception::SnmpError& ex) ++ { ++ log<level::ERR>("Receiving list of clients from snmpd failed", ++ entry("ERROR=%s", ex.what())); ++ elog<InternalFailure>(); ++ } ++ ++ // Remove excess clients ++ for (auto it = clients.begin(); it != clients.end();) ++ { ++ auto snmpIt = snmpClients.find(it->first); ++ if (snmpIt == snmpClients.end()) ++ { ++ it = clients.erase(it); ++ } ++ else ++ { ++ it++; ++ } ++ } ++ ++ // Add missing clients ++ for (const auto& [id, snmpClient] : snmpClients) ++ { ++ auto it = clients.find(id); ++ if (it == clients.end()) ++ { ++ fs::path objPath = objectPath; ++ objPath /= std::to_string(id); ++ ++ auto client = std::make_unique<Client>( ++ bus, objPath.string().c_str(), *this, std::get<0>(snmpClient), ++ std::get<1>(snmpClient)); ++ clients.emplace(id, std::move(client)); ++ ++ if (id > lastClientId) ++ { ++ lastClientId = id; ++ } ++ } ++ } ++#else + if (!fs::exists(dbusPersistentLocation) || + fs::is_empty(dbusPersistentLocation)) + { +@@ -152,6 +358,7 @@ void ConfManager::restoreClients() + } + } + } ++#endif + } + + } // namespace snmp +diff --git a/snmp_conf_manager.hpp b/snmp_conf_manager.hpp +index afbfad8..10ed4dd 100644 +--- a/snmp_conf_manager.hpp ++++ b/snmp_conf_manager.hpp +@@ -1,6 +1,9 @@ + #pragma once + + #include "snmp_client.hpp" ++#if defined( USE_SNMPD ) && ( USE_SNMPD == 1 ) ++#include "snmp/session.hpp" ++#endif + + #include <sdbusplus/bus.hpp> + #include <xyz/openbmc_project/Network/Client/Create/server.hpp> +@@ -84,6 +87,11 @@ class ConfManager : public details::CreateIface + /** @brief map of SNMP Client dbus objects and their ID */ + ClientList clients; + ++#if defined( USE_SNMPD ) && ( USE_SNMPD == 1 ) ++ /** @brief SNMP client session */ ++ ::snmp::session session; ++#endif ++ + /** @brief Id of the last SNMP manager entry */ + Id lastClientId = 0; + +diff --git a/snmp_main.cpp b/snmp_main.cpp +index df5850b..d6e6646 100644 +--- a/snmp_main.cpp ++++ b/snmp_main.cpp +@@ -42,5 +42,16 @@ int main(int /*argc*/, char** /*argv[]*/) + sdeventplus::source::Signal sigTerm(event, SIGTERM, cleanExit); + sdeventplus::source::Signal sigInt(event, SIGINT, cleanExit); + ++#if defined( USE_SNMPD ) && ( USE_SNMPD == 1 ) ++ // Reload list of clients ++ stdplus::signal::block(SIGHUP); ++ sdeventplus::source::Signal sigHup( ++ event, SIGHUP, ++ [&manager](sdeventplus::source::Signal&, ++ const struct signalfd_siginfo*) { ++ manager->restoreClients(); ++ }); ++#endif ++ + return event.loop(); + } +-- +2.35.1 + diff --git a/meta-ibs/meta-cp2-5422/recipes-phosphor/network/phosphor-snmp/snmpd-dependency.conf b/meta-ibs/meta-cp2-5422/recipes-phosphor/network/phosphor-snmp/snmpd-dependency.conf new file mode 100644 index 0000000000..1fcd359e10 --- /dev/null +++ b/meta-ibs/meta-cp2-5422/recipes-phosphor/network/phosphor-snmp/snmpd-dependency.conf @@ -0,0 +1,3 @@ +[Unit] +Requires=snmpd.service +After=snmpd.service
\ No newline at end of file diff --git a/meta-ibs/meta-cp2-5422/recipes-phosphor/network/phosphor-snmp_%.bbappend b/meta-ibs/meta-cp2-5422/recipes-phosphor/network/phosphor-snmp_%.bbappend new file mode 100644 index 0000000000..a784b2510b --- /dev/null +++ b/meta-ibs/meta-cp2-5422/recipes-phosphor/network/phosphor-snmp_%.bbappend @@ -0,0 +1,23 @@ +FILESEXTRAPATHS:prepend := "${THISDIR}/${PN}:" + +SRC_URI += " \ + file://0001-main-Use-sdeventplus.patch \ + file://0002-main-Add-UNIX-signals-handler.patch \ + file://0003-conf_manager-use-snmpd-as-a-clients-storage.patch \ + file://snmpd-dependency.conf \ + " + +EXTRA_OEMESON:append = " -Dsnmpd-support=enabled -Dsnmpd-address=localhost -Dsnmpd-community=private" + +DEPENDS += "sdeventplus" +DEPENDS += "stdplus" + +RDEPENDS:${PN} += " \ + net-snmp \ + phosphor-logging \ + phosphor-dbus-interfaces \ + " + +SYSTEMD_OVERRIDE:${PN}:append = " \ + snmpd-dependency.conf:xyz.openbmc_project.Network.SNMP.service.d/snmpd-dependency.conf \ + " |