transportHandler: Separate some util functions to hpp

When I implemented the transport oem command as suggested
in [1], I found that transporthandler.cpp already implements
a lot of util functions that are useful for implementing
transporthandler_oem.cpp

So, there is this patch, which moves a lot of functions
already implemented in cpp to hpp, so that transporthandler_oem.cpp can use them.
I believe it will be useful for others to implement the transport oem commands as well.

[1] https://github.com/openbmc/phosphor-host-ipmid/blob/master/transporthandler.cpp#L1264

Signed-off-by: John Wang <wangzhiqiang.bj@bytedance.com>
Change-Id: I580f005df3622ea9340cd303040d98a5527de55e
diff --git a/transporthandler.cpp b/transporthandler.cpp
index 0012746..cacf2ec 100644
--- a/transporthandler.cpp
+++ b/transporthandler.cpp
@@ -1,42 +1,5 @@
 #include "transporthandler.hpp"
 
-#include "app/channel.hpp"
-#include "user_channel/cipher_mgmt.hpp"
-
-#include <arpa/inet.h>
-#include <netinet/ether.h>
-
-#include <array>
-#include <bitset>
-#include <cinttypes>
-#include <cstdint>
-#include <cstring>
-#include <fstream>
-#include <functional>
-#include <ipmid/api.hpp>
-#include <ipmid/message.hpp>
-#include <ipmid/message/types.hpp>
-#include <ipmid/types.hpp>
-#include <ipmid/utils.hpp>
-#include <optional>
-#include <phosphor-logging/elog-errors.hpp>
-#include <phosphor-logging/elog.hpp>
-#include <phosphor-logging/log.hpp>
-#include <sdbusplus/bus.hpp>
-#include <sdbusplus/exception.hpp>
-#include <string>
-#include <string_view>
-#include <type_traits>
-#include <unordered_map>
-#include <unordered_set>
-#include <user_channel/channel_layer.hpp>
-#include <utility>
-#include <vector>
-#include <xyz/openbmc_project/Common/error.hpp>
-#include <xyz/openbmc_project/Network/EthernetInterface/server.hpp>
-#include <xyz/openbmc_project/Network/IP/server.hpp>
-#include <xyz/openbmc_project/Network/Neighbor/server.hpp>
-
 using phosphor::logging::commit;
 using phosphor::logging::elog;
 using phosphor::logging::entry;
