blob: 273a67947f154c63dd8b26defdd7407b4819dd31 [file] [log] [blame]
Peter Hanson4a589852017-06-07 17:40:45 -07001#include <cstdio>
2#include <cstring>
3#include <map>
4#include <utility>
5
6#include "host-ipmid/oemrouter.hpp"
7
8namespace oem
9{
10
11using Key = std::pair<Number, ipmi_cmd_t>;
12
13// Private implementation of OemRouter Interface.
14class RouterImpl : public Router
15{
16 public:
17 RouterImpl() {}
18
19 // Implement OemRouter Interface.
20 void activate() override;
21 void registerHandler(Number oen, ipmi_cmd_t cmd,
22 Handler handler) override;
23
24 // Actual message routing function.
25 ipmi_ret_t routeMsg(ipmi_cmd_t cmd, const uint8_t* reqBuf,
26 uint8_t* replyBuf, size_t* dataLen);
27
28 private:
29 std::map<Key, Handler> handlers;
30};
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 {
52 fprintf(stderr, "NetFn:[0x2E], OEM:[%zu bytes?], Cmd:[%#04X]\n",
53 *dataLen, cmd);
54 (*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 {
69 fprintf(stderr, "No Registered handler for NetFn:[0x2E], "
70 "OEM:[%#08X], Cmd:[%#04X]\n", number, cmd);
71 *dataLen = groupMagicSize;
72 return IPMI_CC_INVALID;
73 }
74#ifdef __IPMI_DEBUG__
75 fprintf(stderr, "Wildcard NetFn:[0x2E], OEM:[%#08X], Cmd:[%#04X]\n",
76 number, cmd);
77#endif
78 }
79 else
80 {
81#ifdef __IPMI_DEBUG__
82 fprintf(stderr, "Match NetFn:[0x2E], OEM:[%#08X], Cmd:[%#04X]\n",
83 number, cmd);
84#endif
85 }
86
87 // Copy OEMGroup here, by analogy to IPMI CC code at netfn router;
88 // OemHandler should deal only with optional following data bytes.
89 std::memcpy(replyBuf, reqBuf, groupMagicSize);
90
91 size_t oemDataLen = *dataLen - groupMagicSize;
92 Handler& handler = iter->second;
93
94 auto rc = handler(cmd, reqBuf + groupMagicSize,
95 replyBuf + groupMagicSize, &oemDataLen);
96
97 // Add OEMGroup bytes to nominal reply.
98 *dataLen = oemDataLen + groupMagicSize;
99 return rc;
100}
101
102// Function suitable for use as ipmi_netfn_router() call-back.
103// Translates call-back pointer args to more specific types.
104ipmi_ret_t ipmi_oem_wildcard_handler(ipmi_netfn_t /* netfn */,
105 ipmi_cmd_t cmd, ipmi_request_t request,
106 ipmi_response_t response,
107 ipmi_data_len_t dataLen,
108 ipmi_context_t context)
109{
110 // View requests & responses as byte sequences.
111 const uint8_t* reqBuf = static_cast<uint8_t*>(request);
112 uint8_t* replyBuf = static_cast<uint8_t*>(response);
113
114 // View context as router object, defaulting nullptr to global object.
115 auto router = static_cast<RouterImpl*>(
116 context ? context : mutableRouter());
117
118 // Send message parameters to dispatcher.
119 return router->routeMsg(cmd, reqBuf, replyBuf, dataLen);
120}
121
122// Enable message routing to begin.
123void RouterImpl::activate()
124{
125 // Register netfn 0x2e OEM Group, any (wildcard) command.
126 printf("Registering NetFn:[0x%X], Cmd:[0x%X]\n",
127 NETFUN_OEM_GROUP, IPMI_CMD_WILDCARD);
128 ipmi_register_callback(NETFUN_OEM_GROUP, IPMI_CMD_WILDCARD, this,
129 ipmi_oem_wildcard_handler, PRIVILEGE_OEM);
130}
131
132void RouterImpl::registerHandler(Number oen, ipmi_cmd_t cmd,
133 Handler handler)
134{
135 auto cmdKey = std::make_pair(oen, cmd);
136 auto iter = handlers.find(cmdKey);
137 if (iter == handlers.end())
138 {
139 // Add handler if key not already taken.
140 handlers.emplace(cmdKey, handler);
141 }
142 else
143 {
144 fprintf(stderr, "ERROR : Duplicate registration for NetFn:[0x2E], "
145 "OEM:[%#08X], Cmd:[%#04X]\n", oen, cmd);
146 }
147}
148
149} // namespace oem