#pragma once #include "app/channel.hpp" #include "transportconstants.hpp" #include "user_channel/cipher_mgmt.hpp" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include namespace ipmi { namespace transport { /** @brief The dbus parameters for the interface corresponding to a channel * This helps reduce the number of mapper lookups we need for each * query and simplifies finding the VLAN interface if needed. */ struct ChannelParams { /** @brief The channel ID */ int id; /** @brief channel name for the interface */ std::string ifname; /** @brief Name of the service on the bus */ std::string service; /** @brief Lower level adapter path that is guaranteed to not be a VLAN */ std::string ifPath; /** @brief Logical adapter path used for address assignment */ std::string logicalPath; }; /** @brief Determines the ethernet interface name corresponding to a channel * Tries to map a VLAN object first so that the address information * is accurate. Otherwise it gets the standard ethernet interface. * * @param[in] bus - The bus object used for lookups * @param[in] channel - The channel id corresponding to an ethernet interface * @return Ethernet interface service and object path if it exists */ std::optional maybeGetChannelParams(sdbusplus::bus_t& bus, uint8_t channel); /** @brief A trivial helper around maybeGetChannelParams() that throws an * exception when it is unable to acquire parameters for the channel. * * @param[in] bus - The bus object used for lookups * @param[in] channel - The channel id corresponding to an ethernet interface * @return Ethernet interface service and object path */ ChannelParams getChannelParams(sdbusplus::bus_t& bus, uint8_t channel); /** @brief Trivializes using parameter getter functions by providing a bus * and channel parameters automatically. * * @param[in] channel - The channel id corresponding to an ethernet interface * ... */ template auto channelCall(uint8_t channel, Args&&... args) { sdbusplus::bus_t bus(ipmid_get_sd_bus_connection()); auto params = getChannelParams(bus, channel); return std::invoke(func, bus, params, std::forward(args)...); } /** @brief Generic paramters for different address families */ template struct AddrFamily {}; /** @brief Parameter specialization for IPv4 */ template <> struct AddrFamily { using addr = stdplus::In4Addr; static constexpr auto protocol = sdbusplus::server::xyz::openbmc_project::network::IP::Protocol::IPv4; static constexpr size_t maxStrLen = INET6_ADDRSTRLEN; static constexpr uint8_t defaultPrefix = 32; static constexpr char propertyGateway[] = "DefaultGateway"; }; /** @brief Parameter specialization for IPv6 */ template <> struct AddrFamily { using addr = stdplus::In6Addr; static constexpr auto protocol = sdbusplus::server::xyz::openbmc_project::network::IP::Protocol::IPv6; static constexpr size_t maxStrLen = INET6_ADDRSTRLEN; static constexpr uint8_t defaultPrefix = 128; static constexpr char propertyGateway[] = "DefaultGateway6"; }; /** @brief Interface Neighbor configuration parameters */ template struct IfNeigh { std::string path; typename AddrFamily::addr ip; stdplus::EtherAddr mac; }; /** @brief Interface IP Address configuration parameters */ template struct IfAddr { std::string path; typename AddrFamily::addr address; sdbusplus::server::xyz::openbmc_project::network::IP::AddressOrigin origin; uint8_t prefix; }; /** @brief Valid address origins for IPv6 */ static inline const std::unordered_set< sdbusplus::server::xyz::openbmc_project::network::IP::AddressOrigin> originsV6Static = {sdbusplus::server::xyz::openbmc_project::network::IP:: AddressOrigin::Static}; static inline const std::unordered_set< sdbusplus::server::xyz::openbmc_project::network::IP::AddressOrigin> originsV6Dynamic = { sdbusplus::server::xyz::openbmc_project::network::IP::AddressOrigin:: DHCP, sdbusplus::server::xyz::openbmc_project::network::IP::AddressOrigin:: SLAAC, }; /** @brief A lazy lookup mechanism for iterating over object properties stored * in DBus. This will only perform the object lookup when needed, and * retains a cache of previous lookups to speed up future iterations. */ class ObjectLookupCache { public: using PropertiesCache = std::unordered_map; /** @brief Creates a new ObjectLookupCache for the interface on the bus * NOTE: The inputs to this object must outlive the object since * they are only referenced by it. * * @param[in] bus - The bus object used for lookups * @param[in] params - The parameters for the channel * @param[in] intf - The interface we are looking up */ ObjectLookupCache(sdbusplus::bus_t& bus, const ChannelParams& params, const char* intf) : bus(bus), params(params), intf(intf), objs(getAllDbusObjects(bus, params.logicalPath, intf, "")) {} class iterator : public ObjectTree::const_iterator { public: using value_type = PropertiesCache::value_type; iterator(ObjectTree::const_iterator it, ObjectLookupCache& container) : ObjectTree::const_iterator(it), container(container), ret(container.cache.end()) {} value_type& operator*() { ret = container.get(ObjectTree::const_iterator::operator*().first); return *ret; } value_type* operator->() { return &operator*(); } private: ObjectLookupCache& container; PropertiesCache::iterator ret; }; iterator begin() noexcept { return iterator(objs.begin(), *this); } iterator end() noexcept { return iterator(objs.end(), *this); } private: sdbusplus::bus_t& bus; const ChannelParams& params; const char* const intf; const ObjectTree objs; PropertiesCache cache; /** @brief Gets a cached copy of the object properties if possible * Otherwise performs a query on DBus to look them up * * @param[in] path - The object path to lookup * @return An iterator for the specified object path + properties */ PropertiesCache::iterator get(const std::string& path) { auto it = cache.find(path); if (it != cache.end()) { return it; } auto properties = getAllDbusProperties(bus, params.service, path, intf); return cache.insert({path, std::move(properties)}).first; } }; /** @brief Searches the ip object lookup cache for an address matching * the input parameters. NOTE: The index lacks stability across address * changes since the network daemon has no notion of stable indicies. * * @param[in] bus - The bus object used for lookups * @param[in] params - The parameters for the channel * @param[in] idx - The index of the desired address on the interface * @param[in] origins - The allowed origins for the address objects * @param[in] ips - The object lookup cache holding all of the address info * @return The address and prefix if it was found */ template std::optional> findIfAddr( [[maybe_unused]] sdbusplus::bus_t& bus, [[maybe_unused]] const ChannelParams& params, uint8_t idx, const std::unordered_set< sdbusplus::server::xyz::openbmc_project::network::IP::AddressOrigin>& origins, ObjectLookupCache& ips) { for (const auto& [path, properties] : ips) { std::optional::addr> addr; try { addr.emplace(stdplus::fromStr::addr>( std::get(properties.at("Address")))); } catch (...) { continue; } sdbusplus::server::xyz::openbmc_project::network::IP::AddressOrigin origin = sdbusplus::server::xyz::openbmc_project::network::IP:: convertAddressOriginFromString( std::get(properties.at("Origin"))); if (origins.find(origin) == origins.end()) { continue; } if (idx > 0) { idx--; continue; } IfAddr ifaddr; ifaddr.path = path; ifaddr.address = *addr; ifaddr.prefix = std::get(properties.at("PrefixLength")); ifaddr.origin = origin; return ifaddr; } return std::nullopt; } /** @brief Trivial helper around findIfAddr that simplifies calls * for one off lookups. Don't use this if you intend to do multiple * lookups at a time. * * @param[in] bus - The bus object used for lookups * @param[in] params - The parameters for the channel * @param[in] idx - The index of the desired address on the interface * @param[in] origins - The allowed origins for the address objects * @return The address and prefix if it was found */ template auto getIfAddr( sdbusplus::bus_t& bus, const ChannelParams& params, uint8_t idx, const std::unordered_set< sdbusplus::server::xyz::openbmc_project::network::IP::AddressOrigin>& origins) { ObjectLookupCache ips(bus, params, INTF_IP); return findIfAddr(bus, params, idx, origins, ips); } /** @brief Reconfigures the IPv6 address info configured for the interface * * @param[in] bus - The bus object used for lookups * @param[in] params - The parameters for the channel * @param[in] idx - The address index to operate on * @param[in] address - The new address * @param[in] prefix - The new address prefix */ void reconfigureIfAddr6(sdbusplus::bus_t& bus, const ChannelParams& params, uint8_t idx, stdplus::In6Addr address, uint8_t prefix); /** @brief Retrieves the current gateway for the address family on the system * NOTE: The gateway is per channel instead of the system wide one. * * @param[in] bus - The bus object used for lookups * @param[in] params - The parameters for the channel * @return An address representing the gateway address if it exists */ template std::optional::addr> getGatewayProperty(sdbusplus::bus_t& bus, const ChannelParams& params) { auto objPath = "/xyz/openbmc_project/network/" + params.ifname; auto gatewayStr = std::get( getDbusProperty(bus, params.service, objPath, INTF_ETHERNET, AddrFamily::propertyGateway)); if (gatewayStr.empty()) { return std::nullopt; } return stdplus::fromStr::addr>(gatewayStr); } template std::optional> findStaticNeighbor(sdbusplus::bus_t&, const ChannelParams&, typename AddrFamily::addr ip, ObjectLookupCache& neighbors) { using sdbusplus::server::xyz::openbmc_project::network::Neighbor; const auto state = sdbusplus::common::xyz::openbmc_project::network::convertForMessage( Neighbor::State::Permanent); for (const auto& [path, neighbor] : neighbors) { std::optional::addr> neighIP; try { neighIP.emplace(stdplus::fromStr::addr>( std::get(neighbor.at("IPAddress")))); } catch (...) { continue; } if (*neighIP != ip) { continue; } if (state != std::get(neighbor.at("State"))) { continue; } IfNeigh ret; ret.path = path; ret.ip = ip; ret.mac = stdplus::fromStr( std::get(neighbor.at("MACAddress"))); return ret; } return std::nullopt; } template void createNeighbor(sdbusplus::bus_t& bus, const ChannelParams& params, typename AddrFamily::addr address, stdplus::EtherAddr mac) { auto newreq = bus.new_method_call(params.service.c_str(), params.logicalPath.c_str(), INTF_NEIGHBOR_CREATE_STATIC, "Neighbor"); stdplus::ToStrHandle> macToStr; stdplus::ToStrHandle::addr>> addrToStr; newreq.append(addrToStr(address), macToStr(mac)); bus.call_noreply(newreq); } /** @brief Deletes the dbus object. Ignores empty objects or objects that are * missing from the bus. * * @param[in] bus - The bus object used for lookups * @param[in] service - The name of the service * @param[in] path - The path of the object to delete */ void deleteObjectIfExists(sdbusplus::bus_t& bus, const std::string& service, const std::string& path); /** @brief Sets the value for the default gateway of the channel * * @param[in] bus - The bus object used for lookups * @param[in] params - The parameters for the channel * @param[in] gateway - Gateway address to apply */ template void setGatewayProperty(sdbusplus::bus_t& bus, const ChannelParams& params, typename AddrFamily::addr address) { // Save the old gateway MAC address if it exists so we can recreate it auto gateway = getGatewayProperty(bus, params); std::optional> neighbor; if (gateway) { ObjectLookupCache neighbors(bus, params, INTF_NEIGHBOR); neighbor = findStaticNeighbor(bus, params, *gateway, neighbors); } auto objPath = "/xyz/openbmc_project/network/" + params.ifname; setDbusProperty(bus, params.service, objPath, INTF_ETHERNET, AddrFamily::propertyGateway, stdplus::toStr(address)); // Restore the gateway MAC if we had one if (neighbor) { deleteObjectIfExists(bus, params.service, neighbor->path); createNeighbor(bus, params, address, neighbor->mac); } } } // namespace transport } // namespace ipmi