@@ -85,150 +48,15 @@
 namespace transport
 {
 
-// D-Bus Network Daemon definitions
-constexpr auto PATH_ROOT = "/xyz/openbmc_project/network";
-constexpr auto PATH_SYSTEMCONFIG = "/xyz/openbmc_project/network/config";
-
-constexpr auto INTF_SYSTEMCONFIG =
-    "xyz.openbmc_project.Network.SystemConfiguration";
-constexpr auto INTF_ETHERNET = "xyz.openbmc_project.Network.EthernetInterface";
-constexpr auto INTF_IP = "xyz.openbmc_project.Network.IP";
-constexpr auto INTF_IP_CREATE = "xyz.openbmc_project.Network.IP.Create";
-constexpr auto INTF_MAC = "xyz.openbmc_project.Network.MACAddress";
-constexpr auto INTF_NEIGHBOR = "xyz.openbmc_project.Network.Neighbor";
-constexpr auto INTF_NEIGHBOR_CREATE_STATIC =
-    "xyz.openbmc_project.Network.Neighbor.CreateStatic";
-constexpr auto INTF_VLAN = "xyz.openbmc_project.Network.VLAN";
-constexpr auto INTF_VLAN_CREATE = "xyz.openbmc_project.Network.VLAN.Create";
-
-/** @brief Generic paramters for different address families */
-template <int family>
-struct AddrFamily
-{
-};
-
-/** @brief Parameter specialization for IPv4 */
-template <>
-struct AddrFamily<AF_INET>
-{
-    using addr = in_addr;
-    static constexpr auto protocol = 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<AF_INET6>
-{
-    using addr = in6_addr;
-    static constexpr auto protocol = IP::Protocol::IPv6;
-    static constexpr size_t maxStrLen = INET6_ADDRSTRLEN;
-    static constexpr uint8_t defaultPrefix = 128;
-    static constexpr char propertyGateway[] = "DefaultGateway6";
-};
-
 /** @brief Valid address origins for IPv4 */
 const std::unordered_set<IP::AddressOrigin> originsV4 = {
     IP::AddressOrigin::Static,
     IP::AddressOrigin::DHCP,
 };
 
-/** @brief Valid address origins for IPv6 */
-const std::unordered_set<IP::AddressOrigin> originsV6Static = {
-    IP::AddressOrigin::Static};
-const std::unordered_set<IP::AddressOrigin> originsV6Dynamic = {
-    IP::AddressOrigin::DHCP,
-    IP::AddressOrigin::SLAAC,
-};
-
-/** @brief Interface IP Address configuration parameters */
-template <int family>
-struct IfAddr
-{
-    std::string path;
-    typename AddrFamily<family>::addr address;
-    IP::AddressOrigin origin;
-    uint8_t prefix;
-};
-
-/** @brief Interface Neighbor configuration parameters */
-template <int family>
-struct IfNeigh
-{
-    std::string path;
-    typename AddrFamily<family>::addr ip;
-    ether_addr mac;
-};
-
 static constexpr uint8_t oemCmdStart = 192;
 static constexpr uint8_t oemCmdEnd = 255;
 
-/** @brief A trivial helper used to determine if two PODs are equal
- *
- *  @params[in] a - The first object to compare
- *  @params[in] b - The second object to compare
- *  @return True if the objects are the same bytewise
- */
-template <typename T>
-bool equal(const T& a, const T& b)
-{
-    static_assert(std::is_trivially_copyable_v<T>);
-    return std::memcmp(&a, &b, sizeof(T)) == 0;
-}
-
-/** @brief Copies bytes from an array into a trivially copyable container
- *
- *  @params[out] t     - The container receiving the data
- *  @params[in]  bytes - The data to copy
- */
-template <size_t N, typename T>
-void copyInto(T& t, const std::array<uint8_t, N>& bytes)
-{
-    static_assert(std::is_trivially_copyable_v<T>);
-    static_assert(N == sizeof(T));
-    std::memcpy(&t, bytes.data(), bytes.size());
-}
-
-/** @brief Gets a generic view of the bytes in the input container
- *
- *  @params[in] t - The data to reference
- *  @return A string_view referencing the bytes in the container
- */
-template <typename T>
-std::string_view dataRef(const T& t)
-{
-    static_assert(std::is_trivially_copyable_v<T>);
-    return {reinterpret_cast<const char*>(&t), sizeof(T)};
-}
-
-/** @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<ChannelParams> maybeGetChannelParams(sdbusplus::bus::bus& bus,
                                                    uint8_t channel)
 {
@@ -300,13 +128,6 @@
     return std::move(params);
 }
 
-/** @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::bus& bus, uint8_t channel)
 {
     auto params = maybeGetChannelParams(bus, channel);
@@ -341,26 +162,6 @@
     return log<level>(std::forward<Args>(args)...);
 }
 
-/** @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 func, typename... Args>
-auto channelCall(uint8_t channel, Args&&... args)
-{
-    sdbusplus::bus::bus bus(ipmid_get_sd_bus_connection());
-    auto params = getChannelParams(bus, channel);
-    return std::invoke(func, bus, params, std::forward<Args>(args)...);
-}
-
-/** @brief Determines if the ethernet interface is using DHCP
- *
- *  @param[in] bus    - The bus object used for lookups
- *  @param[in] params - The parameters for the channel
- *  @return DHCPConf enumeration
- */
 EthernetInterface::DHCPConf getDHCPProperty(sdbusplus::bus::bus& bus,
                                             const ChannelParams& params)
 {
@@ -418,14 +219,6 @@
                     "DHCPEnabled", newDhcp);
 }
 
-/** @brief Sets the DHCP v6 state on the given interface
- *
- *  @param[in] bus           - The bus object used for lookups
- *  @param[in] params        - The parameters for the channel
- *  @param[in] requestedDhcp - DHCP state to assign (none, v6, both)
- *  @param[in] defaultMode   - True: Use algorithmic assignment
- *                             False: requestedDhcp assigned unconditionally
- */
 void setDHCPv6Property(sdbusplus::bus::bus& bus, const ChannelParams& params,
                        const EthernetInterface::DHCPConf requestedDhcp,
                        const bool defaultMode = true)
@@ -475,11 +268,6 @@
                     "DHCPEnabled", newDhcp);
 }
 
