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