blob: 00f66f1b42f24ff8c47f36aafdde8a7335d3baa5 [file] [log] [blame]
Peter Hanson4a589852017-06-07 17:40:45 -07001#include <cstdio>
2#include <cstring>
William A. Kennington III194375f2018-12-14 02:14:33 -08003#include <ipmid/oemrouter.hpp>
Peter Hanson4a589852017-06-07 17:40:45 -07004#include <map>
5#include <utility>
6
Peter Hanson4a589852017-06-07 17:40:45 -07007namespace oem
8{
9
10using Key = std::pair<Number, ipmi_cmd_t>;
11
12// Private implementation of OemRouter Interface.
13class RouterImpl : public Router
14{
Patrick Venture0b02be92018-08-31 11:55:55 -070015 public:
16 RouterImpl()
17 {
18 }
Peter Hanson4a589852017-06-07 17:40:45 -070019
Patrick Venture0b02be92018-08-31 11:55:55 -070020 // Implement OemRouter Interface.
21 void activate() override;
22 void registerHandler(Number oen, ipmi_cmd_t cmd, Handler handler) override;
Peter Hanson4a589852017-06-07 17:40:45 -070023
Patrick Venture0b02be92018-08-31 11:55:55 -070024 // Actual message routing function.
25 ipmi_ret_t routeMsg(ipmi_cmd_t cmd, const uint8_t* reqBuf,
26 uint8_t* replyBuf, size_t* dataLen);
Peter Hanson4a589852017-06-07 17:40:45 -070027
Patrick Venture0b02be92018-08-31 11:55:55 -070028 private:
29 std::map<Key, Handler> handlers;
Peter Hanson4a589852017-06-07 17:40:45 -070030};
31
32// Static global instance for simplicity.
33static RouterImpl* globalRouterImpl;
34
35// TODO Refactor ipmid to avoid need for singleton here.
36Router* mutableRouter()
37{
38 if (!globalRouterImpl)
39 {
40 globalRouterImpl = new RouterImpl;
41 }
42 return globalRouterImpl;
43}
44
45ipmi_ret_t RouterImpl::routeMsg(ipmi_cmd_t cmd, const uint8_t* reqBuf,
46 uint8_t* replyBuf, size_t* dataLen)
47{
48 // Not entirely clear we can route reply without complete OEM group.
49 // TODO: consider adding a way to suppress malformed replies.
50 if (*dataLen < groupMagicSize)
51 {
Patrick Venturedde78e02018-09-10 15:13:38 -070052 std::fprintf(stderr, "NetFn:[0x2E], OEM:[%zu bytes?], Cmd:[%#04X]\n",
53 *dataLen, cmd);
Peter Hanson4a589852017-06-07 17:40:45 -070054 (*dataLen) = 0;
55 return IPMI_CC_REQ_DATA_LEN_INVALID;
56 }
57
58 // Find registered handler or reject request.
59 auto number = toOemNumber(reqBuf);
60 auto cmdKey = std::make_pair(number, cmd);
61
62 auto iter = handlers.find(cmdKey);
63 if (iter == handlers.end())
64 {
65 auto wildKey = std::make_pair(number, IPMI_CMD_WILDCARD);
66 iter = handlers.find(wildKey);
67 if (iter == handlers.end())
68 {
Patrick Venturedde78e02018-09-10 15:13:38 -070069 std::fprintf(stderr,
70 "No Registered handler for NetFn:[0x2E], "
71 "OEM:[%#08X], Cmd:[%#04X]\n",
72 number, cmd);
Peter Hanson4a589852017-06-07 17:40:45 -070073 *dataLen = groupMagicSize;
74 return IPMI_CC_INVALID;
75 }
76#ifdef __IPMI_DEBUG__
Patrick Venturedde78e02018-09-10 15:13:38 -070077 std::fprintf(stderr,
78 "Wildcard NetFn:[0x2E], OEM:[%#08X], Cmd:[%#04X]\n",
79 number, cmd);
Peter Hanson4a589852017-06-07 17:40:45 -070080#endif
81 }
82 else
83 {
84#ifdef __IPMI_DEBUG__
Patrick Venturedde78e02018-09-10 15:13:38 -070085 std::fprintf(stderr, "Match NetFn:[0x2E], OEM:[%#08X], Cmd:[%#04X]\n",
86 number, cmd);
Peter Hanson4a589852017-06-07 17:40:45 -070087#endif
88 }
89
90 // Copy OEMGroup here, by analogy to IPMI CC code at netfn router;
91 // OemHandler should deal only with optional following data bytes.
92 std::memcpy(replyBuf, reqBuf, groupMagicSize);
93
94 size_t oemDataLen = *dataLen - groupMagicSize;
95 Handler& handler = iter->second;
96
Patrick Venture0b02be92018-08-31 11:55:55 -070097 auto rc = handler(cmd, reqBuf + groupMagicSize, replyBuf + groupMagicSize,
98 &oemDataLen);
Peter Hanson4a589852017-06-07 17:40:45 -070099
100 // Add OEMGroup bytes to nominal reply.
101 *dataLen = oemDataLen + groupMagicSize;
102 return rc;
103}
104
105// Function suitable for use as ipmi_netfn_router() call-back.
106// Translates call-back pointer args to more specific types.
Patrick Venture0b02be92018-08-31 11:55:55 -0700107ipmi_ret_t ipmi_oem_wildcard_handler(ipmi_netfn_t /* netfn */, ipmi_cmd_t cmd,
108 ipmi_request_t request,
Peter Hanson4a589852017-06-07 17:40:45 -0700109 ipmi_response_t response,
110 ipmi_data_len_t dataLen,
111 ipmi_context_t context)
112{
113 // View requests & responses as byte sequences.
114 const uint8_t* reqBuf = static_cast<uint8_t*>(request);
115 uint8_t* replyBuf = static_cast<uint8_t*>(response);
116
117 // View context as router object, defaulting nullptr to global object.
Patrick Venture0b02be92018-08-31 11:55:55 -0700118 auto router = static_cast<RouterImpl*>(context ? context : mutableRouter());
Peter Hanson4a589852017-06-07 17:40:45 -0700119
120 // Send message parameters to dispatcher.
121 return router->routeMsg(cmd, reqBuf, replyBuf, dataLen);
122}
123
124// Enable message routing to begin.
125void RouterImpl::activate()
126{
127 // Register netfn 0x2e OEM Group, any (wildcard) command.
Patrick Venturedde78e02018-09-10 15:13:38 -0700128 std::printf("Registering NetFn:[0x%X], Cmd:[0x%X]\n", NETFUN_OEM_GROUP,
129 IPMI_CMD_WILDCARD);
Peter Hanson4a589852017-06-07 17:40:45 -0700130 ipmi_register_callback(NETFUN_OEM_GROUP, IPMI_CMD_WILDCARD, this,
131 ipmi_oem_wildcard_handler, PRIVILEGE_OEM);
132}
133
Patrick Venture0b02be92018-08-31 11:55:55 -0700134void RouterImpl::registerHandler(Number oen, ipmi_cmd_t cmd, Handler handler)
Peter Hanson4a589852017-06-07 17:40:45 -0700135{
136 auto cmdKey = std::make_pair(oen, cmd);
137 auto iter = handlers.find(cmdKey);
138 if (iter == handlers.end())
139 {
140 // Add handler if key not already taken.
141 handlers.emplace(cmdKey, handler);
142 }
143 else
144 {
Patrick Venturedde78e02018-09-10 15:13:38 -0700145 std::fprintf(stderr,
146 "ERROR : Duplicate registration for NetFn:[0x2E], "
147 "OEM:[%#08X], Cmd:[%#04X]\n",
148 oen, cmd);
Peter Hanson4a589852017-06-07 17:40:45 -0700149 }
150}
151
Patrick Venture0b02be92018-08-31 11:55:55 -0700152} // namespace oem