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