From 4f4e9c898a078be194f449838addbfb04aca7e88 Mon Sep 17 00:00:00 2001 From: "Andrey V.Kosteltsev" Date: Sun, 17 Jul 2022 19:37:13 +0300 Subject: IBS: SNMP Support (First Approx) --- .../images/obmc-phosphor-image.bbappend | 10 + .../packagegroups/packagegroup-ibs-apps.bb | 2 + .../0001-config_os_libs2-no-pci-lookup.patch | 16 + .../recipes-protocols/net-snmp/net-snmp/snmpd.conf | 34 + .../recipes-protocols/net-snmp/net-snmp_%.bbappend | 10 + .../recipes-phosphor/network/obmc-sila-snmp_git.bb | 65 ++ .../phosphor-snmp/0001-main-Use-sdeventplus.patch | 123 +++ .../0002-main-Add-UNIX-signals-handler.patch | 102 +++ ...nf_manager-use-snmpd-as-a-clients-storage.patch | 859 +++++++++++++++++++++ .../network/phosphor-snmp/snmpd-dependency.conf | 3 + .../network/phosphor-snmp_%.bbappend | 23 + 11 files changed, 1247 insertions(+) create mode 100644 meta-ibs/meta-common/recipes-protocols/net-snmp/net-snmp/0001-config_os_libs2-no-pci-lookup.patch create mode 100644 meta-ibs/meta-common/recipes-protocols/net-snmp/net-snmp/snmpd.conf create mode 100644 meta-ibs/meta-common/recipes-protocols/net-snmp/net-snmp_%.bbappend create mode 100644 meta-ibs/meta-cp2-5422/recipes-phosphor/network/obmc-sila-snmp_git.bb create mode 100644 meta-ibs/meta-cp2-5422/recipes-phosphor/network/phosphor-snmp/0001-main-Use-sdeventplus.patch create mode 100644 meta-ibs/meta-cp2-5422/recipes-phosphor/network/phosphor-snmp/0002-main-Add-UNIX-signals-handler.patch create mode 100644 meta-ibs/meta-cp2-5422/recipes-phosphor/network/phosphor-snmp/0003-conf_manager-use-snmpd-as-a-clients-storage.patch create mode 100644 meta-ibs/meta-cp2-5422/recipes-phosphor/network/phosphor-snmp/snmpd-dependency.conf create mode 100644 meta-ibs/meta-cp2-5422/recipes-phosphor/network/phosphor-snmp_%.bbappend diff --git a/meta-ibs/meta-common/recipes-ibs/images/obmc-phosphor-image.bbappend b/meta-ibs/meta-common/recipes-ibs/images/obmc-phosphor-image.bbappend index cd56e009b3..914444cfb1 100644 --- a/meta-ibs/meta-common/recipes-ibs/images/obmc-phosphor-image.bbappend +++ b/meta-ibs/meta-common/recipes-ibs/images/obmc-phosphor-image.bbappend @@ -13,6 +13,16 @@ IMAGE_INSTALL += " openssl-bin \ smbios-mdrv2 \ " +IMAGE_INSTALL += " net-snmp-lib-agent \ + net-snmp-lib-helpers \ + net-snmp-lib-mibs \ + net-snmp-lib-netsnmp \ + net-snmp-lib-trapd \ + net-snmp-mibs \ + net-snmp-server-snmpd \ + net-snmp-server-snmptrapd \ + " + OBMC_IMAGE_EXTRA_INSTALL += " " fix_shadow_perms() { diff --git a/meta-ibs/meta-common/recipes-ibs/packagegroups/packagegroup-ibs-apps.bb b/meta-ibs/meta-common/recipes-ibs/packagegroups/packagegroup-ibs-apps.bb index 35ee6c8459..92f95a235d 100644 --- a/meta-ibs/meta-common/recipes-ibs/packagegroups/packagegroup-ibs-apps.bb +++ b/meta-ibs/meta-common/recipes-ibs/packagegroups/packagegroup-ibs-apps.bb @@ -59,6 +59,8 @@ RDEPENDS:${PN}-interface = " \ webui-vue \ phosphor-ipmi-ipmb \ phosphor-snmp \ + obmc-sila-snmp-agent \ + obmc-sila-snmp-cfg-manager \ " SUMMARY:${PN}-cli = "CLI utils" diff --git a/meta-ibs/meta-common/recipes-protocols/net-snmp/net-snmp/0001-config_os_libs2-no-pci-lookup.patch b/meta-ibs/meta-common/recipes-protocols/net-snmp/net-snmp/0001-config_os_libs2-no-pci-lookup.patch new file mode 100644 index 0000000000..1ac465b28d --- /dev/null +++ b/meta-ibs/meta-common/recipes-protocols/net-snmp/net-snmp/0001-config_os_libs2-no-pci-lookup.patch @@ -0,0 +1,16 @@ +diff -bu -Nr net-snmp-5.9.1-orig/configure.d/config_os_libs2 net-snmp-5.9.1/configure.d/config_os_libs2 +--- net-snmp-5.9.1-orig/configure.d/config_os_libs2 2022-07-16 18:27:39.958781773 +0300 ++++ net-snmp-5.9.1/configure.d/config_os_libs2 2022-07-16 20:07:49.846845832 +0300 +@@ -185,9 +185,9 @@ + # libpci + # (for if-mib description) + # +-NETSNMP_SEARCH_LIBS(pci_lookup_name, pci, +- AC_DEFINE(HAVE_PCI_LOOKUP_NAME, 1, +- [define if you have pci_lookup_name()]),,,LMIBLIBS) ++#NETSNMP_SEARCH_LIBS(pci_lookup_name, pci, ++# AC_DEFINE(HAVE_PCI_LOOKUP_NAME, 1, ++# [define if you have pci_lookup_name()]),,,LMIBLIBS) + + # LM-SENSORS-MIB support + # diff --git a/meta-ibs/meta-common/recipes-protocols/net-snmp/net-snmp/snmpd.conf b/meta-ibs/meta-common/recipes-protocols/net-snmp/net-snmp/snmpd.conf new file mode 100644 index 0000000000..9244009421 --- /dev/null +++ b/meta-ibs/meta-common/recipes-protocols/net-snmp/net-snmp/snmpd.conf @@ -0,0 +1,34 @@ + +rwcommunity private localhost + +# sec.name source community +com2sec readonly default public +com2sec rwsink localhost private + +# sec.model sec.name +group MyROGroup v1 readonly +group MyROGroup v2c readonly +group MyROGroup usm readonly +group MyRWGroup usm root +group MyRwGroup v2c rwsink + +# incl/excl subtree mask +view readonly included .1.3.6.1.2.1.1 +view readonly included .1.3.6.1.2.1.25.1.1 +view readonly included .1.3.6.1.4.1.49769 +view rwsink included .1.3.6.1.6.3 + +# context sec.model sec.level match read write notif +access MyROGroup "" any noauth exact readonly none none +access MyRWGroup "" any priv exact all all none +access MyRwGroup "" v2c noauth exact all rwsink none + +############################################################################### +# System contact information +# + +syslocation Solar System, Earth (configure /etc/snmp/snmpd.conf) +syscontact Root (configure /etc/snmp/snmpd.conf) + +# ----------------------------------------------------------------------------- +master agentx diff --git a/meta-ibs/meta-common/recipes-protocols/net-snmp/net-snmp_%.bbappend b/meta-ibs/meta-common/recipes-protocols/net-snmp/net-snmp_%.bbappend new file mode 100644 index 0000000000..71b972804c --- /dev/null +++ b/meta-ibs/meta-common/recipes-protocols/net-snmp/net-snmp_%.bbappend @@ -0,0 +1,10 @@ +FILESEXTRAPATHS:prepend := "${THISDIR}/${PN}:" + +# +# meta-openembedded/meta-networking/recipes-protocols/net-snmp: +# + +SRC_URI += " \ + file://snmpd.conf \ + file://0001-config_os_libs2-no-pci-lookup.patch \ + " 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" +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 +--- + 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 + #include ++#include + #include + + #include + +-/* 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; +- + 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" +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 +--- + 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 + #include + #include ++#include + #include ++#include + + #include + ++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" +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 +--- + 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 ++#include ++ ++#include ++ ++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 ++#include ++ ++#include ++#include ++ ++namespace snmp ++{ ++ ++struct OID : public std::vector ++{ ++ using std::vector::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(ret); ++ } ++ ++ std::string dump() const ++ { ++ return std::forward(dump(data(), size())); ++ } ++ ++ template OID operator+(const T& tail) const ++ { ++ OID res = *this; ++ res.insert(res.end(), tail.begin(), tail.end()); ++ return std::forward(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 ++#include ++ ++#include ++#include ++ ++namespace snmp ++{ ++ ++class session; ++ ++namespace details ++{ ++ ++struct PduDeleter ++{ ++ void operator()(netsnmp_pdu* pduPtr) const ++ { ++ snmp_free_pdu(pduPtr); ++ } ++}; ++ ++using PduPtr = std::unique_ptr; ++} // 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(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& 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 ++#include ++ ++#include ++#include ++ ++namespace snmp ++{ ++ ++namespace details ++{ ++ ++struct SessionDeleter ++{ ++ void operator()(netsnmp_session* sessionPtr) const ++ { ++ snmp_close(sessionPtr); ++ } ++}; ++ ++using SessionPtr = std::unique_ptr; ++ ++} // 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(peername.c_str()); ++ ss.community = const_cast( ++ reinterpret_cast(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(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 + + #include +@@ -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 encodeAddr(const std::string& addr, uint16_t port) ++{ ++ std::vector 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("getaddrinfo failed", ++ entry("ADDRESS=%s", addr.c_str())); ++ elog(); ++ } ++ ++ AddrPtr addrPtr{ptr}; ++ ptr = nullptr; ++ ++ auto sa = reinterpret_cast(addrPtr->ai_addr); ++ auto begin = reinterpret_cast(&(sa->sin_addr)); ++ data.insert(data.end(), begin, begin + sizeof(sa->sin_addr)); ++ ++ begin = reinterpret_cast(&(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("Sending client data to snmpd failed", ++ entry("ERROR=%s", ex.what()), ++ entry("ADDRESS=%s", address.c_str())); ++ elog(); ++ } ++#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(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( + 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("Deleting client data from snmpd failed", ++ entry("ERROR=%s", ex.what()), ++ entry("CLIENT_UD=%zu", id)); ++ elog(); ++ } ++#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; ++ using SnmpClients = std::map; ++ 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( ++ 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("Receiving list of clients from snmpd failed", ++ entry("ERROR=%s", ex.what())); ++ elog(); ++ } ++ ++ // 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( ++ 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 + #include +@@ -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 \ + " -- cgit v1.2.3