1 #include "transporthandler.hpp" 2 3 #include "app/channel.hpp" 4 #include "user_channel/cipher_mgmt.hpp" 5 6 #include <arpa/inet.h> 7 #include <netinet/ether.h> 8 9 #include <array> 10 #include <bitset> 11 #include <cinttypes> 12 #include <cstdint> 13 #include <cstring> 14 #include <fstream> 15 #include <functional> 16 #include <ipmid/api.hpp> 17 #include <ipmid/message.hpp> 18 #include <ipmid/message/types.hpp> 19 #include <ipmid/types.hpp> 20 #include <ipmid/utils.hpp> 21 #include <optional> 22 #include <phosphor-logging/elog-errors.hpp> 23 #include <phosphor-logging/elog.hpp> 24 #include <phosphor-logging/log.hpp> 25 #include <sdbusplus/bus.hpp> 26 #include <sdbusplus/exception.hpp> 27 #include <string> 28 #include <string_view> 29 #include <type_traits> 30 #include <unordered_map> 31 #include <unordered_set> 32 #include <user_channel/channel_layer.hpp> 33 #include <utility> 34 #include <vector> 35 #include <xyz/openbmc_project/Common/error.hpp> 36 #include <xyz/openbmc_project/Network/EthernetInterface/server.hpp> 37 #include <xyz/openbmc_project/Network/IP/server.hpp> 38 #include <xyz/openbmc_project/Network/Neighbor/server.hpp> 39 40 using phosphor::logging::commit; 41 using phosphor::logging::elog; 42 using phosphor::logging::entry; 43 using phosphor::logging::level; 44 using phosphor::logging::log; 45 using sdbusplus::xyz::openbmc_project::Common::Error::InternalFailure; 46 using sdbusplus::xyz::openbmc_project::Network::server::EthernetInterface; 47 using sdbusplus::xyz::openbmc_project::Network::server::IP; 48 using sdbusplus::xyz::openbmc_project::Network::server::Neighbor; 49 50 namespace cipher 51 { 52 53 std::vector<uint8_t> getCipherList() 54 { 55 std::vector<uint8_t> cipherList; 56 57 std::ifstream jsonFile(cipher::configFile); 58 if (!jsonFile.is_open()) 59 { 60 log<level::ERR>("Channel Cipher suites file not found"); 61 elog<InternalFailure>(); 62 } 63 64 auto data = Json::parse(jsonFile, nullptr, false); 65 if (data.is_discarded()) 66 { 67 log<level::ERR>("Parsing channel cipher suites JSON failed"); 68 elog<InternalFailure>(); 69 } 70 71 // Byte 1 is reserved 72 cipherList.push_back(0x00); 73 74 for (const auto& record : data) 75 { 76 cipherList.push_back(record.value(cipher, 0)); 77 } 78 79 return cipherList; 80 } 81 } // namespace cipher 82 83 namespace ipmi 84 { 85 namespace transport 86 { 87 88 // D-Bus Network Daemon definitions 89 constexpr auto PATH_ROOT = "/xyz/openbmc_project/network"; 90 constexpr auto PATH_SYSTEMCONFIG = "/xyz/openbmc_project/network/config"; 91 92 constexpr auto INTF_SYSTEMCONFIG = 93 "xyz.openbmc_project.Network.SystemConfiguration"; 94 constexpr auto INTF_ETHERNET = "xyz.openbmc_project.Network.EthernetInterface"; 95 constexpr auto INTF_IP = "xyz.openbmc_project.Network.IP"; 96 constexpr auto INTF_IP_CREATE = "xyz.openbmc_project.Network.IP.Create"; 97 constexpr auto INTF_MAC = "xyz.openbmc_project.Network.MACAddress"; 98 constexpr auto INTF_NEIGHBOR = "xyz.openbmc_project.Network.Neighbor"; 99 constexpr auto INTF_NEIGHBOR_CREATE_STATIC = 100 "xyz.openbmc_project.Network.Neighbor.CreateStatic"; 101 constexpr auto INTF_VLAN = "xyz.openbmc_project.Network.VLAN"; 102 constexpr auto INTF_VLAN_CREATE = "xyz.openbmc_project.Network.VLAN.Create"; 103 104 /** @brief Generic paramters for different address families */ 105 template <int family> 106 struct AddrFamily 107 { 108 }; 109 110 /** @brief Parameter specialization for IPv4 */ 111 template <> 112 struct AddrFamily<AF_INET> 113 { 114 using addr = in_addr; 115 static constexpr auto protocol = IP::Protocol::IPv4; 116 static constexpr size_t maxStrLen = INET6_ADDRSTRLEN; 117 static constexpr uint8_t defaultPrefix = 32; 118 static constexpr char propertyGateway[] = "DefaultGateway"; 119 }; 120 121 /** @brief Parameter specialization for IPv6 */ 122 template <> 123 struct AddrFamily<AF_INET6> 124 { 125 using addr = in6_addr; 126 static constexpr auto protocol = IP::Protocol::IPv6; 127 static constexpr size_t maxStrLen = INET6_ADDRSTRLEN; 128 static constexpr uint8_t defaultPrefix = 128; 129 static constexpr char propertyGateway[] = "DefaultGateway6"; 130 }; 131 132 /** @brief Valid address origins for IPv4 */ 133 const std::unordered_set<IP::AddressOrigin> originsV4 = { 134 IP::AddressOrigin::Static, 135 IP::AddressOrigin::DHCP, 136 }; 137 138 /** @brief Valid address origins for IPv6 */ 139 const std::unordered_set<IP::AddressOrigin> originsV6Static = { 140 IP::AddressOrigin::Static}; 141 const std::unordered_set<IP::AddressOrigin> originsV6Dynamic = { 142 IP::AddressOrigin::DHCP, 143 IP::AddressOrigin::SLAAC, 144 }; 145 146 /** @brief Interface IP Address configuration parameters */ 147 template <int family> 148 struct IfAddr 149 { 150 std::string path; 151 typename AddrFamily<family>::addr address; 152 IP::AddressOrigin origin; 153 uint8_t prefix; 154 }; 155 156 /** @brief Interface Neighbor configuration parameters */ 157 template <int family> 158 struct IfNeigh 159 { 160 std::string path; 161 typename AddrFamily<family>::addr ip; 162 ether_addr mac; 163 }; 164 165 static constexpr uint8_t oemCmdStart = 192; 166 static constexpr uint8_t oemCmdEnd = 255; 167 168 /** @brief A trivial helper used to determine if two PODs are equal 169 * 170 * @params[in] a - The first object to compare 171 * @params[in] b - The second object to compare 172 * @return True if the objects are the same bytewise 173 */ 174 template <typename T> 175 bool equal(const T& a, const T& b) 176 { 177 static_assert(std::is_trivially_copyable_v<T>); 178 return std::memcmp(&a, &b, sizeof(T)) == 0; 179 } 180 181 /** @brief Copies bytes from an array into a trivially copyable container 182 * 183 * @params[out] t - The container receiving the data 184 * @params[in] bytes - The data to copy 185 */ 186 template <size_t N, typename T> 187 void copyInto(T& t, const std::array<uint8_t, N>& bytes) 188 { 189 static_assert(std::is_trivially_copyable_v<T>); 190 static_assert(N == sizeof(T)); 191 std::memcpy(&t, bytes.data(), bytes.size()); 192 } 193 194 /** @brief Gets a generic view of the bytes in the input container 195 * 196 * @params[in] t - The data to reference 197 * @return A string_view referencing the bytes in the container 198 */ 199 template <typename T> 200 std::string_view dataRef(const T& t) 201 { 202 static_assert(std::is_trivially_copyable_v<T>); 203 return {reinterpret_cast<const char*>(&t), sizeof(T)}; 204 } 205 206 /** @brief The dbus parameters for the interface corresponding to a channel 207 * This helps reduce the number of mapper lookups we need for each 208 * query and simplifies finding the VLAN interface if needed. 209 */ 210 struct ChannelParams 211 { 212 /** @brief The channel ID */ 213 int id; 214 /** @brief channel name for the interface */ 215 std::string ifname; 216 /** @brief Name of the service on the bus */ 217 std::string service; 218 /** @brief Lower level adapter path that is guaranteed to not be a VLAN */ 219 std::string ifPath; 220 /** @brief Logical adapter path used for address assignment */ 221 std::string logicalPath; 222 }; 223 224 /** @brief Determines the ethernet interface name corresponding to a channel 225 * Tries to map a VLAN object first so that the address information 226 * is accurate. Otherwise it gets the standard ethernet interface. 227 * 228 * @param[in] bus - The bus object used for lookups 229 * @param[in] channel - The channel id corresponding to an ethernet interface 230 * @return Ethernet interface service and object path if it exists 231 */ 232 std::optional<ChannelParams> maybeGetChannelParams(sdbusplus::bus::bus& bus, 233 uint8_t channel) 234 { 235 auto ifname = getChannelName(channel); 236 if (ifname.empty()) 237 { 238 return std::nullopt; 239 } 240 241 // Enumerate all VLAN + ETHERNET interfaces 242 auto req = bus.new_method_call(MAPPER_BUS_NAME, MAPPER_OBJ, MAPPER_INTF, 243 "GetSubTree"); 244 req.append(PATH_ROOT, 0, 245 std::vector<std::string>{INTF_VLAN, INTF_ETHERNET}); 246 auto reply = bus.call(req); 247 ObjectTree objs; 248 reply.read(objs); 249 250 ChannelParams params; 251 for (const auto& [path, impls] : objs) 252 { 253 if (path.find(ifname) == path.npos) 254 { 255 continue; 256 } 257 for (const auto& [service, intfs] : impls) 258 { 259 bool vlan = false; 260 bool ethernet = false; 261 for (const auto& intf : intfs) 262 { 263 if (intf == INTF_VLAN) 264 { 265 vlan = true; 266 } 267 else if (intf == INTF_ETHERNET) 268 { 269 ethernet = true; 270 } 271 } 272 if (params.service.empty() && (vlan || ethernet)) 273 { 274 params.service = service; 275 } 276 if (params.ifPath.empty() && !vlan && ethernet) 277 { 278 params.ifPath = path; 279 } 280 if (params.logicalPath.empty() && vlan) 281 { 282 params.logicalPath = path; 283 } 284 } 285 } 286 287 // We must have a path for the underlying interface 288 if (params.ifPath.empty()) 289 { 290 return std::nullopt; 291 } 292 // We don't have a VLAN so the logical path is the same 293 if (params.logicalPath.empty()) 294 { 295 params.logicalPath = params.ifPath; 296 } 297 298 params.id = channel; 299 params.ifname = std::move(ifname); 300 return std::move(params); 301 } 302 303 /** @brief A trivial helper around maybeGetChannelParams() that throws an 304 * exception when it is unable to acquire parameters for the channel. 305 * 306 * @param[in] bus - The bus object used for lookups 307 * @param[in] channel - The channel id corresponding to an ethernet interface 308 * @return Ethernet interface service and object path 309 */ 310 ChannelParams getChannelParams(sdbusplus::bus::bus& bus, uint8_t channel) 311 { 312 auto params = maybeGetChannelParams(bus, channel); 313 if (!params) 314 { 315 log<level::ERR>("Failed to get channel params", 316 entry("CHANNEL=%" PRIu8, channel)); 317 elog<InternalFailure>(); 318 } 319 return std::move(*params); 320 } 321 322 /** @brief Wraps the phosphor logging method to insert some additional metadata 323 * 324 * @param[in] params - The parameters for the channel 325 * ... 326 */ 327 template <auto level, typename... Args> 328 auto logWithChannel(const ChannelParams& params, Args&&... args) 329 { 330 return log<level>(std::forward<Args>(args)..., 331 entry("CHANNEL=%d", params.id), 332 entry("IFNAME=%s", params.ifname.c_str())); 333 } 334 template <auto level, typename... Args> 335 auto logWithChannel(const std::optional<ChannelParams>& params, Args&&... args) 336 { 337 if (params) 338 { 339 return logWithChannel<level>(*params, std::forward<Args>(args)...); 340 } 341 return log<level>(std::forward<Args>(args)...); 342 } 343 344 /** @brief Trivializes using parameter getter functions by providing a bus 345 * and channel parameters automatically. 346 * 347 * @param[in] channel - The channel id corresponding to an ethernet interface 348 * ... 349 */ 350 template <auto func, typename... Args> 351 auto channelCall(uint8_t channel, Args&&... args) 352 { 353 sdbusplus::bus::bus bus(ipmid_get_sd_bus_connection()); 354 auto params = getChannelParams(bus, channel); 355 return std::invoke(func, bus, params, std::forward<Args>(args)...); 356 } 357 358 /** @brief Determines if the ethernet interface is using DHCP 359 * 360 * @param[in] bus - The bus object used for lookups 361 * @param[in] params - The parameters for the channel 362 * @return DHCPConf enumeration 363 */ 364 EthernetInterface::DHCPConf getDHCPProperty(sdbusplus::bus::bus& bus, 365 const ChannelParams& params) 366 { 367 std::string dhcpstr = std::get<std::string>(getDbusProperty( 368 bus, params.service, params.logicalPath, INTF_ETHERNET, "DHCPEnabled")); 369 return EthernetInterface::convertDHCPConfFromString(dhcpstr); 370 } 371 372 /** @brief Sets the DHCP v4 state on the given interface 373 * 374 * @param[in] bus - The bus object used for lookups 375 * @param[in] params - The parameters for the channel 376 * @param[in] requestedDhcp - DHCP state to assign 377 * (EthernetInterface::DHCPConf::none, 378 * EthernetInterface::DHCPConf::v4, 379 * EthernetInterface::DHCPConf::v6, 380 * EthernetInterface::DHCPConf::both) 381 */ 382 void setDHCPv4Property(sdbusplus::bus::bus& bus, const ChannelParams& params, 383 const EthernetInterface::DHCPConf requestedDhcp) 384 { 385 EthernetInterface::DHCPConf currentDhcp = getDHCPProperty(bus, params); 386 EthernetInterface::DHCPConf nextDhcp = EthernetInterface::DHCPConf::none; 387 388 if ((currentDhcp == EthernetInterface::DHCPConf::v6) && 389 (requestedDhcp == EthernetInterface::DHCPConf::v4)) 390 { 391 nextDhcp = EthernetInterface::DHCPConf::both; 392 } 393 else if ((currentDhcp == EthernetInterface::DHCPConf::none) && 394 (requestedDhcp == EthernetInterface::DHCPConf::v4)) 395 396 { 397 nextDhcp = requestedDhcp; 398 } 399 else if (requestedDhcp == EthernetInterface::DHCPConf::none) 400 { 401 if (currentDhcp == EthernetInterface::DHCPConf::both) 402 { 403 nextDhcp = EthernetInterface::DHCPConf::v6; 404 } 405 else if (currentDhcp == EthernetInterface::DHCPConf::v4) 406 { 407 nextDhcp = EthernetInterface::DHCPConf::none; 408 } 409 } 410 else 411 { 412 nextDhcp = currentDhcp; 413 } 414 std::string newDhcp = 415 sdbusplus::xyz::openbmc_project::Network::server::convertForMessage( 416 nextDhcp); 417 setDbusProperty(bus, params.service, params.logicalPath, INTF_ETHERNET, 418 "DHCPEnabled", newDhcp); 419 } 420 421 /** @brief Sets the DHCP v6 state on the given interface 422 * 423 * @param[in] bus - The bus object used for lookups 424 * @param[in] params - The parameters for the channel 425 * @param[in] requestedDhcp - DHCP state to assign (none, v6, both) 426 * @param[in] defaultMode - True: Use algorithmic assignment 427 * False: requestedDhcp assigned unconditionally 428 */ 429 void setDHCPv6Property(sdbusplus::bus::bus& bus, const ChannelParams& params, 430 const EthernetInterface::DHCPConf requestedDhcp, 431 const bool defaultMode = true) 432 { 433 EthernetInterface::DHCPConf currentDhcp = getDHCPProperty(bus, params); 434 EthernetInterface::DHCPConf nextDhcp = EthernetInterface::DHCPConf::none; 435 436 if (defaultMode) 437 { 438 if ((currentDhcp == EthernetInterface::DHCPConf::v4) && 439 (requestedDhcp == EthernetInterface::DHCPConf::v6)) 440 { 441 nextDhcp = EthernetInterface::DHCPConf::both; 442 } 443 else if ((currentDhcp == EthernetInterface::DHCPConf::none) && 444 (requestedDhcp == EthernetInterface::DHCPConf::v6)) 445 446 { 447 nextDhcp = requestedDhcp; 448 } 449 else if (requestedDhcp == EthernetInterface::DHCPConf::none) 450 { 451 if (currentDhcp == EthernetInterface::DHCPConf::both) 452 { 453 nextDhcp = EthernetInterface::DHCPConf::v4; 454 } 455 else if (currentDhcp == EthernetInterface::DHCPConf::v6) 456 { 457 nextDhcp = EthernetInterface::DHCPConf::none; 458 } 459 } 460 else 461 { 462 nextDhcp = currentDhcp; 463 } 464 } 465 else 466 { 467 // allow the v6 call to set any value 468 nextDhcp = requestedDhcp; 469 } 470 471 std::string newDhcp = 472 sdbusplus::xyz::openbmc_project::Network::server::convertForMessage( 473 nextDhcp); 474 setDbusProperty(bus, params.service, params.logicalPath, INTF_ETHERNET, 475 "DHCPEnabled", newDhcp); 476 } 477 478 /** @brief Converts a human readable MAC string into MAC bytes 479 * 480 * @param[in] mac - The MAC string 481 * @return MAC in bytes 482 */ 483 ether_addr stringToMAC(const char* mac) 484 { 485 const ether_addr* ret = ether_aton(mac); 486 if (ret == nullptr) 487 { 488 log<level::ERR>("Invalid MAC Address", entry("MAC=%s", mac)); 489 elog<InternalFailure>(); 490 } 491 return *ret; 492 } 493 494 /** @brief Determines the MAC of the ethernet interface 495 * 496 * @param[in] bus - The bus object used for lookups 497 * @param[in] params - The parameters for the channel 498 * @return The configured mac address 499 */ 500 ether_addr getMACProperty(sdbusplus::bus::bus& bus, const ChannelParams& params) 501 { 502 auto macStr = std::get<std::string>(getDbusProperty( 503 bus, params.service, params.ifPath, INTF_MAC, "MACAddress")); 504 return stringToMAC(macStr.c_str()); 505 } 506 507 /** @brief Sets the system value for MAC address on the given interface 508 * 509 * @param[in] bus - The bus object used for lookups 510 * @param[in] params - The parameters for the channel 511 * @param[in] mac - MAC address to apply 512 */ 513 void setMACProperty(sdbusplus::bus::bus& bus, const ChannelParams& params, 514 const ether_addr& mac) 515 { 516 std::string macStr = ether_ntoa(&mac); 517 setDbusProperty(bus, params.service, params.ifPath, INTF_MAC, "MACAddress", 518 macStr); 519 } 520 521 /** @brief Turns an IP address string into the network byte order form 522 * NOTE: This version strictly validates family matches 523 * 524 * @param[in] address - The string form of the address 525 * @return A network byte order address or none if conversion failed 526 */ 527 template <int family> 528 std::optional<typename AddrFamily<family>::addr> 529 maybeStringToAddr(const char* address) 530 { 531 typename AddrFamily<family>::addr ret; 532 if (inet_pton(family, address, &ret) == 1) 533 { 534 return ret; 535 } 536 return std::nullopt; 537 } 538 539 /** @brief Turns an IP address string into the network byte order form 540 * NOTE: This version strictly validates family matches 541 * 542 * @param[in] address - The string form of the address 543 * @return A network byte order address 544 */ 545 template <int family> 546 typename AddrFamily<family>::addr stringToAddr(const char* address) 547 { 548 auto ret = maybeStringToAddr<family>(address); 549 if (!ret) 550 { 551 log<level::ERR>("Failed to convert IP Address", 552 entry("FAMILY=%d", family), 553 entry("ADDRESS=%s", address)); 554 elog<InternalFailure>(); 555 } 556 return *ret; 557 } 558 559 /** @brief Turns an IP address in network byte order into a string 560 * 561 * @param[in] address - The string form of the address 562 * @return A network byte order address 563 */ 564 template <int family> 565 std::string addrToString(const typename AddrFamily<family>::addr& address) 566 { 567 std::string ret(AddrFamily<family>::maxStrLen, '\0'); 568 inet_ntop(family, &address, ret.data(), ret.size()); 569 ret.resize(strlen(ret.c_str())); 570 return ret; 571 } 572 573 /** @brief Retrieves the current gateway for the address family on the system 574 * NOTE: The gateway is currently system wide and not per channel 575 * 576 * @param[in] bus - The bus object used for lookups 577 * @param[in] params - The parameters for the channel 578 * @return An address representing the gateway address if it exists 579 */ 580 template <int family> 581 std::optional<typename AddrFamily<family>::addr> 582 getGatewayProperty(sdbusplus::bus::bus& bus, const ChannelParams& params) 583 { 584 auto gatewayStr = std::get<std::string>(getDbusProperty( 585 bus, params.service, PATH_SYSTEMCONFIG, INTF_SYSTEMCONFIG, 586 AddrFamily<family>::propertyGateway)); 587 if (gatewayStr.empty()) 588 { 589 return std::nullopt; 590 } 591 return stringToAddr<family>(gatewayStr.c_str()); 592 } 593 594 /** @brief A lazy lookup mechanism for iterating over object properties stored 595 * in DBus. This will only perform the object lookup when needed, and 596 * retains a cache of previous lookups to speed up future iterations. 597 */ 598 class ObjectLookupCache 599 { 600 public: 601 using PropertiesCache = std::unordered_map<std::string, PropertyMap>; 602 603 /** @brief Creates a new ObjectLookupCache for the interface on the bus 604 * NOTE: The inputs to this object must outlive the object since 605 * they are only referenced by it. 606 * 607 * @param[in] bus - The bus object used for lookups 608 * @param[in] params - The parameters for the channel 609 * @param[in] intf - The interface we are looking up 610 */ 611 ObjectLookupCache(sdbusplus::bus::bus& bus, const ChannelParams& params, 612 const char* intf) : 613 bus(bus), 614 params(params), intf(intf), 615 objs(getAllDbusObjects(bus, params.logicalPath, intf, "")) 616 { 617 } 618 619 class iterator : public ObjectTree::const_iterator 620 { 621 public: 622 using value_type = PropertiesCache::value_type; 623 624 iterator(ObjectTree::const_iterator it, ObjectLookupCache& container) : 625 ObjectTree::const_iterator(it), container(container), 626 ret(container.cache.end()) 627 { 628 } 629 value_type& operator*() 630 { 631 ret = container.get(ObjectTree::const_iterator::operator*().first); 632 return *ret; 633 } 634 value_type* operator->() 635 { 636 return &operator*(); 637 } 638 639 private: 640 ObjectLookupCache& container; 641 PropertiesCache::iterator ret; 642 }; 643 644 iterator begin() noexcept 645 { 646 return iterator(objs.begin(), *this); 647 } 648 649 iterator end() noexcept 650 { 651 return iterator(objs.end(), *this); 652 } 653 654 private: 655 sdbusplus::bus::bus& bus; 656 const ChannelParams& params; 657 const char* const intf; 658 const ObjectTree objs; 659 PropertiesCache cache; 660 661 /** @brief Gets a cached copy of the object properties if possible 662 * Otherwise performs a query on DBus to look them up 663 * 664 * @param[in] path - The object path to lookup 665 * @return An iterator for the specified object path + properties 666 */ 667 PropertiesCache::iterator get(const std::string& path) 668 { 669 auto it = cache.find(path); 670 if (it != cache.end()) 671 { 672 return it; 673 } 674 auto properties = getAllDbusProperties(bus, params.service, path, intf); 675 return cache.insert({path, std::move(properties)}).first; 676 } 677 }; 678 679 /** @brief Searches the ip object lookup cache for an address matching 680 * the input parameters. NOTE: The index lacks stability across address 681 * changes since the network daemon has no notion of stable indicies. 682 * 683 * @param[in] bus - The bus object used for lookups 684 * @param[in] params - The parameters for the channel 685 * @param[in] idx - The index of the desired address on the interface 686 * @param[in] origins - The allowed origins for the address objects 687 * @param[in] ips - The object lookup cache holding all of the address info 688 * @return The address and prefix if it was found 689 */ 690 template <int family> 691 std::optional<IfAddr<family>> 692 findIfAddr(sdbusplus::bus::bus& bus, const ChannelParams& params, 693 uint8_t idx, 694 const std::unordered_set<IP::AddressOrigin>& origins, 695 ObjectLookupCache& ips) 696 { 697 for (const auto& [path, properties] : ips) 698 { 699 const auto& addrStr = std::get<std::string>(properties.at("Address")); 700 auto addr = maybeStringToAddr<family>(addrStr.c_str()); 701 if (!addr) 702 { 703 continue; 704 } 705 706 IP::AddressOrigin origin = IP::convertAddressOriginFromString( 707 std::get<std::string>(properties.at("Origin"))); 708 if (origins.find(origin) == origins.end()) 709 { 710 continue; 711 } 712 713 if (idx > 0) 714 { 715 idx--; 716 continue; 717 } 718 719 IfAddr<family> ifaddr; 720 ifaddr.path = path; 721 ifaddr.address = *addr; 722 ifaddr.prefix = std::get<uint8_t>(properties.at("PrefixLength")); 723 ifaddr.origin = origin; 724 return std::move(ifaddr); 725 } 726 727 return std::nullopt; 728 } 729 730 /** @brief Trivial helper around findIfAddr that simplifies calls 731 * for one off lookups. Don't use this if you intend to do multiple 732 * lookups at a time. 733 * 734 * @param[in] bus - The bus object used for lookups 735 * @param[in] params - The parameters for the channel 736 * @param[in] idx - The index of the desired address on the interface 737 * @param[in] origins - The allowed origins for the address objects 738 * @return The address and prefix if it was found 739 */ 740 template <int family> 741 auto getIfAddr(sdbusplus::bus::bus& bus, const ChannelParams& params, 742 uint8_t idx, 743 const std::unordered_set<IP::AddressOrigin>& origins) 744 { 745 ObjectLookupCache ips(bus, params, INTF_IP); 746 return findIfAddr<family>(bus, params, idx, origins, ips); 747 } 748 749 /** @brief Deletes the dbus object. Ignores empty objects or objects that are 750 * missing from the bus. 751 * 752 * @param[in] bus - The bus object used for lookups 753 * @param[in] service - The name of the service 754 * @param[in] path - The path of the object to delete 755 */ 756 void deleteObjectIfExists(sdbusplus::bus::bus& bus, const std::string& service, 757 const std::string& path) 758 { 759 if (path.empty()) 760 { 761 return; 762 } 763 try 764 { 765 auto req = bus.new_method_call(service.c_str(), path.c_str(), 766 ipmi::DELETE_INTERFACE, "Delete"); 767 bus.call_noreply(req); 768 } 769 catch (const sdbusplus::exception::SdBusError& e) 770 { 771 if (strcmp(e.name(), 772 "xyz.openbmc_project.Common.Error.InternalFailure") != 0 && 773 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 setDHCPv6Property(bus, params, EthernetInterface::DHCPConf::none, 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 EthernetInterface::DHCPConf 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 setDHCPv6Property(bus, params, dhcp, false); 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 * @brief is MAC address valid. 1318 * 1319 * This function checks whether the MAC address is valid or not. 1320 * 1321 * @param[in] mac - MAC address. 1322 * @return true if MAC address is valid else retun false. 1323 **/ 1324 bool isValidMACAddress(const ether_addr& mac) 1325 { 1326 // check if mac address is empty 1327 if (equal(mac, ether_addr{})) 1328 { 1329 return false; 1330 } 1331 // we accept only unicast MAC addresses and same thing has been checked in 1332 // phosphor-network layer. If the least significant bit of the first octet 1333 // is set to 1, it is multicast MAC else it is unicast MAC address. 1334 if (mac.ether_addr_octet[0] & 1) 1335 { 1336 return false; 1337 } 1338 return true; 1339 } 1340 1341 RspType<> setLan(Context::ptr ctx, uint4_t channelBits, uint4_t reserved1, 1342 uint8_t parameter, message::Payload& req) 1343 { 1344 const uint8_t channel = convertCurrentChannelNum( 1345 static_cast<uint8_t>(channelBits), ctx->channel); 1346 if (reserved1 || !isValidChannel(channel)) 1347 { 1348 log<level::ERR>("Set Lan - Invalid field in request"); 1349 req.trailingOk = true; 1350 return responseInvalidFieldRequest(); 1351 } 1352 1353 switch (static_cast<LanParam>(parameter)) 1354 { 1355 case LanParam::SetStatus: 1356 { 1357 uint2_t flag; 1358 uint6_t rsvd; 1359 if (req.unpack(flag, rsvd) != 0 || !req.fullyUnpacked()) 1360 { 1361 return responseReqDataLenInvalid(); 1362 } 1363 if (rsvd) 1364 { 1365 return responseInvalidFieldRequest(); 1366 } 1367 auto status = static_cast<SetStatus>(static_cast<uint8_t>(flag)); 1368 switch (status) 1369 { 1370 case SetStatus::Complete: 1371 { 1372 getSetStatus(channel) = status; 1373 return responseSuccess(); 1374 } 1375 case SetStatus::InProgress: 1376 { 1377 auto& storedStatus = getSetStatus(channel); 1378 if (storedStatus == SetStatus::InProgress) 1379 { 1380 return response(ccParamSetLocked); 1381 } 1382 storedStatus = status; 1383 return responseSuccess(); 1384 } 1385 case SetStatus::Commit: 1386 if (getSetStatus(channel) != SetStatus::InProgress) 1387 { 1388 return responseInvalidFieldRequest(); 1389 } 1390 return responseSuccess(); 1391 } 1392 return response(ccParamNotSupported); 1393 } 1394 case LanParam::AuthSupport: 1395 { 1396 req.trailingOk = true; 1397 return response(ccParamReadOnly); 1398 } 1399 case LanParam::AuthEnables: 1400 { 1401 req.trailingOk = true; 1402 return response(ccParamReadOnly); 1403 } 1404 case LanParam::IP: 1405 { 1406 EthernetInterface::DHCPConf dhcp = 1407 channelCall<getDHCPProperty>(channel); 1408 if ((dhcp == EthernetInterface::DHCPConf::v4) || 1409 (dhcp == EthernetInterface::DHCPConf::both)) 1410 { 1411 return responseCommandNotAvailable(); 1412 } 1413 in_addr ip; 1414 std::array<uint8_t, sizeof(ip)> bytes; 1415 if (req.unpack(bytes) != 0 || !req.fullyUnpacked()) 1416 { 1417 return responseReqDataLenInvalid(); 1418 } 1419 copyInto(ip, bytes); 1420 channelCall<reconfigureIfAddr4>(channel, ip, std::nullopt); 1421 return responseSuccess(); 1422 } 1423 case LanParam::IPSrc: 1424 { 1425 uint4_t flag; 1426 uint4_t rsvd; 1427 if (req.unpack(flag, rsvd) != 0 || !req.fullyUnpacked()) 1428 { 1429 return responseReqDataLenInvalid(); 1430 } 1431 if (rsvd) 1432 { 1433 return responseInvalidFieldRequest(); 1434 } 1435 switch (static_cast<IPSrc>(static_cast<uint8_t>(flag))) 1436 { 1437 case IPSrc::DHCP: 1438 { 1439 // The IPSrc IPMI command is only for IPv4 1440 // management. Modifying IPv6 state is done using 1441 // a completely different Set LAN Configuration 1442 // subcommand. 1443 channelCall<setDHCPv4Property>( 1444 channel, EthernetInterface::DHCPConf::v4); 1445 return responseSuccess(); 1446 } 1447 case IPSrc::Unspecified: 1448 case IPSrc::Static: 1449 { 1450 channelCall<setDHCPv4Property>( 1451 channel, EthernetInterface::DHCPConf::none); 1452 return responseSuccess(); 1453 } 1454 case IPSrc::BIOS: 1455 case IPSrc::BMC: 1456 { 1457 return responseInvalidFieldRequest(); 1458 } 1459 } 1460 return response(ccParamNotSupported); 1461 } 1462 case LanParam::MAC: 1463 { 1464 ether_addr mac; 1465 std::array<uint8_t, sizeof(mac)> bytes; 1466 if (req.unpack(bytes) != 0 || !req.fullyUnpacked()) 1467 { 1468 return responseReqDataLenInvalid(); 1469 } 1470 copyInto(mac, bytes); 1471 1472 if (!isValidMACAddress(mac)) 1473 { 1474 return responseInvalidFieldRequest(); 1475 } 1476 channelCall<setMACProperty>(channel, mac); 1477 return responseSuccess(); 1478 } 1479 case LanParam::SubnetMask: 1480 { 1481 EthernetInterface::DHCPConf dhcp = 1482 channelCall<getDHCPProperty>(channel); 1483 if ((dhcp == EthernetInterface::DHCPConf::v4) || 1484 (dhcp == EthernetInterface::DHCPConf::both)) 1485 { 1486 return responseCommandNotAvailable(); 1487 } 1488 in_addr netmask; 1489 std::array<uint8_t, sizeof(netmask)> bytes; 1490 if (req.unpack(bytes) != 0 || !req.fullyUnpacked()) 1491 { 1492 return responseReqDataLenInvalid(); 1493 } 1494 copyInto(netmask, bytes); 1495 channelCall<reconfigureIfAddr4>(channel, std::nullopt, 1496 netmaskToPrefix(netmask)); 1497 return responseSuccess(); 1498 } 1499 case LanParam::Gateway1: 1500 { 1501 EthernetInterface::DHCPConf dhcp = 1502 channelCall<getDHCPProperty>(channel); 1503 if ((dhcp == EthernetInterface::DHCPConf::v4) || 1504 (dhcp == EthernetInterface::DHCPConf::both)) 1505 { 1506 return responseCommandNotAvailable(); 1507 } 1508 in_addr gateway; 1509 std::array<uint8_t, sizeof(gateway)> bytes; 1510 if (req.unpack(bytes) != 0 || !req.fullyUnpacked()) 1511 { 1512 return responseReqDataLenInvalid(); 1513 } 1514 copyInto(gateway, bytes); 1515 channelCall<setGatewayProperty<AF_INET>>(channel, gateway); 1516 return responseSuccess(); 1517 } 1518 case LanParam::Gateway1MAC: 1519 { 1520 ether_addr gatewayMAC; 1521 std::array<uint8_t, sizeof(gatewayMAC)> bytes; 1522 if (req.unpack(bytes) != 0 || !req.fullyUnpacked()) 1523 { 1524 return responseReqDataLenInvalid(); 1525 } 1526 copyInto(gatewayMAC, bytes); 1527 channelCall<reconfigureGatewayMAC<AF_INET>>(channel, gatewayMAC); 1528 return responseSuccess(); 1529 } 1530 case LanParam::VLANId: 1531 { 1532 uint12_t vlanData = 0; 1533 uint3_t reserved = 0; 1534 bool vlanEnable = 0; 1535 1536 if (req.unpack(vlanData) || req.unpack(reserved) || 1537 req.unpack(vlanEnable) || !req.fullyUnpacked()) 1538 { 1539 return responseReqDataLenInvalid(); 1540 } 1541 1542 if (reserved) 1543 { 1544 return responseInvalidFieldRequest(); 1545 } 1546 1547 uint16_t vlan = static_cast<uint16_t>(vlanData); 1548 1549 if (!vlanEnable) 1550 { 1551 lastDisabledVlan[channel] = vlan; 1552 vlan = 0; 1553 } 1554 else if (vlan == 0 || vlan == VLAN_VALUE_MASK) 1555 { 1556 return responseInvalidFieldRequest(); 1557 } 1558 1559 channelCall<reconfigureVLAN>(channel, vlan); 1560 return responseSuccess(); 1561 } 1562 case LanParam::CiphersuiteSupport: 1563 case LanParam::CiphersuiteEntries: 1564 case LanParam::IPFamilySupport: 1565 { 1566 req.trailingOk = true; 1567 return response(ccParamReadOnly); 1568 } 1569 case LanParam::IPFamilyEnables: 1570 { 1571 uint8_t enables; 1572 if (req.unpack(enables) != 0 || !req.fullyUnpacked()) 1573 { 1574 return responseReqDataLenInvalid(); 1575 } 1576 switch (static_cast<IPFamilyEnables>(enables)) 1577 { 1578 case IPFamilyEnables::DualStack: 1579 return responseSuccess(); 1580 case IPFamilyEnables::IPv4Only: 1581 case IPFamilyEnables::IPv6Only: 1582 return response(ccParamNotSupported); 1583 } 1584 return response(ccParamNotSupported); 1585 } 1586 case LanParam::IPv6Status: 1587 { 1588 req.trailingOk = true; 1589 return response(ccParamReadOnly); 1590 } 1591 case LanParam::IPv6StaticAddresses: 1592 { 1593 uint8_t set; 1594 uint7_t rsvd; 1595 bool enabled; 1596 in6_addr ip; 1597 std::array<uint8_t, sizeof(ip)> ipbytes; 1598 uint8_t prefix; 1599 uint8_t status; 1600 if (req.unpack(set, rsvd, enabled, ipbytes, prefix, status) != 0 || 1601 !req.fullyUnpacked()) 1602 { 1603 return responseReqDataLenInvalid(); 1604 } 1605 if (rsvd) 1606 { 1607 return responseInvalidFieldRequest(); 1608 } 1609 copyInto(ip, ipbytes); 1610 if (enabled) 1611 { 1612 channelCall<reconfigureIfAddr6>(channel, set, ip, prefix); 1613 } 1614 else 1615 { 1616 channelCall<deconfigureIfAddr6>(channel, set); 1617 } 1618 return responseSuccess(); 1619 } 1620 case LanParam::IPv6DynamicAddresses: 1621 { 1622 req.trailingOk = true; 1623 return response(ccParamReadOnly); 1624 } 1625 case LanParam::IPv6RouterControl: 1626 { 1627 std::bitset<8> control; 1628 if (req.unpack(control) != 0 || !req.fullyUnpacked()) 1629 { 1630 return responseReqDataLenInvalid(); 1631 } 1632 std::bitset<8> expected; 1633 EthernetInterface::DHCPConf dhcp = 1634 channelCall<getDHCPProperty>(channel); 1635 if ((dhcp == EthernetInterface::DHCPConf::both) | 1636 (dhcp == EthernetInterface::DHCPConf::v6)) 1637 { 1638 expected[IPv6RouterControlFlag::Dynamic] = 1; 1639 } 1640 else 1641 { 1642 expected[IPv6RouterControlFlag::Static] = 1; 1643 } 1644 if (expected != control) 1645 { 1646 return responseInvalidFieldRequest(); 1647 } 1648 return responseSuccess(); 1649 } 1650 case LanParam::IPv6StaticRouter1IP: 1651 { 1652 in6_addr gateway; 1653 std::array<uint8_t, sizeof(gateway)> bytes; 1654 if (req.unpack(bytes) != 0 || !req.fullyUnpacked()) 1655 { 1656 return responseReqDataLenInvalid(); 1657 } 1658 copyInto(gateway, bytes); 1659 channelCall<setGatewayProperty<AF_INET6>>(channel, gateway); 1660 return responseSuccess(); 1661 } 1662 case LanParam::IPv6StaticRouter1MAC: 1663 { 1664 ether_addr mac; 1665 std::array<uint8_t, sizeof(mac)> bytes; 1666 if (req.unpack(bytes) != 0 || !req.fullyUnpacked()) 1667 { 1668 return responseReqDataLenInvalid(); 1669 } 1670 copyInto(mac, bytes); 1671 channelCall<reconfigureGatewayMAC<AF_INET6>>(channel, mac); 1672 return responseSuccess(); 1673 } 1674 case LanParam::IPv6StaticRouter1PrefixLength: 1675 { 1676 uint8_t prefix; 1677 if (req.unpack(prefix) != 0 || !req.fullyUnpacked()) 1678 { 1679 return responseReqDataLenInvalid(); 1680 } 1681 if (prefix != 0) 1682 { 1683 return responseInvalidFieldRequest(); 1684 } 1685 return responseSuccess(); 1686 } 1687 case LanParam::IPv6StaticRouter1PrefixValue: 1688 { 1689 std::array<uint8_t, sizeof(in6_addr)> bytes; 1690 if (req.unpack(bytes) != 0 || !req.fullyUnpacked()) 1691 { 1692 return responseReqDataLenInvalid(); 1693 } 1694 // Accept any prefix value since our prefix length has to be 0 1695 return responseSuccess(); 1696 } 1697 case LanParam::cipherSuitePrivilegeLevels: 1698 { 1699 uint8_t reserved; 1700 std::array<uint4_t, ipmi::maxCSRecords> cipherSuitePrivs; 1701 1702 if (req.unpack(reserved, cipherSuitePrivs) || !req.fullyUnpacked()) 1703 { 1704 return responseReqDataLenInvalid(); 1705 } 1706 1707 if (reserved) 1708 { 1709 return responseInvalidFieldRequest(); 1710 } 1711 1712 uint8_t resp = 1713 getCipherConfigObject(csPrivFileName, csPrivDefaultFileName) 1714 .setCSPrivilegeLevels(channel, cipherSuitePrivs); 1715 if (!resp) 1716 { 1717 return responseSuccess(); 1718 } 1719 else 1720 { 1721 req.trailingOk = true; 1722 return response(resp); 1723 } 1724 } 1725 } 1726 1727 if ((parameter >= oemCmdStart) && (parameter <= oemCmdEnd)) 1728 { 1729 return setLanOem(channel, parameter, req); 1730 } 1731 1732 req.trailingOk = true; 1733 return response(ccParamNotSupported); 1734 } 1735 1736 RspType<message::Payload> getLan(Context::ptr ctx, uint4_t channelBits, 1737 uint3_t reserved, bool revOnly, 1738 uint8_t parameter, uint8_t set, uint8_t block) 1739 { 1740 message::Payload ret; 1741 constexpr uint8_t current_revision = 0x11; 1742 ret.pack(current_revision); 1743 1744 if (revOnly) 1745 { 1746 return responseSuccess(std::move(ret)); 1747 } 1748 1749 const uint8_t channel = convertCurrentChannelNum( 1750 static_cast<uint8_t>(channelBits), ctx->channel); 1751 if (reserved || !isValidChannel(channel)) 1752 { 1753 log<level::ERR>("Get Lan - Invalid field in request"); 1754 return responseInvalidFieldRequest(); 1755 } 1756 1757 static std::vector<uint8_t> cipherList; 1758 static bool listInit = false; 1759 if (!listInit) 1760 { 1761 try 1762 { 1763 cipherList = cipher::getCipherList(); 1764 listInit = true; 1765 } 1766 catch (const std::exception& e) 1767 { 1768 } 1769 } 1770 1771 switch (static_cast<LanParam>(parameter)) 1772 { 1773 case LanParam::SetStatus: 1774 { 1775 SetStatus status; 1776 try 1777 { 1778 status = setStatus.at(channel); 1779 } 1780 catch (const std::out_of_range&) 1781 { 1782 status = SetStatus::Complete; 1783 } 1784 ret.pack(static_cast<uint2_t>(status), uint6_t{}); 1785 return responseSuccess(std::move(ret)); 1786 } 1787 case LanParam::AuthSupport: 1788 { 1789 std::bitset<6> support; 1790 ret.pack(support, uint2_t{}); 1791 return responseSuccess(std::move(ret)); 1792 } 1793 case LanParam::AuthEnables: 1794 { 1795 std::bitset<6> enables; 1796 ret.pack(enables, uint2_t{}); // Callback 1797 ret.pack(enables, uint2_t{}); // User 1798 ret.pack(enables, uint2_t{}); // Operator 1799 ret.pack(enables, uint2_t{}); // Admin 1800 ret.pack(enables, uint2_t{}); // OEM 1801 return responseSuccess(std::move(ret)); 1802 } 1803 case LanParam::IP: 1804 { 1805 auto ifaddr = channelCall<getIfAddr4>(channel); 1806 in_addr addr{}; 1807 if (ifaddr) 1808 { 1809 addr = ifaddr->address; 1810 } 1811 ret.pack(dataRef(addr)); 1812 return responseSuccess(std::move(ret)); 1813 } 1814 case LanParam::IPSrc: 1815 { 1816 auto src = IPSrc::Static; 1817 EthernetInterface::DHCPConf dhcp = 1818 channelCall<getDHCPProperty>(channel); 1819 if ((dhcp == EthernetInterface::DHCPConf::v4) || 1820 (dhcp == EthernetInterface::DHCPConf::both)) 1821 { 1822 src = IPSrc::DHCP; 1823 } 1824 ret.pack(static_cast<uint4_t>(src), uint4_t{}); 1825 return responseSuccess(std::move(ret)); 1826 } 1827 case LanParam::MAC: 1828 { 1829 ether_addr mac = channelCall<getMACProperty>(channel); 1830 ret.pack(dataRef(mac)); 1831 return responseSuccess(std::move(ret)); 1832 } 1833 case LanParam::SubnetMask: 1834 { 1835 auto ifaddr = channelCall<getIfAddr4>(channel); 1836 uint8_t prefix = AddrFamily<AF_INET>::defaultPrefix; 1837 if (ifaddr) 1838 { 1839 prefix = ifaddr->prefix; 1840 } 1841 in_addr netmask = prefixToNetmask(prefix); 1842 ret.pack(dataRef(netmask)); 1843 return responseSuccess(std::move(ret)); 1844 } 1845 case LanParam::Gateway1: 1846 { 1847 auto gateway = 1848 channelCall<getGatewayProperty<AF_INET>>(channel).value_or( 1849 in_addr{}); 1850 ret.pack(dataRef(gateway)); 1851 return responseSuccess(std::move(ret)); 1852 } 1853 case LanParam::Gateway1MAC: 1854 { 1855 ether_addr mac{}; 1856 auto neighbor = channelCall<getGatewayNeighbor<AF_INET>>(channel); 1857 if (neighbor) 1858 { 1859 mac = neighbor->mac; 1860 } 1861 ret.pack(dataRef(mac)); 1862 return responseSuccess(std::move(ret)); 1863 } 1864 case LanParam::VLANId: 1865 { 1866 uint16_t vlan = channelCall<getVLANProperty>(channel); 1867 if (vlan != 0) 1868 { 1869 vlan |= VLAN_ENABLE_FLAG; 1870 } 1871 else 1872 { 1873 vlan = lastDisabledVlan[channel]; 1874 } 1875 ret.pack(vlan); 1876 return responseSuccess(std::move(ret)); 1877 } 1878 case LanParam::CiphersuiteSupport: 1879 { 1880 if (getChannelSessionSupport(channel) == 1881 EChannelSessSupported::none) 1882 { 1883 return responseInvalidFieldRequest(); 1884 } 1885 if (!listInit) 1886 { 1887 return responseUnspecifiedError(); 1888 } 1889 ret.pack(static_cast<uint8_t>(cipherList.size() - 1)); 1890 return responseSuccess(std::move(ret)); 1891 } 1892 case LanParam::CiphersuiteEntries: 1893 { 1894 if (getChannelSessionSupport(channel) == 1895 EChannelSessSupported::none) 1896 { 1897 return responseInvalidFieldRequest(); 1898 } 1899 if (!listInit) 1900 { 1901 return responseUnspecifiedError(); 1902 } 1903 ret.pack(cipherList); 1904 return responseSuccess(std::move(ret)); 1905 } 1906 case LanParam::IPFamilySupport: 1907 { 1908 std::bitset<8> support; 1909 support[IPFamilySupportFlag::IPv6Only] = 0; 1910 support[IPFamilySupportFlag::DualStack] = 1; 1911 support[IPFamilySupportFlag::IPv6Alerts] = 1; 1912 ret.pack(support); 1913 return responseSuccess(std::move(ret)); 1914 } 1915 case LanParam::IPFamilyEnables: 1916 { 1917 ret.pack(static_cast<uint8_t>(IPFamilyEnables::DualStack)); 1918 return responseSuccess(std::move(ret)); 1919 } 1920 case LanParam::IPv6Status: 1921 { 1922 ret.pack(MAX_IPV6_STATIC_ADDRESSES); 1923 ret.pack(MAX_IPV6_DYNAMIC_ADDRESSES); 1924 std::bitset<8> support; 1925 support[IPv6StatusFlag::DHCP] = 1; 1926 support[IPv6StatusFlag::SLAAC] = 1; 1927 ret.pack(support); 1928 return responseSuccess(std::move(ret)); 1929 } 1930 case LanParam::IPv6StaticAddresses: 1931 { 1932 if (set >= MAX_IPV6_STATIC_ADDRESSES) 1933 { 1934 return responseParmOutOfRange(); 1935 } 1936 getLanIPv6Address(ret, channel, set, originsV6Static); 1937 return responseSuccess(std::move(ret)); 1938 } 1939 case LanParam::IPv6DynamicAddresses: 1940 { 1941 if (set >= MAX_IPV6_DYNAMIC_ADDRESSES) 1942 { 1943 return responseParmOutOfRange(); 1944 } 1945 getLanIPv6Address(ret, channel, set, originsV6Dynamic); 1946 return responseSuccess(std::move(ret)); 1947 } 1948 case LanParam::IPv6RouterControl: 1949 { 1950 std::bitset<8> control; 1951 EthernetInterface::DHCPConf dhcp = 1952 channelCall<getDHCPProperty>(channel); 1953 if ((dhcp == EthernetInterface::DHCPConf::both) || 1954 (dhcp == EthernetInterface::DHCPConf::v6)) 1955 { 1956 control[IPv6RouterControlFlag::Dynamic] = 1; 1957 } 1958 else 1959 { 1960 control[IPv6RouterControlFlag::Static] = 1; 1961 } 1962 ret.pack(control); 1963 return responseSuccess(std::move(ret)); 1964 } 1965 case LanParam::IPv6StaticRouter1IP: 1966 { 1967 in6_addr gateway{}; 1968 EthernetInterface::DHCPConf dhcp = 1969 channelCall<getDHCPProperty>(channel); 1970 if ((dhcp == EthernetInterface::DHCPConf::v4) || 1971 (dhcp == EthernetInterface::DHCPConf::none)) 1972 { 1973 gateway = 1974 channelCall<getGatewayProperty<AF_INET6>>(channel).value_or( 1975 in6_addr{}); 1976 } 1977 ret.pack(dataRef(gateway)); 1978 return responseSuccess(std::move(ret)); 1979 } 1980 case LanParam::IPv6StaticRouter1MAC: 1981 { 1982 ether_addr mac{}; 1983 auto neighbor = channelCall<getGatewayNeighbor<AF_INET6>>(channel); 1984 if (neighbor) 1985 { 1986 mac = neighbor->mac; 1987 } 1988 ret.pack(dataRef(mac)); 1989 return responseSuccess(std::move(ret)); 1990 } 1991 case LanParam::IPv6StaticRouter1PrefixLength: 1992 { 1993 ret.pack(UINT8_C(0)); 1994 return responseSuccess(std::move(ret)); 1995 } 1996 case LanParam::IPv6StaticRouter1PrefixValue: 1997 { 1998 in6_addr prefix{}; 1999 ret.pack(dataRef(prefix)); 2000 return responseSuccess(std::move(ret)); 2001 } 2002 case LanParam::cipherSuitePrivilegeLevels: 2003 { 2004 std::array<uint4_t, ipmi::maxCSRecords> csPrivilegeLevels; 2005 2006 uint8_t resp = 2007 getCipherConfigObject(csPrivFileName, csPrivDefaultFileName) 2008 .getCSPrivilegeLevels(channel, csPrivilegeLevels); 2009 if (!resp) 2010 { 2011 constexpr uint8_t reserved1 = 0x00; 2012 ret.pack(reserved1, csPrivilegeLevels); 2013 return responseSuccess(std::move(ret)); 2014 } 2015 else 2016 { 2017 return response(resp); 2018 } 2019 } 2020 } 2021 2022 if ((parameter >= oemCmdStart) && (parameter <= oemCmdEnd)) 2023 { 2024 return getLanOem(channel, parameter, set, block); 2025 } 2026 2027 return response(ccParamNotSupported); 2028 } 2029 2030 } // namespace transport 2031 } // namespace ipmi 2032 2033 void register_netfn_transport_functions() __attribute__((constructor)); 2034 2035 void register_netfn_transport_functions() 2036 { 2037 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnTransport, 2038 ipmi::transport::cmdSetLanConfigParameters, 2039 ipmi::Privilege::Admin, ipmi::transport::setLan); 2040 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnTransport, 2041 ipmi::transport::cmdGetLanConfigParameters, 2042 ipmi::Privilege::Operator, ipmi::transport::getLan); 2043 } 2044