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