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 <iostream>
9 #include <phosphor-logging/elog-errors.hpp>
10 #include <phosphor-logging/log.hpp>
11 #include <xyz/openbmc_project/Common/error.hpp>
12 
13 namespace phosphor
14 {
15 namespace network
16 {
17 namespace ncsi
18 {
19 
20 using namespace phosphor::logging;
21 using namespace sdbusplus::xyz::openbmc_project::Common::Error;
22 
23 using CallBack = int (*)(struct nl_msg* msg, void* arg);
24 
25 namespace internal
26 {
27 
28 using nlMsgPtr = std::unique_ptr<nl_msg, decltype(&::nlmsg_free)>;
29 using nlSocketPtr = std::unique_ptr<nl_sock, decltype(&::nl_socket_free)>;
30 
31 CallBack infoCallBack = [](struct nl_msg* msg, void* /*arg*/) {
32     using namespace phosphor::network::ncsi;
33     auto nlh = nlmsg_hdr(msg);
34 
35     struct nlattr* tb[NCSI_ATTR_MAX + 1] = {nullptr};
36     struct nla_policy ncsiPolicy[NCSI_ATTR_MAX + 1] = {
37         {NLA_UNSPEC, 0, 0}, {NLA_U32, 0, 0}, {NLA_NESTED, 0, 0},
38         {NLA_U32, 0, 0},    {NLA_U32, 0, 0},
39     };
40 
41     struct nlattr* packagetb[NCSI_PKG_ATTR_MAX + 1] = {nullptr};
42     struct nla_policy packagePolicy[NCSI_PKG_ATTR_MAX + 1] = {
43         {NLA_UNSPEC, 0, 0}, {NLA_NESTED, 0, 0}, {NLA_U32, 0, 0},
44         {NLA_FLAG, 0, 0},   {NLA_NESTED, 0, 0},
45     };
46 
47     struct nlattr* channeltb[NCSI_CHANNEL_ATTR_MAX + 1] = {nullptr};
48     struct nla_policy channelPolicy[NCSI_CHANNEL_ATTR_MAX + 1] = {
49         {NLA_UNSPEC, 0, 0}, {NLA_NESTED, 0, 0}, {NLA_U32, 0, 0},
50         {NLA_FLAG, 0, 0},   {NLA_NESTED, 0, 0}, {NLA_UNSPEC, 0, 0},
51     };
52 
53     auto ret = genlmsg_parse(nlh, 0, tb, NCSI_ATTR_MAX, ncsiPolicy);
54     if (!tb[NCSI_ATTR_PACKAGE_LIST])
55     {
56         std::cerr << "No Packages" << std::endl;
57         return -1;
58     }
59 
60     auto attrTgt = static_cast<nlattr*>(nla_data(tb[NCSI_ATTR_PACKAGE_LIST]));
61     if (!attrTgt)
62     {
63         std::cerr << "Package list attribute is null" << std::endl;
64         return -1;
65     }
66 
67     auto rem = nla_len(tb[NCSI_ATTR_PACKAGE_LIST]);
68     nla_for_each_nested(attrTgt, tb[NCSI_ATTR_PACKAGE_LIST], rem)
69     {
70         ret = nla_parse_nested(packagetb, NCSI_PKG_ATTR_MAX, attrTgt,
71                                packagePolicy);
72         if (ret < 0)
73         {
74             std::cerr << "Failed to parse package nested" << std::endl;
75             return -1;
76         }
77 
78         if (packagetb[NCSI_PKG_ATTR_ID])
79         {
80             auto attrID = nla_get_u32(packagetb[NCSI_PKG_ATTR_ID]);
81             std::cout << "Package has id : " << std::hex << attrID << std::endl;
82         }
83         else
84         {
85             std::cout << "Package with no id" << std::endl;
86         }
87 
88         if (packagetb[NCSI_PKG_ATTR_FORCED])
89         {
90             std::cout << "This package is forced" << std::endl;
91         }
92 
93         auto channelListTarget = static_cast<nlattr*>(
94             nla_data(packagetb[NCSI_PKG_ATTR_CHANNEL_LIST]));
95 
96         auto channelrem = nla_len(packagetb[NCSI_PKG_ATTR_CHANNEL_LIST]);
97         nla_for_each_nested(channelListTarget,
98                             packagetb[NCSI_PKG_ATTR_CHANNEL_LIST], channelrem)
99         {
100             ret = nla_parse_nested(channeltb, NCSI_CHANNEL_ATTR_MAX,
101                                    channelListTarget, channelPolicy);
102             if (ret < 0)
103             {
104                 std::cerr << "Failed to parse channel nested" << std::endl;
105                 return -1;
106             }
107 
108             if (channeltb[NCSI_CHANNEL_ATTR_ID])
109             {
110                 auto channel = nla_get_u32(channeltb[NCSI_CHANNEL_ATTR_ID]);
111                 if (channeltb[NCSI_CHANNEL_ATTR_ACTIVE])
112                 {
113                     std::cout << "Channel Active : " << std::hex << channel
114                               << std::endl;
115                 }
116                 else
117                 {
118                     std::cout << "Channel Not Active : " << std::hex << channel
119                               << std::endl;
120                 }
121 
122                 if (channeltb[NCSI_CHANNEL_ATTR_FORCED])
123                 {
124                     std::cout << "Channel is forced" << std::endl;
125                 }
126             }
127             else
128             {
129                 std::cout << "Channel with no ID" << std::endl;
130             }
131 
132             if (channeltb[NCSI_CHANNEL_ATTR_VERSION_MAJOR])
133             {
134                 auto major =
135                     nla_get_u32(channeltb[NCSI_CHANNEL_ATTR_VERSION_MAJOR]);
136                 std::cout << "Channel Major Version : " << std::hex << major
137                           << std::endl;
138             }
139             if (channeltb[NCSI_CHANNEL_ATTR_VERSION_MINOR])
140             {
141                 auto minor =
142                     nla_get_u32(channeltb[NCSI_CHANNEL_ATTR_VERSION_MINOR]);
143                 std::cout << "Channel Minor Version : " << std::hex << minor
144                           << std::endl;
145             }
146             if (channeltb[NCSI_CHANNEL_ATTR_VERSION_STR])
147             {
148                 auto str =
149                     nla_get_string(channeltb[NCSI_CHANNEL_ATTR_VERSION_STR]);
150                 std::cout << "Channel Version Str :" << str << std::endl;
151             }
152             if (channeltb[NCSI_CHANNEL_ATTR_LINK_STATE])
153             {
154 
155                 auto link =
156                     nla_get_u32(channeltb[NCSI_CHANNEL_ATTR_LINK_STATE]);
157                 std::cout << "Channel Link State : " << std::hex << link
158                           << std::endl;
159             }
160             if (channeltb[NCSI_CHANNEL_ATTR_VLAN_LIST])
161             {
162                 std::cout << "Active Vlan ids" << std::endl;
163                 auto vids = channeltb[NCSI_CHANNEL_ATTR_VLAN_LIST];
164                 auto vid = static_cast<nlattr*>(nla_data(vids));
165                 auto len = nla_len(vids);
166                 while (nla_ok(vid, len))
167                 {
168                     auto id = nla_get_u16(vid);
169                     std::cout << "VID : " << id << std::endl;
170                     vid = nla_next(vid, &len);
171                 }
172             }
173         }
174     }
175     return (int)NL_SKIP;
176 };
177 
178 int applyCmd(int ifindex, int cmd, int package = DEFAULT_VALUE,
179              int channel = DEFAULT_VALUE, int flags = NONE,
180              CallBack function = nullptr)
181 {
182     nlSocketPtr socket(nl_socket_alloc(), &::nl_socket_free);
183     if (socket == nullptr)
184     {
185         log<level::ERR>("Unable to allocate memory for the socket.");
186         return -ENOMEM;
187     }
188 
189     auto ret = genl_connect(socket.get());
190     if (ret < 0)
191     {
192         std::cerr << "Failed to open the socket , RC : " << ret << std::endl;
193         return ret;
194     }
195 
196     auto driverID = genl_ctrl_resolve(socket.get(), "NCSI");
197     if (driverID < 0)
198     {
199         std::cerr << "Failed to resolve, RC : " << ret << std::endl;
200         return driverID;
201     }
202 
203     nlMsgPtr msg(nlmsg_alloc(), &::nlmsg_free);
204     if (msg == nullptr)
205     {
206         log<level::ERR>("Unable to allocate memory for the message.");
207         return -ENOMEM;
208     }
209 
210     auto msgHdr = genlmsg_put(msg.get(), 0, 0, driverID, 0, flags, cmd, 0);
211     if (!msgHdr)
212     {
213         std::cerr << "Unable to add the netlink headers , COMMAND : " << cmd
214                   << std::endl;
215         return -ENOMEM;
216     }
217 
218     if (package != DEFAULT_VALUE)
219     {
220         ret = nla_put_u32(msg.get(), ncsi_nl_attrs::NCSI_ATTR_PACKAGE_ID,
221                           package);
222         if (ret < 0)
223         {
224             std::cerr << "Failed to set the attribute , RC : " << ret
225                       << "PACKAGE " << std::hex << package << std::endl;
226             return ret;
227         }
228     }
229 
230     if (channel != DEFAULT_VALUE)
231     {
232         ret = nla_put_u32(msg.get(), ncsi_nl_attrs::NCSI_ATTR_CHANNEL_ID,
233                           channel);
234         if (ret < 0)
235         {
236             std::cerr << "Failed to set the attribute , RC : " << ret
237                       << "CHANNEL : " << std::hex << channel << std::endl;
238             return ret;
239         }
240     }
241 
242     ret = nla_put_u32(msg.get(), ncsi_nl_attrs::NCSI_ATTR_IFINDEX, ifindex);
243     if (ret < 0)
244     {
245         std::cerr << "Failed to set the attribute , RC : " << ret
246                   << "INTERFACE : " << std::hex << ifindex << std::endl;
247         return ret;
248     }
249 
250     if (function)
251     {
252         // Add a callback function to the socket
253         nl_socket_modify_cb(socket.get(), NL_CB_VALID, NL_CB_CUSTOM, function,
254                             nullptr);
255     }
256 
257     ret = nl_send_auto(socket.get(), msg.get());
258     if (ret < 0)
259     {
260         std::cerr << "Failed to send the message , RC : " << ret << std::endl;
261         return ret;
262     }
263 
264     ret = nl_recvmsgs_default(socket.get());
265     if (ret < 0)
266     {
267         std::cerr << "Failed to receive the message , RC : " << ret
268                   << std::endl;
269     }
270     return ret;
271 }
272 
273 } // namespace internal
274 
275 int setChannel(int ifindex, int package, int channel)
276 {
277     std::cout << "Set Channel : " << std::hex << channel
278               << ", PACKAGE : " << std::hex << package
279               << ", IFINDEX :  " << std::hex << ifindex << std::endl;
280     return internal::applyCmd(ifindex, ncsi_nl_commands::NCSI_CMD_SET_INTERFACE,
281                               package, channel);
282 }
283 
284 int clearInterface(int ifindex)
285 {
286     std::cout << "ClearInterface , IFINDEX :" << std::hex << ifindex
287               << std::endl;
288     return internal::applyCmd(ifindex,
289                               ncsi_nl_commands::NCSI_CMD_CLEAR_INTERFACE);
290 }
291 
292 int getInfo(int ifindex, int package)
293 {
294     std::cout << "Get Info , PACKAGE :  " << std::hex << package
295               << ", IFINDEX :  " << std::hex << ifindex << std::endl;
296     if (package == DEFAULT_VALUE)
297     {
298         return internal::applyCmd(ifindex, ncsi_nl_commands::NCSI_CMD_PKG_INFO,
299                                   package, DEFAULT_VALUE, NLM_F_DUMP,
300                                   internal::infoCallBack);
301     }
302     else
303     {
304         return internal::applyCmd(ifindex, ncsi_nl_commands::NCSI_CMD_PKG_INFO,
305                                   package, DEFAULT_VALUE, NONE,
306                                   internal::infoCallBack);
307     }
308 }
309 
310 } // namespace ncsi
311 } // namespace network
312 } // namespace phosphor
313