1 #include <arpa/inet.h> 2 #include <netinet/ether.h> 3 4 #include <array> 5 #include <bitset> 6 #include <cinttypes> 7 #include <cstdint> 8 #include <cstring> 9 #include <functional> 10 #include <ipmid/api.hpp> 11 #include <ipmid/message.hpp> 12 #include <ipmid/message/types.hpp> 13 #include <ipmid/types.hpp> 14 #include <ipmid/utils.hpp> 15 #include <optional> 16 #include <phosphor-logging/elog-errors.hpp> 17 #include <phosphor-logging/elog.hpp> 18 #include <phosphor-logging/log.hpp> 19 #include <sdbusplus/bus.hpp> 20 #include <sdbusplus/exception.hpp> 21 #include <string> 22 #include <string_view> 23 #include <type_traits> 24 #include <unordered_map> 25 #include <unordered_set> 26 #include <user_channel/channel_layer.hpp> 27 #include <utility> 28 #include <vector> 29 #include <xyz/openbmc_project/Common/error.hpp> 30 #include <xyz/openbmc_project/Network/IP/server.hpp> 31 32 namespace ipmi 33 { 34 namespace transport 35 { 36 37 using phosphor::logging::commit; 38 using phosphor::logging::elog; 39 using phosphor::logging::entry; 40 using phosphor::logging::level; 41 using phosphor::logging::log; 42 using sdbusplus::xyz::openbmc_project::Common::Error::InternalFailure; 43 using sdbusplus::xyz::openbmc_project::Network::server::IP; 44 45 // LAN Handler specific response codes 46 constexpr Cc ccParamNotSupported = 0x80; 47 constexpr Cc ccParamSetLocked = 0x81; 48 constexpr Cc ccParamReadOnly = 0x82; 49 50 // VLANs are a 12-bit value 51 constexpr uint16_t VLAN_VALUE_MASK = 0x0fff; 52 constexpr uint16_t VLAN_ENABLE_FLAG = 0x8000; 53 54 // D-Bus Network Daemon definitions 55 constexpr auto PATH_ROOT = "/xyz/openbmc_project/network"; 56 constexpr auto PATH_SYSTEMCONFIG = "/xyz/openbmc_project/network/config"; 57 58 constexpr auto INTF_SYSTEMCONFIG = 59 "xyz.openbmc_project.Network.SystemConfiguration"; 60 constexpr auto INTF_ETHERNET = "xyz.openbmc_project.Network.EthernetInterface"; 61 constexpr auto INTF_IP = "xyz.openbmc_project.Network.IP"; 62 constexpr auto INTF_IP_CREATE = "xyz.openbmc_project.Network.IP.Create"; 63 constexpr auto INTF_MAC = "xyz.openbmc_project.Network.MACAddress"; 64 constexpr auto INTF_VLAN = "xyz.openbmc_project.Network.VLAN"; 65 constexpr auto INTF_VLAN_CREATE = "xyz.openbmc_project.Network.VLAN.Create"; 66 67 /** @brief Generic paramters for different address families */ 68 template <int family> 69 struct AddrFamily 70 { 71 }; 72 73 /** @brief Parameter specialization for IPv4 */ 74 template <> 75 struct AddrFamily<AF_INET> 76 { 77 using addr = in_addr; 78 static constexpr auto protocol = IP::Protocol::IPv4; 79 static constexpr size_t maxStrLen = INET6_ADDRSTRLEN; 80 static constexpr uint8_t defaultPrefix = 32; 81 static constexpr char propertyGateway[] = "DefaultGateway"; 82 }; 83 84 /** @brief Valid address origins for IPv4 */ 85 const std::unordered_set<IP::AddressOrigin> originsV4 = { 86 IP::AddressOrigin::Static, 87 IP::AddressOrigin::DHCP, 88 }; 89 90 /** @brief Interface IP Address configuration parameters */ 91 template <int family> 92 struct IfAddr 93 { 94 std::string path; 95 typename AddrFamily<family>::addr address; 96 IP::AddressOrigin origin; 97 uint8_t prefix; 98 }; 99 100 /** @brief IPMI LAN Parameters */ 101 enum class LanParam : uint8_t 102 { 103 SetStatus = 0, 104 AuthSupport = 1, 105 AuthEnables = 2, 106 IP = 3, 107 IPSrc = 4, 108 MAC = 5, 109 SubnetMask = 6, 110 Gateway1 = 12, 111 VLANId = 20, 112 CiphersuiteSupport = 22, 113 CiphersuiteEntries = 23, 114 }; 115 116 /** @brief IPMI IP Origin Types */ 117 enum class IPSrc : uint8_t 118 { 119 Unspecified = 0, 120 Static = 1, 121 DHCP = 2, 122 BIOS = 3, 123 BMC = 4, 124 }; 125 126 /** @brief IPMI Set Status */ 127 enum class SetStatus : uint8_t 128 { 129 Complete = 0, 130 InProgress = 1, 131 Commit = 2, 132 }; 133 134 /** @brief Copies bytes from an array into a trivially copyable container 135 * 136 * @params[out] t - The container receiving the data 137 * @params[in] bytes - The data to copy 138 */ 139 template <size_t N, typename T> 140 void copyInto(T& t, const std::array<uint8_t, N>& bytes) 141 { 142 static_assert(std::is_trivially_copyable_v<T>); 143 static_assert(N == sizeof(T)); 144 std::memcpy(&t, bytes.data(), bytes.size()); 145 } 146 147 /** @brief Gets a generic view of the bytes in the input container 148 * 149 * @params[in] t - The data to reference 150 * @return A string_view referencing the bytes in the container 151 */ 152 template <typename T> 153 std::string_view dataRef(const T& t) 154 { 155 static_assert(std::is_trivially_copyable_v<T>); 156 return {reinterpret_cast<const char*>(&t), sizeof(T)}; 157 } 158 159 /** @brief The dbus parameters for the interface corresponding to a channel 160 * This helps reduce the number of mapper lookups we need for each 161 * query and simplifies finding the VLAN interface if needed. 162 */ 163 struct ChannelParams 164 { 165 /** @brief The channel ID */ 166 int id; 167 /** @brief channel name for the interface */ 168 std::string ifname; 169 /** @brief Name of the service on the bus */ 170 std::string service; 171 /** @brief Lower level adapter path that is guaranteed to not be a VLAN */ 172 std::string ifPath; 173 /** @brief Logical adapter path used for address assignment */ 174 std::string logicalPath; 175 }; 176 177 /** @brief Determines the ethernet interface name corresponding to a channel 178 * Tries to map a VLAN object first so that the address information 179 * is accurate. Otherwise it gets the standard ethernet interface. 180 * 181 * @param[in] bus - The bus object used for lookups 182 * @param[in] channel - The channel id corresponding to an ethernet interface 183 * @return Ethernet interface service and object path if it exists 184 */ 185 std::optional<ChannelParams> maybeGetChannelParams(sdbusplus::bus::bus& bus, 186 uint8_t channel) 187 { 188 auto ifname = getChannelName(channel); 189 if (ifname.empty()) 190 { 191 return std::nullopt; 192 } 193 194 // Enumerate all VLAN + ETHERNET interfaces 195 auto req = bus.new_method_call(MAPPER_BUS_NAME, MAPPER_OBJ, MAPPER_INTF, 196 "GetSubTree"); 197 req.append(PATH_ROOT, 0, 198 std::vector<std::string>{INTF_VLAN, INTF_ETHERNET}); 199 auto reply = bus.call(req); 200 ObjectTree objs; 201 reply.read(objs); 202 203 ChannelParams params; 204 for (const auto& [path, impls] : objs) 205 { 206 if (path.find(ifname) == path.npos) 207 { 208 continue; 209 } 210 for (const auto& [service, intfs] : impls) 211 { 212 bool vlan = false; 213 bool ethernet = false; 214 for (const auto& intf : intfs) 215 { 216 if (intf == INTF_VLAN) 217 { 218 vlan = true; 219 } 220 else if (intf == INTF_ETHERNET) 221 { 222 ethernet = true; 223 } 224 } 225 if (params.service.empty() && (vlan || ethernet)) 226 { 227 params.service = service; 228 } 229 if (params.ifPath.empty() && !vlan && ethernet) 230 { 231 params.ifPath = path; 232 } 233 if (params.logicalPath.empty() && vlan) 234 { 235 params.logicalPath = path; 236 } 237 } 238 } 239 240 // We must have a path for the underlying interface 241 if (params.ifPath.empty()) 242 { 243 return std::nullopt; 244 } 245 // We don't have a VLAN so the logical path is the same 246 if (params.logicalPath.empty()) 247 { 248 params.logicalPath = params.ifPath; 249 } 250 251 params.id = channel; 252 params.ifname = std::move(ifname); 253 return std::move(params); 254 } 255 256 /** @brief A trivial helper around maybeGetChannelParams() that throws an 257 * exception when it is unable to acquire parameters for the channel. 258 * 259 * @param[in] bus - The bus object used for lookups 260 * @param[in] channel - The channel id corresponding to an ethernet interface 261 * @return Ethernet interface service and object path 262 */ 263 ChannelParams getChannelParams(sdbusplus::bus::bus& bus, uint8_t channel) 264 { 265 auto params = maybeGetChannelParams(bus, channel); 266 if (!params) 267 { 268 log<level::ERR>("Failed to get channel params", 269 entry("CHANNEL=%" PRIu8, channel)); 270 elog<InternalFailure>(); 271 } 272 return std::move(*params); 273 } 274 275 /** @brief Wraps the phosphor logging method to insert some additional metadata 276 * 277 * @param[in] params - The parameters for the channel 278 * ... 279 */ 280 template <auto level, typename... Args> 281 auto logWithChannel(const ChannelParams& params, Args&&... args) 282 { 283 return log<level>(std::forward<Args>(args)..., 284 entry("CHANNEL=%d", params.id), 285 entry("IFNAME=%s", params.ifname.c_str())); 286 } 287 template <auto level, typename... Args> 288 auto logWithChannel(const std::optional<ChannelParams>& params, Args&&... args) 289 { 290 if (params) 291 { 292 return logWithChannel<level>(*params, std::forward<Args>(args)...); 293 } 294 return log<level>(std::forward<Args>(args)...); 295 } 296 297 /** @brief Trivializes using parameter getter functions by providing a bus 298 * and channel parameters automatically. 299 * 300 * @param[in] channel - The channel id corresponding to an ethernet interface 301 * ... 302 */ 303 template <auto func, typename... Args> 304 auto channelCall(uint8_t channel, Args&&... args) 305 { 306 sdbusplus::bus::bus bus(ipmid_get_sd_bus_connection()); 307 auto params = getChannelParams(bus, channel); 308 return std::invoke(func, bus, params, std::forward<Args>(args)...); 309 } 310 311 /** @brief Determines if the ethernet interface is using DHCP 312 * 313 * @param[in] bus - The bus object used for lookups 314 * @param[in] params - The parameters for the channel 315 * @return True if DHCP is enabled, false otherwise 316 */ 317 bool getDHCPProperty(sdbusplus::bus::bus& bus, const ChannelParams& params) 318 { 319 return std::get<bool>(getDbusProperty( 320 bus, params.service, params.logicalPath, INTF_ETHERNET, "DHCPEnabled")); 321 } 322 323 /** @brief Sets the system value for DHCP on the given interface 324 * 325 * @param[in] bus - The bus object used for lookups 326 * @param[in] params - The parameters for the channel 327 * @param[in] on - Whether or not to enable DHCP 328 */ 329 void setDHCPProperty(sdbusplus::bus::bus& bus, const ChannelParams& params, 330 bool on) 331 { 332 setDbusProperty(bus, params.service, params.logicalPath, INTF_ETHERNET, 333 "DHCPEnabled", on); 334 } 335 336 /** @brief Converts a human readable MAC string into MAC bytes 337 * 338 * @param[in] mac - The MAC string 339 * @return MAC in bytes 340 */ 341 ether_addr stringToMAC(const char* mac) 342 { 343 const ether_addr* ret = ether_aton(mac); 344 if (ret == nullptr) 345 { 346 log<level::ERR>("Invalid MAC Address", entry("MAC=%s", mac)); 347 elog<InternalFailure>(); 348 } 349 return *ret; 350 } 351 352 /** @brief Determines the MAC of the ethernet interface 353 * 354 * @param[in] bus - The bus object used for lookups 355 * @param[in] params - The parameters for the channel 356 * @return The configured mac address 357 */ 358 ether_addr getMACProperty(sdbusplus::bus::bus& bus, const ChannelParams& params) 359 { 360 auto macStr = std::get<std::string>(getDbusProperty( 361 bus, params.service, params.ifPath, INTF_MAC, "MACAddress")); 362 return stringToMAC(macStr.c_str()); 363 } 364 365 /** @brief Sets the system value for MAC address on the given interface 366 * 367 * @param[in] bus - The bus object used for lookups 368 * @param[in] params - The parameters for the channel 369 * @param[in] mac - MAC address to apply 370 */ 371 void setMACProperty(sdbusplus::bus::bus& bus, const ChannelParams& params, 372 const ether_addr& mac) 373 { 374 std::string macStr = ether_ntoa(&mac); 375 setDbusProperty(bus, params.service, params.ifPath, INTF_MAC, "MACAddress", 376 macStr); 377 } 378 379 /** @brief Turns an IP address string into the network byte order form 380 * NOTE: This version strictly validates family matches 381 * 382 * @param[in] address - The string form of the address 383 * @return A network byte order address or none if conversion failed 384 */ 385 template <int family> 386 std::optional<typename AddrFamily<family>::addr> 387 maybeStringToAddr(const char* address) 388 { 389 typename AddrFamily<family>::addr ret; 390 if (inet_pton(family, address, &ret) == 1) 391 { 392 return ret; 393 } 394 return std::nullopt; 395 } 396 397 /** @brief Turns an IP address string into the network byte order form 398 * NOTE: This version strictly validates family matches 399 * 400 * @param[in] address - The string form of the address 401 * @return A network byte order address 402 */ 403 template <int family> 404 typename AddrFamily<family>::addr stringToAddr(const char* address) 405 { 406 auto ret = maybeStringToAddr<family>(address); 407 if (!ret) 408 { 409 log<level::ERR>("Failed to convert IP Address", 410 entry("FAMILY=%d", family), 411 entry("ADDRESS=%s", address)); 412 elog<InternalFailure>(); 413 } 414 return *ret; 415 } 416 417 /** @brief Turns an IP address in network byte order into a string 418 * 419 * @param[in] address - The string form of the address 420 * @return A network byte order address 421 */ 422 template <int family> 423 std::string addrToString(const typename AddrFamily<family>::addr& address) 424 { 425 std::string ret(AddrFamily<family>::maxStrLen, '\0'); 426 inet_ntop(family, &address, ret.data(), ret.size()); 427 ret.resize(strlen(ret.c_str())); 428 return ret; 429 } 430 431 /** @brief Retrieves the current gateway for the address family on the system 432 * NOTE: The gateway is currently system wide and not per channel 433 * 434 * @param[in] bus - The bus object used for lookups 435 * @param[in] params - The parameters for the channel 436 * @return An address representing the gateway address if it exists 437 */ 438 template <int family> 439 std::optional<typename AddrFamily<family>::addr> 440 getGatewayProperty(sdbusplus::bus::bus& bus, const ChannelParams& params) 441 { 442 auto gatewayStr = std::get<std::string>(getDbusProperty( 443 bus, params.service, PATH_SYSTEMCONFIG, INTF_SYSTEMCONFIG, 444 AddrFamily<family>::propertyGateway)); 445 if (gatewayStr.empty()) 446 { 447 return std::nullopt; 448 } 449 return stringToAddr<family>(gatewayStr.c_str()); 450 } 451 452 /** @brief Sets the system wide value for the default gateway 453 * 454 * @param[in] bus - The bus object used for lookups 455 * @param[in] params - The parameters for the channel 456 * @param[in] gateway - Gateway address to apply 457 */ 458 template <int family> 459 void setGatewayProperty(sdbusplus::bus::bus& bus, const ChannelParams& params, 460 const typename AddrFamily<family>::addr& address) 461 { 462 setDbusProperty(bus, params.service, PATH_SYSTEMCONFIG, INTF_SYSTEMCONFIG, 463 AddrFamily<family>::propertyGateway, 464 addrToString<family>(address)); 465 } 466 467 /** @brief A lazy lookup mechanism for iterating over object properties stored 468 * in DBus. This will only perform the object lookup when needed, and 469 * retains a cache of previous lookups to speed up future iterations. 470 */ 471 class ObjectLookupCache 472 { 473 public: 474 using PropertiesCache = std::unordered_map<std::string, PropertyMap>; 475 476 /** @brief Creates a new ObjectLookupCache for the interface on the bus 477 * NOTE: The inputs to this object must outlive the object since 478 * they are only referenced by it. 479 * 480 * @param[in] bus - The bus object used for lookups 481 * @param[in] params - The parameters for the channel 482 * @param[in] intf - The interface we are looking up 483 */ 484 ObjectLookupCache(sdbusplus::bus::bus& bus, const ChannelParams& params, 485 const char* intf) : 486 bus(bus), 487 params(params), intf(intf), 488 objs(getAllDbusObjects(bus, params.logicalPath, intf, "")) 489 { 490 } 491 492 class iterator : public ObjectTree::const_iterator 493 { 494 public: 495 using value_type = PropertiesCache::value_type; 496 497 iterator(ObjectTree::const_iterator it, ObjectLookupCache& container) : 498 ObjectTree::const_iterator(it), container(container), 499 ret(container.cache.end()) 500 { 501 } 502 value_type& operator*() 503 { 504 ret = container.get(ObjectTree::const_iterator::operator*().first); 505 return *ret; 506 } 507 value_type* operator->() 508 { 509 return &operator*(); 510 } 511 512 private: 513 ObjectLookupCache& container; 514 PropertiesCache::iterator ret; 515 }; 516 517 iterator begin() noexcept 518 { 519 return iterator(objs.begin(), *this); 520 } 521 522 iterator end() noexcept 523 { 524 return iterator(objs.end(), *this); 525 } 526 527 private: 528 sdbusplus::bus::bus& bus; 529 const ChannelParams& params; 530 const char* const intf; 531 const ObjectTree objs; 532 PropertiesCache cache; 533 534 /** @brief Gets a cached copy of the object properties if possible 535 * Otherwise performs a query on DBus to look them up 536 * 537 * @param[in] path - The object path to lookup 538 * @return An iterator for the specified object path + properties 539 */ 540 PropertiesCache::iterator get(const std::string& path) 541 { 542 auto it = cache.find(path); 543 if (it != cache.end()) 544 { 545 return it; 546 } 547 auto properties = getAllDbusProperties(bus, params.service, path, intf); 548 return cache.insert({path, std::move(properties)}).first; 549 } 550 }; 551 552 /** @brief Searches the ip object lookup cache for an address matching 553 * the input parameters. NOTE: The index lacks stability across address 554 * changes since the network daemon has no notion of stable indicies. 555 * 556 * @param[in] bus - The bus object used for lookups 557 * @param[in] params - The parameters for the channel 558 * @param[in] idx - The index of the desired address on the interface 559 * @param[in] origins - The allowed origins for the address objects 560 * @param[in] ips - The object lookup cache holding all of the address info 561 * @return The address and prefix if it was found 562 */ 563 template <int family> 564 std::optional<IfAddr<family>> 565 findIfAddr(sdbusplus::bus::bus& bus, const ChannelParams& params, 566 uint8_t idx, 567 const std::unordered_set<IP::AddressOrigin>& origins, 568 ObjectLookupCache& ips) 569 { 570 for (const auto& [path, properties] : ips) 571 { 572 const auto& addrStr = std::get<std::string>(properties.at("Address")); 573 auto addr = maybeStringToAddr<family>(addrStr.c_str()); 574 if (!addr) 575 { 576 continue; 577 } 578 579 IP::AddressOrigin origin = IP::convertAddressOriginFromString( 580 std::get<std::string>(properties.at("Origin"))); 581 if (origins.find(origin) == origins.end()) 582 { 583 continue; 584 } 585 586 if (idx > 0) 587 { 588 idx--; 589 continue; 590 } 591 592 IfAddr<family> ifaddr; 593 ifaddr.path = path; 594 ifaddr.address = *addr; 595 ifaddr.prefix = std::get<uint8_t>(properties.at("PrefixLength")); 596 ifaddr.origin = origin; 597 return std::move(ifaddr); 598 } 599 600 return std::nullopt; 601 } 602 603 /** @brief Trivial helper around findIfAddr that simplifies calls 604 * for one off lookups. Don't use this if you intend to do multiple 605 * lookups at a time. 606 * 607 * @param[in] bus - The bus object used for lookups 608 * @param[in] params - The parameters for the channel 609 * @param[in] idx - The index of the desired address on the interface 610 * @param[in] origins - The allowed origins for the address objects 611 * @return The address and prefix if it was found 612 */ 613 template <int family> 614 auto getIfAddr(sdbusplus::bus::bus& bus, const ChannelParams& params, 615 uint8_t idx, 616 const std::unordered_set<IP::AddressOrigin>& origins) 617 { 618 ObjectLookupCache ips(bus, params, INTF_IP); 619 return findIfAddr<family>(bus, params, idx, origins, ips); 620 } 621 622 /** @brief Deletes the dbus object. Ignores empty objects or objects that are 623 * missing from the bus. 624 * 625 * @param[in] bus - The bus object used for lookups 626 * @param[in] service - The name of the service 627 * @param[in] path - The path of the object to delete 628 */ 629 void deleteObjectIfExists(sdbusplus::bus::bus& bus, const std::string& service, 630 const std::string& path) 631 { 632 if (path.empty()) 633 { 634 return; 635 } 636 try 637 { 638 auto req = bus.new_method_call(service.c_str(), path.c_str(), 639 ipmi::DELETE_INTERFACE, "Delete"); 640 bus.call_noreply(req); 641 } 642 catch (const sdbusplus::exception::SdBusError& e) 643 { 644 if (strcmp(e.name(), "org.freedesktop.DBus.Error.UnknownObject") != 0) 645 { 646 // We want to rethrow real errors 647 throw; 648 } 649 } 650 } 651 652 /** @brief Sets the address info configured for the interface 653 * If a previous address path exists then it will be removed 654 * before the new address is added. 655 * 656 * @param[in] bus - The bus object used for lookups 657 * @param[in] params - The parameters for the channel 658 * @param[in] address - The address of the new IP 659 * @param[in] prefix - The prefix of the new IP 660 */ 661 template <int family> 662 void createIfAddr(sdbusplus::bus::bus& bus, const ChannelParams& params, 663 const typename AddrFamily<family>::addr& address, 664 uint8_t prefix) 665 { 666 auto newreq = 667 bus.new_method_call(params.service.c_str(), params.logicalPath.c_str(), 668 INTF_IP_CREATE, "IP"); 669 std::string protocol = 670 sdbusplus::xyz::openbmc_project::Network::server::convertForMessage( 671 AddrFamily<family>::protocol); 672 newreq.append(protocol, addrToString<family>(address), prefix, ""); 673 bus.call_noreply(newreq); 674 } 675 676 /** @brief Trivial helper for getting the IPv4 address from getIfAddrs() 677 * 678 * @param[in] bus - The bus object used for lookups 679 * @param[in] params - The parameters for the channel 680 * @return The address and prefix if found 681 */ 682 auto getIfAddr4(sdbusplus::bus::bus& bus, const ChannelParams& params) 683 { 684 return getIfAddr<AF_INET>(bus, params, 0, originsV4); 685 } 686 687 /** @brief Reconfigures the IPv4 address info configured for the interface 688 * 689 * @param[in] bus - The bus object used for lookups 690 * @param[in] params - The parameters for the channel 691 * @param[in] address - The new address if specified 692 * @param[in] prefix - The new address prefix if specified 693 */ 694 void reconfigureIfAddr4(sdbusplus::bus::bus& bus, const ChannelParams& params, 695 const std::optional<in_addr>& address, 696 std::optional<uint8_t> prefix) 697 { 698 auto ifaddr = getIfAddr4(bus, params); 699 if (!ifaddr && !address) 700 { 701 log<level::ERR>("Missing address for IPv4 assignment"); 702 elog<InternalFailure>(); 703 } 704 uint8_t fallbackPrefix = AddrFamily<AF_INET>::defaultPrefix; 705 if (ifaddr) 706 { 707 fallbackPrefix = ifaddr->prefix; 708 deleteObjectIfExists(bus, params.service, ifaddr->path); 709 } 710 createIfAddr<AF_INET>(bus, params, address.value_or(ifaddr->address), 711 prefix.value_or(fallbackPrefix)); 712 } 713 714 /** @brief Gets the vlan ID configured on the interface 715 * 716 * @param[in] bus - The bus object used for lookups 717 * @param[in] params - The parameters for the channel 718 * @return VLAN id or the standard 0 for no VLAN 719 */ 720 uint16_t getVLANProperty(sdbusplus::bus::bus& bus, const ChannelParams& params) 721 { 722 // VLAN devices will always have a separate logical object 723 if (params.ifPath == params.logicalPath) 724 { 725 return 0; 726 } 727 728 auto vlan = std::get<uint32_t>(getDbusProperty( 729 bus, params.service, params.logicalPath, INTF_VLAN, "Id")); 730 if ((vlan & VLAN_VALUE_MASK) != vlan) 731 { 732 logWithChannel<level::ERR>(params, "networkd returned an invalid vlan", 733 entry("VLAN=%" PRIu32, vlan)); 734 elog<InternalFailure>(); 735 } 736 return vlan; 737 } 738 739 /** @brief Deletes all of the possible configuration parameters for a channel 740 * 741 * @param[in] bus - The bus object used for lookups 742 * @param[in] params - The parameters for the channel 743 */ 744 void deconfigureChannel(sdbusplus::bus::bus& bus, ChannelParams& params) 745 { 746 // Delete all objects associated with the interface 747 auto objreq = bus.new_method_call(MAPPER_BUS_NAME, MAPPER_OBJ, MAPPER_INTF, 748 "GetSubTree"); 749 objreq.append(PATH_ROOT, 0, std::vector<std::string>{DELETE_INTERFACE}); 750 auto objreply = bus.call(objreq); 751 ObjectTree objs; 752 objreply.read(objs); 753 for (const auto& [path, impls] : objs) 754 { 755 if (path.find(params.ifname) == path.npos) 756 { 757 continue; 758 } 759 for (const auto& [service, intfs] : impls) 760 { 761 deleteObjectIfExists(bus, service, path); 762 } 763 // Update params to reflect the deletion of vlan 764 if (path == params.logicalPath) 765 { 766 params.logicalPath = params.ifPath; 767 } 768 } 769 770 // Clear out any settings on the lower physical interface 771 setDHCPProperty(bus, params, false); 772 } 773 774 /** @brief Creates a new VLAN on the specified interface 775 * 776 * @param[in] bus - The bus object used for lookups 777 * @param[in] params - The parameters for the channel 778 * @param[in] vlan - The id of the new vlan 779 */ 780 void createVLAN(sdbusplus::bus::bus& bus, ChannelParams& params, uint16_t vlan) 781 { 782 if (vlan == 0) 783 { 784 return; 785 } 786 787 auto req = bus.new_method_call(params.service.c_str(), PATH_ROOT, 788 INTF_VLAN_CREATE, "VLAN"); 789 req.append(params.ifname, static_cast<uint32_t>(vlan)); 790 auto reply = bus.call(req); 791 sdbusplus::message::object_path newPath; 792 reply.read(newPath); 793 params.logicalPath = std::move(newPath); 794 } 795 796 /** @brief Performs the necessary reconfiguration to change the VLAN 797 * 798 * @param[in] bus - The bus object used for lookups 799 * @param[in] params - The parameters for the channel 800 * @param[in] vlan - The new vlan id to use 801 */ 802 void reconfigureVLAN(sdbusplus::bus::bus& bus, ChannelParams& params, 803 uint16_t vlan) 804 { 805 // Unfortunatetly we don't have built-in functions to migrate our interface 806 // customizations to new VLAN interfaces, or have some kind of decoupling. 807 // We therefore must retain all of our old information, setup the new VLAN 808 // configuration, then restore the old info. 809 810 // Save info from the old logical interface 811 ObjectLookupCache ips(bus, params, INTF_IP); 812 auto ifaddr4 = findIfAddr<AF_INET>(bus, params, 0, originsV4, ips); 813 auto dhcp = getDHCPProperty(bus, params); 814 815 deconfigureChannel(bus, params); 816 createVLAN(bus, params, vlan); 817 818 // Re-establish the saved settings 819 setDHCPProperty(bus, params, dhcp); 820 if (ifaddr4) 821 { 822 createIfAddr<AF_INET>(bus, params, ifaddr4->address, ifaddr4->prefix); 823 } 824 } 825 826 /** @brief Turns a prefix into a netmask 827 * 828 * @param[in] prefix - The prefix length 829 * @return The netmask 830 */ 831 in_addr prefixToNetmask(uint8_t prefix) 832 { 833 if (prefix > 32) 834 { 835 log<level::ERR>("Invalid prefix", entry("PREFIX=%" PRIu8, prefix)); 836 elog<InternalFailure>(); 837 } 838 if (prefix == 0) 839 { 840 // Avoids 32-bit lshift by 32 UB 841 return {}; 842 } 843 return {htobe32(~UINT32_C(0) << (32 - prefix))}; 844 } 845 846 /** @brief Turns a a netmask into a prefix length 847 * 848 * @param[in] netmask - The netmask in byte form 849 * @return The prefix length 850 */ 851 uint8_t netmaskToPrefix(in_addr netmask) 852 { 853 uint32_t x = be32toh(netmask.s_addr); 854 if ((~x & (~x + 1)) != 0) 855 { 856 char maskStr[INET_ADDRSTRLEN]; 857 inet_ntop(AF_INET, &netmask, maskStr, sizeof(maskStr)); 858 log<level::ERR>("Invalid netmask", entry("NETMASK=%s", maskStr)); 859 elog<InternalFailure>(); 860 } 861 return 32 - __builtin_ctz(x); 862 } 863 864 // We need to store this value so it can be returned to the client 865 // It is volatile so safe to store in daemon memory. 866 static std::unordered_map<uint8_t, SetStatus> setStatus; 867 868 // Until we have good support for fixed versions of IPMI tool 869 // we need to return the VLAN id for disabled VLANs. The value is only 870 // used for verification that a disable operation succeeded and will only 871 // be sent if our system indicates that vlans are disabled. 872 static std::unordered_map<uint8_t, uint16_t> lastDisabledVlan; 873 874 /** @brief Gets the set status for the channel if it exists 875 * Otherise populates and returns the default value. 876 * 877 * @param[in] channel - The channel id corresponding to an ethernet interface 878 * @return A reference to the SetStatus for the channel 879 */ 880 SetStatus& getSetStatus(uint8_t channel) 881 { 882 auto it = setStatus.find(channel); 883 if (it != setStatus.end()) 884 { 885 return it->second; 886 } 887 return setStatus[channel] = SetStatus::Complete; 888 } 889 890 RspType<> setLan(uint4_t channelBits, uint4_t, uint8_t parameter, 891 message::Payload& req) 892 { 893 auto channel = static_cast<uint8_t>(channelBits); 894 if (!doesDeviceExist(channel)) 895 { 896 req.trailingOk = true; 897 return responseInvalidFieldRequest(); 898 } 899 900 switch (static_cast<LanParam>(parameter)) 901 { 902 case LanParam::SetStatus: 903 { 904 uint2_t flag; 905 uint6_t rsvd; 906 if (req.unpack(flag, rsvd) != 0 || !req.fullyUnpacked()) 907 { 908 return responseReqDataLenInvalid(); 909 } 910 auto status = static_cast<SetStatus>(static_cast<uint8_t>(flag)); 911 switch (status) 912 { 913 case SetStatus::Complete: 914 { 915 getSetStatus(channel) = status; 916 return responseSuccess(); 917 } 918 case SetStatus::InProgress: 919 { 920 auto& storedStatus = getSetStatus(channel); 921 if (storedStatus == SetStatus::InProgress) 922 { 923 return response(ccParamSetLocked); 924 } 925 storedStatus = status; 926 return responseSuccess(); 927 } 928 case SetStatus::Commit: 929 if (getSetStatus(channel) != SetStatus::InProgress) 930 { 931 return responseInvalidFieldRequest(); 932 } 933 return responseSuccess(); 934 } 935 return response(ccParamNotSupported); 936 } 937 case LanParam::AuthSupport: 938 { 939 req.trailingOk = true; 940 return response(ccParamReadOnly); 941 } 942 case LanParam::AuthEnables: 943 { 944 req.trailingOk = true; 945 return response(ccParamNotSupported); 946 } 947 case LanParam::IP: 948 { 949 in_addr ip; 950 std::array<uint8_t, sizeof(ip)> bytes; 951 if (req.unpack(bytes) != 0 || !req.fullyUnpacked()) 952 { 953 return responseReqDataLenInvalid(); 954 } 955 copyInto(ip, bytes); 956 channelCall<reconfigureIfAddr4>(channel, ip, std::nullopt); 957 return responseSuccess(); 958 } 959 case LanParam::IPSrc: 960 { 961 uint4_t flag; 962 uint4_t rsvd; 963 if (req.unpack(flag, rsvd) != 0 || !req.fullyUnpacked()) 964 { 965 return responseReqDataLenInvalid(); 966 } 967 switch (static_cast<IPSrc>(static_cast<uint8_t>(flag))) 968 { 969 case IPSrc::DHCP: 970 { 971 channelCall<setDHCPProperty>(channel, true); 972 return responseSuccess(); 973 } 974 case IPSrc::Unspecified: 975 case IPSrc::Static: 976 case IPSrc::BIOS: 977 case IPSrc::BMC: 978 { 979 channelCall<setDHCPProperty>(channel, false); 980 return responseSuccess(); 981 } 982 } 983 return response(ccParamNotSupported); 984 } 985 case LanParam::MAC: 986 { 987 ether_addr mac; 988 std::array<uint8_t, sizeof(mac)> bytes; 989 if (req.unpack(bytes) != 0 || !req.fullyUnpacked()) 990 { 991 return responseReqDataLenInvalid(); 992 } 993 copyInto(mac, bytes); 994 channelCall<setMACProperty>(channel, mac); 995 return responseSuccess(); 996 } 997 case LanParam::SubnetMask: 998 { 999 in_addr netmask; 1000 std::array<uint8_t, sizeof(netmask)> bytes; 1001 if (req.unpack(bytes) != 0 || !req.fullyUnpacked()) 1002 { 1003 return responseReqDataLenInvalid(); 1004 } 1005 copyInto(netmask, bytes); 1006 channelCall<reconfigureIfAddr4>(channel, std::nullopt, 1007 netmaskToPrefix(netmask)); 1008 return responseSuccess(); 1009 } 1010 case LanParam::Gateway1: 1011 { 1012 in_addr gateway; 1013 std::array<uint8_t, sizeof(gateway)> bytes; 1014 if (req.unpack(bytes) != 0 || !req.fullyUnpacked()) 1015 { 1016 return responseReqDataLenInvalid(); 1017 } 1018 copyInto(gateway, bytes); 1019 channelCall<setGatewayProperty<AF_INET>>(channel, gateway); 1020 return responseSuccess(); 1021 } 1022 case LanParam::VLANId: 1023 { 1024 uint16_t vlanData; 1025 if (req.unpack(vlanData) != 0 || !req.fullyUnpacked()) 1026 { 1027 return responseReqDataLenInvalid(); 1028 } 1029 if ((vlanData & VLAN_ENABLE_FLAG) == 0) 1030 { 1031 lastDisabledVlan[channel] = vlanData & VLAN_VALUE_MASK; 1032 vlanData = 0; 1033 } 1034 channelCall<reconfigureVLAN>(channel, vlanData & VLAN_VALUE_MASK); 1035 return responseSuccess(); 1036 } 1037 case LanParam::CiphersuiteSupport: 1038 case LanParam::CiphersuiteEntries: 1039 { 1040 req.trailingOk = true; 1041 return response(ccParamReadOnly); 1042 } 1043 } 1044 1045 req.trailingOk = true; 1046 return response(ccParamNotSupported); 1047 } 1048 1049 RspType<message::Payload> getLan(uint4_t channelBits, uint3_t, bool revOnly, 1050 uint8_t parameter, uint8_t set, uint8_t block) 1051 { 1052 message::Payload ret; 1053 constexpr uint8_t current_revision = 0x11; 1054 ret.pack(current_revision); 1055 1056 if (revOnly) 1057 { 1058 return responseSuccess(std::move(ret)); 1059 } 1060 1061 auto channel = static_cast<uint8_t>(channelBits); 1062 if (!doesDeviceExist(channel)) 1063 { 1064 return responseInvalidFieldRequest(); 1065 } 1066 1067 switch (static_cast<LanParam>(parameter)) 1068 { 1069 case LanParam::SetStatus: 1070 { 1071 SetStatus status; 1072 try 1073 { 1074 status = setStatus.at(channel); 1075 } 1076 catch (const std::out_of_range&) 1077 { 1078 status = SetStatus::Complete; 1079 } 1080 ret.pack(static_cast<uint2_t>(status), uint6_t{}); 1081 return responseSuccess(std::move(ret)); 1082 } 1083 case LanParam::AuthSupport: 1084 { 1085 std::bitset<6> support; 1086 ret.pack(support, uint2_t{}); 1087 return responseSuccess(std::move(ret)); 1088 } 1089 case LanParam::AuthEnables: 1090 { 1091 std::bitset<6> enables; 1092 ret.pack(enables, uint2_t{}); // Callback 1093 ret.pack(enables, uint2_t{}); // User 1094 ret.pack(enables, uint2_t{}); // Operator 1095 ret.pack(enables, uint2_t{}); // Admin 1096 ret.pack(enables, uint2_t{}); // OEM 1097 return responseSuccess(std::move(ret)); 1098 } 1099 case LanParam::IP: 1100 { 1101 auto ifaddr = channelCall<getIfAddr4>(channel); 1102 in_addr addr{}; 1103 if (ifaddr) 1104 { 1105 addr = ifaddr->address; 1106 } 1107 ret.pack(dataRef(addr)); 1108 return responseSuccess(std::move(ret)); 1109 } 1110 case LanParam::IPSrc: 1111 { 1112 auto src = IPSrc::Static; 1113 if (channelCall<getDHCPProperty>(channel)) 1114 { 1115 src = IPSrc::DHCP; 1116 } 1117 ret.pack(static_cast<uint4_t>(src), uint4_t{}); 1118 return responseSuccess(std::move(ret)); 1119 } 1120 case LanParam::MAC: 1121 { 1122 ether_addr mac = channelCall<getMACProperty>(channel); 1123 ret.pack(dataRef(mac)); 1124 return responseSuccess(std::move(ret)); 1125 } 1126 case LanParam::SubnetMask: 1127 { 1128 auto ifaddr = channelCall<getIfAddr4>(channel); 1129 uint8_t prefix = AddrFamily<AF_INET>::defaultPrefix; 1130 if (ifaddr) 1131 { 1132 prefix = ifaddr->prefix; 1133 } 1134 in_addr netmask = prefixToNetmask(prefix); 1135 ret.pack(dataRef(netmask)); 1136 return responseSuccess(std::move(ret)); 1137 } 1138 case LanParam::Gateway1: 1139 { 1140 auto gateway = 1141 channelCall<getGatewayProperty<AF_INET>>(channel).value_or( 1142 in_addr{}); 1143 ret.pack(dataRef(gateway)); 1144 return responseSuccess(std::move(ret)); 1145 } 1146 case LanParam::VLANId: 1147 { 1148 uint16_t vlan = channelCall<getVLANProperty>(channel); 1149 if (vlan != 0) 1150 { 1151 vlan |= VLAN_ENABLE_FLAG; 1152 } 1153 else 1154 { 1155 vlan = lastDisabledVlan[channel]; 1156 } 1157 ret.pack(vlan); 1158 return responseSuccess(std::move(ret)); 1159 } 1160 case LanParam::CiphersuiteSupport: 1161 case LanParam::CiphersuiteEntries: 1162 return response(ccParamNotSupported); 1163 } 1164 1165 return response(ccParamNotSupported); 1166 } 1167 1168 } // namespace transport 1169 } // namespace ipmi 1170 1171 void register_netfn_transport_functions() __attribute__((constructor)); 1172 1173 void register_netfn_transport_functions() 1174 { 1175 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnTransport, 1176 ipmi::transport::cmdSetLanConfigParameters, 1177 ipmi::Privilege::Admin, ipmi::transport::setLan); 1178 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnTransport, 1179 ipmi::transport::cmdGetLanConfigParameters, 1180 ipmi::Privilege::Admin, ipmi::transport::getLan); 1181 } 1182