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