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     auto ret = genl_connect(socket.get());
184     if (ret < 0)
185     {
186         std::cerr << "Failed to open the socket , RC : " << ret << std::endl;
187         return ret;
188     }
189 
190     auto driverID = genl_ctrl_resolve(socket.get(), "NCSI");
191     if (driverID < 0)
192     {
193         std::cerr << "Failed to resolve, RC : " << ret << std::endl;
194         return driverID;
195     }
196 
197     nlMsgPtr msg(nlmsg_alloc(), &::nlmsg_free);
198 
199     auto msgHdr = genlmsg_put(msg.get(), 0, 0, driverID, 0, flags, cmd, 0);
200     if (!msgHdr)
201     {
202         std::cerr << "Unable to add the netlink headers , COMMAND : " << cmd
203                   << std::endl;
204         return -1;
205     }
206 
207     if (package != DEFAULT_VALUE)
208     {
209         ret = nla_put_u32(msg.get(), ncsi_nl_attrs::NCSI_ATTR_PACKAGE_ID,
210                           package);
211         if (ret < 0)
212         {
213             std::cerr << "Failed to set the attribute , RC : " << ret
214                       << "PACKAGE " << std::hex << package << std::endl;
215             return ret;
216         }
217     }
218 
219     if (channel != DEFAULT_VALUE)
220     {
221         ret = nla_put_u32(msg.get(), ncsi_nl_attrs::NCSI_ATTR_CHANNEL_ID,
222                           channel);
223         if (ret < 0)
224         {
225             std::cerr << "Failed to set the attribute , RC : " << ret
226                       << "CHANNEL : " << std::hex << channel << std::endl;
227             return ret;
228         }
229     }
230 
231     ret = nla_put_u32(msg.get(), ncsi_nl_attrs::NCSI_ATTR_IFINDEX, ifindex);
232     if (ret < 0)
233     {
234         std::cerr << "Failed to set the attribute , RC : " << ret
235                   << "INTERFACE : " << std::hex << ifindex << std::endl;
236         return ret;
237     }
238 
239     if (function)
240     {
241         // Add a callback function to the socket
242         nl_socket_modify_cb(socket.get(), NL_CB_VALID, NL_CB_CUSTOM, function,
243                             nullptr);
244     }
245 
246     ret = nl_send_auto(socket.get(), msg.get());
247     if (ret < 0)
248     {
249         std::cerr << "Failed to send the message , RC : " << ret << std::endl;
250         return ret;
251     }
252 
253     ret = nl_recvmsgs_default(socket.get());
254     if (ret < 0)
255     {
256         std::cerr << "Failed to receive the message , RC : " << ret
257                   << std::endl;
258     }
259     return ret;
260 }
261 
262 } // namespace internal
263 
264 int setChannel(int ifindex, int package, int channel)
265 {
266     std::cout << "Set Channel : " << std::hex << channel
267               << ", PACKAGE : " << std::hex << package
268               << ", IFINDEX :  " << std::hex << ifindex << std::endl;
269     return internal::applyCmd(ifindex, ncsi_nl_commands::NCSI_CMD_SET_INTERFACE,
270                               package, channel);
271 }
272 
273 int clearInterface(int ifindex)
274 {
275     std::cout << "ClearInterface , IFINDEX :" << std::hex << ifindex
276               << std::endl;
277     return internal::applyCmd(ifindex,
278                               ncsi_nl_commands::NCSI_CMD_CLEAR_INTERFACE);
279 }
280 
281 int getInfo(int ifindex, int package)
282 {
283     std::cout << "Get Info , PACKAGE :  " << std::hex << package
284               << ", IFINDEX :  " << std::hex << ifindex << std::endl;
285     if (package == DEFAULT_VALUE)
286     {
287         return internal::applyCmd(ifindex, ncsi_nl_commands::NCSI_CMD_PKG_INFO,
288                                   package, DEFAULT_VALUE, NLM_F_DUMP,
289                                   internal::infoCallBack);
290     }
291     else
292     {
293         return internal::applyCmd(ifindex, ncsi_nl_commands::NCSI_CMD_PKG_INFO,
294                                   package, DEFAULT_VALUE, NONE,
295                                   internal::infoCallBack);
296     }
297 }
298 
299 } // namespace ncsi
300 } // namespace network
301 } // namespace phosphor
302