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