1 #include "app/channel.hpp" 2 3 #include <arpa/inet.h> 4 #include <netinet/ether.h> 5 6 #include <array> 7 #include <bitset> 8 #include <cinttypes> 9 #include <cstdint> 10 #include <cstring> 11 #include <fstream> 12 #include <functional> 13 #include <ipmid/api.hpp> 14 #include <ipmid/message.hpp> 15 #include <ipmid/message/types.hpp> 16 #include <ipmid/types.hpp> 17 #include <ipmid/utils.hpp> 18 #include <optional> 19 #include <phosphor-logging/elog-errors.hpp> 20 #include <phosphor-logging/elog.hpp> 21 #include <phosphor-logging/log.hpp> 22 #include <sdbusplus/bus.hpp> 23 #include <sdbusplus/exception.hpp> 24 #include <string> 25 #include <string_view> 26 #include <type_traits> 27 #include <unordered_map> 28 #include <unordered_set> 29 #include <user_channel/channel_layer.hpp> 30 #include <utility> 31 #include <vector> 32 #include <xyz/openbmc_project/Common/error.hpp> 33 #include <xyz/openbmc_project/Network/IP/server.hpp> 34 #include <xyz/openbmc_project/Network/Neighbor/server.hpp> 35 36 using phosphor::logging::commit; 37 using phosphor::logging::elog; 38 using phosphor::logging::entry; 39 using phosphor::logging::level; 40 using phosphor::logging::log; 41 using sdbusplus::xyz::openbmc_project::Common::Error::InternalFailure; 42 using sdbusplus::xyz::openbmc_project::Network::server::IP; 43 using sdbusplus::xyz::openbmc_project::Network::server::Neighbor; 44 45 namespace cipher 46 { 47 48 std::vector<uint8_t> getCipherList() 49 { 50 std::vector<uint8_t> cipherList; 51 52 std::ifstream jsonFile(cipher::configFile); 53 if (!jsonFile.is_open()) 54 { 55 log<level::ERR>("Channel Cipher suites file not found"); 56 elog<InternalFailure>(); 57 } 58 59 auto data = Json::parse(jsonFile, nullptr, false); 60 if (data.is_discarded()) 61 { 62 log<level::ERR>("Parsing channel cipher suites JSON failed"); 63 elog<InternalFailure>(); 64 } 65 66 // Byte 1 is reserved 67 cipherList.push_back(0x00); 68 69 for (const auto& record : data) 70 { 71 cipherList.push_back(record.value(cipher, 0)); 72 } 73 74 return cipherList; 75 } 76 } // namespace cipher 77 78 namespace ipmi 79 { 80 namespace transport 81 { 82 83 // LAN Handler specific response codes 84 constexpr Cc ccParamNotSupported = 0x80; 85 constexpr Cc ccParamSetLocked = 0x81; 86 constexpr Cc ccParamReadOnly = 0x82; 87 88 // VLANs are a 12-bit value 89 constexpr uint16_t VLAN_VALUE_MASK = 0x0fff; 90 constexpr uint16_t VLAN_ENABLE_FLAG = 0x8000; 91 92 // Arbitrary v6 Address Limits to prevent too much output in ipmitool 93 constexpr uint8_t MAX_IPV6_STATIC_ADDRESSES = 15; 94 constexpr uint8_t MAX_IPV6_DYNAMIC_ADDRESSES = 15; 95 96 // D-Bus Network Daemon definitions 97 constexpr auto PATH_ROOT = "/xyz/openbmc_project/network"; 98 constexpr auto PATH_SYSTEMCONFIG = "/xyz/openbmc_project/network/config"; 99 100 constexpr auto INTF_SYSTEMCONFIG = 101 "xyz.openbmc_project.Network.SystemConfiguration"; 102 constexpr auto INTF_ETHERNET = "xyz.openbmc_project.Network.EthernetInterface"; 103 constexpr auto INTF_IP = "xyz.openbmc_project.Network.IP"; 104 constexpr auto INTF_IP_CREATE = "xyz.openbmc_project.Network.IP.Create"; 105 constexpr auto INTF_MAC = "xyz.openbmc_project.Network.MACAddress"; 106 constexpr auto INTF_NEIGHBOR = "xyz.openbmc_project.Network.Neighbor"; 107 constexpr auto INTF_NEIGHBOR_CREATE_STATIC = 108 "xyz.openbmc_project.Network.Neighbor.CreateStatic"; 109 constexpr auto INTF_VLAN = "xyz.openbmc_project.Network.VLAN"; 110 constexpr auto INTF_VLAN_CREATE = "xyz.openbmc_project.Network.VLAN.Create"; 111 112 /** @brief Generic paramters for different address families */ 113 template <int family> 114 struct AddrFamily 115 { 116 }; 117 118 /** @brief Parameter specialization for IPv4 */ 119 template <> 120 struct AddrFamily<AF_INET> 121 { 122 using addr = in_addr; 123 static constexpr auto protocol = IP::Protocol::IPv4; 124 static constexpr size_t maxStrLen = INET6_ADDRSTRLEN; 125 static constexpr uint8_t defaultPrefix = 32; 126 static constexpr char propertyGateway[] = "DefaultGateway"; 127 }; 128 129 /** @brief Parameter specialization for IPv6 */ 130 template <> 131 struct AddrFamily<AF_INET6> 132 { 133 using addr = in6_addr; 134 static constexpr auto protocol = IP::Protocol::IPv6; 135 static constexpr size_t maxStrLen = INET6_ADDRSTRLEN; 136 static constexpr uint8_t defaultPrefix = 128; 137 static constexpr char propertyGateway[] = "DefaultGateway6"; 138 }; 139 140 /** @brief Valid address origins for IPv4 */ 141 const std::unordered_set<IP::AddressOrigin> originsV4 = { 142 IP::AddressOrigin::Static, 143 IP::AddressOrigin::DHCP, 144 }; 145 146 /** @brief Valid address origins for IPv6 */ 147 const std::unordered_set<IP::AddressOrigin> originsV6Static = { 148 IP::AddressOrigin::Static}; 149 const std::unordered_set<IP::AddressOrigin> originsV6Dynamic = { 150 IP::AddressOrigin::DHCP, 151 IP::AddressOrigin::SLAAC, 152 }; 153 154 /** @brief Interface IP Address configuration parameters */ 155 template <int family> 156 struct IfAddr 157 { 158 std::string path; 159 typename AddrFamily<family>::addr address; 160 IP::AddressOrigin origin; 161 uint8_t prefix; 162 }; 163 164 /** @brief Interface Neighbor configuration parameters */ 165 template <int family> 166 struct IfNeigh 167 { 168 std::string path; 169 typename AddrFamily<family>::addr ip; 170 ether_addr mac; 171 }; 172 173 /** @brief IPMI LAN Parameters */ 174 enum class LanParam : uint8_t 175 { 176 SetStatus = 0, 177 AuthSupport = 1, 178 AuthEnables = 2, 179 IP = 3, 180 IPSrc = 4, 181 MAC = 5, 182 SubnetMask = 6, 183 Gateway1 = 12, 184 Gateway1MAC = 13, 185 VLANId = 20, 186 CiphersuiteSupport = 22, 187 CiphersuiteEntries = 23, 188 IPFamilySupport = 50, 189 IPFamilyEnables = 51, 190 IPv6Status = 55, 191 IPv6StaticAddresses = 56, 192 IPv6DynamicAddresses = 59, 193 IPv6RouterControl = 64, 194 IPv6StaticRouter1IP = 65, 195 IPv6StaticRouter1MAC = 66, 196 IPv6StaticRouter1PrefixLength = 67, 197 IPv6StaticRouter1PrefixValue = 68, 198 }; 199 200 static constexpr uint8_t oemCmdStart = 192; 201 static constexpr uint8_t oemCmdEnd = 255; 202 203 /** @brief IPMI IP Origin Types */ 204 enum class IPSrc : uint8_t 205 { 206 Unspecified = 0, 207 Static = 1, 208 DHCP = 2, 209 BIOS = 3, 210 BMC = 4, 211 }; 212 213 /** @brief IPMI Set Status */ 214 enum class SetStatus : uint8_t 215 { 216 Complete = 0, 217 InProgress = 1, 218 Commit = 2, 219 }; 220 221 /** @brief IPMI Family Suport Bits */ 222 namespace IPFamilySupportFlag 223 { 224 constexpr uint8_t IPv6Only = 0; 225 constexpr uint8_t DualStack = 1; 226 constexpr uint8_t IPv6Alerts = 2; 227 } // namespace IPFamilySupportFlag 228 229 /** @brief IPMI IPFamily Enables Flag */ 230 enum class IPFamilyEnables : uint8_t 231 { 232 IPv4Only = 0, 233 IPv6Only = 1, 234 DualStack = 2, 235 }; 236 237 /** @brief IPMI IPv6 Dyanmic Status Bits */ 238 namespace IPv6StatusFlag 239 { 240 constexpr uint8_t DHCP = 0; 241 constexpr uint8_t SLAAC = 1; 242 }; // namespace IPv6StatusFlag 243 244 /** @brief IPMI IPv6 Source */ 245 enum class IPv6Source : uint8_t 246 { 247 Static = 0, 248 SLAAC = 1, 249 DHCP = 2, 250 }; 251 252 /** @brief IPMI IPv6 Address Status */ 253 enum class IPv6AddressStatus : uint8_t 254 { 255 Active = 0, 256 Disabled = 1, 257 }; 258 259 namespace IPv6RouterControlFlag 260 { 261 constexpr uint8_t Static = 0; 262 constexpr uint8_t Dynamic = 1; 263 }; // namespace IPv6RouterControlFlag 264 265 /** @brief A trivial helper used to determine if two PODs are equal 266 * 267 * @params[in] a - The first object to compare 268 * @params[in] b - The second object to compare 269 * @return True if the objects are the same bytewise 270 */ 271 template <typename T> 272 bool equal(const T& a, const T& b) 273 { 274 static_assert(std::is_trivially_copyable_v<T>); 275 return std::memcmp(&a, &b, sizeof(T)) == 0; 276 } 277 278 /** @brief Copies bytes from an array into a trivially copyable container 279 * 280 * @params[out] t - The container receiving the data 281 * @params[in] bytes - The data to copy 282 */ 283 template <size_t N, typename T> 284 void copyInto(T& t, const std::array<uint8_t, N>& bytes) 285 { 286 static_assert(std::is_trivially_copyable_v<T>); 287 static_assert(N == sizeof(T)); 288 std::memcpy(&t, bytes.data(), bytes.size()); 289 } 290 291 /** @brief Gets a generic view of the bytes in the input container 292 * 293 * @params[in] t - The data to reference 294 * @return A string_view referencing the bytes in the container 295 */ 296 template <typename T> 297 std::string_view dataRef(const T& t) 298 { 299 static_assert(std::is_trivially_copyable_v<T>); 300 return {reinterpret_cast<const char*>(&t), sizeof(T)}; 301 } 302 303 /** @brief The dbus parameters for the interface corresponding to a channel 304 * This helps reduce the number of mapper lookups we need for each 305 * query and simplifies finding the VLAN interface if needed. 306 */ 307 struct ChannelParams 308 { 309 /** @brief The channel ID */ 310 int id; 311 /** @brief channel name for the interface */ 312 std::string ifname; 313 /** @brief Name of the service on the bus */ 314 std::string service; 315 /** @brief Lower level adapter path that is guaranteed to not be a VLAN */ 316 std::string ifPath; 317 /** @brief Logical adapter path used for address assignment */ 318 std::string logicalPath; 319 }; 320 321 /** @brief Determines the ethernet interface name corresponding to a channel 322 * Tries to map a VLAN object first so that the address information 323 * is accurate. Otherwise it gets the standard ethernet interface. 324 * 325 * @param[in] bus - The bus object used for lookups 326 * @param[in] channel - The channel id corresponding to an ethernet interface 327 * @return Ethernet interface service and object path if it exists 328 */ 329 std::optional<ChannelParams> maybeGetChannelParams(sdbusplus::bus::bus& bus, 330 uint8_t channel) 331 { 332 auto ifname = getChannelName(channel); 333 if (ifname.empty()) 334 { 335 return std::nullopt; 336 } 337 338 // Enumerate all VLAN + ETHERNET interfaces 339 auto req = bus.new_method_call(MAPPER_BUS_NAME, MAPPER_OBJ, MAPPER_INTF, 340 "GetSubTree"); 341 req.append(PATH_ROOT, 0, 342 std::vector<std::string>{INTF_VLAN, INTF_ETHERNET}); 343 auto reply = bus.call(req); 344 ObjectTree objs; 345 reply.read(objs); 346 347 ChannelParams params; 348 for (const auto& [path, impls] : objs) 349 { 350 if (path.find(ifname) == path.npos) 351 { 352 continue; 353 } 354 for (const auto& [service, intfs] : impls) 355 { 356 bool vlan = false; 357 bool ethernet = false; 358 for (const auto& intf : intfs) 359 { 360 if (intf == INTF_VLAN) 361 { 362 vlan = true; 363 } 364 else if (intf == INTF_ETHERNET) 365 { 366 ethernet = true; 367 } 368 } 369 if (params.service.empty() && (vlan || ethernet)) 370 { 371 params.service = service; 372 } 373 if (params.ifPath.empty() && !vlan && ethernet) 374 { 375 params.ifPath = path; 376 } 377 if (params.logicalPath.empty() && vlan) 378 { 379 params.logicalPath = path; 380 } 381 } 382 } 383 384 // We must have a path for the underlying interface 385 if (params.ifPath.empty()) 386 { 387 return std::nullopt; 388 } 389 // We don't have a VLAN so the logical path is the same 390 if (params.logicalPath.empty()) 391 { 392 params.logicalPath = params.ifPath; 393 } 394 395 params.id = channel; 396 params.ifname = std::move(ifname); 397 return std::move(params); 398 } 399 400 /** @brief A trivial helper around maybeGetChannelParams() that throws an 401 * exception when it is unable to acquire parameters for the channel. 402 * 403 * @param[in] bus - The bus object used for lookups 404 * @param[in] channel - The channel id corresponding to an ethernet interface 405 * @return Ethernet interface service and object path 406 */ 407 ChannelParams getChannelParams(sdbusplus::bus::bus& bus, uint8_t channel) 408 { 409 auto params = maybeGetChannelParams(bus, channel); 410 if (!params) 411 { 412 log<level::ERR>("Failed to get channel params", 413 entry("CHANNEL=%" PRIu8, channel)); 414 elog<InternalFailure>(); 415 } 416 return std::move(*params); 417 } 418 419 /** @brief Wraps the phosphor logging method to insert some additional metadata 420 * 421 * @param[in] params - The parameters for the channel 422 * ... 423 */ 424 template <auto level, typename... Args> 425 auto logWithChannel(const ChannelParams& params, Args&&... args) 426 { 427 return log<level>(std::forward<Args>(args)..., 428 entry("CHANNEL=%d", params.id), 429 entry("IFNAME=%s", params.ifname.c_str())); 430 } 431 template <auto level, typename... Args> 432 auto logWithChannel(const std::optional<ChannelParams>& params, Args&&... args) 433 { 434 if (params) 435 { 436 return logWithChannel<level>(*params, std::forward<Args>(args)...); 437 } 438 return log<level>(std::forward<Args>(args)...); 439 } 440 441 /** @brief Trivializes using parameter getter functions by providing a bus 442 * and channel parameters automatically. 443 * 444 * @param[in] channel - The channel id corresponding to an ethernet interface 445 * ... 446 */ 447 template <auto func, typename... Args> 448 auto channelCall(uint8_t channel, Args&&... args) 449 { 450 sdbusplus::bus::bus bus(ipmid_get_sd_bus_connection()); 451 auto params = getChannelParams(bus, channel); 452 return std::invoke(func, bus, params, std::forward<Args>(args)...); 453 } 454 455 /** @brief Determines if the ethernet interface is using DHCP 456 * 457 * @param[in] bus - The bus object used for lookups 458 * @param[in] params - The parameters for the channel 459 * @return True if DHCP is enabled, false otherwise 460 */ 461 bool getDHCPProperty(sdbusplus::bus::bus& bus, const ChannelParams& params) 462 { 463 return std::get<bool>(getDbusProperty( 464 bus, params.service, params.logicalPath, INTF_ETHERNET, "DHCPEnabled")); 465 } 466 467 /** @brief Sets the system value for DHCP on the given interface 468 * 469 * @param[in] bus - The bus object used for lookups 470 * @param[in] params - The parameters for the channel 471 * @param[in] on - Whether or not to enable DHCP 472 */ 473 void setDHCPProperty(sdbusplus::bus::bus& bus, const ChannelParams& params, 474 bool on) 475 { 476 setDbusProperty(bus, params.service, params.logicalPath, INTF_ETHERNET, 477 "DHCPEnabled", on); 478 } 479 480 /** @brief Converts a human readable MAC string into MAC bytes 481 * 482 * @param[in] mac - The MAC string 483 * @return MAC in bytes 484 */ 485 ether_addr stringToMAC(const char* mac) 486 { 487 const ether_addr* ret = ether_aton(mac); 488 if (ret == nullptr) 489 { 490 log<level::ERR>("Invalid MAC Address", entry("MAC=%s", mac)); 491 elog<InternalFailure>(); 492 } 493 return *ret; 494 } 495 496 /** @brief Determines the MAC of the ethernet interface 497 * 498 * @param[in] bus - The bus object used for lookups 499 * @param[in] params - The parameters for the channel 500 * @return The configured mac address 501 */ 502 ether_addr getMACProperty(sdbusplus::bus::bus& bus, const ChannelParams& params) 503 { 504 auto macStr = std::get<std::string>(getDbusProperty( 505 bus, params.service, params.ifPath, INTF_MAC, "MACAddress")); 506 return stringToMAC(macStr.c_str()); 507 } 508 509 /** @brief Sets the system value for MAC address on the given interface 510 * 511 * @param[in] bus - The bus object used for lookups 512 * @param[in] params - The parameters for the channel 513 * @param[in] mac - MAC address to apply 514 */ 515 void setMACProperty(sdbusplus::bus::bus& bus, const ChannelParams& params, 516 const ether_addr& mac) 517 { 518 std::string macStr = ether_ntoa(&mac); 519 setDbusProperty(bus, params.service, params.ifPath, INTF_MAC, "MACAddress", 520 macStr); 521 } 522 523 /** @brief Turns an IP address string into the network byte order form 524 * NOTE: This version strictly validates family matches 525 * 526 * @param[in] address - The string form of the address 527 * @return A network byte order address or none if conversion failed 528 */ 529 template <int family> 530 std::optional<typename AddrFamily<family>::addr> 531 maybeStringToAddr(const char* address) 532 { 533 typename AddrFamily<family>::addr ret; 534 if (inet_pton(family, address, &ret) == 1) 535 { 536 return ret; 537 } 538 return std::nullopt; 539 } 540 541 /** @brief Turns an IP address string into the network byte order form 542 * NOTE: This version strictly validates family matches 543 * 544 * @param[in] address - The string form of the address 545 * @return A network byte order address 546 */ 547 template <int family> 548 typename AddrFamily<family>::addr stringToAddr(const char* address) 549 { 550 auto ret = maybeStringToAddr<family>(address); 551 if (!ret) 552 { 553 log<level::ERR>("Failed to convert IP Address", 554 entry("FAMILY=%d", family), 555 entry("ADDRESS=%s", address)); 556 elog<InternalFailure>(); 557 } 558 return *ret; 559 } 560 561 /** @brief Turns an IP address in network byte order into a string 562 * 563 * @param[in] address - The string form of the address 564 * @return A network byte order address 565 */ 566 template <int family> 567 std::string addrToString(const typename AddrFamily<family>::addr& address) 568 { 569 std::string ret(AddrFamily<family>::maxStrLen, '\0'); 570 inet_ntop(family, &address, ret.data(), ret.size()); 571 ret.resize(strlen(ret.c_str())); 572 return ret; 573 } 574 575 /** @brief Retrieves the current gateway for the address family on the system 576 * NOTE: The gateway is currently system wide and not per channel 577 * 578 * @param[in] bus - The bus object used for lookups 579 * @param[in] params - The parameters for the channel 580 * @return An address representing the gateway address if it exists 581 */ 582 template <int family> 583 std::optional<typename AddrFamily<family>::addr> 584 getGatewayProperty(sdbusplus::bus::bus& bus, const ChannelParams& params) 585 { 586 auto gatewayStr = std::get<std::string>(getDbusProperty( 587 bus, params.service, PATH_SYSTEMCONFIG, INTF_SYSTEMCONFIG, 588 AddrFamily<family>::propertyGateway)); 589 if (gatewayStr.empty()) 590 { 591 return std::nullopt; 592 } 593 return stringToAddr<family>(gatewayStr.c_str()); 594 } 595 596 /** @brief A lazy lookup mechanism for iterating over object properties stored 597 * in DBus. This will only perform the object lookup when needed, and 598 * retains a cache of previous lookups to speed up future iterations. 599 */ 600 class ObjectLookupCache 601 { 602 public: 603 using PropertiesCache = std::unordered_map<std::string, PropertyMap>; 604 605 /** @brief Creates a new ObjectLookupCache for the interface on the bus 606 * NOTE: The inputs to this object must outlive the object since 607 * they are only referenced by it. 608 * 609 * @param[in] bus - The bus object used for lookups 610 * @param[in] params - The parameters for the channel 611 * @param[in] intf - The interface we are looking up 612 */ 613 ObjectLookupCache(sdbusplus::bus::bus& bus, const ChannelParams& params, 614 const char* intf) : 615 bus(bus), 616 params(params), intf(intf), 617 objs(getAllDbusObjects(bus, params.logicalPath, intf, "")) 618 { 619 } 620 621 class iterator : public ObjectTree::const_iterator 622 { 623 public: 624 using value_type = PropertiesCache::value_type; 625 626 iterator(ObjectTree::const_iterator it, ObjectLookupCache& container) : 627 ObjectTree::const_iterator(it), container(container), 628 ret(container.cache.end()) 629 { 630 } 631 value_type& operator*() 632 { 633 ret = container.get(ObjectTree::const_iterator::operator*().first); 634 return *ret; 635 } 636 value_type* operator->() 637 { 638 return &operator*(); 639 } 640 641 private: 642 ObjectLookupCache& container; 643 PropertiesCache::iterator ret; 644 }; 645 646 iterator begin() noexcept 647 { 648 return iterator(objs.begin(), *this); 649 } 650 651 iterator end() noexcept 652 { 653 return iterator(objs.end(), *this); 654 } 655 656 private: 657 sdbusplus::bus::bus& bus; 658 const ChannelParams& params; 659 const char* const intf; 660 const ObjectTree objs; 661 PropertiesCache cache; 662 663 /** @brief Gets a cached copy of the object properties if possible 664 * Otherwise performs a query on DBus to look them up 665 * 666 * @param[in] path - The object path to lookup 667 * @return An iterator for the specified object path + properties 668 */ 669 PropertiesCache::iterator get(const std::string& path) 670 { 671 auto it = cache.find(path); 672 if (it != cache.end()) 673 { 674 return it; 675 } 676 auto properties = getAllDbusProperties(bus, params.service, path, intf); 677 return cache.insert({path, std::move(properties)}).first; 678 } 679 }; 680 681 /** @brief Searches the ip object lookup cache for an address matching 682 * the input parameters. NOTE: The index lacks stability across address 683 * changes since the network daemon has no notion of stable indicies. 684 * 685 * @param[in] bus - The bus object used for lookups 686 * @param[in] params - The parameters for the channel 687 * @param[in] idx - The index of the desired address on the interface 688 * @param[in] origins - The allowed origins for the address objects 689 * @param[in] ips - The object lookup cache holding all of the address info 690 * @return The address and prefix if it was found 691 */ 692 template <int family> 693 std::optional<IfAddr<family>> 694 findIfAddr(sdbusplus::bus::bus& bus, const ChannelParams& params, 695 uint8_t idx, 696 const std::unordered_set<IP::AddressOrigin>& origins, 697 ObjectLookupCache& ips) 698 { 699 for (const auto& [path, properties] : ips) 700 { 701 const auto& addrStr = std::get<std::string>(properties.at("Address")); 702 auto addr = maybeStringToAddr<family>(addrStr.c_str()); 703 if (!addr) 704 { 705 continue; 706 } 707 708 IP::AddressOrigin origin = IP::convertAddressOriginFromString( 709 std::get<std::string>(properties.at("Origin"))); 710 if (origins.find(origin) == origins.end()) 711 { 712 continue; 713 } 714 715 if (idx > 0) 716 { 717 idx--; 718 continue; 719 } 720 721 IfAddr<family> ifaddr; 722 ifaddr.path = path; 723 ifaddr.address = *addr; 724 ifaddr.prefix = std::get<uint8_t>(properties.at("PrefixLength")); 725 ifaddr.origin = origin; 726 return std::move(ifaddr); 727 } 728 729 return std::nullopt; 730 } 731 732 /** @brief Trivial helper around findIfAddr that simplifies calls 733 * for one off lookups. Don't use this if you intend to do multiple 734 * lookups at a time. 735 * 736 * @param[in] bus - The bus object used for lookups 737 * @param[in] params - The parameters for the channel 738 * @param[in] idx - The index of the desired address on the interface 739 * @param[in] origins - The allowed origins for the address objects 740 * @return The address and prefix if it was found 741 */ 742 template <int family> 743 auto getIfAddr(sdbusplus::bus::bus& bus, const ChannelParams& params, 744 uint8_t idx, 745 const std::unordered_set<IP::AddressOrigin>& origins) 746 { 747 ObjectLookupCache ips(bus, params, INTF_IP); 748 return findIfAddr<family>(bus, params, idx, origins, ips); 749 } 750 751 /** @brief Deletes the dbus object. Ignores empty objects or objects that are 752 * missing from the bus. 753 * 754 * @param[in] bus - The bus object used for lookups 755 * @param[in] service - The name of the service 756 * @param[in] path - The path of the object to delete 757 */ 758 void deleteObjectIfExists(sdbusplus::bus::bus& bus, const std::string& service, 759 const std::string& path) 760 { 761 if (path.empty()) 762 { 763 return; 764 } 765 try 766 { 767 auto req = bus.new_method_call(service.c_str(), path.c_str(), 768 ipmi::DELETE_INTERFACE, "Delete"); 769 bus.call_noreply(req); 770 } 771 catch (const sdbusplus::exception::SdBusError& e) 772 { 773 if (strcmp(e.name(), "org.freedesktop.DBus.Error.UnknownObject") != 0) 774 { 775 // We want to rethrow real errors 776 throw; 777 } 778 } 779 } 780 781 /** @brief Sets the address info configured for the interface 782 * If a previous address path exists then it will be removed 783 * before the new address is added. 784 * 785 * @param[in] bus - The bus object used for lookups 786 * @param[in] params - The parameters for the channel 787 * @param[in] address - The address of the new IP 788 * @param[in] prefix - The prefix of the new IP 789 */ 790 template <int family> 791 void createIfAddr(sdbusplus::bus::bus& bus, const ChannelParams& params, 792 const typename AddrFamily<family>::addr& address, 793 uint8_t prefix) 794 { 795 auto newreq = 796 bus.new_method_call(params.service.c_str(), params.logicalPath.c_str(), 797 INTF_IP_CREATE, "IP"); 798 std::string protocol = 799 sdbusplus::xyz::openbmc_project::Network::server::convertForMessage( 800 AddrFamily<family>::protocol); 801 newreq.append(protocol, addrToString<family>(address), prefix, ""); 802 bus.call_noreply(newreq); 803 } 804 805 /** @brief Trivial helper for getting the IPv4 address from getIfAddrs() 806 * 807 * @param[in] bus - The bus object used for lookups 808 * @param[in] params - The parameters for the channel 809 * @return The address and prefix if found 810 */ 811 auto getIfAddr4(sdbusplus::bus::bus& bus, const ChannelParams& params) 812 { 813 return getIfAddr<AF_INET>(bus, params, 0, originsV4); 814 } 815 816 /** @brief Reconfigures the IPv4 address info configured for the interface 817 * 818 * @param[in] bus - The bus object used for lookups 819 * @param[in] params - The parameters for the channel 820 * @param[in] address - The new address if specified 821 * @param[in] prefix - The new address prefix if specified 822 */ 823 void reconfigureIfAddr4(sdbusplus::bus::bus& bus, const ChannelParams& params, 824 const std::optional<in_addr>& address, 825 std::optional<uint8_t> prefix) 826 { 827 auto ifaddr = getIfAddr4(bus, params); 828 if (!ifaddr && !address) 829 { 830 log<level::ERR>("Missing address for IPv4 assignment"); 831 elog<InternalFailure>(); 832 } 833 uint8_t fallbackPrefix = AddrFamily<AF_INET>::defaultPrefix; 834 if (ifaddr) 835 { 836 fallbackPrefix = ifaddr->prefix; 837 deleteObjectIfExists(bus, params.service, ifaddr->path); 838 } 839 createIfAddr<AF_INET>(bus, params, address.value_or(ifaddr->address), 840 prefix.value_or(fallbackPrefix)); 841 } 842 843 template <int family> 844 std::optional<IfNeigh<family>> 845 findStaticNeighbor(sdbusplus::bus::bus& bus, const ChannelParams& params, 846 const typename AddrFamily<family>::addr& ip, 847 ObjectLookupCache& neighbors) 848 { 849 const auto state = 850 sdbusplus::xyz::openbmc_project::Network::server::convertForMessage( 851 Neighbor::State::Permanent); 852 for (const auto& [path, neighbor] : neighbors) 853 { 854 const auto& ipStr = std::get<std::string>(neighbor.at("IPAddress")); 855 auto neighIP = maybeStringToAddr<family>(ipStr.c_str()); 856 if (!neighIP) 857 { 858 continue; 859 } 860 if (!equal(*neighIP, ip)) 861 { 862 continue; 863 } 864 if (state != std::get<std::string>(neighbor.at("State"))) 865 { 866 continue; 867 } 868 869 IfNeigh<family> ret; 870 ret.path = path; 871 ret.ip = ip; 872 const auto& macStr = std::get<std::string>(neighbor.at("MACAddress")); 873 ret.mac = stringToMAC(macStr.c_str()); 874 return std::move(ret); 875 } 876 877 return std::nullopt; 878 } 879 880 template <int family> 881 void createNeighbor(sdbusplus::bus::bus& bus, const ChannelParams& params, 882 const typename AddrFamily<family>::addr& address, 883 const ether_addr& mac) 884 { 885 auto newreq = 886 bus.new_method_call(params.service.c_str(), params.logicalPath.c_str(), 887 INTF_NEIGHBOR_CREATE_STATIC, "Neighbor"); 888 std::string macStr = ether_ntoa(&mac); 889 newreq.append(addrToString<family>(address), macStr); 890 bus.call_noreply(newreq); 891 } 892 893 /** @brief Sets the system wide value for the default gateway 894 * 895 * @param[in] bus - The bus object used for lookups 896 * @param[in] params - The parameters for the channel 897 * @param[in] gateway - Gateway address to apply 898 */ 899 template <int family> 900 void setGatewayProperty(sdbusplus::bus::bus& bus, const ChannelParams& params, 901 const typename AddrFamily<family>::addr& address) 902 { 903 // Save the old gateway MAC address if it exists so we can recreate it 904 auto gateway = getGatewayProperty<family>(bus, params); 905 std::optional<IfNeigh<family>> neighbor; 906 if (gateway) 907 { 908 ObjectLookupCache neighbors(bus, params, INTF_NEIGHBOR); 909 neighbor = findStaticNeighbor<family>(bus, params, *gateway, neighbors); 910 } 911 912 setDbusProperty(bus, params.service, PATH_SYSTEMCONFIG, INTF_SYSTEMCONFIG, 913 AddrFamily<family>::propertyGateway, 914 addrToString<family>(address)); 915 916 // Restore the gateway MAC if we had one 917 if (neighbor) 918 { 919 deleteObjectIfExists(bus, params.service, neighbor->path); 920 createNeighbor<family>(bus, params, address, neighbor->mac); 921 } 922 } 923 924 template <int family> 925 std::optional<IfNeigh<family>> findGatewayNeighbor(sdbusplus::bus::bus& bus, 926 const ChannelParams& params, 927 ObjectLookupCache& neighbors) 928 { 929 auto gateway = getGatewayProperty<family>(bus, params); 930 if (!gateway) 931 { 932 return std::nullopt; 933 } 934 935 return findStaticNeighbor<family>(bus, params, *gateway, neighbors); 936 } 937 938 template <int family> 939 std::optional<IfNeigh<family>> getGatewayNeighbor(sdbusplus::bus::bus& bus, 940 const ChannelParams& params) 941 { 942 ObjectLookupCache neighbors(bus, params, INTF_NEIGHBOR); 943 return findGatewayNeighbor<family>(bus, params, neighbors); 944 } 945 946 template <int family> 947 void reconfigureGatewayMAC(sdbusplus::bus::bus& bus, 948 const ChannelParams& params, const ether_addr& mac) 949 { 950 auto gateway = getGatewayProperty<family>(bus, params); 951 if (!gateway) 952 { 953 log<level::ERR>("Tried to set Gateway MAC without Gateway"); 954 elog<InternalFailure>(); 955 } 956 957 ObjectLookupCache neighbors(bus, params, INTF_NEIGHBOR); 958 auto neighbor = 959 findStaticNeighbor<family>(bus, params, *gateway, neighbors); 960 if (neighbor) 961 { 962 deleteObjectIfExists(bus, params.service, neighbor->path); 963 } 964 965 createNeighbor<family>(bus, params, *gateway, mac); 966 } 967 968 /** @brief Deconfigures the IPv6 address info configured for the interface 969 * 970 * @param[in] bus - The bus object used for lookups 971 * @param[in] params - The parameters for the channel 972 * @param[in] idx - The address index to operate on 973 */ 974 void deconfigureIfAddr6(sdbusplus::bus::bus& bus, const ChannelParams& params, 975 uint8_t idx) 976 { 977 auto ifaddr = getIfAddr<AF_INET6>(bus, params, idx, originsV6Static); 978 if (ifaddr) 979 { 980 deleteObjectIfExists(bus, params.service, ifaddr->path); 981 } 982 } 983 984 /** @brief Reconfigures the IPv6 address info configured for the interface 985 * 986 * @param[in] bus - The bus object used for lookups 987 * @param[in] params - The parameters for the channel 988 * @param[in] idx - The address index to operate on 989 * @param[in] address - The new address 990 * @param[in] prefix - The new address prefix 991 */ 992 void reconfigureIfAddr6(sdbusplus::bus::bus& bus, const ChannelParams& params, 993 uint8_t idx, const in6_addr& address, uint8_t prefix) 994 { 995 deconfigureIfAddr6(bus, params, idx); 996 createIfAddr<AF_INET6>(bus, params, address, prefix); 997 } 998 999 /** @brief Converts the AddressOrigin into an IPv6Source 1000 * 1001 * @param[in] origin - The DBus Address Origin to convert 1002 * @return The IPv6Source version of the origin 1003 */ 1004 IPv6Source originToSourceType(IP::AddressOrigin origin) 1005 { 1006 switch (origin) 1007 { 1008 case IP::AddressOrigin::Static: 1009 return IPv6Source::Static; 1010 case IP::AddressOrigin::DHCP: 1011 return IPv6Source::DHCP; 1012 case IP::AddressOrigin::SLAAC: 1013 return IPv6Source::SLAAC; 1014 default: 1015 { 1016 auto originStr = sdbusplus::xyz::openbmc_project::Network::server:: 1017 convertForMessage(origin); 1018 log<level::ERR>( 1019 "Invalid IP::AddressOrigin conversion to IPv6Source", 1020 entry("ORIGIN=%s", originStr.c_str())); 1021 elog<InternalFailure>(); 1022 } 1023 } 1024 } 1025 1026 /** @brief Packs the IPMI message response with IPv6 address data 1027 * 1028 * @param[out] ret - The IPMI response payload to be packed 1029 * @param[in] channel - The channel id corresponding to an ethernet interface 1030 * @param[in] set - The set selector for determining address index 1031 * @param[in] origins - Set of valid origins for address filtering 1032 */ 1033 void getLanIPv6Address(message::Payload& ret, uint8_t channel, uint8_t set, 1034 const std::unordered_set<IP::AddressOrigin>& origins) 1035 { 1036 auto source = IPv6Source::Static; 1037 bool enabled = false; 1038 in6_addr addr{}; 1039 uint8_t prefix = AddrFamily<AF_INET6>::defaultPrefix; 1040 auto status = IPv6AddressStatus::Disabled; 1041 1042 auto ifaddr = channelCall<getIfAddr<AF_INET6>>(channel, set, origins); 1043 if (ifaddr) 1044 { 1045 source = originToSourceType(ifaddr->origin); 1046 enabled = true; 1047 addr = ifaddr->address; 1048 prefix = ifaddr->prefix; 1049 status = IPv6AddressStatus::Active; 1050 } 1051 1052 ret.pack(set); 1053 ret.pack(static_cast<uint4_t>(source), uint3_t{}, enabled); 1054 ret.pack(std::string_view(reinterpret_cast<char*>(&addr), sizeof(addr))); 1055 ret.pack(prefix); 1056 ret.pack(static_cast<uint8_t>(status)); 1057 } 1058 1059 /** @brief Gets the vlan ID configured on the interface 1060 * 1061 * @param[in] bus - The bus object used for lookups 1062 * @param[in] params - The parameters for the channel 1063 * @return VLAN id or the standard 0 for no VLAN 1064 */ 1065 uint16_t getVLANProperty(sdbusplus::bus::bus& bus, const ChannelParams& params) 1066 { 1067 // VLAN devices will always have a separate logical object 1068 if (params.ifPath == params.logicalPath) 1069 { 1070 return 0; 1071 } 1072 1073 auto vlan = std::get<uint32_t>(getDbusProperty( 1074 bus, params.service, params.logicalPath, INTF_VLAN, "Id")); 1075 if ((vlan & VLAN_VALUE_MASK) != vlan) 1076 { 1077 logWithChannel<level::ERR>(params, "networkd returned an invalid vlan", 1078 entry("VLAN=%" PRIu32, vlan)); 1079 elog<InternalFailure>(); 1080 } 1081 return vlan; 1082 } 1083 1084 /** @brief Deletes all of the possible configuration parameters for a channel 1085 * 1086 * @param[in] bus - The bus object used for lookups 1087 * @param[in] params - The parameters for the channel 1088 */ 1089 void deconfigureChannel(sdbusplus::bus::bus& bus, ChannelParams& params) 1090 { 1091 // Delete all objects associated with the interface 1092 auto objreq = bus.new_method_call(MAPPER_BUS_NAME, MAPPER_OBJ, MAPPER_INTF, 1093 "GetSubTree"); 1094 objreq.append(PATH_ROOT, 0, std::vector<std::string>{DELETE_INTERFACE}); 1095 auto objreply = bus.call(objreq); 1096 ObjectTree objs; 1097 objreply.read(objs); 1098 for (const auto& [path, impls] : objs) 1099 { 1100 if (path.find(params.ifname) == path.npos) 1101 { 1102 continue; 1103 } 1104 for (const auto& [service, intfs] : impls) 1105 { 1106 deleteObjectIfExists(bus, service, path); 1107 } 1108 // Update params to reflect the deletion of vlan 1109 if (path == params.logicalPath) 1110 { 1111 params.logicalPath = params.ifPath; 1112 } 1113 } 1114 1115 // Clear out any settings on the lower physical interface 1116 setDHCPProperty(bus, params, false); 1117 } 1118 1119 /** @brief Creates a new VLAN on the specified interface 1120 * 1121 * @param[in] bus - The bus object used for lookups 1122 * @param[in] params - The parameters for the channel 1123 * @param[in] vlan - The id of the new vlan 1124 */ 1125 void createVLAN(sdbusplus::bus::bus& bus, ChannelParams& params, uint16_t vlan) 1126 { 1127 if (vlan == 0) 1128 { 1129 return; 1130 } 1131 1132 auto req = bus.new_method_call(params.service.c_str(), PATH_ROOT, 1133 INTF_VLAN_CREATE, "VLAN"); 1134 req.append(params.ifname, static_cast<uint32_t>(vlan)); 1135 auto reply = bus.call(req); 1136 sdbusplus::message::object_path newPath; 1137 reply.read(newPath); 1138 params.logicalPath = std::move(newPath); 1139 } 1140 1141 /** @brief Performs the necessary reconfiguration to change the VLAN 1142 * 1143 * @param[in] bus - The bus object used for lookups 1144 * @param[in] params - The parameters for the channel 1145 * @param[in] vlan - The new vlan id to use 1146 */ 1147 void reconfigureVLAN(sdbusplus::bus::bus& bus, ChannelParams& params, 1148 uint16_t vlan) 1149 { 1150 // Unfortunatetly we don't have built-in functions to migrate our interface 1151 // customizations to new VLAN interfaces, or have some kind of decoupling. 1152 // We therefore must retain all of our old information, setup the new VLAN 1153 // configuration, then restore the old info. 1154 1155 // Save info from the old logical interface 1156 ObjectLookupCache ips(bus, params, INTF_IP); 1157 auto ifaddr4 = findIfAddr<AF_INET>(bus, params, 0, originsV4, ips); 1158 std::vector<IfAddr<AF_INET6>> ifaddrs6; 1159 for (uint8_t i = 0; i < MAX_IPV6_STATIC_ADDRESSES; ++i) 1160 { 1161 auto ifaddr6 = 1162 findIfAddr<AF_INET6>(bus, params, i, originsV6Static, ips); 1163 if (!ifaddr6) 1164 { 1165 break; 1166 } 1167 ifaddrs6.push_back(std::move(*ifaddr6)); 1168 } 1169 auto dhcp = getDHCPProperty(bus, params); 1170 ObjectLookupCache neighbors(bus, params, INTF_NEIGHBOR); 1171 auto neighbor4 = findGatewayNeighbor<AF_INET>(bus, params, neighbors); 1172 auto neighbor6 = findGatewayNeighbor<AF_INET6>(bus, params, neighbors); 1173 1174 deconfigureChannel(bus, params); 1175 createVLAN(bus, params, vlan); 1176 1177 // Re-establish the saved settings 1178 setDHCPProperty(bus, params, dhcp); 1179 if (ifaddr4) 1180 { 1181 createIfAddr<AF_INET>(bus, params, ifaddr4->address, ifaddr4->prefix); 1182 } 1183 for (const auto& ifaddr6 : ifaddrs6) 1184 { 1185 createIfAddr<AF_INET6>(bus, params, ifaddr6.address, ifaddr6.prefix); 1186 } 1187 if (neighbor4) 1188 { 1189 createNeighbor<AF_INET>(bus, params, neighbor4->ip, neighbor4->mac); 1190 } 1191 if (neighbor6) 1192 { 1193 createNeighbor<AF_INET6>(bus, params, neighbor6->ip, neighbor6->mac); 1194 } 1195 } 1196 1197 /** @brief Turns a prefix into a netmask 1198 * 1199 * @param[in] prefix - The prefix length 1200 * @return The netmask 1201 */ 1202 in_addr prefixToNetmask(uint8_t prefix) 1203 { 1204 if (prefix > 32) 1205 { 1206 log<level::ERR>("Invalid prefix", entry("PREFIX=%" PRIu8, prefix)); 1207 elog<InternalFailure>(); 1208 } 1209 if (prefix == 0) 1210 { 1211 // Avoids 32-bit lshift by 32 UB 1212 return {}; 1213 } 1214 return {htobe32(~UINT32_C(0) << (32 - prefix))}; 1215 } 1216 1217 /** @brief Turns a a netmask into a prefix length 1218 * 1219 * @param[in] netmask - The netmask in byte form 1220 * @return The prefix length 1221 */ 1222 uint8_t netmaskToPrefix(in_addr netmask) 1223 { 1224 uint32_t x = be32toh(netmask.s_addr); 1225 if ((~x & (~x + 1)) != 0) 1226 { 1227 char maskStr[INET_ADDRSTRLEN]; 1228 inet_ntop(AF_INET, &netmask, maskStr, sizeof(maskStr)); 1229 log<level::ERR>("Invalid netmask", entry("NETMASK=%s", maskStr)); 1230 elog<InternalFailure>(); 1231 } 1232 return static_cast<bool>(x) 1233 ? AddrFamily<AF_INET>::defaultPrefix - __builtin_ctz(x) 1234 : 0; 1235 } 1236 1237 // We need to store this value so it can be returned to the client 1238 // It is volatile so safe to store in daemon memory. 1239 static std::unordered_map<uint8_t, SetStatus> setStatus; 1240 1241 // Until we have good support for fixed versions of IPMI tool 1242 // we need to return the VLAN id for disabled VLANs. The value is only 1243 // used for verification that a disable operation succeeded and will only 1244 // be sent if our system indicates that vlans are disabled. 1245 static std::unordered_map<uint8_t, uint16_t> lastDisabledVlan; 1246 1247 /** @brief Gets the set status for the channel if it exists 1248 * Otherise populates and returns the default value. 1249 * 1250 * @param[in] channel - The channel id corresponding to an ethernet interface 1251 * @return A reference to the SetStatus for the channel 1252 */ 1253 SetStatus& getSetStatus(uint8_t channel) 1254 { 1255 auto it = setStatus.find(channel); 1256 if (it != setStatus.end()) 1257 { 1258 return it->second; 1259 } 1260 return setStatus[channel] = SetStatus::Complete; 1261 } 1262 1263 /** 1264 * Define placeholder command handlers for the OEM Extension bytes for the Set 1265 * LAN Configuration Parameters and Get LAN Configuration Parameters 1266 * commands. Using "weak" linking allows the placeholder setLanOem/getLanOem 1267 * functions below to be overridden. 1268 * To create handlers for your own proprietary command set: 1269 * Create/modify a phosphor-ipmi-host Bitbake append file within your Yocto 1270 * recipe 1271 * Create C++ file(s) that define IPMI handler functions matching the 1272 * function names below (i.e. setLanOem). The default name for the 1273 * transport IPMI commands is transporthandler_oem.cpp. 1274 * Add: 1275 * EXTRA_OECONF_append = " --enable-transport-oem=yes" 1276 * Create a do_compile_prepend()/do_install_append method in your 1277 * bbappend file to copy the file to the build directory. 1278 * Add: 1279 * PROJECT_SRC_DIR := "${THISDIR}/${PN}" 1280 * # Copy the "strong" functions into the working directory, overriding the 1281 * # placeholder functions. 1282 * do_compile_prepend(){ 1283 * cp -f ${PROJECT_SRC_DIR}/transporthandler_oem.cpp ${S} 1284 * } 1285 * 1286 * # Clean up after complilation has completed 1287 * do_install_append(){ 1288 * rm -f ${S}/transporthandler_oem.cpp 1289 * } 1290 * 1291 */ 1292 1293 /** 1294 * Define the placeholder OEM commands as having weak linkage. Create 1295 * setLanOem, and getLanOem functions in the transporthandler_oem.cpp 1296 * file. The functions defined there must not have the "weak" attribute 1297 * applied to them. 1298 */ 1299 RspType<> setLanOem(uint8_t channel, uint8_t parameter, message::Payload& req) 1300 __attribute__((weak)); 1301 RspType<message::Payload> getLanOem(uint8_t channel, uint8_t parameter, 1302 uint8_t set, uint8_t block) 1303 __attribute__((weak)); 1304 1305 RspType<> setLanOem(uint8_t channel, uint8_t parameter, message::Payload& req) 1306 { 1307 req.trailingOk = true; 1308 return response(ccParamNotSupported); 1309 } 1310 1311 RspType<message::Payload> getLanOem(uint8_t channel, uint8_t parameter, 1312 uint8_t set, uint8_t block) 1313 { 1314 return response(ccParamNotSupported); 1315 } 1316 1317 RspType<> setLan(uint4_t channelBits, uint4_t, uint8_t parameter, 1318 message::Payload& req) 1319 { 1320 auto channel = static_cast<uint8_t>(channelBits); 1321 if (!doesDeviceExist(channel)) 1322 { 1323 req.trailingOk = true; 1324 return responseInvalidFieldRequest(); 1325 } 1326 1327 switch (static_cast<LanParam>(parameter)) 1328 { 1329 case LanParam::SetStatus: 1330 { 1331 uint2_t flag; 1332 uint6_t rsvd; 1333 if (req.unpack(flag, rsvd) != 0 || !req.fullyUnpacked()) 1334 { 1335 return responseReqDataLenInvalid(); 1336 } 1337 if (rsvd) 1338 { 1339 return responseInvalidFieldRequest(); 1340 } 1341 auto status = static_cast<SetStatus>(static_cast<uint8_t>(flag)); 1342 switch (status) 1343 { 1344 case SetStatus::Complete: 1345 { 1346 getSetStatus(channel) = status; 1347 return responseSuccess(); 1348 } 1349 case SetStatus::InProgress: 1350 { 1351 auto& storedStatus = getSetStatus(channel); 1352 if (storedStatus == SetStatus::InProgress) 1353 { 1354 return response(ccParamSetLocked); 1355 } 1356 storedStatus = status; 1357 return responseSuccess(); 1358 } 1359 case SetStatus::Commit: 1360 if (getSetStatus(channel) != SetStatus::InProgress) 1361 { 1362 return responseInvalidFieldRequest(); 1363 } 1364 return responseSuccess(); 1365 } 1366 return response(ccParamNotSupported); 1367 } 1368 case LanParam::AuthSupport: 1369 { 1370 req.trailingOk = true; 1371 return response(ccParamReadOnly); 1372 } 1373 case LanParam::AuthEnables: 1374 { 1375 req.trailingOk = true; 1376 return response(ccParamReadOnly); 1377 } 1378 case LanParam::IP: 1379 { 1380 in_addr ip; 1381 std::array<uint8_t, sizeof(ip)> bytes; 1382 if (req.unpack(bytes) != 0 || !req.fullyUnpacked()) 1383 { 1384 return responseReqDataLenInvalid(); 1385 } 1386 copyInto(ip, bytes); 1387 channelCall<reconfigureIfAddr4>(channel, ip, std::nullopt); 1388 return responseSuccess(); 1389 } 1390 case LanParam::IPSrc: 1391 { 1392 uint4_t flag; 1393 uint4_t rsvd; 1394 if (req.unpack(flag, rsvd) != 0 || !req.fullyUnpacked()) 1395 { 1396 return responseReqDataLenInvalid(); 1397 } 1398 if (rsvd) 1399 { 1400 return responseInvalidFieldRequest(); 1401 } 1402 switch (static_cast<IPSrc>(static_cast<uint8_t>(flag))) 1403 { 1404 case IPSrc::DHCP: 1405 { 1406 channelCall<setDHCPProperty>(channel, true); 1407 return responseSuccess(); 1408 } 1409 case IPSrc::Unspecified: 1410 case IPSrc::Static: 1411 case IPSrc::BIOS: 1412 case IPSrc::BMC: 1413 { 1414 channelCall<setDHCPProperty>(channel, false); 1415 return responseSuccess(); 1416 } 1417 } 1418 return response(ccParamNotSupported); 1419 } 1420 case LanParam::MAC: 1421 { 1422 ether_addr mac; 1423 std::array<uint8_t, sizeof(mac)> bytes; 1424 if (req.unpack(bytes) != 0 || !req.fullyUnpacked()) 1425 { 1426 return responseReqDataLenInvalid(); 1427 } 1428 copyInto(mac, bytes); 1429 channelCall<setMACProperty>(channel, mac); 1430 return responseSuccess(); 1431 } 1432 case LanParam::SubnetMask: 1433 { 1434 in_addr netmask; 1435 std::array<uint8_t, sizeof(netmask)> bytes; 1436 if (req.unpack(bytes) != 0 || !req.fullyUnpacked()) 1437 { 1438 return responseReqDataLenInvalid(); 1439 } 1440 copyInto(netmask, bytes); 1441 channelCall<reconfigureIfAddr4>(channel, std::nullopt, 1442 netmaskToPrefix(netmask)); 1443 return responseSuccess(); 1444 } 1445 case LanParam::Gateway1: 1446 { 1447 in_addr gateway; 1448 std::array<uint8_t, sizeof(gateway)> bytes; 1449 if (req.unpack(bytes) != 0 || !req.fullyUnpacked()) 1450 { 1451 return responseReqDataLenInvalid(); 1452 } 1453 copyInto(gateway, bytes); 1454 channelCall<setGatewayProperty<AF_INET>>(channel, gateway); 1455 return responseSuccess(); 1456 } 1457 case LanParam::Gateway1MAC: 1458 { 1459 ether_addr gatewayMAC; 1460 std::array<uint8_t, sizeof(gatewayMAC)> bytes; 1461 if (req.unpack(bytes) != 0 || !req.fullyUnpacked()) 1462 { 1463 return responseReqDataLenInvalid(); 1464 } 1465 copyInto(gatewayMAC, bytes); 1466 channelCall<reconfigureGatewayMAC<AF_INET>>(channel, gatewayMAC); 1467 return responseSuccess(); 1468 } 1469 case LanParam::VLANId: 1470 { 1471 uint16_t vlanData; 1472 if (req.unpack(vlanData) != 0 || !req.fullyUnpacked()) 1473 { 1474 return responseReqDataLenInvalid(); 1475 } 1476 if ((vlanData & VLAN_ENABLE_FLAG) == 0) 1477 { 1478 lastDisabledVlan[channel] = vlanData & VLAN_VALUE_MASK; 1479 vlanData = 0; 1480 } 1481 channelCall<reconfigureVLAN>(channel, vlanData & VLAN_VALUE_MASK); 1482 return responseSuccess(); 1483 } 1484 case LanParam::CiphersuiteSupport: 1485 case LanParam::CiphersuiteEntries: 1486 case LanParam::IPFamilySupport: 1487 { 1488 req.trailingOk = true; 1489 return response(ccParamReadOnly); 1490 } 1491 case LanParam::IPFamilyEnables: 1492 { 1493 uint8_t enables; 1494 if (req.unpack(enables) != 0 || !req.fullyUnpacked()) 1495 { 1496 return responseReqDataLenInvalid(); 1497 } 1498 switch (static_cast<IPFamilyEnables>(enables)) 1499 { 1500 case IPFamilyEnables::DualStack: 1501 return responseSuccess(); 1502 case IPFamilyEnables::IPv4Only: 1503 case IPFamilyEnables::IPv6Only: 1504 return response(ccParamNotSupported); 1505 } 1506 return response(ccParamNotSupported); 1507 } 1508 case LanParam::IPv6Status: 1509 { 1510 req.trailingOk = true; 1511 return response(ccParamReadOnly); 1512 } 1513 case LanParam::IPv6StaticAddresses: 1514 { 1515 uint8_t set; 1516 uint7_t rsvd; 1517 bool enabled; 1518 in6_addr ip; 1519 std::array<uint8_t, sizeof(ip)> ipbytes; 1520 uint8_t prefix; 1521 uint8_t status; 1522 if (req.unpack(set, rsvd, enabled, ipbytes, prefix, status) != 0 || 1523 !req.fullyUnpacked()) 1524 { 1525 return responseReqDataLenInvalid(); 1526 } 1527 if (rsvd) 1528 { 1529 return responseInvalidFieldRequest(); 1530 } 1531 copyInto(ip, ipbytes); 1532 if (enabled) 1533 { 1534 channelCall<reconfigureIfAddr6>(channel, set, ip, prefix); 1535 } 1536 else 1537 { 1538 channelCall<deconfigureIfAddr6>(channel, set); 1539 } 1540 return responseSuccess(); 1541 } 1542 case LanParam::IPv6DynamicAddresses: 1543 { 1544 req.trailingOk = true; 1545 return response(ccParamReadOnly); 1546 } 1547 case LanParam::IPv6RouterControl: 1548 { 1549 std::bitset<8> control; 1550 if (req.unpack(control) != 0 || !req.fullyUnpacked()) 1551 { 1552 return responseReqDataLenInvalid(); 1553 } 1554 std::bitset<8> expected; 1555 if (channelCall<getDHCPProperty>(channel)) 1556 { 1557 expected[IPv6RouterControlFlag::Dynamic] = 1; 1558 } 1559 else 1560 { 1561 expected[IPv6RouterControlFlag::Static] = 1; 1562 } 1563 if (expected != control) 1564 { 1565 return responseInvalidFieldRequest(); 1566 } 1567 return responseSuccess(); 1568 } 1569 case LanParam::IPv6StaticRouter1IP: 1570 { 1571 in6_addr gateway; 1572 std::array<uint8_t, sizeof(gateway)> bytes; 1573 if (req.unpack(bytes) != 0 || !req.fullyUnpacked()) 1574 { 1575 return responseReqDataLenInvalid(); 1576 } 1577 copyInto(gateway, bytes); 1578 channelCall<setGatewayProperty<AF_INET6>>(channel, gateway); 1579 return responseSuccess(); 1580 } 1581 case LanParam::IPv6StaticRouter1MAC: 1582 { 1583 ether_addr mac; 1584 std::array<uint8_t, sizeof(mac)> bytes; 1585 if (req.unpack(bytes) != 0 || !req.fullyUnpacked()) 1586 { 1587 return responseReqDataLenInvalid(); 1588 } 1589 copyInto(mac, bytes); 1590 channelCall<reconfigureGatewayMAC<AF_INET6>>(channel, mac); 1591 return responseSuccess(); 1592 } 1593 case LanParam::IPv6StaticRouter1PrefixLength: 1594 { 1595 uint8_t prefix; 1596 if (req.unpack(prefix) != 0 || !req.fullyUnpacked()) 1597 { 1598 return responseReqDataLenInvalid(); 1599 } 1600 if (prefix != 0) 1601 { 1602 return responseInvalidFieldRequest(); 1603 } 1604 return responseSuccess(); 1605 } 1606 case LanParam::IPv6StaticRouter1PrefixValue: 1607 { 1608 std::array<uint8_t, sizeof(in6_addr)> bytes; 1609 if (req.unpack(bytes) != 0 || !req.fullyUnpacked()) 1610 { 1611 return responseReqDataLenInvalid(); 1612 } 1613 // Accept any prefix value since our prefix length has to be 0 1614 return responseSuccess(); 1615 } 1616 } 1617 1618 if ((parameter >= oemCmdStart) && (parameter <= oemCmdEnd)) 1619 { 1620 return setLanOem(channel, parameter, req); 1621 } 1622 1623 req.trailingOk = true; 1624 return response(ccParamNotSupported); 1625 } 1626 1627 RspType<message::Payload> getLan(uint4_t channelBits, uint3_t, bool revOnly, 1628 uint8_t parameter, uint8_t set, uint8_t block) 1629 { 1630 message::Payload ret; 1631 constexpr uint8_t current_revision = 0x11; 1632 ret.pack(current_revision); 1633 1634 if (revOnly) 1635 { 1636 return responseSuccess(std::move(ret)); 1637 } 1638 1639 auto channel = static_cast<uint8_t>(channelBits); 1640 if (!doesDeviceExist(channel)) 1641 { 1642 return responseInvalidFieldRequest(); 1643 } 1644 1645 static std::vector<uint8_t> cipherList; 1646 static bool listInit = false; 1647 if (!listInit) 1648 { 1649 try 1650 { 1651 cipherList = cipher::getCipherList(); 1652 listInit = true; 1653 } 1654 catch (const std::exception& e) 1655 { 1656 } 1657 } 1658 1659 switch (static_cast<LanParam>(parameter)) 1660 { 1661 case LanParam::SetStatus: 1662 { 1663 SetStatus status; 1664 try 1665 { 1666 status = setStatus.at(channel); 1667 } 1668 catch (const std::out_of_range&) 1669 { 1670 status = SetStatus::Complete; 1671 } 1672 ret.pack(static_cast<uint2_t>(status), uint6_t{}); 1673 return responseSuccess(std::move(ret)); 1674 } 1675 case LanParam::AuthSupport: 1676 { 1677 std::bitset<6> support; 1678 ret.pack(support, uint2_t{}); 1679 return responseSuccess(std::move(ret)); 1680 } 1681 case LanParam::AuthEnables: 1682 { 1683 std::bitset<6> enables; 1684 ret.pack(enables, uint2_t{}); // Callback 1685 ret.pack(enables, uint2_t{}); // User 1686 ret.pack(enables, uint2_t{}); // Operator 1687 ret.pack(enables, uint2_t{}); // Admin 1688 ret.pack(enables, uint2_t{}); // OEM 1689 return responseSuccess(std::move(ret)); 1690 } 1691 case LanParam::IP: 1692 { 1693 auto ifaddr = channelCall<getIfAddr4>(channel); 1694 in_addr addr{}; 1695 if (ifaddr) 1696 { 1697 addr = ifaddr->address; 1698 } 1699 ret.pack(dataRef(addr)); 1700 return responseSuccess(std::move(ret)); 1701 } 1702 case LanParam::IPSrc: 1703 { 1704 auto src = IPSrc::Static; 1705 if (channelCall<getDHCPProperty>(channel)) 1706 { 1707 src = IPSrc::DHCP; 1708 } 1709 ret.pack(static_cast<uint4_t>(src), uint4_t{}); 1710 return responseSuccess(std::move(ret)); 1711 } 1712 case LanParam::MAC: 1713 { 1714 ether_addr mac = channelCall<getMACProperty>(channel); 1715 ret.pack(dataRef(mac)); 1716 return responseSuccess(std::move(ret)); 1717 } 1718 case LanParam::SubnetMask: 1719 { 1720 auto ifaddr = channelCall<getIfAddr4>(channel); 1721 uint8_t prefix = AddrFamily<AF_INET>::defaultPrefix; 1722 if (ifaddr) 1723 { 1724 prefix = ifaddr->prefix; 1725 } 1726 in_addr netmask = prefixToNetmask(prefix); 1727 ret.pack(dataRef(netmask)); 1728 return responseSuccess(std::move(ret)); 1729 } 1730 case LanParam::Gateway1: 1731 { 1732 auto gateway = 1733 channelCall<getGatewayProperty<AF_INET>>(channel).value_or( 1734 in_addr{}); 1735 ret.pack(dataRef(gateway)); 1736 return responseSuccess(std::move(ret)); 1737 } 1738 case LanParam::Gateway1MAC: 1739 { 1740 ether_addr mac{}; 1741 auto neighbor = channelCall<getGatewayNeighbor<AF_INET>>(channel); 1742 if (neighbor) 1743 { 1744 mac = neighbor->mac; 1745 } 1746 ret.pack(dataRef(mac)); 1747 return responseSuccess(std::move(ret)); 1748 } 1749 case LanParam::VLANId: 1750 { 1751 uint16_t vlan = channelCall<getVLANProperty>(channel); 1752 if (vlan != 0) 1753 { 1754 vlan |= VLAN_ENABLE_FLAG; 1755 } 1756 else 1757 { 1758 vlan = lastDisabledVlan[channel]; 1759 } 1760 ret.pack(vlan); 1761 return responseSuccess(std::move(ret)); 1762 } 1763 case LanParam::CiphersuiteSupport: 1764 { 1765 if (!listInit) 1766 { 1767 return responseUnspecifiedError(); 1768 } 1769 ret.pack(static_cast<uint8_t>(cipherList.size() - 1)); 1770 return responseSuccess(std::move(ret)); 1771 } 1772 case LanParam::CiphersuiteEntries: 1773 { 1774 if (!listInit) 1775 { 1776 return responseUnspecifiedError(); 1777 } 1778 ret.pack(cipherList); 1779 return responseSuccess(std::move(ret)); 1780 } 1781 case LanParam::IPFamilySupport: 1782 { 1783 std::bitset<8> support; 1784 support[IPFamilySupportFlag::IPv6Only] = 0; 1785 support[IPFamilySupportFlag::DualStack] = 1; 1786 support[IPFamilySupportFlag::IPv6Alerts] = 1; 1787 ret.pack(support); 1788 return responseSuccess(std::move(ret)); 1789 } 1790 case LanParam::IPFamilyEnables: 1791 { 1792 ret.pack(static_cast<uint8_t>(IPFamilyEnables::DualStack)); 1793 return responseSuccess(std::move(ret)); 1794 } 1795 case LanParam::IPv6Status: 1796 { 1797 ret.pack(MAX_IPV6_STATIC_ADDRESSES); 1798 ret.pack(MAX_IPV6_DYNAMIC_ADDRESSES); 1799 std::bitset<8> support; 1800 support[IPv6StatusFlag::DHCP] = 1; 1801 support[IPv6StatusFlag::SLAAC] = 1; 1802 ret.pack(support); 1803 return responseSuccess(std::move(ret)); 1804 } 1805 case LanParam::IPv6StaticAddresses: 1806 { 1807 if (set >= MAX_IPV6_STATIC_ADDRESSES) 1808 { 1809 return responseParmOutOfRange(); 1810 } 1811 getLanIPv6Address(ret, channel, set, originsV6Static); 1812 return responseSuccess(std::move(ret)); 1813 } 1814 case LanParam::IPv6DynamicAddresses: 1815 { 1816 if (set >= MAX_IPV6_DYNAMIC_ADDRESSES) 1817 { 1818 return responseParmOutOfRange(); 1819 } 1820 getLanIPv6Address(ret, channel, set, originsV6Dynamic); 1821 return responseSuccess(std::move(ret)); 1822 } 1823 case LanParam::IPv6RouterControl: 1824 { 1825 std::bitset<8> control; 1826 if (channelCall<getDHCPProperty>(channel)) 1827 { 1828 control[IPv6RouterControlFlag::Dynamic] = 1; 1829 } 1830 else 1831 { 1832 control[IPv6RouterControlFlag::Static] = 1; 1833 } 1834 ret.pack(control); 1835 return responseSuccess(std::move(ret)); 1836 } 1837 case LanParam::IPv6StaticRouter1IP: 1838 { 1839 in6_addr gateway{}; 1840 if (!channelCall<getDHCPProperty>(channel)) 1841 { 1842 gateway = 1843 channelCall<getGatewayProperty<AF_INET6>>(channel).value_or( 1844 in6_addr{}); 1845 } 1846 ret.pack(dataRef(gateway)); 1847 return responseSuccess(std::move(ret)); 1848 } 1849 case LanParam::IPv6StaticRouter1MAC: 1850 { 1851 ether_addr mac{}; 1852 auto neighbor = channelCall<getGatewayNeighbor<AF_INET6>>(channel); 1853 if (neighbor) 1854 { 1855 mac = neighbor->mac; 1856 } 1857 ret.pack(dataRef(mac)); 1858 return responseSuccess(std::move(ret)); 1859 } 1860 case LanParam::IPv6StaticRouter1PrefixLength: 1861 { 1862 ret.pack(UINT8_C(0)); 1863 return responseSuccess(std::move(ret)); 1864 } 1865 case LanParam::IPv6StaticRouter1PrefixValue: 1866 { 1867 in6_addr prefix{}; 1868 ret.pack(dataRef(prefix)); 1869 return responseSuccess(std::move(ret)); 1870 } 1871 } 1872 1873 if ((parameter >= oemCmdStart) && (parameter <= oemCmdEnd)) 1874 { 1875 return getLanOem(channel, parameter, set, block); 1876 } 1877 1878 return response(ccParamNotSupported); 1879 } 1880 1881 } // namespace transport 1882 } // namespace ipmi 1883 1884 void register_netfn_transport_functions() __attribute__((constructor)); 1885 1886 void register_netfn_transport_functions() 1887 { 1888 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnTransport, 1889 ipmi::transport::cmdSetLanConfigParameters, 1890 ipmi::Privilege::Admin, ipmi::transport::setLan); 1891 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnTransport, 1892 ipmi::transport::cmdGetLanConfigParameters, 1893 ipmi::Privilege::Operator, ipmi::transport::getLan); 1894 } 1895