1 #include "ncsi_util.hpp" 2 3 #include <errno.h> 4 #include <fcntl.h> 5 #include <linux/mctp.h> 6 #include <linux/ncsi.h> 7 #include <netlink/genl/ctrl.h> 8 #include <netlink/genl/genl.h> 9 #include <netlink/netlink.h> 10 #include <unistd.h> 11 12 #include <phosphor-logging/lg2.hpp> 13 14 #include <optional> 15 #include <span> 16 #include <system_error> 17 #include <vector> 18 19 namespace phosphor 20 { 21 namespace network 22 { 23 namespace ncsi 24 { 25 26 static const char* mctp_iid_path = "/run/ncsi-mctp-iids"; 27 28 NCSICommand::NCSICommand(uint8_t opcode, uint8_t package, 29 std::optional<uint8_t> channel, 30 std::span<unsigned char> payload) : 31 opcode(opcode), package(package), channel(channel) 32 { 33 this->payload.assign(payload.begin(), payload.end()); 34 } 35 36 uint8_t NCSICommand::getChannel() 37 { 38 return channel.value_or(CHANNEL_ID_NONE); 39 } 40 41 using CallBack = int (*)(struct nl_msg* msg, void* arg); 42 43 namespace internal 44 { 45 46 struct NCSIPacketHeader 47 { 48 uint8_t MCID; 49 uint8_t revision; 50 uint8_t reserved; 51 uint8_t id; 52 uint8_t type; 53 uint8_t channel; 54 uint16_t length; 55 uint32_t rsvd[2]; 56 }; 57 58 struct NCSIResponsePayload 59 { 60 uint16_t response; 61 uint16_t reason; 62 }; 63 64 class NetlinkCommand 65 { 66 public: 67 NetlinkCommand() = delete; 68 ~NetlinkCommand() = default; 69 NetlinkCommand(const NetlinkCommand&) = delete; 70 NetlinkCommand& operator=(const NetlinkCommand&) = delete; 71 NetlinkCommand(NetlinkCommand&&) = default; 72 NetlinkCommand& operator=(NetlinkCommand&&) = default; 73 NetlinkCommand( 74 int ncsiCmd, int operation = DEFAULT_VALUE, 75 std::span<const unsigned char> p = std::span<const unsigned char>()) : 76 ncsi_cmd(ncsiCmd), operation(operation), payload(p) 77 {} 78 79 int ncsi_cmd; 80 int operation; 81 std::span<const unsigned char> payload; 82 }; 83 84 using nlMsgPtr = std::unique_ptr<nl_msg, decltype(&::nlmsg_free)>; 85 using nlSocketPtr = std::unique_ptr<nl_sock, decltype(&::nl_socket_free)>; 86 87 struct infoCallBackContext 88 { 89 InterfaceInfo* info; 90 }; 91 92 CallBack infoCallBack = [](struct nl_msg* msg, void* arg) { 93 if (arg == nullptr) 94 { 95 lg2::error("Internal error: invalid info callback context"); 96 return -1; 97 } 98 99 struct infoCallBackContext* info = (struct infoCallBackContext*)arg; 100 using namespace phosphor::network::ncsi; 101 auto nlh = nlmsg_hdr(msg); 102 103 struct nlattr* tb[NCSI_ATTR_MAX + 1] = {nullptr}; 104 struct nla_policy ncsiPolicy[NCSI_ATTR_MAX + 1] = { 105 {NLA_UNSPEC, 0, 0}, {NLA_U32, 0, 0}, {NLA_NESTED, 0, 0}, 106 {NLA_U32, 0, 0}, {NLA_U32, 0, 0}, 107 }; 108 109 struct nlattr* packagetb[NCSI_PKG_ATTR_MAX + 1] = {nullptr}; 110 struct nla_policy packagePolicy[NCSI_PKG_ATTR_MAX + 1] = { 111 {NLA_UNSPEC, 0, 0}, {NLA_NESTED, 0, 0}, {NLA_U32, 0, 0}, 112 {NLA_FLAG, 0, 0}, {NLA_NESTED, 0, 0}, 113 }; 114 115 struct nlattr* channeltb[NCSI_CHANNEL_ATTR_MAX + 1] = {nullptr}; 116 struct nla_policy channelPolicy[NCSI_CHANNEL_ATTR_MAX + 1] = { 117 {NLA_UNSPEC, 0, 0}, {NLA_NESTED, 0, 0}, {NLA_U32, 0, 0}, 118 {NLA_FLAG, 0, 0}, {NLA_NESTED, 0, 0}, {NLA_UNSPEC, 0, 0}, 119 }; 120 121 auto ret = genlmsg_parse(nlh, 0, tb, NCSI_ATTR_MAX, ncsiPolicy); 122 if (!tb[NCSI_ATTR_PACKAGE_LIST]) 123 { 124 lg2::error("No Packages"); 125 return -1; 126 } 127 128 auto attrTgt = static_cast<nlattr*>(nla_data(tb[NCSI_ATTR_PACKAGE_LIST])); 129 if (!attrTgt) 130 { 131 lg2::error("Package list attribute is null"); 132 return -1; 133 } 134 135 auto rem = nla_len(tb[NCSI_ATTR_PACKAGE_LIST]); 136 nla_for_each_nested(attrTgt, tb[NCSI_ATTR_PACKAGE_LIST], rem) 137 { 138 ret = nla_parse_nested(packagetb, NCSI_PKG_ATTR_MAX, attrTgt, 139 packagePolicy); 140 if (ret < 0) 141 { 142 lg2::error("Failed to parse package nested"); 143 return -1; 144 } 145 146 PackageInfo pkg; 147 148 if (packagetb[NCSI_PKG_ATTR_ID]) 149 { 150 auto attrID = nla_get_u32(packagetb[NCSI_PKG_ATTR_ID]); 151 pkg.id = attrID; 152 } 153 else 154 { 155 lg2::debug("Package with no id"); 156 } 157 158 if (packagetb[NCSI_PKG_ATTR_FORCED]) 159 { 160 pkg.forced = true; 161 } 162 163 auto channelListTarget = static_cast<nlattr*>( 164 nla_data(packagetb[NCSI_PKG_ATTR_CHANNEL_LIST])); 165 166 auto channelrem = nla_len(packagetb[NCSI_PKG_ATTR_CHANNEL_LIST]); 167 nla_for_each_nested(channelListTarget, 168 packagetb[NCSI_PKG_ATTR_CHANNEL_LIST], channelrem) 169 { 170 ret = nla_parse_nested(channeltb, NCSI_CHANNEL_ATTR_MAX, 171 channelListTarget, channelPolicy); 172 if (ret < 0) 173 { 174 lg2::error("Failed to parse channel nested"); 175 continue; 176 } 177 178 ChannelInfo chan; 179 180 if (channeltb[NCSI_CHANNEL_ATTR_ID]) 181 { 182 chan.id = nla_get_u32(channeltb[NCSI_CHANNEL_ATTR_ID]); 183 chan.active = !!channeltb[NCSI_CHANNEL_ATTR_ACTIVE]; 184 chan.forced = !!channeltb[NCSI_CHANNEL_ATTR_FORCED]; 185 } 186 else 187 { 188 lg2::debug("Channel with no ID"); 189 continue; 190 } 191 192 if (channeltb[NCSI_CHANNEL_ATTR_VERSION_MAJOR]) 193 { 194 chan.version_major = 195 nla_get_u32(channeltb[NCSI_CHANNEL_ATTR_VERSION_MAJOR]); 196 } 197 if (channeltb[NCSI_CHANNEL_ATTR_VERSION_MINOR]) 198 { 199 chan.version_minor = 200 nla_get_u32(channeltb[NCSI_CHANNEL_ATTR_VERSION_MINOR]); 201 } 202 if (channeltb[NCSI_CHANNEL_ATTR_VERSION_STR]) 203 { 204 chan.version = 205 nla_get_string(channeltb[NCSI_CHANNEL_ATTR_VERSION_STR]); 206 } 207 if (channeltb[NCSI_CHANNEL_ATTR_LINK_STATE]) 208 { 209 chan.link_state = 210 nla_get_u32(channeltb[NCSI_CHANNEL_ATTR_LINK_STATE]); 211 } 212 if (channeltb[NCSI_CHANNEL_ATTR_VLAN_LIST]) 213 { 214 auto vids = channeltb[NCSI_CHANNEL_ATTR_VLAN_LIST]; 215 auto vid = static_cast<nlattr*>(nla_data(vids)); 216 auto len = nla_len(vids); 217 while (nla_ok(vid, len)) 218 { 219 auto id = nla_get_u16(vid); 220 chan.vlan_ids.push_back(id); 221 vid = nla_next(vid, &len); 222 } 223 } 224 pkg.channels.push_back(chan); 225 } 226 227 info->info->packages.push_back(pkg); 228 } 229 return static_cast<int>(NL_STOP); 230 }; 231 232 struct sendCallBackContext 233 { 234 NCSIResponse resp; 235 }; 236 237 CallBack sendCallBack = [](struct nl_msg* msg, void* arg) { 238 using namespace phosphor::network::ncsi; 239 auto nlh = nlmsg_hdr(msg); 240 struct nlattr* tb[NCSI_ATTR_MAX + 1] = {nullptr}; 241 static struct nla_policy ncsiPolicy[NCSI_ATTR_MAX + 1] = { 242 {NLA_UNSPEC, 0, 0}, {NLA_U32, 0, 0}, {NLA_NESTED, 0, 0}, 243 {NLA_U32, 0, 0}, {NLA_U32, 0, 0}, {NLA_BINARY, 0, 0}, 244 {NLA_FLAG, 0, 0}, {NLA_U32, 0, 0}, {NLA_U32, 0, 0}, 245 }; 246 247 if (arg == nullptr) 248 { 249 lg2::error("Internal error: invalid send callback context"); 250 return -1; 251 } 252 253 struct sendCallBackContext* ctx = (struct sendCallBackContext*)arg; 254 255 auto ret = genlmsg_parse(nlh, 0, tb, NCSI_ATTR_MAX, ncsiPolicy); 256 if (ret) 257 { 258 lg2::error("Failed to parse message"); 259 return ret; 260 } 261 262 if (tb[NCSI_ATTR_DATA] == nullptr) 263 { 264 lg2::error("Response: No data"); 265 return -1; 266 } 267 268 size_t data_len = nla_len(tb[NCSI_ATTR_DATA]); 269 unsigned char* data = (unsigned char*)nla_data(tb[NCSI_ATTR_DATA]); 270 271 ctx->resp.full_payload.assign(data, data + data_len); 272 273 int rc = ctx->resp.parseFullPayload(); 274 if (rc) 275 { 276 return -1; 277 } 278 279 return static_cast<int>(NL_STOP); 280 }; 281 282 int applyCmd(NetlinkInterface& interface, const NetlinkCommand& cmd, 283 int package = DEFAULT_VALUE, int channel = DEFAULT_VALUE, 284 int flags = NONE, CallBack function = nullptr, void* arg = nullptr) 285 { 286 nlSocketPtr socket(nl_socket_alloc(), &::nl_socket_free); 287 if (socket == nullptr) 288 { 289 lg2::error("Unable to allocate memory for the socket"); 290 return -ENOMEM; 291 } 292 293 nl_socket_disable_auto_ack(socket.get()); 294 295 auto ret = genl_connect(socket.get()); 296 if (ret < 0) 297 { 298 lg2::error("Failed to open the socket , RC : {RC}", "RC", ret); 299 return ret; 300 } 301 302 auto driverID = genl_ctrl_resolve(socket.get(), "NCSI"); 303 if (driverID < 0) 304 { 305 lg2::error("Failed to resolve, RC : {RC}", "RC", ret); 306 return driverID; 307 } 308 309 nlMsgPtr msg(nlmsg_alloc(), &::nlmsg_free); 310 if (msg == nullptr) 311 { 312 lg2::error("Unable to allocate memory for the message"); 313 return -ENOMEM; 314 } 315 316 auto msgHdr = genlmsg_put(msg.get(), NL_AUTO_PORT, NL_AUTO_SEQ, driverID, 0, 317 flags, cmd.ncsi_cmd, 0); 318 if (!msgHdr) 319 { 320 lg2::error("Unable to add the netlink headers , COMMAND : {COMMAND}", 321 "COMMAND", cmd.ncsi_cmd); 322 return -ENOMEM; 323 } 324 325 if (package != DEFAULT_VALUE) 326 { 327 ret = nla_put_u32(msg.get(), ncsi_nl_attrs::NCSI_ATTR_PACKAGE_ID, 328 package); 329 if (ret < 0) 330 { 331 lg2::error("Failed to set the attribute , RC : {RC} PACKAGE " 332 "{PACKAGE}", 333 "RC", ret, "PACKAGE", lg2::hex, package); 334 return ret; 335 } 336 } 337 338 if (channel != DEFAULT_VALUE) 339 { 340 ret = nla_put_u32(msg.get(), ncsi_nl_attrs::NCSI_ATTR_CHANNEL_ID, 341 channel); 342 if (ret < 0) 343 { 344 lg2::error("Failed to set the attribute , RC : {RC} CHANNEL : " 345 "{CHANNEL}", 346 "RC", ret, "CHANNEL", lg2::hex, channel); 347 return ret; 348 } 349 } 350 351 ret = nla_put_u32(msg.get(), ncsi_nl_attrs::NCSI_ATTR_IFINDEX, 352 interface.ifindex); 353 if (ret < 0) 354 { 355 lg2::error("Failed to set the attribute , RC : {RC} INTERFACE : " 356 "{INTERFACE}", 357 "RC", ret, "INTERFACE", interface); 358 return ret; 359 } 360 361 if ((cmd.ncsi_cmd == ncsi_nl_commands::NCSI_CMD_SET_PACKAGE_MASK) || 362 (cmd.ncsi_cmd == ncsi_nl_commands::NCSI_CMD_SET_CHANNEL_MASK)) 363 { 364 if (cmd.payload.size() != sizeof(unsigned int)) 365 { 366 lg2::error("Package/Channel mask must be 32-bits"); 367 return -EINVAL; 368 } 369 int maskAttr = 370 cmd.ncsi_cmd == ncsi_nl_commands::NCSI_CMD_SET_PACKAGE_MASK 371 ? NCSI_ATTR_PACKAGE_MASK 372 : NCSI_ATTR_CHANNEL_MASK; 373 ret = nla_put_u32( 374 msg.get(), maskAttr, 375 *(reinterpret_cast<const unsigned int*>(cmd.payload.data()))); 376 if (ret < 0) 377 { 378 lg2::error("Failed to set the mask attribute, RC : {RC}", "RC", 379 ret); 380 return ret; 381 } 382 } 383 else if (cmd.ncsi_cmd == ncsi_nl_commands::NCSI_CMD_SEND_CMD) 384 { 385 std::vector<unsigned char> pl( 386 sizeof(NCSIPacketHeader) + cmd.payload.size()); 387 NCSIPacketHeader* hdr = (NCSIPacketHeader*)pl.data(); 388 389 std::copy(cmd.payload.begin(), cmd.payload.end(), 390 pl.begin() + sizeof(NCSIPacketHeader)); 391 392 hdr->type = cmd.operation; 393 hdr->length = htons(cmd.payload.size()); 394 395 ret = nla_put(msg.get(), ncsi_nl_attrs::NCSI_ATTR_DATA, pl.size(), 396 pl.data()); 397 if (ret < 0) 398 { 399 lg2::error("Failed to set the data attribute, RC : {RC}", "RC", 400 ret); 401 return ret; 402 } 403 404 nl_socket_disable_seq_check(socket.get()); 405 } 406 407 // Add a callback function to the socket 408 enum nl_cb_kind cb_kind = function ? NL_CB_CUSTOM : NL_CB_DEFAULT; 409 nl_socket_modify_cb(socket.get(), NL_CB_VALID, cb_kind, function, arg); 410 411 ret = nl_send_auto(socket.get(), msg.get()); 412 if (ret < 0) 413 { 414 lg2::error("Failed to send the message , RC : {RC}", "RC", ret); 415 return ret; 416 } 417 418 ret = nl_recvmsgs_default(socket.get()); 419 if (ret < 0) 420 { 421 lg2::error("Failed to receive the message , RC : {RC}", "RC", ret); 422 return ret; 423 } 424 425 return 0; 426 } 427 428 } // namespace internal 429 430 std::string to_string(Interface& interface) 431 { 432 return interface.toString(); 433 } 434 435 NetlinkInterface::NetlinkInterface(int ifindex) : ifindex(ifindex) {} 436 437 std::string NetlinkInterface::toString() 438 { 439 return std::to_string(ifindex); 440 } 441 442 std::optional<NCSIResponse> NetlinkInterface::sendCommand(NCSICommand& cmd) 443 { 444 lg2::debug("Send Command, CHANNEL : {CHANNEL} , PACKAGE : {PACKAGE}, " 445 "INTERFACE: {INTERFACE}", 446 "CHANNEL", lg2::hex, cmd.getChannel(), "PACKAGE", lg2::hex, 447 cmd.package, "INTERFACE", this); 448 449 internal::sendCallBackContext ctx{}; 450 451 internal::NetlinkCommand nl_cmd(ncsi_nl_commands::NCSI_CMD_SEND_CMD, 452 cmd.opcode, cmd.payload); 453 454 int rc = internal::applyCmd(*this, nl_cmd, cmd.package, cmd.getChannel(), 455 NONE, internal::sendCallBack, &ctx); 456 457 if (rc < 0) 458 { 459 return {}; 460 } 461 462 return ctx.resp; 463 } 464 465 int NetlinkInterface::setChannel(int package, int channel) 466 { 467 lg2::debug("Set CHANNEL : {CHANNEL} , PACKAGE : {PACKAGE}, INTERFACE : " 468 "{INTERFACE}", 469 "CHANNEL", lg2::hex, channel, "PACKAGE", lg2::hex, package, 470 "INTERFACE", this); 471 472 internal::NetlinkCommand cmd(ncsi_nl_commands::NCSI_CMD_SET_INTERFACE); 473 474 return internal::applyCmd(*this, cmd, package, channel); 475 } 476 477 int NetlinkInterface::clearInterface() 478 { 479 lg2::debug("ClearInterface , INTERFACE : {INTERFACE}", "INTERFACE", this); 480 481 internal::NetlinkCommand cmd(ncsi_nl_commands::NCSI_CMD_CLEAR_INTERFACE); 482 return internal::applyCmd(*this, cmd); 483 } 484 485 std::optional<InterfaceInfo> NetlinkInterface::getInfo(int package) 486 { 487 int rc, flags = package == DEFAULT_VALUE ? NLM_F_DUMP : NONE; 488 InterfaceInfo info; 489 490 lg2::debug("Get Info , PACKAGE : {PACKAGE}, INTERFACE: {INTERFACE}", 491 "PACKAGE", lg2::hex, package, "INTERFACE", this); 492 493 struct internal::infoCallBackContext ctx = { 494 .info = &info, 495 }; 496 497 internal::NetlinkCommand cmd(ncsi_nl_commands::NCSI_CMD_PKG_INFO); 498 499 rc = internal::applyCmd(*this, cmd, package, DEFAULT_VALUE, flags, 500 internal::infoCallBack, &ctx); 501 502 if (rc < 0) 503 { 504 return {}; 505 } 506 507 return info; 508 } 509 510 int NetlinkInterface::setPackageMask(unsigned int mask) 511 { 512 lg2::debug("Set Package Mask , INTERFACE: {INTERFACE} MASK: {MASK}", 513 "INTERFACE", this, "MASK", lg2::hex, mask); 514 auto payload = std::span<const unsigned char>( 515 reinterpret_cast<const unsigned char*>(&mask), 516 reinterpret_cast<const unsigned char*>(&mask) + sizeof(decltype(mask))); 517 518 internal::NetlinkCommand cmd(ncsi_nl_commands::NCSI_CMD_SET_PACKAGE_MASK, 0, 519 payload); 520 return internal::applyCmd(*this, cmd); 521 } 522 523 int NetlinkInterface::setChannelMask(int package, unsigned int mask) 524 { 525 lg2::debug( 526 "Set Channel Mask , INTERFACE: {INTERFACE}, PACKAGE : {PACKAGE} MASK: {MASK}", 527 "INTERFACE", this, "PACKAGE", lg2::hex, package, "MASK", lg2::hex, 528 mask); 529 auto payload = std::span<const unsigned char>( 530 reinterpret_cast<const unsigned char*>(&mask), 531 reinterpret_cast<const unsigned char*>(&mask) + sizeof(decltype(mask))); 532 533 internal::NetlinkCommand cmd(ncsi_nl_commands::NCSI_CMD_SET_CHANNEL_MASK, 0, 534 payload); 535 return internal::applyCmd(*this, cmd); 536 } 537 538 int NCSIResponse::parseFullPayload() 539 { 540 if (this->full_payload.size() < sizeof(internal::NCSIPacketHeader) + 541 sizeof(internal::NCSIResponsePayload)) 542 { 543 lg2::error("Response: Not enough data for a response message"); 544 return -1; 545 } 546 547 internal::NCSIPacketHeader* respHeader = 548 reinterpret_cast<decltype(respHeader)>(this->full_payload.data()); 549 550 unsigned int payloadLen = ntohs(respHeader->length & htons(0x0fff)); 551 /* we have determined that the payload size is larger than *respHeader, 552 * so cannot underflow here */ 553 if (payloadLen > this->full_payload.size() - sizeof(*respHeader)) 554 { 555 lg2::error("Invalid header length {HDRLEN} (vs {LEN}) in response", 556 "HDRLEN", payloadLen, "LEN", 557 this->full_payload.size() - sizeof(*respHeader)); 558 return -1; 559 } 560 561 this->opcode = respHeader->type; 562 this->payload = 563 std::span(this->full_payload.begin() + sizeof(*respHeader), payloadLen); 564 565 internal::NCSIResponsePayload* respPayload = 566 reinterpret_cast<decltype(respPayload)>(this->payload.data()); 567 this->response = ntohs(respPayload->response); 568 this->reason = ntohs(respPayload->reason); 569 570 return 0; 571 } 572 573 static const uint8_t MCTP_TYPE_NCSI = 2; 574 575 struct NCSIResponsePayload 576 { 577 uint16_t response; 578 uint16_t reason; 579 }; 580 581 std::optional<NCSIResponse> MCTPInterface::sendCommand(NCSICommand& cmd) 582 { 583 static constexpr uint8_t mcid = 0; /* no need to distinguish controllers */ 584 static constexpr size_t maxRespLen = 16384; 585 size_t payloadLen, padLen; 586 ssize_t wlen, rlen; 587 588 payloadLen = cmd.payload.size(); 589 590 auto tmp = allocateIID(); 591 if (!tmp.has_value()) 592 { 593 return {}; 594 } 595 uint8_t iid = *tmp; 596 597 internal::NCSIPacketHeader cmdHeader{}; 598 cmdHeader.MCID = mcid; 599 cmdHeader.revision = 1; 600 cmdHeader.id = iid; 601 cmdHeader.type = cmd.opcode; 602 cmdHeader.channel = (uint8_t)(cmd.package << 5 | cmd.getChannel()); 603 cmdHeader.length = htons(payloadLen); 604 605 struct iovec iov[3]; 606 iov[0].iov_base = &cmdHeader; 607 iov[0].iov_len = sizeof(cmdHeader); 608 iov[1].iov_base = cmd.payload.data(); 609 iov[1].iov_len = payloadLen; 610 611 /* the checksum must appear on a 4-byte boundary */ 612 padLen = 4 - (payloadLen & 0x3); 613 if (padLen == 4) 614 { 615 padLen = 0; 616 } 617 uint8_t crc32buf[8] = {}; 618 /* todo: set csum; zeros currently indicate no checksum present */ 619 uint32_t crc32 = 0; 620 621 memcpy(crc32buf + padLen, &crc32, sizeof(crc32)); 622 padLen += sizeof(crc32); 623 624 iov[2].iov_base = crc32buf; 625 iov[2].iov_len = padLen; 626 627 struct sockaddr_mctp addr = {}; 628 addr.smctp_family = AF_MCTP; 629 addr.smctp_network = net; 630 addr.smctp_addr.s_addr = eid; 631 addr.smctp_tag = MCTP_TAG_OWNER; 632 addr.smctp_type = MCTP_TYPE_NCSI; 633 634 struct msghdr msg = {}; 635 msg.msg_name = &addr; 636 msg.msg_namelen = sizeof(addr); 637 msg.msg_iov = iov; 638 msg.msg_iovlen = 3; 639 640 wlen = sendmsg(sd, &msg, 0); 641 if (wlen < 0) 642 { 643 lg2::error("Failed to send MCTP message, ERRNO: {ERRNO}", "ERRNO", 644 -wlen); 645 return {}; 646 } 647 else if ((size_t)wlen != sizeof(cmdHeader) + payloadLen + padLen) 648 { 649 lg2::error("Short write sending MCTP message, LEN: {LEN}", "LEN", wlen); 650 return {}; 651 } 652 653 internal::NCSIPacketHeader* respHeader; 654 NCSIResponsePayload* respPayload; 655 NCSIResponse resp{}; 656 657 resp.full_payload.resize(maxRespLen); 658 iov[0].iov_len = resp.full_payload.size(); 659 iov[0].iov_base = resp.full_payload.data(); 660 661 msg.msg_name = &addr; 662 msg.msg_namelen = sizeof(addr); 663 msg.msg_iov = iov; 664 msg.msg_iovlen = 1; 665 666 /* we have set SO_RCVTIMEO, so this won't block forever... */ 667 rlen = recvmsg(sd, &msg, MSG_TRUNC); 668 if (rlen < 0) 669 { 670 lg2::error("Failed to read MCTP response, ERRNO: {ERRNO}", "ERRNO", 671 -rlen); 672 return {}; 673 } 674 else if ((size_t)rlen < sizeof(*respHeader) + sizeof(*respPayload)) 675 { 676 lg2::error("Short read receiving MCTP message, LEN: {LEN}", "LEN", 677 rlen); 678 return {}; 679 } 680 else if ((size_t)rlen > maxRespLen) 681 { 682 lg2::error("MCTP response is too large, LEN: {LEN}", "LEN", rlen); 683 return {}; 684 } 685 686 resp.full_payload.resize(rlen); 687 688 respHeader = 689 reinterpret_cast<decltype(respHeader)>(resp.full_payload.data()); 690 691 /* header validation */ 692 if (respHeader->MCID != mcid) 693 { 694 lg2::error("Invalid MCID {MCID} in response", "MCID", lg2::hex, 695 respHeader->MCID); 696 return {}; 697 } 698 699 if (respHeader->id != iid) 700 { 701 lg2::error("Invalid IID {IID} in response", "IID", lg2::hex, 702 respHeader->id); 703 return {}; 704 } 705 706 if (respHeader->type != (cmd.opcode | 0x80)) 707 { 708 lg2::error("Invalid opcode {OPCODE} in response", "OPCODE", lg2::hex, 709 respHeader->type); 710 return {}; 711 } 712 713 int rc = resp.parseFullPayload(); 714 if (rc) 715 { 716 return {}; 717 } 718 719 return resp; 720 } 721 722 std::string MCTPInterface::toString() 723 { 724 return std::to_string(net) + "," + std::to_string(eid); 725 } 726 727 MCTPInterface::MCTPInterface(int net, uint8_t eid) : net(net), eid(eid) 728 { 729 static const struct timeval receiveTimeout = { 730 .tv_sec = 1, 731 .tv_usec = 0, 732 }; 733 734 int _sd = socket(AF_MCTP, SOCK_DGRAM, 0); 735 if (_sd < 0) 736 { 737 throw std::system_error(errno, std::system_category(), 738 "Can't create MCTP socket"); 739 } 740 741 int rc = setsockopt(_sd, SOL_SOCKET, SO_RCVTIMEO, &receiveTimeout, 742 sizeof(receiveTimeout)); 743 if (rc != 0) 744 { 745 throw std::system_error(errno, std::system_category(), 746 "Can't set socket receive timemout"); 747 } 748 749 sd = _sd; 750 } 751 752 MCTPInterface::~MCTPInterface() 753 { 754 close(sd); 755 } 756 757 /* Small fd wrapper to provide RAII semantics, closing the IID file descriptor 758 * when we go out of scope. 759 */ 760 struct IidFd 761 { 762 int fd; 763 IidFd(int _fd) : fd(_fd) {}; 764 ~IidFd() 765 { 766 close(fd); 767 }; 768 }; 769 770 std::optional<uint8_t> MCTPInterface::allocateIID() 771 { 772 int fd = open(mctp_iid_path, O_RDWR | O_CREAT, 0600); 773 if (fd < 0) 774 { 775 lg2::warning("Error opening IID database {FILE}: {ERROR}", "FILE", 776 mctp_iid_path, "ERROR", strerror(errno)); 777 return {}; 778 } 779 780 IidFd iidFd(fd); 781 782 /* lock while we read/modity/write; the lock will be short-lived, so 783 * we keep it simple and lock the entire file range 784 */ 785 struct flock flock = { 786 .l_type = F_WRLCK, 787 .l_whence = SEEK_SET, 788 .l_start = 0, 789 .l_len = 0, 790 .l_pid = 0, 791 }; 792 793 int rc = fcntl(iidFd.fd, F_OFD_SETLKW, &flock); 794 if (rc) 795 { 796 lg2::warning("Error locking IID database {FILE}: {ERROR}", "FILE", 797 mctp_iid_path, "ERROR", strerror(errno)); 798 return {}; 799 } 800 801 /* An EOF (rc == 0) would indicate that we don't yet have an entry for that 802 * eid, which we handle as iid = 0. 803 */ 804 uint8_t iid = 0; 805 rc = pread(iidFd.fd, &iid, sizeof(iid), eid); 806 if (rc < 0) 807 { 808 lg2::warning("Error reading IID database {FILE}: {ERROR}", "FILE", 809 mctp_iid_path, "ERROR", strerror(errno)); 810 return {}; 811 } 812 813 /* DSP0222 defines valid IIDs in the range [1, 0xff], so manually wrap */ 814 if (iid == 0xff) 815 { 816 iid = 1; 817 } 818 else 819 { 820 iid++; 821 } 822 823 rc = pwrite(iidFd.fd, &iid, sizeof(iid), eid); 824 if (rc != sizeof(iid)) 825 { 826 lg2::warning("Error writing IID database {FILE}: {ERROR}", "FILE", 827 mctp_iid_path, "ERROR", strerror(errno)); 828 return {}; 829 } 830 831 return iid; 832 } 833 834 } // namespace ncsi 835 } // namespace network 836 } // namespace phosphor 837