1 #include "transporthandler.hpp" 2 3 #include <ipmid/utils.hpp> 4 #include <phosphor-logging/lg2.hpp> 5 #include <stdplus/net/addr/subnet.hpp> 6 #include <stdplus/raw.hpp> 7 8 #include <array> 9 #include <fstream> 10 11 using phosphor::logging::elog; 12 using sdbusplus::error::xyz::openbmc_project::common::InternalFailure; 13 using sdbusplus::error::xyz::openbmc_project::common::InvalidArgument; 14 using sdbusplus::server::xyz::openbmc_project::network::EthernetInterface; 15 using sdbusplus::server::xyz::openbmc_project::network::IP; 16 using sdbusplus::server::xyz::openbmc_project::network::Neighbor; 17 18 namespace cipher 19 { 20 21 std::vector<uint8_t> getCipherList() 22 { 23 std::vector<uint8_t> cipherList; 24 25 std::ifstream jsonFile(cipher::configFile); 26 if (!jsonFile.is_open()) 27 { 28 lg2::error("Channel Cipher suites file not found"); 29 elog<InternalFailure>(); 30 } 31 32 auto data = Json::parse(jsonFile, nullptr, false); 33 if (data.is_discarded()) 34 { 35 lg2::error("Parsing channel cipher suites JSON failed"); 36 elog<InternalFailure>(); 37 } 38 39 // Byte 1 is reserved 40 cipherList.push_back(0x00); 41 42 for (const auto& record : data) 43 { 44 cipherList.push_back(record.value(cipher, 0)); 45 } 46 47 return cipherList; 48 } 49 } // namespace cipher 50 51 namespace ipmi 52 { 53 namespace transport 54 { 55 56 /** @brief Valid address origins for IPv4 */ 57 const std::unordered_set<IP::AddressOrigin> originsV4 = { 58 IP::AddressOrigin::Static, 59 IP::AddressOrigin::DHCP, 60 }; 61 62 static constexpr uint8_t oemCmdStart = 192; 63 64 // Checks if the ifname is part of the networkd path 65 // This assumes the path came from the network subtree PATH_ROOT 66 bool ifnameInPath(std::string_view ifname, std::string_view path) 67 { 68 constexpr auto rs = PATH_ROOT.size() + 1; // ROOT + separator 69 const auto is = rs + ifname.size(); // ROOT + sep + ifname 70 return path.size() > rs && path.substr(rs).starts_with(ifname) && 71 (path.size() == is || path[is] == '/'); 72 } 73 74 std::optional<ChannelParams> 75 maybeGetChannelParams(sdbusplus::bus_t& bus, uint8_t channel) 76 { 77 auto ifname = getChannelName(channel); 78 if (ifname.empty()) 79 { 80 return std::nullopt; 81 } 82 83 // Enumerate all VLAN + ETHERNET interfaces 84 std::vector<std::string> interfaces = {INTF_VLAN, INTF_ETHERNET}; 85 ipmi::ObjectTree objs = 86 ipmi::getSubTree(bus, interfaces, std::string{PATH_ROOT}); 87 88 ChannelParams params; 89 for (const auto& [path, impls] : objs) 90 { 91 if (!ifnameInPath(ifname, path)) 92 { 93 continue; 94 } 95 for (const auto& [service, intfs] : impls) 96 { 97 bool vlan = false; 98 bool ethernet = false; 99 for (const auto& intf : intfs) 100 { 101 if (intf == INTF_VLAN) 102 { 103 vlan = true; 104 } 105 else if (intf == INTF_ETHERNET) 106 { 107 ethernet = true; 108 } 109 } 110 if (params.service.empty() && (vlan || ethernet)) 111 { 112 params.service = service; 113 } 114 if (params.ifPath.empty() && !vlan && ethernet) 115 { 116 params.ifPath = path; 117 } 118 if (params.logicalPath.empty() && vlan) 119 { 120 params.logicalPath = path; 121 } 122 } 123 } 124 125 // We must have a path for the underlying interface 126 if (params.ifPath.empty()) 127 { 128 return std::nullopt; 129 } 130 // We don't have a VLAN so the logical path is the same 131 if (params.logicalPath.empty()) 132 { 133 params.logicalPath = params.ifPath; 134 } 135 136 params.id = channel; 137 params.ifname = std::move(ifname); 138 return params; 139 } 140 141 ChannelParams getChannelParams(sdbusplus::bus_t& bus, uint8_t channel) 142 { 143 auto params = maybeGetChannelParams(bus, channel); 144 if (!params) 145 { 146 lg2::error("Failed to get channel params: {CHANNEL}", "CHANNEL", 147 channel); 148 elog<InternalFailure>(); 149 } 150 return std::move(*params); 151 } 152 153 /** @brief Get / Set the Property value from phosphor-networkd EthernetInterface 154 */ 155 template <typename T> 156 static T getEthProp(sdbusplus::bus_t& bus, const ChannelParams& params, 157 const std::string& prop) 158 { 159 return std::get<T>(getDbusProperty(bus, params.service, params.logicalPath, 160 INTF_ETHERNET, prop)); 161 } 162 template <typename T> 163 static void setEthProp(sdbusplus::bus_t& bus, const ChannelParams& params, 164 const std::string& prop, const T& t) 165 { 166 return setDbusProperty(bus, params.service, params.logicalPath, 167 INTF_ETHERNET, prop, t); 168 } 169 170 /** @brief Determines the MAC of the ethernet interface 171 * 172 * @param[in] bus - The bus object used for lookups 173 * @param[in] params - The parameters for the channel 174 * @return The configured mac address 175 */ 176 stdplus::EtherAddr getMACProperty(sdbusplus::bus_t& bus, 177 const ChannelParams& params) 178 { 179 auto prop = getDbusProperty(bus, params.service, params.ifPath, INTF_MAC, 180 "MACAddress"); 181 return stdplus::fromStr<stdplus::EtherAddr>(std::get<std::string>(prop)); 182 } 183 184 /** @brief Sets the system value for MAC address on the given interface 185 * 186 * @param[in] bus - The bus object used for lookups 187 * @param[in] params - The parameters for the channel 188 * @param[in] mac - MAC address to apply 189 */ 190 void setMACProperty(sdbusplus::bus_t& bus, const ChannelParams& params, 191 stdplus::EtherAddr mac) 192 { 193 setDbusProperty(bus, params.service, params.ifPath, INTF_MAC, "MACAddress", 194 stdplus::toStr(mac)); 195 } 196 197 void deleteObjectIfExists(sdbusplus::bus_t& bus, const std::string& service, 198 const std::string& path) 199 { 200 if (path.empty()) 201 { 202 return; 203 } 204 try 205 { 206 auto req = bus.new_method_call(service.c_str(), path.c_str(), 207 ipmi::DELETE_INTERFACE, "Delete"); 208 bus.call_noreply(req); 209 } 210 catch (const sdbusplus::exception_t& e) 211 { 212 if (strcmp(e.name(), 213 "xyz.openbmc_project.Common.Error.InternalFailure") != 0 && 214 strcmp(e.name(), "org.freedesktop.DBus.Error.UnknownObject") != 0) 215 { 216 // We want to rethrow real errors 217 throw; 218 } 219 } 220 } 221 222 /** @brief Sets the address info configured for the interface 223 * If a previous address path exists then it will be removed 224 * before the new address is added. 225 * 226 * @param[in] bus - The bus object used for lookups 227 * @param[in] params - The parameters for the channel 228 * @param[in] address - The address of the new IP 229 * @param[in] prefix - The prefix of the new IP 230 */ 231 template <int family> 232 void createIfAddr(sdbusplus::bus_t& bus, const ChannelParams& params, 233 typename AddrFamily<family>::addr address, uint8_t prefix) 234 { 235 auto newreq = 236 bus.new_method_call(params.service.c_str(), params.logicalPath.c_str(), 237 INTF_IP_CREATE, "IP"); 238 std::string protocol = 239 sdbusplus::common::xyz::openbmc_project::network::convertForMessage( 240 AddrFamily<family>::protocol); 241 stdplus::ToStrHandle<stdplus::ToStr<typename AddrFamily<family>::addr>> tsh; 242 newreq.append(protocol, tsh(address), prefix, ""); 243 bus.call_noreply(newreq); 244 } 245 246 /** @brief Trivial helper for getting the IPv4 address from getIfAddrs() 247 * 248 * @param[in] bus - The bus object used for lookups 249 * @param[in] params - The parameters for the channel 250 * @return The address and prefix if found 251 */ 252 auto getIfAddr4(sdbusplus::bus_t& bus, const ChannelParams& params) 253 { 254 std::optional<IfAddr<AF_INET>> ifaddr4 = std::nullopt; 255 IP::AddressOrigin src; 256 257 try 258 { 259 src = std::get<bool>( 260 getDbusProperty(bus, params.service, params.logicalPath, 261 INTF_ETHERNET, "DHCP4")) 262 ? IP::AddressOrigin::DHCP 263 : IP::AddressOrigin::Static; 264 } 265 catch (const sdbusplus::exception_t& e) 266 { 267 lg2::error("Failed to get IPv4 source"); 268 return ifaddr4; 269 } 270 271 for (uint8_t i = 0; i < MAX_IPV4_ADDRESSES; ++i) 272 { 273 ifaddr4 = getIfAddr<AF_INET>(bus, params, i, originsV4); 274 if (src == ifaddr4->origin) 275 { 276 break; 277 } 278 else 279 { 280 ifaddr4 = std::nullopt; 281 } 282 } 283 return ifaddr4; 284 } 285 286 /** @brief Reconfigures the IPv4 address info configured for the interface 287 * 288 * @param[in] bus - The bus object used for lookups 289 * @param[in] params - The parameters for the channel 290 * @param[in] address - The new address if specified 291 * @param[in] prefix - The new address prefix if specified 292 */ 293 void reconfigureIfAddr4(sdbusplus::bus_t& bus, const ChannelParams& params, 294 std::optional<stdplus::In4Addr> address, 295 std::optional<uint8_t> prefix) 296 { 297 auto ifaddr = getIfAddr4(bus, params); 298 if (!ifaddr && !address) 299 { 300 lg2::error("Missing address for IPv4 assignment"); 301 elog<InternalFailure>(); 302 } 303 uint8_t fallbackPrefix = AddrFamily<AF_INET>::defaultPrefix; 304 if (ifaddr) 305 { 306 fallbackPrefix = ifaddr->prefix; 307 deleteObjectIfExists(bus, params.service, ifaddr->path); 308 } 309 auto addr = address.value_or(ifaddr->address); 310 if (addr != stdplus::In4Addr{}) 311 { 312 createIfAddr<AF_INET>(bus, params, addr, 313 prefix.value_or(fallbackPrefix)); 314 } 315 } 316 317 template <int family> 318 std::optional<IfNeigh<family>> 319 findGatewayNeighbor(sdbusplus::bus_t& bus, const ChannelParams& params, 320 ObjectLookupCache& neighbors) 321 { 322 auto gateway = getGatewayProperty<family>(bus, params); 323 if (!gateway) 324 { 325 return std::nullopt; 326 } 327 328 return findStaticNeighbor<family>(bus, params, *gateway, neighbors); 329 } 330 331 template <int family> 332 std::optional<IfNeigh<family>> 333 getGatewayNeighbor(sdbusplus::bus_t& bus, const ChannelParams& params) 334 { 335 ObjectLookupCache neighbors(bus, params, INTF_NEIGHBOR); 336 return findGatewayNeighbor<family>(bus, params, neighbors); 337 } 338 339 template <int family> 340 void reconfigureGatewayMAC(sdbusplus::bus_t& bus, const ChannelParams& params, 341 stdplus::EtherAddr mac) 342 { 343 auto gateway = getGatewayProperty<family>(bus, params); 344 if (!gateway) 345 { 346 lg2::error("Tried to set Gateway MAC without Gateway"); 347 elog<InternalFailure>(); 348 } 349 350 ObjectLookupCache neighbors(bus, params, INTF_NEIGHBOR); 351 auto neighbor = 352 findStaticNeighbor<family>(bus, params, *gateway, neighbors); 353 if (neighbor) 354 { 355 deleteObjectIfExists(bus, params.service, neighbor->path); 356 } 357 358 createNeighbor<family>(bus, params, *gateway, mac); 359 } 360 361 /** @brief Deconfigures the IPv6 address info configured for the interface 362 * 363 * @param[in] bus - The bus object used for lookups 364 * @param[in] params - The parameters for the channel 365 * @param[in] idx - The address index to operate on 366 */ 367 void deconfigureIfAddr6(sdbusplus::bus_t& bus, const ChannelParams& params, 368 uint8_t idx) 369 { 370 auto ifaddr = getIfAddr<AF_INET6>(bus, params, idx, originsV6Static); 371 if (ifaddr) 372 { 373 deleteObjectIfExists(bus, params.service, ifaddr->path); 374 } 375 } 376 377 /** @brief Reconfigures the IPv6 address info configured for the interface 378 * 379 * @param[in] bus - The bus object used for lookups 380 * @param[in] params - The parameters for the channel 381 * @param[in] idx - The address index to operate on 382 * @param[in] address - The new address 383 * @param[in] prefix - The new address prefix 384 */ 385 void reconfigureIfAddr6(sdbusplus::bus_t& bus, const ChannelParams& params, 386 uint8_t idx, stdplus::In6Addr address, uint8_t prefix) 387 { 388 deconfigureIfAddr6(bus, params, idx); 389 createIfAddr<AF_INET6>(bus, params, address, prefix); 390 } 391 392 /** @brief Converts the AddressOrigin into an IPv6Source 393 * 394 * @param[in] origin - The DBus Address Origin to convert 395 * @return The IPv6Source version of the origin 396 */ 397 IPv6Source originToSourceType(IP::AddressOrigin origin) 398 { 399 switch (origin) 400 { 401 case IP::AddressOrigin::Static: 402 return IPv6Source::Static; 403 case IP::AddressOrigin::DHCP: 404 return IPv6Source::DHCP; 405 case IP::AddressOrigin::SLAAC: 406 return IPv6Source::SLAAC; 407 default: 408 { 409 auto originStr = sdbusplus::common::xyz::openbmc_project::network:: 410 convertForMessage(origin); 411 lg2::error("Invalid IP::AddressOrigin conversion to IPv6Source, " 412 "origin: {ORIGIN}", 413 "ORIGIN", originStr); 414 elog<InternalFailure>(); 415 } 416 } 417 } 418 419 /** @brief Packs the IPMI message response with IPv6 address data 420 * 421 * @param[out] ret - The IPMI response payload to be packed 422 * @param[in] channel - The channel id corresponding to an ethernet interface 423 * @param[in] set - The set selector for determining address index 424 * @param[in] origins - Set of valid origins for address filtering 425 */ 426 void getLanIPv6Address(message::Payload& ret, uint8_t channel, uint8_t set, 427 const std::unordered_set<IP::AddressOrigin>& origins) 428 { 429 auto source = IPv6Source::Static; 430 bool enabled = false; 431 stdplus::In6Addr addr{}; 432 uint8_t prefix{}; 433 auto status = IPv6AddressStatus::Disabled; 434 435 auto ifaddr = channelCall<getIfAddr<AF_INET6>>(channel, set, origins); 436 if (ifaddr) 437 { 438 source = originToSourceType(ifaddr->origin); 439 enabled = (origins == originsV6Static); 440 addr = ifaddr->address; 441 prefix = ifaddr->prefix; 442 status = IPv6AddressStatus::Active; 443 } 444 445 ret.pack(set); 446 ret.pack(types::enum_cast<uint4_t>(source), uint3_t{}, enabled); 447 ret.pack(stdplus::raw::asView<char>(addr)); 448 ret.pack(prefix); 449 ret.pack(types::enum_cast<uint8_t>(status)); 450 } 451 452 /** @brief Gets the vlan ID configured on the interface 453 * 454 * @param[in] bus - The bus object used for lookups 455 * @param[in] params - The parameters for the channel 456 * @return VLAN id or the standard 0 for no VLAN 457 */ 458 uint16_t getVLANProperty(sdbusplus::bus_t& bus, const ChannelParams& params) 459 { 460 // VLAN devices will always have a separate logical object 461 if (params.ifPath == params.logicalPath) 462 { 463 return 0; 464 } 465 466 auto vlan = std::get<uint32_t>(getDbusProperty( 467 bus, params.service, params.logicalPath, INTF_VLAN, "Id")); 468 if ((vlan & VLAN_VALUE_MASK) != vlan) 469 { 470 lg2::error("networkd returned an invalid vlan: {VLAN} " 471 "(CH={CHANNEL}, IF={IFNAME})", 472 "CHANNEL", params.id, "IFNAME", params.ifname, "VLAN", vlan); 473 elog<InternalFailure>(); 474 } 475 return vlan; 476 } 477 478 /** @brief Deletes all of the possible configuration parameters for a channel 479 * 480 * @param[in] bus - The bus object used for lookups 481 * @param[in] params - The parameters for the channel 482 */ 483 void deconfigureChannel(sdbusplus::bus_t& bus, ChannelParams& params) 484 { 485 // Delete all objects associated with the interface 486 ObjectTree objs = 487 ipmi::getSubTree(bus, std::vector<std::string>{DELETE_INTERFACE}, 488 std::string{PATH_ROOT}); 489 for (const auto& [path, impls] : objs) 490 { 491 if (!ifnameInPath(params.ifname, path)) 492 { 493 continue; 494 } 495 for (const auto& [service, intfs] : impls) 496 { 497 deleteObjectIfExists(bus, service, path); 498 } 499 // Update params to reflect the deletion of vlan 500 if (path == params.logicalPath) 501 { 502 params.logicalPath = params.ifPath; 503 } 504 } 505 506 // Clear out any settings on the lower physical interface 507 setEthProp(bus, params, "DHCP4", false); 508 setEthProp(bus, params, "DHCP6", false); 509 setEthProp(bus, params, "IPv6AcceptRA", false); 510 } 511 512 /** @brief Creates a new VLAN on the specified interface 513 * 514 * @param[in] bus - The bus object used for lookups 515 * @param[in] params - The parameters for the channel 516 * @param[in] vlan - The id of the new vlan 517 */ 518 void createVLAN(sdbusplus::bus_t& bus, ChannelParams& params, uint16_t vlan) 519 { 520 if (vlan == 0) 521 { 522 return; 523 } 524 525 auto req = bus.new_method_call(params.service.c_str(), PATH_ROOT.c_str(), 526 INTF_VLAN_CREATE, "VLAN"); 527 req.append(params.ifname, static_cast<uint32_t>(vlan)); 528 auto reply = bus.call(req); 529 sdbusplus::message::object_path newPath; 530 reply.read(newPath); 531 params.logicalPath = std::move(newPath); 532 } 533 534 /** @brief Performs the necessary reconfiguration to change the VLAN 535 * 536 * @param[in] bus - The bus object used for lookups 537 * @param[in] params - The parameters for the channel 538 * @param[in] vlan - The new vlan id to use 539 */ 540 void reconfigureVLAN(sdbusplus::bus_t& bus, ChannelParams& params, 541 uint16_t vlan) 542 { 543 // Unfortunatetly we don't have built-in functions to migrate our interface 544 // customizations to new VLAN interfaces, or have some kind of decoupling. 545 // We therefore must retain all of our old information, setup the new VLAN 546 // configuration, then restore the old info. 547 548 // Save info from the old logical interface 549 bool dhcp4 = getEthProp<bool>(bus, params, "DHCP4"); 550 bool dhcp6 = getEthProp<bool>(bus, params, "DHCP6"); 551 bool ra = getEthProp<bool>(bus, params, "IPv6AcceptRA"); 552 ObjectLookupCache ips(bus, params, INTF_IP); 553 auto ifaddr4 = findIfAddr<AF_INET>(bus, params, 0, originsV4, ips); 554 std::vector<IfAddr<AF_INET6>> ifaddrs6; 555 for (uint8_t i = 0; i < MAX_IPV6_STATIC_ADDRESSES; ++i) 556 { 557 auto ifaddr6 = 558 findIfAddr<AF_INET6>(bus, params, i, originsV6Static, ips); 559 if (!ifaddr6) 560 { 561 break; 562 } 563 ifaddrs6.push_back(std::move(*ifaddr6)); 564 } 565 ObjectLookupCache neighbors(bus, params, INTF_NEIGHBOR); 566 auto neighbor4 = findGatewayNeighbor<AF_INET>(bus, params, neighbors); 567 auto neighbor6 = findGatewayNeighbor<AF_INET6>(bus, params, neighbors); 568 569 deconfigureChannel(bus, params); 570 createVLAN(bus, params, vlan); 571 572 // Re-establish the saved settings 573 setEthProp(bus, params, "DHCP4", dhcp4); 574 setEthProp(bus, params, "DHCP6", dhcp6); 575 setEthProp(bus, params, "IPv6AcceptRA", ra); 576 if (ifaddr4) 577 { 578 createIfAddr<AF_INET>(bus, params, ifaddr4->address, ifaddr4->prefix); 579 } 580 for (const auto& ifaddr6 : ifaddrs6) 581 { 582 createIfAddr<AF_INET6>(bus, params, ifaddr6.address, ifaddr6.prefix); 583 } 584 if (neighbor4) 585 { 586 createNeighbor<AF_INET>(bus, params, neighbor4->ip, neighbor4->mac); 587 } 588 if (neighbor6) 589 { 590 createNeighbor<AF_INET6>(bus, params, neighbor6->ip, neighbor6->mac); 591 } 592 } 593 594 // We need to store this value so it can be returned to the client 595 // It is volatile so safe to store in daemon memory. 596 static std::unordered_map<uint8_t, SetStatus> setStatus; 597 598 // Until we have good support for fixed versions of IPMI tool 599 // we need to return the VLAN id for disabled VLANs. The value is only 600 // used for verification that a disable operation succeeded and will only 601 // be sent if our system indicates that vlans are disabled. 602 static std::unordered_map<uint8_t, uint16_t> lastDisabledVlan; 603 604 /** @brief Gets the set status for the channel if it exists 605 * Otherise populates and returns the default value. 606 * 607 * @param[in] channel - The channel id corresponding to an ethernet interface 608 * @return A reference to the SetStatus for the channel 609 */ 610 SetStatus& getSetStatus(uint8_t channel) 611 { 612 auto it = setStatus.find(channel); 613 if (it != setStatus.end()) 614 { 615 return it->second; 616 } 617 return setStatus[channel] = SetStatus::Complete; 618 } 619 620 /** @brief Unpacks the trivially copyable type from the message */ 621 template <typename T> 622 static T unpackT(message::Payload& req) 623 { 624 std::array<uint8_t, sizeof(T)> bytes; 625 if (req.unpack(bytes) != 0) 626 { 627 throw ccReqDataLenInvalid; 628 } 629 return stdplus::raw::copyFrom<T>(bytes); 630 } 631 632 /** @brief Ensure the message is fully unpacked */ 633 static void unpackFinal(message::Payload& req) 634 { 635 if (!req.fullyUnpacked()) 636 { 637 throw ccReqDataTruncated; 638 } 639 } 640 641 /** 642 * Define placeholder command handlers for the OEM Extension bytes for the Set 643 * LAN Configuration Parameters and Get LAN Configuration Parameters 644 * commands. Using "weak" linking allows the placeholder setLanOem/getLanOem 645 * functions below to be overridden. 646 * To create handlers for your own proprietary command set: 647 * Create/modify a phosphor-ipmi-host Bitbake append file within your Yocto 648 * recipe 649 * Create C++ file(s) that define IPMI handler functions matching the 650 * function names below (i.e. setLanOem). The default name for the 651 * transport IPMI commands is transporthandler_oem.cpp. 652 * Add: 653 * EXTRA_OEMESON:append = "-Dtransport-oem=enabled" 654 * Create a do_configure:prepend()/do_install:append() method in your 655 * bbappend file to copy the file to the build directory. 656 * Add: 657 * PROJECT_SRC_DIR := "${THISDIR}/${PN}" 658 * # Copy the "strong" functions into the working directory, overriding the 659 * # placeholder functions. 660 * do_configure:prepend(){ 661 * cp -f ${PROJECT_SRC_DIR}/transporthandler_oem.cpp ${S} 662 * } 663 * 664 * # Clean up after complilation has completed 665 * do_install:append(){ 666 * rm -f ${S}/transporthandler_oem.cpp 667 * } 668 * 669 */ 670 671 /** 672 * Define the placeholder OEM commands as having weak linkage. Create 673 * setLanOem, and getLanOem functions in the transporthandler_oem.cpp 674 * file. The functions defined there must not have the "weak" attribute 675 * applied to them. 676 */ 677 RspType<> setLanOem(uint8_t channel, uint8_t parameter, message::Payload& req) 678 __attribute__((weak)); 679 RspType<message::Payload> 680 getLanOem(uint8_t channel, uint8_t parameter, uint8_t set, uint8_t block) 681 __attribute__((weak)); 682 683 RspType<> setLanOem(uint8_t, uint8_t, message::Payload& req) 684 { 685 req.trailingOk = true; 686 return response(ccParamNotSupported); 687 } 688 689 RspType<message::Payload> getLanOem(uint8_t, uint8_t, uint8_t, uint8_t) 690 { 691 return response(ccParamNotSupported); 692 } 693 694 /** 695 * @brief is a valid LAN channel. 696 * 697 * This function checks whether the input channel is a valid LAN channel or not. 698 * 699 * @param[in] channel: the channel number. 700 * @return nullopt if the channel is invalid, false if the channel is not a LAN 701 * channel, true if the channel is a LAN channel. 702 **/ 703 std::optional<bool> isLanChannel(uint8_t channel) 704 { 705 ChannelInfo chInfo; 706 auto cc = getChannelInfo(channel, chInfo); 707 if (cc != ccSuccess) 708 { 709 return std::nullopt; 710 } 711 712 return chInfo.mediumType == 713 static_cast<uint8_t>(EChannelMediumType::lan8032); 714 } 715 716 RspType<> setLanInt(Context::ptr ctx, uint4_t channelBits, uint4_t reserved1, 717 uint8_t parameter, message::Payload& req) 718 { 719 const uint8_t channel = convertCurrentChannelNum( 720 static_cast<uint8_t>(channelBits), ctx->channel); 721 if (reserved1 || !isValidChannel(channel)) 722 { 723 lg2::error("Set Lan - Invalid field in request"); 724 req.trailingOk = true; 725 return responseInvalidFieldRequest(); 726 } 727 728 if (!isLanChannel(channel).value_or(false)) 729 { 730 lg2::error("Set Lan - Not a LAN channel"); 731 return responseInvalidFieldRequest(); 732 } 733 734 switch (static_cast<LanParam>(parameter)) 735 { 736 case LanParam::SetStatus: 737 { 738 uint2_t flag; 739 uint6_t rsvd; 740 if (req.unpack(flag, rsvd) != 0) 741 { 742 return responseReqDataLenInvalid(); 743 } 744 unpackFinal(req); 745 if (rsvd) 746 { 747 return responseInvalidFieldRequest(); 748 } 749 auto status = static_cast<SetStatus>(static_cast<uint8_t>(flag)); 750 switch (status) 751 { 752 case SetStatus::Complete: 753 { 754 getSetStatus(channel) = status; 755 return responseSuccess(); 756 } 757 case SetStatus::InProgress: 758 { 759 auto& storedStatus = getSetStatus(channel); 760 if (storedStatus == SetStatus::InProgress) 761 { 762 return response(ccParamSetLocked); 763 } 764 storedStatus = status; 765 return responseSuccess(); 766 } 767 case SetStatus::Commit: 768 if (getSetStatus(channel) != SetStatus::InProgress) 769 { 770 return responseInvalidFieldRequest(); 771 } 772 return responseSuccess(); 773 } 774 return response(ccParamNotSupported); 775 } 776 case LanParam::AuthSupport: 777 { 778 req.trailingOk = true; 779 return response(ccParamReadOnly); 780 } 781 case LanParam::AuthEnables: 782 { 783 req.trailingOk = true; 784 return response(ccParamReadOnly); 785 } 786 case LanParam::IP: 787 { 788 if (channelCall<getEthProp<bool>>(channel, "DHCP4")) 789 { 790 return responseCommandNotAvailable(); 791 } 792 auto ip = unpackT<stdplus::In4Addr>(req); 793 unpackFinal(req); 794 channelCall<reconfigureIfAddr4>(channel, ip, std::nullopt); 795 return responseSuccess(); 796 } 797 case LanParam::IPSrc: 798 { 799 uint4_t flag; 800 uint4_t rsvd; 801 if (req.unpack(flag, rsvd) != 0) 802 { 803 return responseReqDataLenInvalid(); 804 } 805 unpackFinal(req); 806 if (rsvd) 807 { 808 return responseInvalidFieldRequest(); 809 } 810 switch (static_cast<IPSrc>(static_cast<uint8_t>(flag))) 811 { 812 case IPSrc::DHCP: 813 // The IPSrc IPMI command is only for IPv4 814 // management. Modifying IPv6 state is done using 815 // a completely different Set LAN Configuration 816 // subcommand. 817 channelCall<setEthProp<bool>>(channel, "DHCP4", true); 818 return responseSuccess(); 819 case IPSrc::Unspecified: 820 case IPSrc::Static: 821 channelCall<setEthProp<bool>>(channel, "DHCP4", false); 822 return responseSuccess(); 823 case IPSrc::BIOS: 824 case IPSrc::BMC: 825 return responseInvalidFieldRequest(); 826 } 827 return response(ccParamNotSupported); 828 } 829 case LanParam::MAC: 830 { 831 auto mac = unpackT<stdplus::EtherAddr>(req); 832 unpackFinal(req); 833 channelCall<setMACProperty>(channel, mac); 834 return responseSuccess(); 835 } 836 case LanParam::SubnetMask: 837 { 838 if (channelCall<getEthProp<bool>>(channel, "DHCP4")) 839 { 840 return responseCommandNotAvailable(); 841 } 842 auto pfx = stdplus::maskToPfx(unpackT<stdplus::In4Addr>(req)); 843 unpackFinal(req); 844 channelCall<reconfigureIfAddr4>(channel, std::nullopt, pfx); 845 return responseSuccess(); 846 } 847 case LanParam::Gateway1: 848 { 849 if (channelCall<getEthProp<bool>>(channel, "DHCP4")) 850 { 851 return responseCommandNotAvailable(); 852 } 853 auto gateway = unpackT<stdplus::In4Addr>(req); 854 unpackFinal(req); 855 channelCall<setGatewayProperty<AF_INET>>(channel, gateway); 856 return responseSuccess(); 857 } 858 case LanParam::Gateway1MAC: 859 { 860 auto gatewayMAC = unpackT<stdplus::EtherAddr>(req); 861 unpackFinal(req); 862 channelCall<reconfigureGatewayMAC<AF_INET>>(channel, gatewayMAC); 863 return responseSuccess(); 864 } 865 case LanParam::VLANId: 866 { 867 uint12_t vlanData; 868 uint3_t rsvd; 869 bool vlanEnable; 870 871 if (req.unpack(vlanData, rsvd, vlanEnable) != 0) 872 { 873 return responseReqDataLenInvalid(); 874 } 875 unpackFinal(req); 876 877 if (rsvd) 878 { 879 return responseInvalidFieldRequest(); 880 } 881 882 uint16_t vlan = static_cast<uint16_t>(vlanData); 883 884 if (!vlanEnable) 885 { 886 lastDisabledVlan[channel] = vlan; 887 vlan = 0; 888 } 889 else if (vlan == 0 || vlan == VLAN_VALUE_MASK) 890 { 891 return responseInvalidFieldRequest(); 892 } 893 894 channelCall<reconfigureVLAN>(channel, vlan); 895 return responseSuccess(); 896 } 897 case LanParam::CiphersuiteSupport: 898 case LanParam::CiphersuiteEntries: 899 case LanParam::IPFamilySupport: 900 { 901 req.trailingOk = true; 902 return response(ccParamReadOnly); 903 } 904 case LanParam::IPFamilyEnables: 905 { 906 uint8_t enables; 907 if (req.unpack(enables) != 0) 908 { 909 return responseReqDataLenInvalid(); 910 } 911 unpackFinal(req); 912 switch (static_cast<IPFamilyEnables>(enables)) 913 { 914 case IPFamilyEnables::DualStack: 915 return responseSuccess(); 916 case IPFamilyEnables::IPv4Only: 917 case IPFamilyEnables::IPv6Only: 918 return response(ccParamNotSupported); 919 } 920 return response(ccParamNotSupported); 921 } 922 case LanParam::IPv6Status: 923 { 924 req.trailingOk = true; 925 return response(ccParamReadOnly); 926 } 927 case LanParam::IPv6StaticAddresses: 928 { 929 uint8_t set; 930 uint7_t rsvd; 931 bool enabled; 932 uint8_t prefix; 933 uint8_t status; 934 if (req.unpack(set, rsvd, enabled) != 0) 935 { 936 return responseReqDataLenInvalid(); 937 } 938 auto ip = unpackT<stdplus::In6Addr>(req); 939 if (req.unpack(prefix, status) != 0) 940 { 941 return responseReqDataLenInvalid(); 942 } 943 unpackFinal(req); 944 if (rsvd) 945 { 946 return responseInvalidFieldRequest(); 947 } 948 if (enabled) 949 { 950 if (prefix < MIN_IPV6_PREFIX_LENGTH || 951 prefix > MAX_IPV6_PREFIX_LENGTH) 952 { 953 return responseParmOutOfRange(); 954 } 955 channelCall<reconfigureIfAddr6>(channel, set, ip, prefix); 956 } 957 else 958 { 959 channelCall<deconfigureIfAddr6>(channel, set); 960 } 961 return responseSuccess(); 962 } 963 case LanParam::IPv6DynamicAddresses: 964 { 965 req.trailingOk = true; 966 return response(ccParamReadOnly); 967 } 968 case LanParam::IPv6RouterControl: 969 { 970 std::bitset<8> control; 971 constexpr uint8_t reservedRACCBits = 0xfc; 972 if (req.unpack(control) != 0) 973 { 974 return responseReqDataLenInvalid(); 975 } 976 unpackFinal(req); 977 if (std::bitset<8> expected( 978 control & std::bitset<8>(reservedRACCBits)); 979 expected.any()) 980 { 981 return response(ccParamNotSupported); 982 } 983 984 bool enableRA = control[IPv6RouterControlFlag::Dynamic]; 985 channelCall<setEthProp<bool>>(channel, "IPv6AcceptRA", enableRA); 986 channelCall<setEthProp<bool>>(channel, "DHCP6", enableRA); 987 return responseSuccess(); 988 } 989 case LanParam::IPv6StaticRouter1IP: 990 { 991 auto gateway = unpackT<stdplus::In6Addr>(req); 992 unpackFinal(req); 993 channelCall<setGatewayProperty<AF_INET6>>(channel, gateway); 994 return responseSuccess(); 995 } 996 case LanParam::IPv6StaticRouter1MAC: 997 { 998 auto mac = unpackT<stdplus::EtherAddr>(req); 999 unpackFinal(req); 1000 channelCall<reconfigureGatewayMAC<AF_INET6>>(channel, mac); 1001 return responseSuccess(); 1002 } 1003 case LanParam::IPv6StaticRouter1PrefixLength: 1004 { 1005 uint8_t prefix; 1006 if (req.unpack(prefix) != 0) 1007 { 1008 return responseReqDataLenInvalid(); 1009 } 1010 unpackFinal(req); 1011 if (prefix != 0) 1012 { 1013 return responseInvalidFieldRequest(); 1014 } 1015 return responseSuccess(); 1016 } 1017 case LanParam::IPv6StaticRouter1PrefixValue: 1018 { 1019 unpackT<stdplus::In6Addr>(req); 1020 unpackFinal(req); 1021 // Accept any prefix value since our prefix length has to be 0 1022 return responseSuccess(); 1023 } 1024 case LanParam::cipherSuitePrivilegeLevels: 1025 { 1026 uint8_t rsvd; 1027 std::array<uint4_t, ipmi::maxCSRecords> cipherSuitePrivs; 1028 1029 if (req.unpack(rsvd, cipherSuitePrivs)) 1030 { 1031 return responseReqDataLenInvalid(); 1032 } 1033 unpackFinal(req); 1034 1035 if (rsvd) 1036 { 1037 return responseInvalidFieldRequest(); 1038 } 1039 1040 uint8_t resp = 1041 getCipherConfigObject(csPrivFileName, csPrivDefaultFileName) 1042 .setCSPrivilegeLevels(channel, cipherSuitePrivs); 1043 if (!resp) 1044 { 1045 return responseSuccess(); 1046 } 1047 else 1048 { 1049 req.trailingOk = true; 1050 return response(resp); 1051 } 1052 } 1053 } 1054 1055 if (parameter >= oemCmdStart) 1056 { 1057 return setLanOem(channel, parameter, req); 1058 } 1059 1060 req.trailingOk = true; 1061 return response(ccParamNotSupported); 1062 } 1063 1064 RspType<> setLan(Context::ptr ctx, uint4_t channelBits, uint4_t reserved1, 1065 uint8_t parameter, message::Payload& req) 1066 { 1067 try 1068 { 1069 return setLanInt(ctx, channelBits, reserved1, parameter, req); 1070 } 1071 catch (ipmi::Cc cc) 1072 { 1073 return response(cc); 1074 } 1075 catch (const sdbusplus::exception_t& e) 1076 { 1077 if (std::string_view{InvalidArgument::errName} == e.name()) 1078 { 1079 return responseInvalidFieldRequest(); 1080 } 1081 throw; 1082 } 1083 } 1084 1085 RspType<message::Payload> getLan(Context::ptr ctx, uint4_t channelBits, 1086 uint3_t reserved, bool revOnly, 1087 uint8_t parameter, uint8_t set, uint8_t block) 1088 { 1089 message::Payload ret; 1090 constexpr uint8_t current_revision = 0x11; 1091 ret.pack(current_revision); 1092 1093 if (revOnly) 1094 { 1095 return responseSuccess(std::move(ret)); 1096 } 1097 1098 const uint8_t channel = convertCurrentChannelNum( 1099 static_cast<uint8_t>(channelBits), ctx->channel); 1100 if (reserved || !isValidChannel(channel)) 1101 { 1102 lg2::error("Get Lan - Invalid field in request"); 1103 return responseInvalidFieldRequest(); 1104 } 1105 1106 if (!isLanChannel(channel).value_or(false)) 1107 { 1108 lg2::error("Set Lan - Not a LAN channel"); 1109 return responseInvalidFieldRequest(); 1110 } 1111 1112 static std::vector<uint8_t> cipherList; 1113 static bool listInit = false; 1114 if (!listInit) 1115 { 1116 try 1117 { 1118 cipherList = cipher::getCipherList(); 1119 listInit = true; 1120 } 1121 catch (const std::exception& e) 1122 {} 1123 } 1124 1125 switch (static_cast<LanParam>(parameter)) 1126 { 1127 case LanParam::SetStatus: 1128 { 1129 SetStatus status; 1130 try 1131 { 1132 status = setStatus.at(channel); 1133 } 1134 catch (const std::out_of_range&) 1135 { 1136 status = SetStatus::Complete; 1137 } 1138 ret.pack(types::enum_cast<uint2_t>(status), uint6_t{}); 1139 return responseSuccess(std::move(ret)); 1140 } 1141 case LanParam::AuthSupport: 1142 { 1143 std::bitset<6> support; 1144 ret.pack(support, uint2_t{}); 1145 return responseSuccess(std::move(ret)); 1146 } 1147 case LanParam::AuthEnables: 1148 { 1149 std::bitset<6> enables; 1150 ret.pack(enables, uint2_t{}); // Callback 1151 ret.pack(enables, uint2_t{}); // User 1152 ret.pack(enables, uint2_t{}); // Operator 1153 ret.pack(enables, uint2_t{}); // Admin 1154 ret.pack(enables, uint2_t{}); // OEM 1155 return responseSuccess(std::move(ret)); 1156 } 1157 case LanParam::IP: 1158 { 1159 auto ifaddr = channelCall<getIfAddr4>(channel); 1160 stdplus::In4Addr addr{}; 1161 if (ifaddr) 1162 { 1163 addr = ifaddr->address; 1164 } 1165 ret.pack(stdplus::raw::asView<char>(addr)); 1166 return responseSuccess(std::move(ret)); 1167 } 1168 case LanParam::IPSrc: 1169 { 1170 auto src = channelCall<getEthProp<bool>>(channel, "DHCP4") 1171 ? IPSrc::DHCP 1172 : IPSrc::Static; 1173 ret.pack(types::enum_cast<uint4_t>(src), uint4_t{}); 1174 return responseSuccess(std::move(ret)); 1175 } 1176 case LanParam::MAC: 1177 { 1178 auto mac = channelCall<getMACProperty>(channel); 1179 ret.pack(stdplus::raw::asView<char>(mac)); 1180 return responseSuccess(std::move(ret)); 1181 } 1182 case LanParam::SubnetMask: 1183 { 1184 auto ifaddr = channelCall<getIfAddr4>(channel); 1185 uint8_t prefix = AddrFamily<AF_INET>::defaultPrefix; 1186 if (ifaddr) 1187 { 1188 prefix = ifaddr->prefix; 1189 } 1190 auto netmask = stdplus::pfxToMask<stdplus::In4Addr>(prefix); 1191 ret.pack(stdplus::raw::asView<char>(netmask)); 1192 return responseSuccess(std::move(ret)); 1193 } 1194 case LanParam::Gateway1: 1195 { 1196 auto gateway = channelCall<getGatewayProperty<AF_INET>>(channel); 1197 ret.pack(stdplus::raw::asView<char>( 1198 gateway.value_or(stdplus::In4Addr{}))); 1199 return responseSuccess(std::move(ret)); 1200 } 1201 case LanParam::Gateway1MAC: 1202 { 1203 stdplus::EtherAddr mac{}; 1204 auto neighbor = channelCall<getGatewayNeighbor<AF_INET>>(channel); 1205 if (neighbor) 1206 { 1207 mac = neighbor->mac; 1208 } 1209 ret.pack(stdplus::raw::asView<char>(mac)); 1210 return responseSuccess(std::move(ret)); 1211 } 1212 case LanParam::VLANId: 1213 { 1214 uint16_t vlan = channelCall<getVLANProperty>(channel); 1215 if (vlan != 0) 1216 { 1217 vlan |= VLAN_ENABLE_FLAG; 1218 } 1219 else 1220 { 1221 vlan = lastDisabledVlan[channel]; 1222 } 1223 ret.pack(vlan); 1224 return responseSuccess(std::move(ret)); 1225 } 1226 case LanParam::CiphersuiteSupport: 1227 { 1228 if (getChannelSessionSupport(channel) == 1229 EChannelSessSupported::none) 1230 { 1231 return responseInvalidFieldRequest(); 1232 } 1233 if (!listInit) 1234 { 1235 return responseUnspecifiedError(); 1236 } 1237 ret.pack(static_cast<uint8_t>(cipherList.size() - 1)); 1238 return responseSuccess(std::move(ret)); 1239 } 1240 case LanParam::CiphersuiteEntries: 1241 { 1242 if (getChannelSessionSupport(channel) == 1243 EChannelSessSupported::none) 1244 { 1245 return responseInvalidFieldRequest(); 1246 } 1247 if (!listInit) 1248 { 1249 return responseUnspecifiedError(); 1250 } 1251 ret.pack(cipherList); 1252 return responseSuccess(std::move(ret)); 1253 } 1254 case LanParam::IPFamilySupport: 1255 { 1256 std::bitset<8> support; 1257 support[IPFamilySupportFlag::IPv6Only] = 0; 1258 support[IPFamilySupportFlag::DualStack] = 1; 1259 support[IPFamilySupportFlag::IPv6Alerts] = 1; 1260 ret.pack(support); 1261 return responseSuccess(std::move(ret)); 1262 } 1263 case LanParam::IPFamilyEnables: 1264 { 1265 ret.pack(static_cast<uint8_t>(IPFamilyEnables::DualStack)); 1266 return responseSuccess(std::move(ret)); 1267 } 1268 case LanParam::IPv6Status: 1269 { 1270 ret.pack(MAX_IPV6_STATIC_ADDRESSES); 1271 ret.pack(MAX_IPV6_DYNAMIC_ADDRESSES); 1272 std::bitset<8> support; 1273 support[IPv6StatusFlag::DHCP] = 1; 1274 support[IPv6StatusFlag::SLAAC] = 1; 1275 ret.pack(support); 1276 return responseSuccess(std::move(ret)); 1277 } 1278 case LanParam::IPv6StaticAddresses: 1279 { 1280 if (set >= MAX_IPV6_STATIC_ADDRESSES) 1281 { 1282 return responseParmOutOfRange(); 1283 } 1284 getLanIPv6Address(ret, channel, set, originsV6Static); 1285 return responseSuccess(std::move(ret)); 1286 } 1287 case LanParam::IPv6DynamicAddresses: 1288 { 1289 if (set >= MAX_IPV6_DYNAMIC_ADDRESSES) 1290 { 1291 return responseParmOutOfRange(); 1292 } 1293 getLanIPv6Address(ret, channel, set, originsV6Dynamic); 1294 return responseSuccess(std::move(ret)); 1295 } 1296 case LanParam::IPv6RouterControl: 1297 { 1298 std::bitset<8> control; 1299 control[IPv6RouterControlFlag::Dynamic] = 1300 channelCall<getEthProp<bool>>(channel, "IPv6AcceptRA"); 1301 control[IPv6RouterControlFlag::Static] = 1; 1302 ret.pack(control); 1303 return responseSuccess(std::move(ret)); 1304 } 1305 case LanParam::IPv6StaticRouter1IP: 1306 { 1307 stdplus::In6Addr gateway{}; 1308 if (!channelCall<getEthProp<bool>>(channel, "IPv6AcceptRA")) 1309 { 1310 gateway = 1311 channelCall<getGatewayProperty<AF_INET6>>(channel).value_or( 1312 stdplus::In6Addr{}); 1313 } 1314 ret.pack(stdplus::raw::asView<char>(gateway)); 1315 return responseSuccess(std::move(ret)); 1316 } 1317 case LanParam::IPv6StaticRouter1MAC: 1318 { 1319 stdplus::EtherAddr mac{}; 1320 auto neighbor = channelCall<getGatewayNeighbor<AF_INET6>>(channel); 1321 if (neighbor) 1322 { 1323 mac = neighbor->mac; 1324 } 1325 ret.pack(stdplus::raw::asView<char>(mac)); 1326 return responseSuccess(std::move(ret)); 1327 } 1328 case LanParam::IPv6StaticRouter1PrefixLength: 1329 { 1330 ret.pack(uint8_t{0}); 1331 return responseSuccess(std::move(ret)); 1332 } 1333 case LanParam::IPv6StaticRouter1PrefixValue: 1334 { 1335 ret.pack(stdplus::raw::asView<char>(stdplus::In6Addr{})); 1336 return responseSuccess(std::move(ret)); 1337 } 1338 case LanParam::cipherSuitePrivilegeLevels: 1339 { 1340 std::array<uint4_t, ipmi::maxCSRecords> csPrivilegeLevels; 1341 1342 uint8_t resp = 1343 getCipherConfigObject(csPrivFileName, csPrivDefaultFileName) 1344 .getCSPrivilegeLevels(channel, csPrivilegeLevels); 1345 if (!resp) 1346 { 1347 constexpr uint8_t reserved1 = 0x00; 1348 ret.pack(reserved1, csPrivilegeLevels); 1349 return responseSuccess(std::move(ret)); 1350 } 1351 else 1352 { 1353 return response(resp); 1354 } 1355 } 1356 } 1357 1358 if (parameter >= oemCmdStart) 1359 { 1360 return getLanOem(channel, parameter, set, block); 1361 } 1362 1363 return response(ccParamNotSupported); 1364 } 1365 1366 constexpr const char* solInterface = "xyz.openbmc_project.Ipmi.SOL"; 1367 constexpr const char* solPath = "/xyz/openbmc_project/ipmi/sol/"; 1368 constexpr const uint16_t solDefaultPort = 623; 1369 1370 RspType<> setSolConfParams(Context::ptr ctx, uint4_t channelBits, 1371 uint4_t /*reserved*/, uint8_t parameter, 1372 message::Payload& req) 1373 { 1374 const uint8_t channel = convertCurrentChannelNum( 1375 static_cast<uint8_t>(channelBits), ctx->channel); 1376 1377 if (!isValidChannel(channel)) 1378 { 1379 lg2::error("Set Sol Config - Invalid channel in request"); 1380 return responseInvalidFieldRequest(); 1381 } 1382 1383 std::string solService{}; 1384 std::string solPathWitheEthName = solPath + ipmi::getChannelName(channel); 1385 1386 if (ipmi::getService(ctx, solInterface, solPathWitheEthName, solService)) 1387 { 1388 lg2::error("Set Sol Config - Invalid solInterface, service: {SERVICE}, " 1389 "object path: {OBJPATH}, interface: {INTERFACE}", 1390 "SERVICE", solService, "OBJPATH", solPathWitheEthName, 1391 "INTERFACE", solInterface); 1392 return responseInvalidFieldRequest(); 1393 } 1394 1395 switch (static_cast<SolConfParam>(parameter)) 1396 { 1397 case SolConfParam::Progress: 1398 { 1399 uint8_t progress; 1400 if (req.unpack(progress) != 0 || !req.fullyUnpacked()) 1401 { 1402 return responseReqDataLenInvalid(); 1403 } 1404 1405 if (ipmi::setDbusProperty(ctx, solService, solPathWitheEthName, 1406 solInterface, "Progress", progress)) 1407 { 1408 return responseUnspecifiedError(); 1409 } 1410 break; 1411 } 1412 case SolConfParam::Enable: 1413 { 1414 bool enable; 1415 uint7_t reserved2; 1416 1417 if (req.unpack(enable, reserved2) != 0 || !req.fullyUnpacked()) 1418 { 1419 return responseReqDataLenInvalid(); 1420 } 1421 1422 if (ipmi::setDbusProperty(ctx, solService, solPathWitheEthName, 1423 solInterface, "Enable", enable)) 1424 { 1425 return responseUnspecifiedError(); 1426 } 1427 break; 1428 } 1429 case SolConfParam::Authentication: 1430 { 1431 uint4_t privilegeBits{}; 1432 uint2_t reserved2{}; 1433 bool forceAuth = false; 1434 bool forceEncrypt = false; 1435 1436 if (req.unpack(privilegeBits, reserved2, forceAuth, forceEncrypt) != 1437 0 || 1438 !req.fullyUnpacked()) 1439 { 1440 return responseReqDataLenInvalid(); 1441 } 1442 1443 uint8_t privilege = static_cast<uint8_t>(privilegeBits); 1444 if (privilege < static_cast<uint8_t>(Privilege::User) || 1445 privilege > static_cast<uint8_t>(Privilege::Oem)) 1446 { 1447 return ipmi::responseInvalidFieldRequest(); 1448 } 1449 1450 if (ipmi::setDbusProperty(ctx, solService, solPathWitheEthName, 1451 solInterface, "Privilege", privilege)) 1452 { 1453 return responseUnspecifiedError(); 1454 } 1455 1456 if (ipmi::setDbusProperty(ctx, solService, solPathWitheEthName, 1457 solInterface, "ForceEncryption", 1458 forceEncrypt)) 1459 { 1460 return responseUnspecifiedError(); 1461 } 1462 1463 if (ipmi::setDbusProperty(ctx, solService, solPathWitheEthName, 1464 solInterface, "ForceAuthentication", 1465 forceAuth)) 1466 { 1467 return responseUnspecifiedError(); 1468 } 1469 break; 1470 } 1471 case SolConfParam::Accumulate: 1472 { 1473 uint8_t interval; 1474 uint8_t threshold; 1475 if (req.unpack(interval, threshold) != 0 || !req.fullyUnpacked()) 1476 { 1477 return responseReqDataLenInvalid(); 1478 } 1479 1480 if (threshold == 0) 1481 { 1482 return responseInvalidFieldRequest(); 1483 } 1484 1485 if (ipmi::setDbusProperty(ctx, solService, solPathWitheEthName, 1486 solInterface, "AccumulateIntervalMS", 1487 interval)) 1488 { 1489 return responseUnspecifiedError(); 1490 } 1491 1492 if (ipmi::setDbusProperty(ctx, solService, solPathWitheEthName, 1493 solInterface, "Threshold", threshold)) 1494 { 1495 return responseUnspecifiedError(); 1496 } 1497 break; 1498 } 1499 case SolConfParam::Retry: 1500 { 1501 uint3_t countBits; 1502 uint5_t reserved2; 1503 uint8_t interval; 1504 1505 if (req.unpack(countBits, reserved2, interval) != 0 || 1506 !req.fullyUnpacked()) 1507 { 1508 return responseReqDataLenInvalid(); 1509 } 1510 1511 uint8_t count = static_cast<uint8_t>(countBits); 1512 if (ipmi::setDbusProperty(ctx, solService, solPathWitheEthName, 1513 solInterface, "RetryCount", count)) 1514 { 1515 return responseUnspecifiedError(); 1516 } 1517 1518 if (ipmi::setDbusProperty(ctx, solService, solPathWitheEthName, 1519 solInterface, "RetryIntervalMS", 1520 interval)) 1521 { 1522 return responseUnspecifiedError(); 1523 } 1524 break; 1525 } 1526 case SolConfParam::Port: 1527 { 1528 return response(ipmiCCWriteReadParameter); 1529 } 1530 case SolConfParam::NonVbitrate: 1531 case SolConfParam::Vbitrate: 1532 case SolConfParam::Channel: 1533 default: 1534 return response(ipmiCCParamNotSupported); 1535 } 1536 return responseSuccess(); 1537 } 1538 1539 RspType<message::Payload> getSolConfParams( 1540 Context::ptr ctx, uint4_t channelBits, uint3_t /*reserved*/, bool revOnly, 1541 uint8_t parameter, uint8_t /*set*/, uint8_t /*block*/) 1542 { 1543 message::Payload ret; 1544 constexpr uint8_t current_revision = 0x11; 1545 ret.pack(current_revision); 1546 if (revOnly) 1547 { 1548 return responseSuccess(std::move(ret)); 1549 } 1550 1551 const uint8_t channel = convertCurrentChannelNum( 1552 static_cast<uint8_t>(channelBits), ctx->channel); 1553 1554 if (!isValidChannel(channel)) 1555 { 1556 lg2::error("Get Sol Config - Invalid channel in request"); 1557 return responseInvalidFieldRequest(); 1558 } 1559 1560 std::string solService{}; 1561 std::string solPathWitheEthName = solPath + ipmi::getChannelName(channel); 1562 1563 if (ipmi::getService(ctx, solInterface, solPathWitheEthName, solService)) 1564 { 1565 lg2::error("Set Sol Config - Invalid solInterface, service: {SERVICE}, " 1566 "object path: {OBJPATH}, interface: {INTERFACE}", 1567 "SERVICE", solService, "OBJPATH", solPathWitheEthName, 1568 "INTERFACE", solInterface); 1569 return responseInvalidFieldRequest(); 1570 } 1571 1572 switch (static_cast<SolConfParam>(parameter)) 1573 { 1574 case SolConfParam::Progress: 1575 { 1576 uint8_t progress; 1577 if (ipmi::getDbusProperty(ctx, solService, solPathWitheEthName, 1578 solInterface, "Progress", progress)) 1579 { 1580 return responseUnspecifiedError(); 1581 } 1582 ret.pack(progress); 1583 return responseSuccess(std::move(ret)); 1584 } 1585 case SolConfParam::Enable: 1586 { 1587 bool enable{}; 1588 if (ipmi::getDbusProperty(ctx, solService, solPathWitheEthName, 1589 solInterface, "Enable", enable)) 1590 { 1591 return responseUnspecifiedError(); 1592 } 1593 ret.pack(enable, uint7_t{}); 1594 return responseSuccess(std::move(ret)); 1595 } 1596 case SolConfParam::Authentication: 1597 { 1598 // 4bits, cast when pack 1599 uint8_t privilege; 1600 bool forceAuth = false; 1601 bool forceEncrypt = false; 1602 1603 if (ipmi::getDbusProperty(ctx, solService, solPathWitheEthName, 1604 solInterface, "Privilege", privilege)) 1605 { 1606 return responseUnspecifiedError(); 1607 } 1608 1609 if (ipmi::getDbusProperty(ctx, solService, solPathWitheEthName, 1610 solInterface, "ForceAuthentication", 1611 forceAuth)) 1612 { 1613 return responseUnspecifiedError(); 1614 } 1615 1616 if (ipmi::getDbusProperty(ctx, solService, solPathWitheEthName, 1617 solInterface, "ForceEncryption", 1618 forceEncrypt)) 1619 { 1620 return responseUnspecifiedError(); 1621 } 1622 ret.pack(uint4_t{privilege}, uint2_t{}, forceAuth, forceEncrypt); 1623 return responseSuccess(std::move(ret)); 1624 } 1625 case SolConfParam::Accumulate: 1626 { 1627 uint8_t interval{}, threshold{}; 1628 1629 if (ipmi::getDbusProperty(ctx, solService, solPathWitheEthName, 1630 solInterface, "AccumulateIntervalMS", 1631 interval)) 1632 { 1633 return responseUnspecifiedError(); 1634 } 1635 1636 if (ipmi::getDbusProperty(ctx, solService, solPathWitheEthName, 1637 solInterface, "Threshold", threshold)) 1638 { 1639 return responseUnspecifiedError(); 1640 } 1641 ret.pack(interval, threshold); 1642 return responseSuccess(std::move(ret)); 1643 } 1644 case SolConfParam::Retry: 1645 { 1646 // 3bits, cast when cast 1647 uint8_t count{}; 1648 uint8_t interval{}; 1649 1650 if (ipmi::getDbusProperty(ctx, solService, solPathWitheEthName, 1651 solInterface, "RetryCount", count)) 1652 { 1653 return responseUnspecifiedError(); 1654 } 1655 1656 if (ipmi::getDbusProperty(ctx, solService, solPathWitheEthName, 1657 solInterface, "RetryIntervalMS", 1658 interval)) 1659 { 1660 return responseUnspecifiedError(); 1661 } 1662 ret.pack(uint3_t{count}, uint5_t{}, interval); 1663 return responseSuccess(std::move(ret)); 1664 } 1665 case SolConfParam::Port: 1666 { 1667 auto port = solDefaultPort; 1668 ret.pack(static_cast<uint16_t>(port)); 1669 return responseSuccess(std::move(ret)); 1670 } 1671 case SolConfParam::Channel: 1672 { 1673 ret.pack(channel); 1674 return responseSuccess(std::move(ret)); 1675 } 1676 case SolConfParam::NonVbitrate: 1677 { 1678 uint64_t baudRate; 1679 uint8_t encodedBitRate = 0; 1680 if (ipmi::getDbusProperty( 1681 ctx, "xyz.openbmc_project.Console.default", 1682 "/xyz/openbmc_project/console/default", 1683 "xyz.openbmc_project.Console.UART", "Baud", baudRate)) 1684 { 1685 return ipmi::responseUnspecifiedError(); 1686 } 1687 switch (baudRate) 1688 { 1689 case 9600: 1690 encodedBitRate = 0x06; 1691 break; 1692 case 19200: 1693 encodedBitRate = 0x07; 1694 break; 1695 case 38400: 1696 encodedBitRate = 0x08; 1697 break; 1698 case 57600: 1699 encodedBitRate = 0x09; 1700 break; 1701 case 115200: 1702 encodedBitRate = 0x0a; 1703 break; 1704 default: 1705 break; 1706 } 1707 ret.pack(encodedBitRate); 1708 return responseSuccess(std::move(ret)); 1709 } 1710 case SolConfParam::Vbitrate: 1711 default: 1712 return response(ipmiCCParamNotSupported); 1713 } 1714 1715 return response(ccParamNotSupported); 1716 } 1717 1718 } // namespace transport 1719 } // namespace ipmi 1720 1721 void register_netfn_transport_functions() __attribute__((constructor)); 1722 1723 void register_netfn_transport_functions() 1724 { 1725 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnTransport, 1726 ipmi::transport::cmdSetLanConfigParameters, 1727 ipmi::Privilege::Admin, ipmi::transport::setLan); 1728 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnTransport, 1729 ipmi::transport::cmdGetLanConfigParameters, 1730 ipmi::Privilege::Operator, ipmi::transport::getLan); 1731 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnTransport, 1732 ipmi::transport::cmdSetSolConfigParameters, 1733 ipmi::Privilege::Admin, 1734 ipmi::transport::setSolConfParams); 1735 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnTransport, 1736 ipmi::transport::cmdGetSolConfigParameters, 1737 ipmi::Privilege::User, 1738 ipmi::transport::getSolConfParams); 1739 } 1740