1 #pragma once 2 3 #include "app/channel.hpp" 4 #include "user_channel/cipher_mgmt.hpp" 5 6 #include <ipmid/api-types.hpp> 7 #include <ipmid/api.hpp> 8 #include <ipmid/message.hpp> 9 #include <ipmid/message/types.hpp> 10 #include <ipmid/types.hpp> 11 #include <ipmid/utils.hpp> 12 #include <phosphor-logging/elog-errors.hpp> 13 #include <phosphor-logging/elog.hpp> 14 #include <phosphor-logging/log.hpp> 15 #include <sdbusplus/bus.hpp> 16 #include <sdbusplus/exception.hpp> 17 #include <stdplus/net/addr/ether.hpp> 18 #include <stdplus/net/addr/ip.hpp> 19 #include <stdplus/str/conv.hpp> 20 #include <stdplus/zstring_view.hpp> 21 #include <user_channel/channel_layer.hpp> 22 #include <xyz/openbmc_project/Common/error.hpp> 23 #include <xyz/openbmc_project/Network/EthernetInterface/server.hpp> 24 #include <xyz/openbmc_project/Network/IP/server.hpp> 25 #include <xyz/openbmc_project/Network/Neighbor/server.hpp> 26 27 #include <cinttypes> 28 #include <functional> 29 #include <optional> 30 #include <string> 31 #include <string_view> 32 #include <unordered_map> 33 #include <unordered_set> 34 #include <utility> 35 36 namespace ipmi 37 { 38 namespace transport 39 { 40 41 using stdplus::operator""_zsv; 42 43 // D-Bus Network Daemon definitions 44 constexpr auto PATH_ROOT = "/xyz/openbmc_project/network"_zsv; 45 constexpr auto INTF_ETHERNET = "xyz.openbmc_project.Network.EthernetInterface"; 46 constexpr auto INTF_IP = "xyz.openbmc_project.Network.IP"; 47 constexpr auto INTF_IP_CREATE = "xyz.openbmc_project.Network.IP.Create"; 48 constexpr auto INTF_MAC = "xyz.openbmc_project.Network.MACAddress"; 49 constexpr auto INTF_NEIGHBOR = "xyz.openbmc_project.Network.Neighbor"; 50 constexpr auto INTF_NEIGHBOR_CREATE_STATIC = 51 "xyz.openbmc_project.Network.Neighbor.CreateStatic"; 52 constexpr auto INTF_VLAN = "xyz.openbmc_project.Network.VLAN"; 53 constexpr auto INTF_VLAN_CREATE = "xyz.openbmc_project.Network.VLAN.Create"; 54 55 /** @brief IPMI LAN Parameters */ 56 enum class LanParam : uint8_t 57 { 58 SetStatus = 0, 59 AuthSupport = 1, 60 AuthEnables = 2, 61 IP = 3, 62 IPSrc = 4, 63 MAC = 5, 64 SubnetMask = 6, 65 Gateway1 = 12, 66 Gateway1MAC = 13, 67 VLANId = 20, 68 CiphersuiteSupport = 22, 69 CiphersuiteEntries = 23, 70 cipherSuitePrivilegeLevels = 24, 71 IPFamilySupport = 50, 72 IPFamilyEnables = 51, 73 IPv6Status = 55, 74 IPv6StaticAddresses = 56, 75 IPv6DynamicAddresses = 59, 76 IPv6RouterControl = 64, 77 IPv6StaticRouter1IP = 65, 78 IPv6StaticRouter1MAC = 66, 79 IPv6StaticRouter1PrefixLength = 67, 80 IPv6StaticRouter1PrefixValue = 68, 81 }; 82 83 /** @brief IPMI IP Origin Types */ 84 enum class IPSrc : uint8_t 85 { 86 Unspecified = 0, 87 Static = 1, 88 DHCP = 2, 89 BIOS = 3, 90 BMC = 4, 91 }; 92 93 /** @brief IPMI Set Status */ 94 enum class SetStatus : uint8_t 95 { 96 Complete = 0, 97 InProgress = 1, 98 Commit = 2, 99 }; 100 101 /** @brief IPMI Family Suport Bits */ 102 namespace IPFamilySupportFlag 103 { 104 constexpr uint8_t IPv6Only = 0; 105 constexpr uint8_t DualStack = 1; 106 constexpr uint8_t IPv6Alerts = 2; 107 } // namespace IPFamilySupportFlag 108 109 /** @brief IPMI IPFamily Enables Flag */ 110 enum class IPFamilyEnables : uint8_t 111 { 112 IPv4Only = 0, 113 IPv6Only = 1, 114 DualStack = 2, 115 }; 116 117 /** @brief IPMI IPv6 Dyanmic Status Bits */ 118 namespace IPv6StatusFlag 119 { 120 constexpr uint8_t DHCP = 0; 121 constexpr uint8_t SLAAC = 1; 122 }; // namespace IPv6StatusFlag 123 124 /** @brief IPMI IPv6 Source */ 125 enum class IPv6Source : uint8_t 126 { 127 Static = 0, 128 SLAAC = 1, 129 DHCP = 2, 130 }; 131 132 /** @brief IPMI IPv6 Address Status */ 133 enum class IPv6AddressStatus : uint8_t 134 { 135 Active = 0, 136 Disabled = 1, 137 }; 138 139 namespace IPv6RouterControlFlag 140 { 141 constexpr uint8_t Static = 0; 142 constexpr uint8_t Dynamic = 1; 143 }; // namespace IPv6RouterControlFlag 144 145 // LAN Handler specific response codes 146 constexpr Cc ccParamNotSupported = 0x80; 147 constexpr Cc ccParamSetLocked = 0x81; 148 constexpr Cc ccParamReadOnly = 0x82; 149 150 // VLANs are a 12-bit value 151 constexpr uint16_t VLAN_VALUE_MASK = 0x0fff; 152 constexpr uint16_t VLAN_ENABLE_FLAG = 0x8000; 153 154 // Arbitrary v6 Address Limits to prevent too much output in ipmitool 155 constexpr uint8_t MAX_IPV6_STATIC_ADDRESSES = 15; 156 constexpr uint8_t MAX_IPV6_DYNAMIC_ADDRESSES = 15; 157 158 // Prefix length limits of phosphor-networkd 159 constexpr uint8_t MIN_IPV6_PREFIX_LENGTH = 1; 160 constexpr uint8_t MAX_IPV6_PREFIX_LENGTH = 128; 161 162 /** @brief The dbus parameters for the interface corresponding to a channel 163 * This helps reduce the number of mapper lookups we need for each 164 * query and simplifies finding the VLAN interface if needed. 165 */ 166 struct ChannelParams 167 { 168 /** @brief The channel ID */ 169 int id; 170 /** @brief channel name for the interface */ 171 std::string ifname; 172 /** @brief Name of the service on the bus */ 173 std::string service; 174 /** @brief Lower level adapter path that is guaranteed to not be a VLAN */ 175 std::string ifPath; 176 /** @brief Logical adapter path used for address assignment */ 177 std::string logicalPath; 178 }; 179 180 /** @brief Determines the ethernet interface name corresponding to a channel 181 * Tries to map a VLAN object first so that the address information 182 * is accurate. Otherwise it gets the standard ethernet interface. 183 * 184 * @param[in] bus - The bus object used for lookups 185 * @param[in] channel - The channel id corresponding to an ethernet interface 186 * @return Ethernet interface service and object path if it exists 187 */ 188 std::optional<ChannelParams> maybeGetChannelParams(sdbusplus::bus_t& bus, 189 uint8_t channel); 190 191 /** @brief A trivial helper around maybeGetChannelParams() that throws an 192 * exception when it is unable to acquire parameters for the channel. 193 * 194 * @param[in] bus - The bus object used for lookups 195 * @param[in] channel - The channel id corresponding to an ethernet interface 196 * @return Ethernet interface service and object path 197 */ 198 ChannelParams getChannelParams(sdbusplus::bus_t& bus, uint8_t channel); 199 200 /** @brief Trivializes using parameter getter functions by providing a bus 201 * and channel parameters automatically. 202 * 203 * @param[in] channel - The channel id corresponding to an ethernet interface 204 * ... 205 */ 206 template <auto func, typename... Args> 207 auto channelCall(uint8_t channel, Args&&... args) 208 { 209 sdbusplus::bus_t bus(ipmid_get_sd_bus_connection()); 210 auto params = getChannelParams(bus, channel); 211 return std::invoke(func, bus, params, std::forward<Args>(args)...); 212 } 213 214 /** @brief Generic paramters for different address families */ 215 template <int family> 216 struct AddrFamily 217 {}; 218 219 /** @brief Parameter specialization for IPv4 */ 220 template <> 221 struct AddrFamily<AF_INET> 222 { 223 using addr = stdplus::In4Addr; 224 static constexpr auto protocol = 225 sdbusplus::server::xyz::openbmc_project::network::IP::Protocol::IPv4; 226 static constexpr size_t maxStrLen = INET6_ADDRSTRLEN; 227 static constexpr uint8_t defaultPrefix = 32; 228 static constexpr char propertyGateway[] = "DefaultGateway"; 229 }; 230 231 /** @brief Parameter specialization for IPv6 */ 232 template <> 233 struct AddrFamily<AF_INET6> 234 { 235 using addr = stdplus::In6Addr; 236 static constexpr auto protocol = 237 sdbusplus::server::xyz::openbmc_project::network::IP::Protocol::IPv6; 238 static constexpr size_t maxStrLen = INET6_ADDRSTRLEN; 239 static constexpr uint8_t defaultPrefix = 128; 240 static constexpr char propertyGateway[] = "DefaultGateway6"; 241 }; 242 243 /** @brief Interface Neighbor configuration parameters */ 244 template <int family> 245 struct IfNeigh 246 { 247 std::string path; 248 typename AddrFamily<family>::addr ip; 249 stdplus::EtherAddr mac; 250 }; 251 252 /** @brief Interface IP Address configuration parameters */ 253 template <int family> 254 struct IfAddr 255 { 256 std::string path; 257 typename AddrFamily<family>::addr address; 258 sdbusplus::server::xyz::openbmc_project::network::IP::AddressOrigin origin; 259 uint8_t prefix; 260 }; 261 262 /** @brief Valid address origins for IPv6 */ 263 static inline const std::unordered_set< 264 sdbusplus::server::xyz::openbmc_project::network::IP::AddressOrigin> 265 originsV6Static = {sdbusplus::server::xyz::openbmc_project::network::IP:: 266 AddressOrigin::Static}; 267 static inline const std::unordered_set< 268 sdbusplus::server::xyz::openbmc_project::network::IP::AddressOrigin> 269 originsV6Dynamic = { 270 sdbusplus::server::xyz::openbmc_project::network::IP::AddressOrigin:: 271 DHCP, 272 sdbusplus::server::xyz::openbmc_project::network::IP::AddressOrigin:: 273 SLAAC, 274 }; 275 276 /** @brief A lazy lookup mechanism for iterating over object properties stored 277 * in DBus. This will only perform the object lookup when needed, and 278 * retains a cache of previous lookups to speed up future iterations. 279 */ 280 class ObjectLookupCache 281 { 282 public: 283 using PropertiesCache = std::unordered_map<std::string, PropertyMap>; 284 285 /** @brief Creates a new ObjectLookupCache for the interface on the bus 286 * NOTE: The inputs to this object must outlive the object since 287 * they are only referenced by it. 288 * 289 * @param[in] bus - The bus object used for lookups 290 * @param[in] params - The parameters for the channel 291 * @param[in] intf - The interface we are looking up 292 */ 293 ObjectLookupCache(sdbusplus::bus_t& bus, const ChannelParams& params, 294 const char* intf) : 295 bus(bus), 296 params(params), intf(intf), 297 objs(getAllDbusObjects(bus, params.logicalPath, intf, "")) 298 {} 299 300 class iterator : public ObjectTree::const_iterator 301 { 302 public: 303 using value_type = PropertiesCache::value_type; 304 305 iterator(ObjectTree::const_iterator it, ObjectLookupCache& container) : 306 ObjectTree::const_iterator(it), container(container), 307 ret(container.cache.end()) 308 {} 309 value_type& operator*() 310 { 311 ret = container.get(ObjectTree::const_iterator::operator*().first); 312 return *ret; 313 } 314 value_type* operator->() 315 { 316 return &operator*(); 317 } 318 319 private: 320 ObjectLookupCache& container; 321 PropertiesCache::iterator ret; 322 }; 323 324 iterator begin() noexcept 325 { 326 return iterator(objs.begin(), *this); 327 } 328 329 iterator end() noexcept 330 { 331 return iterator(objs.end(), *this); 332 } 333 334 private: 335 sdbusplus::bus_t& bus; 336 const ChannelParams& params; 337 const char* const intf; 338 const ObjectTree objs; 339 PropertiesCache cache; 340 341 /** @brief Gets a cached copy of the object properties if possible 342 * Otherwise performs a query on DBus to look them up 343 * 344 * @param[in] path - The object path to lookup 345 * @return An iterator for the specified object path + properties 346 */ 347 PropertiesCache::iterator get(const std::string& path) 348 { 349 auto it = cache.find(path); 350 if (it != cache.end()) 351 { 352 return it; 353 } 354 auto properties = getAllDbusProperties(bus, params.service, path, intf); 355 return cache.insert({path, std::move(properties)}).first; 356 } 357 }; 358 359 /** @brief Searches the ip object lookup cache for an address matching 360 * the input parameters. NOTE: The index lacks stability across address 361 * changes since the network daemon has no notion of stable indicies. 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 index of the desired address on the interface 366 * @param[in] origins - The allowed origins for the address objects 367 * @param[in] ips - The object lookup cache holding all of the address info 368 * @return The address and prefix if it was found 369 */ 370 template <int family> 371 std::optional<IfAddr<family>> findIfAddr( 372 [[maybe_unused]] sdbusplus::bus_t& bus, 373 [[maybe_unused]] const ChannelParams& params, uint8_t idx, 374 const std::unordered_set< 375 sdbusplus::server::xyz::openbmc_project::network::IP::AddressOrigin>& 376 origins, 377 ObjectLookupCache& ips) 378 { 379 for (const auto& [path, properties] : ips) 380 { 381 std::optional<typename AddrFamily<family>::addr> addr; 382 try 383 { 384 addr.emplace(stdplus::fromStr<typename AddrFamily<family>::addr>( 385 std::get<std::string>(properties.at("Address")))); 386 } 387 catch (...) 388 { 389 continue; 390 } 391 392 sdbusplus::server::xyz::openbmc_project::network::IP::AddressOrigin 393 origin = sdbusplus::server::xyz::openbmc_project::network::IP:: 394 convertAddressOriginFromString( 395 std::get<std::string>(properties.at("Origin"))); 396 if (origins.find(origin) == origins.end()) 397 { 398 continue; 399 } 400 401 if (idx > 0) 402 { 403 idx--; 404 continue; 405 } 406 407 IfAddr<family> ifaddr; 408 ifaddr.path = path; 409 ifaddr.address = *addr; 410 ifaddr.prefix = std::get<uint8_t>(properties.at("PrefixLength")); 411 ifaddr.origin = origin; 412 return ifaddr; 413 } 414 415 return std::nullopt; 416 } 417 /** @brief Trivial helper around findIfAddr that simplifies calls 418 * for one off lookups. Don't use this if you intend to do multiple 419 * lookups at a time. 420 * 421 * @param[in] bus - The bus object used for lookups 422 * @param[in] params - The parameters for the channel 423 * @param[in] idx - The index of the desired address on the interface 424 * @param[in] origins - The allowed origins for the address objects 425 * @return The address and prefix if it was found 426 */ 427 template <int family> 428 auto getIfAddr( 429 sdbusplus::bus_t& bus, const ChannelParams& params, uint8_t idx, 430 const std::unordered_set< 431 sdbusplus::server::xyz::openbmc_project::network::IP::AddressOrigin>& 432 origins) 433 { 434 ObjectLookupCache ips(bus, params, INTF_IP); 435 return findIfAddr<family>(bus, params, idx, origins, ips); 436 } 437 438 /** @brief Reconfigures the IPv6 address info configured for the interface 439 * 440 * @param[in] bus - The bus object used for lookups 441 * @param[in] params - The parameters for the channel 442 * @param[in] idx - The address index to operate on 443 * @param[in] address - The new address 444 * @param[in] prefix - The new address prefix 445 */ 446 void reconfigureIfAddr6(sdbusplus::bus_t& bus, const ChannelParams& params, 447 uint8_t idx, stdplus::In6Addr address, uint8_t prefix); 448 449 /** @brief Retrieves the current gateway for the address family on the system 450 * NOTE: The gateway is per channel instead of the system wide one. 451 * 452 * @param[in] bus - The bus object used for lookups 453 * @param[in] params - The parameters for the channel 454 * @return An address representing the gateway address if it exists 455 */ 456 template <int family> 457 std::optional<typename AddrFamily<family>::addr> 458 getGatewayProperty(sdbusplus::bus_t& bus, const ChannelParams& params) 459 { 460 auto objPath = "/xyz/openbmc_project/network/" + params.ifname; 461 auto gatewayStr = std::get<std::string>( 462 getDbusProperty(bus, params.service, objPath, INTF_ETHERNET, 463 AddrFamily<family>::propertyGateway)); 464 if (gatewayStr.empty()) 465 { 466 return std::nullopt; 467 } 468 return stdplus::fromStr<typename AddrFamily<family>::addr>(gatewayStr); 469 } 470 471 template <int family> 472 std::optional<IfNeigh<family>> 473 findStaticNeighbor(sdbusplus::bus_t&, const ChannelParams&, 474 typename AddrFamily<family>::addr ip, 475 ObjectLookupCache& neighbors) 476 { 477 using sdbusplus::server::xyz::openbmc_project::network::Neighbor; 478 const auto state = 479 sdbusplus::common::xyz::openbmc_project::network::convertForMessage( 480 Neighbor::State::Permanent); 481 for (const auto& [path, neighbor] : neighbors) 482 { 483 std::optional<typename AddrFamily<family>::addr> neighIP; 484 try 485 { 486 neighIP.emplace(stdplus::fromStr<typename AddrFamily<family>::addr>( 487 std::get<std::string>(neighbor.at("IPAddress")))); 488 } 489 catch (...) 490 { 491 continue; 492 } 493 if (*neighIP != ip) 494 { 495 continue; 496 } 497 if (state != std::get<std::string>(neighbor.at("State"))) 498 { 499 continue; 500 } 501 502 IfNeigh<family> ret; 503 ret.path = path; 504 ret.ip = ip; 505 ret.mac = stdplus::fromStr<stdplus::EtherAddr>( 506 std::get<std::string>(neighbor.at("MACAddress"))); 507 return ret; 508 } 509 510 return std::nullopt; 511 } 512 513 template <int family> 514 void createNeighbor(sdbusplus::bus_t& bus, const ChannelParams& params, 515 typename AddrFamily<family>::addr address, 516 stdplus::EtherAddr mac) 517 { 518 auto newreq = bus.new_method_call(params.service.c_str(), 519 params.logicalPath.c_str(), 520 INTF_NEIGHBOR_CREATE_STATIC, "Neighbor"); 521 stdplus::ToStrHandle<stdplus::ToStr<stdplus::EtherAddr>> macToStr; 522 stdplus::ToStrHandle<stdplus::ToStr<typename AddrFamily<family>::addr>> 523 addrToStr; 524 newreq.append(addrToStr(address), macToStr(mac)); 525 bus.call_noreply(newreq); 526 } 527 528 /** @brief Deletes the dbus object. Ignores empty objects or objects that are 529 * missing from the bus. 530 * 531 * @param[in] bus - The bus object used for lookups 532 * @param[in] service - The name of the service 533 * @param[in] path - The path of the object to delete 534 */ 535 void deleteObjectIfExists(sdbusplus::bus_t& bus, const std::string& service, 536 const std::string& path); 537 538 /** @brief Sets the value for the default gateway of the channel 539 * 540 * @param[in] bus - The bus object used for lookups 541 * @param[in] params - The parameters for the channel 542 * @param[in] gateway - Gateway address to apply 543 */ 544 template <int family> 545 void setGatewayProperty(sdbusplus::bus_t& bus, const ChannelParams& params, 546 typename AddrFamily<family>::addr address) 547 { 548 // Save the old gateway MAC address if it exists so we can recreate it 549 auto gateway = getGatewayProperty<family>(bus, params); 550 std::optional<IfNeigh<family>> neighbor; 551 if (gateway) 552 { 553 ObjectLookupCache neighbors(bus, params, INTF_NEIGHBOR); 554 neighbor = findStaticNeighbor<family>(bus, params, *gateway, neighbors); 555 } 556 557 auto objPath = "/xyz/openbmc_project/network/" + params.ifname; 558 setDbusProperty(bus, params.service, objPath, INTF_ETHERNET, 559 AddrFamily<family>::propertyGateway, 560 stdplus::toStr(address)); 561 562 // Restore the gateway MAC if we had one 563 if (neighbor) 564 { 565 deleteObjectIfExists(bus, params.service, neighbor->path); 566 createNeighbor<family>(bus, params, address, neighbor->mac); 567 } 568 } 569 570 /** @enum SolConfParam 571 * 572 * using for Set/Get SOL configuration parameters command. 573 */ 574 enum class SolConfParam : uint8_t 575 { 576 Progress, //!< Set In Progress. 577 Enable, //!< SOL Enable. 578 Authentication, //!< SOL Authentication. 579 Accumulate, //!< Character Accumulate Interval & Send Threshold. 580 Retry, //!< SOL Retry. 581 NonVbitrate, //!< SOL non-volatile bit rate. 582 Vbitrate, //!< SOL volatile bit rate. 583 Channel, //!< SOL payload channel. 584 Port, //!< SOL payload port. 585 }; 586 587 constexpr uint8_t ipmiCCParamNotSupported = 0x80; 588 constexpr uint8_t ipmiCCWriteReadParameter = 0x82; 589 590 } // namespace transport 591 } // namespace ipmi 592