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