1 #include "ncsi_util.hpp"
2
3 #include <linux/ncsi.h>
4 #include <netlink/genl/ctrl.h>
5 #include <netlink/genl/genl.h>
6 #include <netlink/netlink.h>
7
8 #include <phosphor-logging/lg2.hpp>
9 #include <stdplus/numeric/str.hpp>
10 #include <stdplus/str/buf.hpp>
11
12 #include <iomanip>
13 #include <iostream>
14 #include <vector>
15
16 namespace phosphor
17 {
18 namespace network
19 {
20 namespace ncsi
21 {
22
23 using CallBack = int (*)(struct nl_msg* msg, void* arg);
24
toHexStr(std::span<const uint8_t> c)25 static stdplus::StrBuf toHexStr(std::span<const uint8_t> c) noexcept
26 {
27 stdplus::StrBuf ret;
28 if (c.empty())
29 {
30 return ret;
31 }
32 stdplus::IntToStr<16, uint8_t> its;
33 auto oit = ret.append(c.size() * 3 - 1);
34 auto cit = c.begin();
35 oit = its(oit, *cit++, 2);
36 for (; cit != c.end(); ++cit)
37 {
38 *oit++ = ' ';
39 oit = its(oit, *cit, 2);
40 }
41 return ret;
42 }
43
44 namespace internal
45 {
46
47 struct NCSIPacketHeader
48 {
49 uint8_t MCID;
50 uint8_t revision;
51 uint8_t reserved;
52 uint8_t id;
53 uint8_t type;
54 uint8_t channel;
55 uint16_t length;
56 uint32_t rsvd[2];
57 };
58
59 class Command
60 {
61 public:
62 Command() = delete;
63 ~Command() = default;
64 Command(const Command&) = delete;
65 Command& operator=(const Command&) = delete;
66 Command(Command&&) = default;
67 Command& operator=(Command&&) = default;
Command(int c,int nc=DEFAULT_VALUE,std::span<const unsigned char> p=std::span<const unsigned char> ())68 Command(
69 int c, int nc = DEFAULT_VALUE,
70 std::span<const unsigned char> p = std::span<const unsigned char>()) :
71 cmd(c),
72 ncsi_cmd(nc), payload(p)
73 {}
74
75 int cmd;
76 int ncsi_cmd;
77 std::span<const unsigned char> payload;
78 };
79
80 using nlMsgPtr = std::unique_ptr<nl_msg, decltype(&::nlmsg_free)>;
81 using nlSocketPtr = std::unique_ptr<nl_sock, decltype(&::nl_socket_free)>;
82
__anon66112f010102(struct nl_msg* msg, void* arg) 83 CallBack infoCallBack = [](struct nl_msg* msg, void* arg) {
84 using namespace phosphor::network::ncsi;
85 auto nlh = nlmsg_hdr(msg);
86
87 struct nlattr* tb[NCSI_ATTR_MAX + 1] = {nullptr};
88 struct nla_policy ncsiPolicy[NCSI_ATTR_MAX + 1] = {
89 {NLA_UNSPEC, 0, 0}, {NLA_U32, 0, 0}, {NLA_NESTED, 0, 0},
90 {NLA_U32, 0, 0}, {NLA_U32, 0, 0},
91 };
92
93 struct nlattr* packagetb[NCSI_PKG_ATTR_MAX + 1] = {nullptr};
94 struct nla_policy packagePolicy[NCSI_PKG_ATTR_MAX + 1] = {
95 {NLA_UNSPEC, 0, 0}, {NLA_NESTED, 0, 0}, {NLA_U32, 0, 0},
96 {NLA_FLAG, 0, 0}, {NLA_NESTED, 0, 0},
97 };
98
99 struct nlattr* channeltb[NCSI_CHANNEL_ATTR_MAX + 1] = {nullptr};
100 struct nla_policy channelPolicy[NCSI_CHANNEL_ATTR_MAX + 1] = {
101 {NLA_UNSPEC, 0, 0}, {NLA_NESTED, 0, 0}, {NLA_U32, 0, 0},
102 {NLA_FLAG, 0, 0}, {NLA_NESTED, 0, 0}, {NLA_UNSPEC, 0, 0},
103 };
104
105 *(int*)arg = 0;
106
107 auto ret = genlmsg_parse(nlh, 0, tb, NCSI_ATTR_MAX, ncsiPolicy);
108 if (!tb[NCSI_ATTR_PACKAGE_LIST])
109 {
110 lg2::error("No Packages");
111 return -1;
112 }
113
114 auto attrTgt = static_cast<nlattr*>(nla_data(tb[NCSI_ATTR_PACKAGE_LIST]));
115 if (!attrTgt)
116 {
117 lg2::error("Package list attribute is null");
118 return -1;
119 }
120
121 auto rem = nla_len(tb[NCSI_ATTR_PACKAGE_LIST]);
122 nla_for_each_nested(attrTgt, tb[NCSI_ATTR_PACKAGE_LIST], rem)
123 {
124 ret = nla_parse_nested(packagetb, NCSI_PKG_ATTR_MAX, attrTgt,
125 packagePolicy);
126 if (ret < 0)
127 {
128 lg2::error("Failed to parse package nested");
129 return -1;
130 }
131
132 if (packagetb[NCSI_PKG_ATTR_ID])
133 {
134 auto attrID = nla_get_u32(packagetb[NCSI_PKG_ATTR_ID]);
135 lg2::debug("Package has id : {ATTR_ID}", "ATTR_ID", lg2::hex,
136 attrID);
137 }
138 else
139 {
140 lg2::debug("Package with no id");
141 }
142
143 if (packagetb[NCSI_PKG_ATTR_FORCED])
144 {
145 lg2::debug("This package is forced");
146 }
147
148 auto channelListTarget = static_cast<nlattr*>(
149 nla_data(packagetb[NCSI_PKG_ATTR_CHANNEL_LIST]));
150
151 auto channelrem = nla_len(packagetb[NCSI_PKG_ATTR_CHANNEL_LIST]);
152 nla_for_each_nested(channelListTarget,
153 packagetb[NCSI_PKG_ATTR_CHANNEL_LIST], channelrem)
154 {
155 ret = nla_parse_nested(channeltb, NCSI_CHANNEL_ATTR_MAX,
156 channelListTarget, channelPolicy);
157 if (ret < 0)
158 {
159 lg2::error("Failed to parse channel nested");
160 return -1;
161 }
162
163 if (channeltb[NCSI_CHANNEL_ATTR_ID])
164 {
165 auto channel = nla_get_u32(channeltb[NCSI_CHANNEL_ATTR_ID]);
166 if (channeltb[NCSI_CHANNEL_ATTR_ACTIVE])
167 {
168 lg2::debug("Channel Active : {CHANNEL}", "CHANNEL",
169 lg2::hex, channel);
170 }
171 else
172 {
173 lg2::debug("Channel Not Active : {CHANNEL}", "CHANNEL",
174 lg2::hex, channel);
175 }
176
177 if (channeltb[NCSI_CHANNEL_ATTR_FORCED])
178 {
179 lg2::debug("Channel is forced");
180 }
181 }
182 else
183 {
184 lg2::debug("Channel with no ID");
185 }
186
187 if (channeltb[NCSI_CHANNEL_ATTR_VERSION_MAJOR])
188 {
189 auto major =
190 nla_get_u32(channeltb[NCSI_CHANNEL_ATTR_VERSION_MAJOR]);
191 lg2::debug("Channel Major Version : {CHANNEL_MAJOR_VERSION}",
192 "CHANNEL_MAJOR_VERSION", lg2::hex, major);
193 }
194 if (channeltb[NCSI_CHANNEL_ATTR_VERSION_MINOR])
195 {
196 auto minor =
197 nla_get_u32(channeltb[NCSI_CHANNEL_ATTR_VERSION_MINOR]);
198 lg2::debug("Channel Minor Version : {CHANNEL_MINOR_VERSION}",
199 "CHANNEL_MINOR_VERSION", lg2::hex, minor);
200 }
201 if (channeltb[NCSI_CHANNEL_ATTR_VERSION_STR])
202 {
203 auto str =
204 nla_get_string(channeltb[NCSI_CHANNEL_ATTR_VERSION_STR]);
205 lg2::debug("Channel Version Str : {CHANNEL_VERSION_STR}",
206 "CHANNEL_VERSION_STR", str);
207 }
208 if (channeltb[NCSI_CHANNEL_ATTR_LINK_STATE])
209 {
210 auto link =
211 nla_get_u32(channeltb[NCSI_CHANNEL_ATTR_LINK_STATE]);
212 lg2::debug("Channel Link State : {LINK_STATE}", "LINK_STATE",
213 lg2::hex, link);
214 }
215 if (channeltb[NCSI_CHANNEL_ATTR_VLAN_LIST])
216 {
217 lg2::debug("Active Vlan ids");
218 auto vids = channeltb[NCSI_CHANNEL_ATTR_VLAN_LIST];
219 auto vid = static_cast<nlattr*>(nla_data(vids));
220 auto len = nla_len(vids);
221 while (nla_ok(vid, len))
222 {
223 auto id = nla_get_u16(vid);
224 lg2::debug("VID : {VLAN_ID}", "VLAN_ID", id);
225 vid = nla_next(vid, &len);
226 }
227 }
228 }
229 }
230 return (int)NL_SKIP;
231 };
232
__anon66112f010202(struct nl_msg* msg, void* arg) 233 CallBack sendCallBack = [](struct nl_msg* msg, void* arg) {
234 using namespace phosphor::network::ncsi;
235 auto nlh = nlmsg_hdr(msg);
236 struct nlattr* tb[NCSI_ATTR_MAX + 1] = {nullptr};
237 static struct nla_policy ncsiPolicy[NCSI_ATTR_MAX + 1] = {
238 {NLA_UNSPEC, 0, 0}, {NLA_U32, 0, 0}, {NLA_NESTED, 0, 0},
239 {NLA_U32, 0, 0}, {NLA_U32, 0, 0}, {NLA_BINARY, 0, 0},
240 {NLA_FLAG, 0, 0}, {NLA_U32, 0, 0}, {NLA_U32, 0, 0},
241 };
242
243 *(int*)arg = 0;
244
245 auto ret = genlmsg_parse(nlh, 0, tb, NCSI_ATTR_MAX, ncsiPolicy);
246 if (ret)
247 {
248 lg2::error("Failed to parse package");
249 return ret;
250 }
251
252 if (tb[NCSI_ATTR_DATA] == nullptr)
253 {
254 lg2::error("Response: No data");
255 return -1;
256 }
257
258 auto data_len = nla_len(tb[NCSI_ATTR_DATA]) - sizeof(NCSIPacketHeader);
259 unsigned char* data = (unsigned char*)nla_data(tb[NCSI_ATTR_DATA]) +
260 sizeof(NCSIPacketHeader);
261
262 // Dump the response to stdout. Enhancement: option to save response data
263 auto str = toHexStr(std::span<const unsigned char>(data, data_len));
264 lg2::debug("Response {DATA_LEN} bytes: {DATA}", "DATA_LEN", data_len,
265 "DATA", str);
266
267 return 0;
268 };
269
applyCmd(int ifindex,const Command & cmd,int package=DEFAULT_VALUE,int channel=DEFAULT_VALUE,int flags=NONE,CallBack function=nullptr)270 int applyCmd(int ifindex, const Command& cmd, int package = DEFAULT_VALUE,
271 int channel = DEFAULT_VALUE, int flags = NONE,
272 CallBack function = nullptr)
273 {
274 int cb_ret = 0;
275 nlSocketPtr socket(nl_socket_alloc(), &::nl_socket_free);
276 if (socket == nullptr)
277 {
278 lg2::error("Unable to allocate memory for the socket");
279 return -ENOMEM;
280 }
281
282 auto ret = genl_connect(socket.get());
283 if (ret < 0)
284 {
285 lg2::error("Failed to open the socket , RC : {RC}", "RC", ret);
286 return ret;
287 }
288
289 auto driverID = genl_ctrl_resolve(socket.get(), "NCSI");
290 if (driverID < 0)
291 {
292 lg2::error("Failed to resolve, RC : {RC}", "RC", ret);
293 return driverID;
294 }
295
296 nlMsgPtr msg(nlmsg_alloc(), &::nlmsg_free);
297 if (msg == nullptr)
298 {
299 lg2::error("Unable to allocate memory for the message");
300 return -ENOMEM;
301 }
302
303 auto msgHdr = genlmsg_put(msg.get(), 0, 0, driverID, 0, flags, cmd.cmd, 0);
304 if (!msgHdr)
305 {
306 lg2::error("Unable to add the netlink headers , COMMAND : {COMMAND}",
307 "COMMAND", cmd.cmd);
308 return -ENOMEM;
309 }
310
311 if (package != DEFAULT_VALUE)
312 {
313 ret = nla_put_u32(msg.get(), ncsi_nl_attrs::NCSI_ATTR_PACKAGE_ID,
314 package);
315 if (ret < 0)
316 {
317 lg2::error("Failed to set the attribute , RC : {RC} PACKAGE "
318 "{PACKAGE}",
319 "RC", ret, "PACKAGE", lg2::hex, package);
320 return ret;
321 }
322 }
323
324 if (channel != DEFAULT_VALUE)
325 {
326 ret = nla_put_u32(msg.get(), ncsi_nl_attrs::NCSI_ATTR_CHANNEL_ID,
327 channel);
328 if (ret < 0)
329 {
330 lg2::error("Failed to set the attribute , RC : {RC} CHANNEL : "
331 "{CHANNEL}",
332 "RC", ret, "CHANNEL", lg2::hex, channel);
333 return ret;
334 }
335 }
336
337 ret = nla_put_u32(msg.get(), ncsi_nl_attrs::NCSI_ATTR_IFINDEX, ifindex);
338 if (ret < 0)
339 {
340 lg2::error("Failed to set the attribute , RC : {RC} INTERFACE : "
341 "{INTERFACE}",
342 "RC", ret, "INTERFACE", lg2::hex, ifindex);
343 return ret;
344 }
345
346 if (cmd.ncsi_cmd != DEFAULT_VALUE)
347 {
348 std::vector<unsigned char> pl(sizeof(NCSIPacketHeader) +
349 cmd.payload.size());
350 NCSIPacketHeader* hdr = (NCSIPacketHeader*)pl.data();
351
352 std::copy(cmd.payload.begin(), cmd.payload.end(),
353 pl.begin() + sizeof(NCSIPacketHeader));
354
355 hdr->type = cmd.ncsi_cmd;
356 hdr->length = htons(cmd.payload.size());
357
358 ret = nla_put(msg.get(), ncsi_nl_attrs::NCSI_ATTR_DATA, pl.size(),
359 pl.data());
360 if (ret < 0)
361 {
362 lg2::error("Failed to set the data attribute, RC : {RC}", "RC",
363 ret);
364 return ret;
365 }
366
367 nl_socket_disable_seq_check(socket.get());
368 }
369
370 if (function)
371 {
372 cb_ret = 1;
373
374 // Add a callback function to the socket
375 nl_socket_modify_cb(socket.get(), NL_CB_VALID, NL_CB_CUSTOM, function,
376 &cb_ret);
377 }
378
379 ret = nl_send_auto(socket.get(), msg.get());
380 if (ret < 0)
381 {
382 lg2::error("Failed to send the message , RC : {RC}", "RC", ret);
383 return ret;
384 }
385
386 do
387 {
388 ret = nl_recvmsgs_default(socket.get());
389 if (ret < 0)
390 {
391 lg2::error("Failed to receive the message , RC : {RC}", "RC", ret);
392 break;
393 }
394 } while (cb_ret);
395
396 return ret;
397 }
398
399 } // namespace internal
400
sendOemCommand(int ifindex,int package,int channel,std::span<const unsigned char> payload)401 int sendOemCommand(int ifindex, int package, int channel,
402 std::span<const unsigned char> payload)
403 {
404 constexpr auto cmd = 0x50;
405
406 lg2::debug("Send OEM Command, CHANNEL : {CHANNEL} , PACKAGE : {PACKAGE}, "
407 "INTERFACE_INDEX: {INTERFACE_INDEX}",
408 "CHANNEL", lg2::hex, channel, "PACKAGE", lg2::hex, package,
409 "INTERFACE_INDEX", lg2::hex, ifindex);
410 if (!payload.empty())
411 {
412 lg2::debug("Payload: {PAYLOAD}", "PAYLOAD", toHexStr(payload));
413 }
414
415 return internal::applyCmd(
416 ifindex,
417 internal::Command(ncsi_nl_commands::NCSI_CMD_SEND_CMD, cmd, payload),
418 package, channel, NONE, internal::sendCallBack);
419 }
420
setChannel(int ifindex,int package,int channel)421 int setChannel(int ifindex, int package, int channel)
422 {
423 lg2::debug(
424 "Set CHANNEL : {CHANNEL} , PACKAGE : {PACKAGE}, INTERFACE_INDEX: "
425 "{INTERFACE_INDEX}",
426 "CHANNEL", lg2::hex, channel, "PACKAGE", lg2::hex, package,
427 "INTERFACE_INDEX", lg2::hex, ifindex);
428 return internal::applyCmd(
429 ifindex, internal::Command(ncsi_nl_commands::NCSI_CMD_SET_INTERFACE),
430 package, channel);
431 }
432
clearInterface(int ifindex)433 int clearInterface(int ifindex)
434 {
435 lg2::debug("ClearInterface , INTERFACE_INDEX : {INTERFACE_INDEX}",
436 "INTERFACE_INDEX", lg2::hex, ifindex);
437 return internal::applyCmd(
438 ifindex, internal::Command(ncsi_nl_commands::NCSI_CMD_CLEAR_INTERFACE));
439 }
440
getInfo(int ifindex,int package)441 int getInfo(int ifindex, int package)
442 {
443 lg2::debug(
444 "Get Info , PACKAGE : {PACKAGE}, INTERFACE_INDEX: {INTERFACE_INDEX}",
445 "PACKAGE", lg2::hex, package, "INTERFACE_INDEX", lg2::hex, ifindex);
446 if (package == DEFAULT_VALUE)
447 {
448 return internal::applyCmd(
449 ifindex, internal::Command(ncsi_nl_commands::NCSI_CMD_PKG_INFO),
450 package, DEFAULT_VALUE, NLM_F_DUMP, internal::infoCallBack);
451 }
452 else
453 {
454 return internal::applyCmd(ifindex, ncsi_nl_commands::NCSI_CMD_PKG_INFO,
455 package, DEFAULT_VALUE, NONE,
456 internal::infoCallBack);
457 }
458 }
459
460 } // namespace ncsi
461 } // namespace network
462 } // namespace phosphor
463