-/** @brief Converts a human readable MAC string into MAC bytes
- *
- *  @param[in] mac - The MAC string
- *  @return MAC in bytes
- */
 ether_addr stringToMAC(const char* mac)
 {
     const ether_addr* ret = ether_aton(mac);
@@ -518,241 +306,6 @@
                     macStr);
 }
 
-/** @brief Turns an IP address string into the network byte order form
- *         NOTE: This version strictly validates family matches
- *
- *  @param[in] address - The string form of the address
- *  @return A network byte order address or none if conversion failed
- */
-template <int family>
-std::optional<typename AddrFamily<family>::addr>
-    maybeStringToAddr(const char* address)
-{
-    typename AddrFamily<family>::addr ret;
-    if (inet_pton(family, address, &ret) == 1)
-    {
-        return ret;
-    }
-    return std::nullopt;
-}
-
-/** @brief Turns an IP address string into the network byte order form
- *         NOTE: This version strictly validates family matches
- *
- *  @param[in] address - The string form of the address
- *  @return A network byte order address
- */
-template <int family>
-typename AddrFamily<family>::addr stringToAddr(const char* address)
-{
-    auto ret = maybeStringToAddr<family>(address);
-    if (!ret)
-    {
-        log<level::ERR>("Failed to convert IP Address",
-                        entry("FAMILY=%d", family),
-                        entry("ADDRESS=%s", address));
-        elog<InternalFailure>();
-    }
-    return *ret;
-}
-
-/** @brief Turns an IP address in network byte order into a string
- *
- *  @param[in] address - The string form of the address
- *  @return A network byte order address
- */
-template <int family>
-std::string addrToString(const typename AddrFamily<family>::addr& address)
-{
-    std::string ret(AddrFamily<family>::maxStrLen, '\0');
-    inet_ntop(family, &address, ret.data(), ret.size());
-    ret.resize(strlen(ret.c_str()));
-    return ret;
-}
-
-/** @brief Retrieves the current gateway for the address family on the system
- *         NOTE: The gateway is currently system wide and not per channel
- *
- *  @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 <int family>
-std::optional<typename AddrFamily<family>::addr>
-    getGatewayProperty(sdbusplus::bus::bus& bus, const ChannelParams& params)
-{
-    auto gatewayStr = std::get<std::string>(getDbusProperty(
-        bus, params.service, PATH_SYSTEMCONFIG, INTF_SYSTEMCONFIG,
-        AddrFamily<family>::propertyGateway));
-    if (gatewayStr.empty())
-    {
-        return std::nullopt;
-    }
-    return stringToAddr<family>(gatewayStr.c_str());
-}
-
-/** @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<std::string, PropertyMap>;
-
-    /** @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::bus& 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::bus& 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 <int family>
-std::optional<IfAddr<family>>
-    findIfAddr(sdbusplus::bus::bus& bus, const ChannelParams& params,
-               uint8_t idx,
-               const std::unordered_set<IP::AddressOrigin>& origins,
-               ObjectLookupCache& ips)
-{
-    for (const auto& [path, properties] : ips)
-    {
-        const auto& addrStr = std::get<std::string>(properties.at("Address"));
-        auto addr = maybeStringToAddr<family>(addrStr.c_str());
-        if (!addr)
-        {
-            continue;
-        }
-
-        IP::AddressOrigin origin = IP::convertAddressOriginFromString(
-            std::get<std::string>(properties.at("Origin")));
-        if (origins.find(origin) == origins.end())
-        {
-            continue;
-        }
-
-        if (idx > 0)
-        {
-            idx--;
-            continue;
-        }
-
-        IfAddr<family> ifaddr;
-        ifaddr.path = path;
-        ifaddr.address = *addr;
-        ifaddr.prefix = std::get<uint8_t>(properties.at("PrefixLength"));
-        ifaddr.origin = origin;
-        return std::move(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 <int family>
-auto getIfAddr(sdbusplus::bus::bus& bus, const ChannelParams& params,
-               uint8_t idx,
-               const std::unordered_set<IP::AddressOrigin>& origins)
-{
-    ObjectLookupCache ips(bus, params, INTF_IP);
-    return findIfAddr<family>(bus, params, idx, origins, ips);
-}
-
-/** @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::bus& bus, const std::string& service,
                           const std::string& path)
 {
@@ -841,87 +394,6 @@
 }
 
 template <int family>
-std::optional<IfNeigh<family>>
-    findStaticNeighbor(sdbusplus::bus::bus& bus, const ChannelParams& params,
-                       const typename AddrFamily<family>::addr& ip,
-                       ObjectLookupCache& neighbors)
-{
-    const auto state =
-        sdbusplus::xyz::openbmc_project::Network::server::convertForMessage(
-            Neighbor::State::Permanent);
-    for (const auto& [path, neighbor] : neighbors)
-    {
-        const auto& ipStr = std::get<std::string>(neighbor.at("IPAddress"));
-        auto neighIP = maybeStringToAddr<family>(ipStr.c_str());
-        if (!neighIP)
-        {
-            continue;
-        }
-        if (!equal(*neighIP, ip))
-        {
-            continue;
-        }
-        if (state != std::get<std::string>(neighbor.at("State")))
-        {
-            continue;
-        }
-
-        IfNeigh<family> ret;
-        ret.path = path;
-        ret.ip = ip;
-        const auto& macStr = std::get<std::string>(neighbor.at("MACAddress"));
-        ret.mac = stringToMAC(macStr.c_str());
-        return std::move(ret);
-    }
-
-    return std::nullopt;
-}
-
-template <int family>
-void createNeighbor(sdbusplus::bus::bus& bus, const ChannelParams& params,
-                    const typename AddrFamily<family>::addr& address,
-                    const ether_addr& mac)
-{
-    auto newreq =
-        bus.new_method_call(params.service.c_str(), params.logicalPath.c_str(),
-                            INTF_NEIGHBOR_CREATE_STATIC, "Neighbor");
-    std::string macStr = ether_ntoa(&mac);
-    newreq.append(addrToString<family>(address), macStr);
-    bus.call_noreply(newreq);
-}
-
-/** @brief Sets the system wide value for the default gateway
- *
- *  @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 <int family>
-void setGatewayProperty(sdbusplus::bus::bus& bus, const ChannelParams& params,
-                        const typename AddrFamily<family>::addr& address)
-{
-    // Save the old gateway MAC address if it exists so we can recreate it
-    auto gateway = getGatewayProperty<family>(bus, params);
-    std::optional<IfNeigh<family>> neighbor;
-    if (gateway)
-    {
-        ObjectLookupCache neighbors(bus, params, INTF_NEIGHBOR);
-        neighbor = findStaticNeighbor<family>(bus, params, *gateway, neighbors);
-    }
-
-    setDbusProperty(bus, params.service, PATH_SYSTEMCONFIG, INTF_SYSTEMCONFIG,
-                    AddrFamily<family>::propertyGateway,
-                    addrToString<family>(address));
-
-    // Restore the gateway MAC if we had one
-    if (neighbor)
-    {
-        deleteObjectIfExists(bus, params.service, neighbor->path);
-        createNeighbor<family>(bus, params, address, neighbor->mac);
-    }
-}
-
-template <int family>
 std::optional<IfNeigh<family>> findGatewayNeighbor(sdbusplus::bus::bus& bus,
                                                    const ChannelParams& params,
                                                    ObjectLookupCache& neighbors)
diff --git a/transporthandler.hpp b/transporthandler.hpp
index c13e05a..a5ec6ea 100644
--- a/transporthandler.hpp
+++ b/transporthandler.hpp
@@ -1,13 +1,64 @@
 #pragma once
 
+#include "app/channel.hpp"
+#include "user_channel/cipher_mgmt.hpp"
+
+#include <arpa/inet.h>
+#include <netinet/ether.h>
+
+#include <array>
+#include <bitset>
+#include <cinttypes>
 #include <cstdint>
+#include <cstring>
+#include <fstream>
+#include <functional>
 #include <ipmid/api-types.hpp>
+#include <ipmid/api.hpp>
+#include <ipmid/message.hpp>
+#include <ipmid/message/types.hpp>
+#include <ipmid/types.hpp>
+#include <ipmid/utils.hpp>
+#include <optional>
+#include <phosphor-logging/elog-errors.hpp>
+#include <phosphor-logging/elog.hpp>
+#include <phosphor-logging/log.hpp>
+#include <sdbusplus/bus.hpp>
+#include <sdbusplus/exception.hpp>
+#include <string>
+#include <string_view>
+#include <type_traits>
+#include <unordered_map>
+#include <unordered_set>
+#include <user_channel/channel_layer.hpp>
+#include <utility>
+#include <vector>
+#include <xyz/openbmc_project/Common/error.hpp>
+#include <xyz/openbmc_project/Network/EthernetInterface/server.hpp>
+#include <xyz/openbmc_project/Network/IP/server.hpp>
+#include <xyz/openbmc_project/Network/Neighbor/server.hpp>
 
 namespace ipmi
 {
 namespace transport
 {
 
+// D-Bus Network Daemon definitions
+constexpr auto PATH_ROOT = "/xyz/openbmc_project/network";
+constexpr auto PATH_SYSTEMCONFIG = "/xyz/openbmc_project/network/config";
+
+constexpr auto INTF_SYSTEMCONFIG =
+    "xyz.openbmc_project.Network.SystemConfiguration";
+constexpr auto INTF_ETHERNET = "xyz.openbmc_project.Network.EthernetInterface";
+constexpr auto INTF_IP = "xyz.openbmc_project.Network.IP";
+constexpr auto INTF_IP_CREATE = "xyz.openbmc_project.Network.IP.Create";
+constexpr auto INTF_MAC = "xyz.openbmc_project.Network.MACAddress";
+constexpr auto INTF_NEIGHBOR = "xyz.openbmc_project.Network.Neighbor";
+constexpr auto INTF_NEIGHBOR_CREATE_STATIC =
+    "xyz.openbmc_project.Network.Neighbor.CreateStatic";
+constexpr auto INTF_VLAN = "xyz.openbmc_project.Network.VLAN";
+constexpr auto INTF_VLAN_CREATE = "xyz.openbmc_project.Network.VLAN.Create";
+
 /** @brief IPMI LAN Parameters */
 enum class LanParam : uint8_t
 {
@@ -111,5 +162,523 @@
 constexpr uint8_t MAX_IPV6_STATIC_ADDRESSES = 15;
 constexpr uint8_t MAX_IPV6_DYNAMIC_ADDRESSES = 15;
 
+/** @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 A trivial helper used to determine if two PODs are equal
+ *
+ *  @params[in] a - The first object to compare
+ *  @params[in] b - The second object to compare
+ *  @return True if the objects are the same bytewise
+ */
+template <typename T>
+bool equal(const T& a, const T& b)
+{
+    static_assert(std::is_trivially_copyable_v<T>);
+    return std::memcmp(&a, &b, sizeof(T)) == 0;
+}
+
+/** @brief Copies bytes from an array into a trivially copyable container
+ *
+ *  @params[out] t     - The container receiving the data
+ *  @params[in]  bytes - The data to copy
+ */
+template <size_t N, typename T>
+void copyInto(T& t, const std::array<uint8_t, N>& bytes)
+{
+    static_assert(std::is_trivially_copyable_v<T>);
+    static_assert(N == sizeof(T));
+    std::memcpy(&t, bytes.data(), bytes.size());
+}
+
+/** @brief Gets a generic view of the bytes in the input container
+ *
+ *  @params[in] t - The data to reference
+ *  @return A string_view referencing the bytes in the container
+ */
+template <typename T>
+std::string_view dataRef(const T& t)
+{
+    static_assert(std::is_trivially_copyable_v<T>);
+    return {reinterpret_cast<const char*>(&t), sizeof(T)};
+}
+
+/** @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<ChannelParams> maybeGetChannelParams(sdbusplus::bus::bus& 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::bus& 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 func, typename... Args>
+auto channelCall(uint8_t channel, Args&&... args)
+{
+    sdbusplus::bus::bus bus(ipmid_get_sd_bus_connection());
+    auto params = getChannelParams(bus, channel);
+    return std::invoke(func, bus, params, std::forward<Args>(args)...);
+}
+
+/** @brief Generic paramters for different address families */
+template <int family>
+struct AddrFamily
+{
+};
+
+/** @brief Parameter specialization for IPv4 */
+template <>
+struct AddrFamily<AF_INET>
+{
+    using addr = in_addr;
+    static constexpr auto protocol =
+        sdbusplus::xyz::openbmc_project::Network::server::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<AF_INET6>
+{
+    using addr = in6_addr;
+    static constexpr auto protocol =
+        sdbusplus::xyz::openbmc_project::Network::server::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 <int family>
+struct IfNeigh
+{
+    std::string path;
+    typename AddrFamily<family>::addr ip;
+    ether_addr mac;
+};
+
+/** @brief Interface IP Address configuration parameters */
+template <int family>
+struct IfAddr
+{
+    std::string path;
+    typename AddrFamily<family>::addr address;
+    sdbusplus::xyz::openbmc_project::Network::server::IP::AddressOrigin origin;
+    uint8_t prefix;
+};
+
+/** @brief Valid address origins for IPv6 */
+static inline const std::unordered_set<
+    sdbusplus::xyz::openbmc_project::Network::server::IP::AddressOrigin>
+    originsV6Static = {sdbusplus::xyz::openbmc_project::Network::server::IP::
+                           AddressOrigin::Static};
+static inline const std::unordered_set<
+    sdbusplus::xyz::openbmc_project::Network::server::IP::AddressOrigin>
+    originsV6Dynamic = {
+        sdbusplus::xyz::openbmc_project::Network::server::IP::AddressOrigin::
+            DHCP,
+        sdbusplus::xyz::openbmc_project::Network::server::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<std::string, PropertyMap>;
+
+    /** @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::bus& 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::bus& 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 Turns an IP address string into the network byte order form
+ *         NOTE: This version strictly validates family matches
+ *
+ *  @param[in] address - The string form of the address
+ *  @return A network byte order address or none if conversion failed
+ */
+template <int family>
+std::optional<typename AddrFamily<family>::addr>
+    maybeStringToAddr(const char* address)
+{
+    typename AddrFamily<family>::addr ret;
+    if (inet_pton(family, address, &ret) == 1)
+    {
+        return ret;
+    }
+    return std::nullopt;
+}
+
+/** @brief Turns an IP address string into the network byte order form
+ *         NOTE: This version strictly validates family matches
+ *
+ *  @param[in] address - The string form of the address
+ *  @return A network byte order address
+ */
+template <int family>
+typename AddrFamily<family>::addr stringToAddr(const char* address)
+{
+    auto ret = maybeStringToAddr<family>(address);
+    if (!ret)
+    {
+        phosphor::logging::log<phosphor::logging::level::ERR>(
+            "Failed to convert IP Address",
+            phosphor::logging::entry("FAMILY=%d", family),
+            phosphor::logging::entry("ADDRESS=%s", address));
+        phosphor::logging::elog<
+            sdbusplus::xyz::openbmc_project::Common::Error::InternalFailure>();
+    }
+    return *ret;
+}
+
+/** @brief Turns an IP address in network byte order into a string
+ *
+ *  @param[in] address - The string form of the address
+ *  @return A network byte order address
+ */
+template <int family>
+std::string addrToString(const typename AddrFamily<family>::addr& address)
+{
+    std::string ret(AddrFamily<family>::maxStrLen, '\0');
+    inet_ntop(family, &address, ret.data(), ret.size());
+    ret.resize(strlen(ret.c_str()));
+    return ret;
+}
+
+/** @brief Converts a human readable MAC string into MAC bytes
+ *
+ *  @param[in] mac - The MAC string
+ *  @return MAC in bytes
+ */
+ether_addr stringToMAC(const char* mac);
+/** @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 <int family>
+std::optional<IfAddr<family>> findIfAddr(
+    sdbusplus::bus::bus& bus, const ChannelParams& params, uint8_t idx,
+    const std::unordered_set<
+        sdbusplus::xyz::openbmc_project::Network::server::IP::AddressOrigin>&
+        origins,
+    ObjectLookupCache& ips)
+{
+    for (const auto& [path, properties] : ips)
+    {
+        const auto& addrStr = std::get<std::string>(properties.at("Address"));
+        auto addr = maybeStringToAddr<family>(addrStr.c_str());
+        if (!addr)
+        {
+            continue;
+        }
+
+        sdbusplus::xyz::openbmc_project::Network::server::IP::AddressOrigin
+            origin = sdbusplus::xyz::openbmc_project::Network::server::IP::
+                convertAddressOriginFromString(
+                    std::get<std::string>(properties.at("Origin")));
+        if (origins.find(origin) == origins.end())
+        {
+            continue;
+        }
+
+        if (idx > 0)
+        {
+            idx--;
+            continue;
+        }
+
+        IfAddr<family> ifaddr;
+        ifaddr.path = path;
+        ifaddr.address = *addr;
+        ifaddr.prefix = std::get<uint8_t>(properties.at("PrefixLength"));
+        ifaddr.origin = origin;
+        return std::move(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 <int family>
+auto getIfAddr(
+    sdbusplus::bus::bus& bus, const ChannelParams& params, uint8_t idx,
+    const std::unordered_set<
+        sdbusplus::xyz::openbmc_project::Network::server::IP::AddressOrigin>&
+        origins)
+{
+    ObjectLookupCache ips(bus, params, INTF_IP);
+    return findIfAddr<family>(bus, params, idx, origins, ips);
+}
+
+/** @brief Determines if the ethernet interface is using DHCP
+ *
+ *  @param[in] bus    - The bus object used for lookups
+ *  @param[in] params - The parameters for the channel
+ *  @return DHCPConf enumeration
+ */
+sdbusplus::xyz::openbmc_project::Network::server::EthernetInterface::DHCPConf
+    getDHCPProperty(sdbusplus::bus::bus& bus, const ChannelParams& params);
+
+/** @brief Sets the DHCP v6 state on the given interface
+ *
+ *  @param[in] bus           - The bus object used for lookups
+ *  @param[in] params        - The parameters for the channel
+ *  @param[in] requestedDhcp - DHCP state to assign (none, v6, both)
+ *  @param[in] defaultMode   - True: Use algorithmic assignment
+ *                             False: requestedDhcp assigned unconditionally
+ */
+void setDHCPv6Property(sdbusplus::bus::bus& bus, const ChannelParams& params,
+                       const sdbusplus::xyz::openbmc_project::Network::server::
+                           EthernetInterface::DHCPConf requestedDhcp,
+                       const bool defaultMode);
+
+/** @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::bus& bus, const ChannelParams& params,
+                        uint8_t idx, const in6_addr& address, uint8_t prefix);
+
+/** @brief Retrieves the current gateway for the address family on the system
+ *         NOTE: The gateway is currently system wide and not per channel
+ *
+ *  @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 <int family>
+std::optional<typename AddrFamily<family>::addr>
+    getGatewayProperty(sdbusplus::bus::bus& bus, const ChannelParams& params)
+{
+    auto gatewayStr = std::get<std::string>(getDbusProperty(
+        bus, params.service, PATH_SYSTEMCONFIG, INTF_SYSTEMCONFIG,
+        AddrFamily<family>::propertyGateway));
+    if (gatewayStr.empty())
+    {
+        return std::nullopt;
+    }
+    return stringToAddr<family>(gatewayStr.c_str());
+}
+
+template <int family>
+std::optional<IfNeigh<family>>
+    findStaticNeighbor(sdbusplus::bus::bus& bus, const ChannelParams& params,
+                       const typename AddrFamily<family>::addr& ip,
+                       ObjectLookupCache& neighbors)
+{
+    using sdbusplus::xyz::openbmc_project::Network::server::Neighbor;
+    const auto state =
+        sdbusplus::xyz::openbmc_project::Network::server::convertForMessage(
+            Neighbor::State::Permanent);
+    for (const auto& [path, neighbor] : neighbors)
+    {
+        const auto& ipStr = std::get<std::string>(neighbor.at("IPAddress"));
+        auto neighIP = maybeStringToAddr<family>(ipStr.c_str());
+        if (!neighIP)
+        {
+            continue;
+        }
+        if (!equal(*neighIP, ip))
+        {
+            continue;
+        }
+        if (state != std::get<std::string>(neighbor.at("State")))
+        {
+            continue;
+        }
+
+        IfNeigh<family> ret;
+        ret.path = path;
+        ret.ip = ip;
+        const auto& macStr = std::get<std::string>(neighbor.at("MACAddress"));
+        ret.mac = stringToMAC(macStr.c_str());
+        return std::move(ret);
+    }
+
+    return std::nullopt;
+}
+
+template <int family>
+void createNeighbor(sdbusplus::bus::bus& bus, const ChannelParams& params,
+                    const typename AddrFamily<family>::addr& address,
+                    const ether_addr& mac)
+{
+    auto newreq =
+        bus.new_method_call(params.service.c_str(), params.logicalPath.c_str(),
+                            INTF_NEIGHBOR_CREATE_STATIC, "Neighbor");
+    std::string macStr = ether_ntoa(&mac);
+    newreq.append(addrToString<family>(address), macStr);
+    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::bus& bus, const std::string& service,
+                          const std::string& path);
+
+/** @brief Sets the system wide value for the default gateway
+ *
+ *  @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 <int family>
+void setGatewayProperty(sdbusplus::bus::bus& bus, const ChannelParams& params,
+                        const typename AddrFamily<family>::addr& address)
+{
+    // Save the old gateway MAC address if it exists so we can recreate it
+    auto gateway = getGatewayProperty<family>(bus, params);
+    std::optional<IfNeigh<family>> neighbor;
+    if (gateway)
+    {
+        ObjectLookupCache neighbors(bus, params, INTF_NEIGHBOR);
+        neighbor = findStaticNeighbor<family>(bus, params, *gateway, neighbors);
+    }
+
+    setDbusProperty(bus, params.service, PATH_SYSTEMCONFIG, INTF_SYSTEMCONFIG,
+                    AddrFamily<family>::propertyGateway,
+                    addrToString<family>(address));
+
+    // Restore the gateway MAC if we had one
+    if (neighbor)
+    {
+        deleteObjectIfExists(bus, params.service, neighbor->path);
+        createNeighbor<family>(bus, params, address, neighbor->mac);
+    }
+}
+
 } // namespace transport
 } // namespace ipmi