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