1 #include "snmp_notification.hpp"
2 #include "snmp_util.hpp"
3 
4 #include <phosphor-logging/elog-errors.hpp>
5 #include <phosphor-logging/log.hpp>
6 
7 #include "xyz/openbmc_project/Common/error.hpp"
8 
9 namespace phosphor
10 {
11 namespace network
12 {
13 namespace snmp
14 {
15 
16 namespace variant_ns = sdbusplus::message::variant_ns;
17 using namespace phosphor::logging;
18 using namespace sdbusplus::xyz::openbmc_project::Common::Error;
19 
20 using snmpSessionPtr =
21     std::unique_ptr<netsnmp_session, decltype(&::snmp_close)>;
22 
23 bool Notification::addPDUVar(netsnmp_pdu& pdu, const OID& objID,
24                              size_t objIDLen, u_char type, Value val)
25 {
26     netsnmp_variable_list* varList = nullptr;
27     switch (type)
28     {
29         case ASN_INTEGER:
30         {
31             auto ltmp = variant_ns::get<int32_t>(val);
32             varList = snmp_pdu_add_variable(&pdu, objID.data(), objIDLen, type,
33                                             &ltmp, sizeof(ltmp));
34         }
35         break;
36         case ASN_UNSIGNED:
37         {
38             auto ltmp = variant_ns::get<uint32_t>(val);
39             varList = snmp_pdu_add_variable(&pdu, objID.data(), objIDLen, type,
40                                             &ltmp, sizeof(ltmp));
41         }
42         break;
43         case ASN_OPAQUE_U64:
44         {
45             auto ltmp = variant_ns::get<uint64_t>(val);
46             varList = snmp_pdu_add_variable(&pdu, objID.data(), objIDLen, type,
47                                             &ltmp, sizeof(ltmp));
48         }
49         break;
50         case ASN_OCTET_STR:
51         {
52             const auto& value = variant_ns::get<std::string>(val);
53             varList = snmp_pdu_add_variable(&pdu, objID.data(), objIDLen, type,
54                                             value.c_str(), value.length());
55         }
56         break;
57     }
58     return (varList == nullptr ? false : true);
59 }
60 
61 void Notification::sendTrap()
62 {
63     constexpr auto comm = "public";
64     netsnmp_session session{0};
65 
66     snmp_sess_init(&session);
67 
68     init_snmp("snmpapp");
69 
70     // TODO: https://github.com/openbmc/openbmc/issues/3145
71     session.version = SNMP_VERSION_2c;
72     session.community = (u_char*)comm;
73     session.community_len = strlen(comm);
74     session.callback = nullptr;
75     session.callback_magic = nullptr;
76 
77     auto mgrs = getManagers();
78 
79     for (auto& mgr : mgrs)
80     {
81         session.peername = const_cast<char*>(mgr.c_str());
82         // create the session
83         auto ss = snmp_add(
84             &session,
85             netsnmp_transport_open_client("snmptrap", session.peername),
86             nullptr, nullptr);
87         if (!ss)
88         {
89             log<level::ERR>("Unable to get the snmp session.",
90                             entry("SNMPMANAGER=%s", mgr.c_str()));
91             elog<InternalFailure>();
92         }
93 
94         // Wrap the raw pointer in RAII
95         snmpSessionPtr sessionPtr(ss, &::snmp_close);
96 
97         ss = nullptr;
98 
99         auto pdu = snmp_pdu_create(SNMP_MSG_TRAP2);
100         if (!pdu)
101         {
102             log<level::ERR>("Failed to create notification PDU");
103             elog<InternalFailure>();
104         }
105 
106         pdu->trap_type = SNMP_TRAP_ENTERPRISESPECIFIC;
107 
108         auto trapInfo = getTrapOID();
109 
110         if (!snmp_pdu_add_variable(pdu, SNMPTrapOID,
111                                    sizeof(SNMPTrapOID) / sizeof(oid),
112                                    ASN_OBJECT_ID, trapInfo.first.data(),
113                                    trapInfo.second * sizeof(oid)))
114         {
115             log<level::ERR>("Failed to add the SNMP var(trapID)");
116             snmp_free_pdu(pdu);
117             elog<InternalFailure>();
118         }
119 
120         auto objectList = getFieldOIDList();
121 
122         for (const auto& object : objectList)
123         {
124             if (!addPDUVar(*pdu, std::get<0>(object), std::get<1>(object),
125                            std::get<2>(object), std::get<3>(object)))
126             {
127                 log<level::ERR>("Failed to add the SNMP var");
128                 snmp_free_pdu(pdu);
129                 elog<InternalFailure>();
130             }
131         }
132         // pdu is freed by snmp_send
133         if (!snmp_send(sessionPtr.get(), pdu))
134         {
135             log<level::ERR>("Failed to send the snmp trap.");
136             elog<InternalFailure>();
137         }
138 
139         log<level::DEBUG>("Sent SNMP Trap", entry("MGR=%s", mgr.c_str()));
140     }
141 }
142 
143 } // namespace snmp
144 } // namespace network
145 } // namespace phosphor
146