transporthandler: Rewrite + New Handler

This rewrites the old transport handler to use the new ipmi handler
registration functions. It attempts to clean up the old code, by
refactoring any business logic out of the code that parses the IPMI
messages. This makes the code paths easier to understand and allows for
better code re-use.

This also gets rid of the concept of the settings change timer. Clients
expect to see their settings take effect as soon as they are set,
regardless of the "Set In Progress" flag. This means we no longer need a
cache for our network settings that are about to be set by the daemon,
and a client can hold the BMC in "Set In Progress" while it verifies
settings like other BMC implementations.

Change-Id: I5406a674f087600afdfc2c0b3adeacde10986abc
Signed-off-by: William A. Kennington III <wak@google.com>
diff --git a/transporthandler.cpp b/transporthandler.cpp
index acff251..e88eb63 100644
--- a/transporthandler.cpp
+++ b/transporthandler.cpp
@@ -1,1023 +1,1181 @@
-#include "transporthandler.hpp"
-
-#include "app/channel.hpp"
-#include "user_channel/channel_layer.hpp"
-
 #include <arpa/inet.h>
+#include <netinet/ether.h>
 
-#include <chrono>
-#include <filesystem>
-#include <fstream>
+#include <array>
+#include <bitset>
+#include <cinttypes>
+#include <cstdint>
+#include <cstring>
+#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/message/types.hpp>
-#include <sdbusplus/timer.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/IP/server.hpp>
 
-#define SYSTEMD_NETWORKD_DBUS 1
-
-#ifdef SYSTEMD_NETWORKD_DBUS
-#include <mapper.h>
-#include <systemd/sd-bus.h>
-#endif
-
-// timer for network changes
-std::unique_ptr<phosphor::Timer> networkTimer = nullptr;
-
-const int SIZE_MAC = 18; // xx:xx:xx:xx:xx:xx
-constexpr auto ipv4Protocol = "xyz.openbmc_project.Network.IP.Protocol.IPv4";
-
-std::map<int, std::unique_ptr<struct ChannelConfig_t>> channelConfig;
-
-using namespace phosphor::logging;
-using namespace sdbusplus::xyz::openbmc_project::Common::Error;
-
-namespace fs = std::filesystem;
-
-void register_netfn_transport_functions() __attribute__((constructor));
-
-struct ChannelConfig_t* getChannelConfig(int channel)
+namespace ipmi
 {
-    auto item = channelConfig.find(channel);
-    if (item == channelConfig.end())
-    {
-        channelConfig[channel] = std::make_unique<struct ChannelConfig_t>();
-    }
+namespace transport
+{
 
-    return channelConfig[channel].get();
+using phosphor::logging::commit;
+using phosphor::logging::elog;
+using phosphor::logging::entry;
+using phosphor::logging::level;
+using phosphor::logging::log;
+using sdbusplus::xyz::openbmc_project::Common::Error::InternalFailure;
+using sdbusplus::xyz::openbmc_project::Network::server::IP;
+
+// LAN Handler specific response codes
+constexpr Cc ccParamNotSupported = 0x80;
+constexpr Cc ccParamSetLocked = 0x81;
+constexpr Cc ccParamReadOnly = 0x82;
+
+// VLANs are a 12-bit value
+constexpr uint16_t VLAN_VALUE_MASK = 0x0fff;
+constexpr uint16_t VLAN_ENABLE_FLAG = 0x8000;
+
+// 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_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 Valid address origins for IPv4 */
+const std::unordered_set<IP::AddressOrigin> originsV4 = {
+    IP::AddressOrigin::Static,
+    IP::AddressOrigin::DHCP,
+};
+
+/** @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 IPMI LAN Parameters */
+enum class LanParam : uint8_t
+{
+    SetStatus = 0,
+    AuthSupport = 1,
+    AuthEnables = 2,
+    IP = 3,
+    IPSrc = 4,
+    MAC = 5,
+    SubnetMask = 6,
+    Gateway1 = 12,
+    VLANId = 20,
+    CiphersuiteSupport = 22,
+    CiphersuiteEntries = 23,
+};
+
+/** @brief IPMI IP Origin Types */
+enum class IPSrc : uint8_t
+{
+    Unspecified = 0,
+    Static = 1,
+    DHCP = 2,
+    BIOS = 3,
+    BMC = 4,
+};
+
+/** @brief IPMI Set Status */
+enum class SetStatus : uint8_t
+{
+    Complete = 0,
+    InProgress = 1,
+    Commit = 2,
+};
+
+/** @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());
 }
 
-// Helper Function to get IP Address/NetMask/Gateway/MAC Address from Network
-// Manager or Cache based on Set-In-Progress State
-ipmi_ret_t getNetworkData(uint8_t lan_param, uint8_t* data, int channel)
+/** @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)
 {
-    ipmi_ret_t rc = IPMI_CC_OK;
-    sdbusplus::bus::bus bus(ipmid_get_sd_bus_connection());
+    static_assert(std::is_trivially_copyable_v<T>);
+    return {reinterpret_cast<const char*>(&t), sizeof(T)};
+}
 
-    auto ethdevice = ipmi::getChannelName(channel);
-    // if ethdevice is an empty string they weren't expecting this channel.
-    if (ethdevice.empty())
+/** @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)
+{
+    auto ifname = getChannelName(channel);
+    if (ifname.empty())
     {
-        // TODO: return error from getNetworkData()
-        return IPMI_CC_INVALID_FIELD_REQUEST;
+        return std::nullopt;
     }
-    auto ethIP = ethdevice + "/" + ipmi::network::IP_TYPE;
-    auto channelConf = getChannelConfig(channel);
 
+    // Enumerate all VLAN + ETHERNET interfaces
+    auto req = bus.new_method_call(MAPPER_BUS_NAME, MAPPER_OBJ, MAPPER_INTF,
+                                   "GetSubTree");
+    req.append(PATH_ROOT, 0,
+               std::vector<std::string>{INTF_VLAN, INTF_ETHERNET});
+    auto reply = bus.call(req);
+    ObjectTree objs;
+    reply.read(objs);
+
+    ChannelParams params;
+    for (const auto& [path, impls] : objs)
+    {
+        if (path.find(ifname) == path.npos)
+        {
+            continue;
+        }
+        for (const auto& [service, intfs] : impls)
+        {
+            bool vlan = false;
+            bool ethernet = false;
+            for (const auto& intf : intfs)
+            {
+                if (intf == INTF_VLAN)
+                {
+                    vlan = true;
+                }
+                else if (intf == INTF_ETHERNET)
+                {
+                    ethernet = true;
+                }
+            }
+            if (params.service.empty() && (vlan || ethernet))
+            {
+                params.service = service;
+            }
+            if (params.ifPath.empty() && !vlan && ethernet)
+            {
+                params.ifPath = path;
+            }
+            if (params.logicalPath.empty() && vlan)
+            {
+                params.logicalPath = path;
+            }
+        }
+    }
+
+    // We must have a path for the underlying interface
+    if (params.ifPath.empty())
+    {
+        return std::nullopt;
+    }
+    // We don't have a VLAN so the logical path is the same
+    if (params.logicalPath.empty())
+    {
+        params.logicalPath = params.ifPath;
+    }
+
+    params.id = channel;
+    params.ifname = std::move(ifname);
+    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);
+    if (!params)
+    {
+        log<level::ERR>("Failed to get channel params",
+                        entry("CHANNEL=%" PRIu8, channel));
+        elog<InternalFailure>();
+    }
+    return std::move(*params);
+}
+
+/** @brief Wraps the phosphor logging method to insert some additional metadata
+ *
+ *  @param[in] params - The parameters for the channel
+ *  ...
+ */
+template <auto level, typename... Args>
+auto logWithChannel(const ChannelParams& params, Args&&... args)
+{
+    return log<level>(std::forward<Args>(args)...,
+                      entry("CHANNEL=%d", params.id),
+                      entry("IFNAME=%s", params.ifname.c_str()));
+}
+template <auto level, typename... Args>
+auto logWithChannel(const std::optional<ChannelParams>& params, Args&&... args)
+{
+    if (params)
+    {
+        return logWithChannel<level>(*params, std::forward<Args>(args)...);
+    }
+    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 True if DHCP is enabled, false otherwise
+ */
+bool getDHCPProperty(sdbusplus::bus::bus& bus, const ChannelParams& params)
+{
+    return std::get<bool>(getDbusProperty(
+        bus, params.service, params.logicalPath, INTF_ETHERNET, "DHCPEnabled"));
+}
+
+/** @brief Sets the system value for DHCP on the given interface
+ *
+ *  @param[in] bus    - The bus object used for lookups
+ *  @param[in] params - The parameters for the channel
+ *  @param[in] on     - Whether or not to enable DHCP
+ */
+void setDHCPProperty(sdbusplus::bus::bus& bus, const ChannelParams& params,
+                     bool on)
+{
+    setDbusProperty(bus, params.service, params.logicalPath, INTF_ETHERNET,
+                    "DHCPEnabled", on);
+}
+
+/** @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);
+    if (ret == nullptr)
+    {
+        log<level::ERR>("Invalid MAC Address", entry("MAC=%s", mac));
+        elog<InternalFailure>();
+    }
+    return *ret;
+}
+
+/** @brief Determines the MAC of the ethernet interface
+ *
+ *  @param[in] bus    - The bus object used for lookups
+ *  @param[in] params - The parameters for the channel
+ *  @return The configured mac address
+ */
+ether_addr getMACProperty(sdbusplus::bus::bus& bus, const ChannelParams& params)
+{
+    auto macStr = std::get<std::string>(getDbusProperty(
+        bus, params.service, params.ifPath, INTF_MAC, "MACAddress"));
+    return stringToMAC(macStr.c_str());
+}
+
+/** @brief Sets the system value for MAC address on the given interface
+ *
+ *  @param[in] bus    - The bus object used for lookups
+ *  @param[in] params - The parameters for the channel
+ *  @param[in] mac    - MAC address to apply
+ */
+void setMACProperty(sdbusplus::bus::bus& bus, const ChannelParams& params,
+                    const ether_addr& mac)
+{
+    std::string macStr = ether_ntoa(&mac);
+    setDbusProperty(bus, params.service, params.ifPath, INTF_MAC, "MACAddress",
+                    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 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)
+{
+    setDbusProperty(bus, params.service, PATH_SYSTEMCONFIG, INTF_SYSTEMCONFIG,
+                    AddrFamily<family>::propertyGateway,
+                    addrToString<family>(address));
+}
+
+/** @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)
+{
+    if (path.empty())
+    {
+        return;
+    }
     try
     {
-        switch (static_cast<LanParam>(lan_param))
+        auto req = bus.new_method_call(service.c_str(), path.c_str(),
+                                       ipmi::DELETE_INTERFACE, "Delete");
+        bus.call_noreply(req);
+    }
+    catch (const sdbusplus::exception::SdBusError& e)
+    {
+        if (strcmp(e.name(), "org.freedesktop.DBus.Error.UnknownObject") != 0)
         {
-            case LanParam::IP:
-            {
-                std::string ipaddress;
-                if (channelConf->lan_set_in_progress == SET_COMPLETE)
-                {
-                    try
-                    {
-                        auto ipObjectInfo =
-                            ipmi::getIPObject(bus, ipmi::network::IP_INTERFACE,
-                                              ipmi::network::ROOT, ethIP);
-
-                        auto properties = ipmi::getAllDbusProperties(
-                            bus, ipObjectInfo.second, ipObjectInfo.first,
-                            ipmi::network::IP_INTERFACE);
-
-                        ipaddress =
-                            std::get<std::string>(properties["Address"]);
-                    }
-                    // ignore the exception, as it is a valid condition that
-                    // the system is not configured with any IP.
-                    catch (InternalFailure& e)
-                    {
-                        // nothing to do.
-                    }
-                }
-                else if (channelConf->lan_set_in_progress == SET_IN_PROGRESS)
-                {
-                    ipaddress = channelConf->ipaddr;
-                }
-
-                inet_pton(AF_INET, ipaddress.c_str(),
-                          reinterpret_cast<void*>(data));
-            }
-            break;
-
-            case LanParam::IPSRC:
-            {
-                std::string networkInterfacePath;
-
-                if (channelConf->lan_set_in_progress == SET_COMPLETE)
-                {
-                    try
-                    {
-                        ipmi::ObjectTree ancestorMap;
-                        // if the system is having ip object,then
-                        // get the IP object.
-                        auto ipObject = ipmi::getDbusObject(
-                            bus, ipmi::network::IP_INTERFACE,
-                            ipmi::network::ROOT, ethIP);
-
-                        // Get the parent interface of the IP object.
-                        try
-                        {
-                            ipmi::InterfaceList interfaces;
-                            interfaces.emplace_back(
-                                ipmi::network::ETHERNET_INTERFACE);
-
-                            ancestorMap = ipmi::getAllAncestors(
-                                bus, ipObject.first, std::move(interfaces));
-                        }
-                        catch (InternalFailure& e)
-                        {
-                            // if unable to get the parent interface
-                            // then commit the error and return.
-                            log<level::ERR>(
-                                "Unable to get the parent interface",
-                                entry("PATH=%s", ipObject.first.c_str()),
-                                entry("INTERFACE=%s",
-                                      ipmi::network::ETHERNET_INTERFACE));
-                            break;
-                        }
-                        // for an ip object there would be single parent
-                        // interface.
-                        networkInterfacePath = ancestorMap.begin()->first;
-                    }
-                    catch (InternalFailure& e)
-                    {
-                        // if there is no ip configured on the system,then
-                        // get the network interface object.
-                        auto networkInterfaceObject = ipmi::getDbusObject(
-                            bus, ipmi::network::ETHERNET_INTERFACE,
-                            ipmi::network::ROOT, ethdevice);
-
-                        networkInterfacePath = networkInterfaceObject.first;
-                    }
-
-                    auto variant = ipmi::getDbusProperty(
-                        bus, ipmi::network::SERVICE, networkInterfacePath,
-                        ipmi::network::ETHERNET_INTERFACE, "DHCPEnabled");
-
-                    auto dhcpEnabled = std::get<bool>(variant);
-                    // As per IPMI spec 2=>DHCP, 1=STATIC
-                    auto ipsrc = dhcpEnabled ? ipmi::network::IPOrigin::DHCP
-                                             : ipmi::network::IPOrigin::STATIC;
-
-                    std::memcpy(data, &ipsrc, ipmi::network::IPSRC_SIZE_BYTE);
-                }
-                else if (channelConf->lan_set_in_progress == SET_IN_PROGRESS)
-                {
-                    std::memcpy(data, &(channelConf->ipsrc),
-                                ipmi::network::IPSRC_SIZE_BYTE);
-                }
-            }
-            break;
-
-            case LanParam::SUBNET:
-            {
-                unsigned long mask{};
-                if (channelConf->lan_set_in_progress == SET_COMPLETE)
-                {
-                    try
-                    {
-                        auto ipObjectInfo =
-                            ipmi::getIPObject(bus, ipmi::network::IP_INTERFACE,
-                                              ipmi::network::ROOT, ethIP);
-
-                        auto properties = ipmi::getAllDbusProperties(
-                            bus, ipObjectInfo.second, ipObjectInfo.first,
-                            ipmi::network::IP_INTERFACE);
-
-                        auto prefix =
-                            std::get<uint8_t>(properties["PrefixLength"]);
-                        mask = ipmi::network::MASK_32_BIT;
-                        mask = htonl(mask << (ipmi::network::BITS_32 - prefix));
-                    }
-                    // ignore the exception, as it is a valid condition that
-                    // the system is not configured with any IP.
-                    catch (InternalFailure& e)
-                    {
-                        // nothing to do
-                    }
-                    std::memcpy(data, &mask,
-                                ipmi::network::IPV4_ADDRESS_SIZE_BYTE);
-                }
-                else if (channelConf->lan_set_in_progress == SET_IN_PROGRESS)
-                {
-                    inet_pton(AF_INET, channelConf->netmask.c_str(),
-                              reinterpret_cast<void*>(data));
-                }
-            }
-            break;
-
-            case LanParam::GATEWAY:
-            {
-                std::string gateway;
-
-                if (channelConf->lan_set_in_progress == SET_COMPLETE)
-                {
-                    try
-                    {
-                        auto systemObject = ipmi::getDbusObject(
-                            bus, ipmi::network::SYSTEMCONFIG_INTERFACE,
-                            ipmi::network::ROOT);
-
-                        auto systemProperties = ipmi::getAllDbusProperties(
-                            bus, systemObject.second, systemObject.first,
-                            ipmi::network::SYSTEMCONFIG_INTERFACE);
-
-                        gateway = std::get<std::string>(
-                            systemProperties["DefaultGateway"]);
-                    }
-                    // ignore the exception, as it is a valid condition that
-                    // the system is not configured with any IP.
-                    catch (InternalFailure& e)
-                    {
-                        // nothing to do
-                    }
-                }
-                else if (channelConf->lan_set_in_progress == SET_IN_PROGRESS)
-                {
-                    gateway = channelConf->gateway;
-                }
-
-                inet_pton(AF_INET, gateway.c_str(),
-                          reinterpret_cast<void*>(data));
-            }
-            break;
-
-            case LanParam::MAC:
-            {
-                std::string macAddress;
-                if (channelConf->lan_set_in_progress == SET_COMPLETE)
-                {
-                    auto macObjectInfo =
-                        ipmi::getDbusObject(bus, ipmi::network::MAC_INTERFACE,
-                                            ipmi::network::ROOT, ethdevice);
-
-                    auto variant = ipmi::getDbusProperty(
-                        bus, macObjectInfo.second, macObjectInfo.first,
-                        ipmi::network::MAC_INTERFACE, "MACAddress");
-
-                    macAddress = std::get<std::string>(variant);
-                }
-                else if (channelConf->lan_set_in_progress == SET_IN_PROGRESS)
-                {
-                    macAddress = channelConf->macAddress;
-                }
-
-                sscanf(macAddress.c_str(), ipmi::network::MAC_ADDRESS_FORMAT,
-                       (data), (data + 1), (data + 2), (data + 3), (data + 4),
-                       (data + 5));
-            }
-            break;
-
-            case LanParam::VLAN:
-            {
-                uint16_t vlanID{};
-                if (channelConf->lan_set_in_progress == SET_COMPLETE)
-                {
-                    try
-                    {
-                        auto ipObjectInfo = ipmi::getIPObject(
-                            bus, ipmi::network::IP_INTERFACE,
-                            ipmi::network::ROOT, ipmi::network::IP_TYPE);
-
-                        vlanID = static_cast<uint16_t>(
-                            ipmi::network::getVLAN(ipObjectInfo.first));
-
-                        vlanID = htole16(vlanID);
-
-                        if (vlanID)
-                        {
-                            // Enable the 16th bit
-                            vlanID |= htole16(ipmi::network::VLAN_ENABLE_MASK);
-                        }
-                    }
-                    // ignore the exception, as it is a valid condition that
-                    // the system is not configured with any IP.
-                    catch (InternalFailure& e)
-                    {
-                        // nothing to do
-                    }
-
-                    std::memcpy(data, &vlanID, ipmi::network::VLAN_SIZE_BYTE);
-                }
-                else if (channelConf->lan_set_in_progress == SET_IN_PROGRESS)
-                {
-                    std::memcpy(data, &(channelConf->vlanID),
-                                ipmi::network::VLAN_SIZE_BYTE);
-                }
-            }
-            break;
-
-            default:
-                rc = IPMI_CC_PARM_OUT_OF_RANGE;
+            // We want to rethrow real errors
+            throw;
         }
     }
-    catch (InternalFailure& e)
-    {
-        commit<InternalFailure>();
-        rc = IPMI_CC_UNSPECIFIED_ERROR;
-        return rc;
-    }
-    return rc;
 }
 
-namespace cipher
+/** @brief Sets the address info configured for the interface
+ *         If a previous address path exists then it will be removed
+ *         before the new address is added.
+ *
+ *  @param[in] bus     - The bus object used for lookups
+ *  @param[in] params  - The parameters for the channel
+ *  @param[in] address - The address of the new IP
+ *  @param[in] prefix  - The prefix of the new IP
+ */
+template <int family>
+void createIfAddr(sdbusplus::bus::bus& bus, const ChannelParams& params,
+                  const typename AddrFamily<family>::addr& address,
+                  uint8_t prefix)
 {
+    auto newreq =
+        bus.new_method_call(params.service.c_str(), params.logicalPath.c_str(),
+                            INTF_IP_CREATE, "IP");
+    std::string protocol =
+        sdbusplus::xyz::openbmc_project::Network::server::convertForMessage(
+            AddrFamily<family>::protocol);
+    newreq.append(protocol, addrToString<family>(address), prefix, "");
+    bus.call_noreply(newreq);
+}
 
-std::vector<uint8_t> getCipherList()
+/** @brief Trivial helper for getting the IPv4 address from getIfAddrs()
+ *
+ *  @param[in] bus    - The bus object used for lookups
+ *  @param[in] params - The parameters for the channel
+ *  @return The address and prefix if found
+ */
+auto getIfAddr4(sdbusplus::bus::bus& bus, const ChannelParams& params)
 {
-    std::vector<uint8_t> cipherList;
+    return getIfAddr<AF_INET>(bus, params, 0, originsV4);
+}
 
-    std::ifstream jsonFile(configFile);
-    if (!jsonFile.is_open())
+/** @brief Reconfigures the IPv4 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] address - The new address if specified
+ *  @param[in] prefix  - The new address prefix if specified
+ */
+void reconfigureIfAddr4(sdbusplus::bus::bus& bus, const ChannelParams& params,
+                        const std::optional<in_addr>& address,
+                        std::optional<uint8_t> prefix)
+{
+    auto ifaddr = getIfAddr4(bus, params);
+    if (!ifaddr && !address)
     {
-        log<level::ERR>("Channel Cipher suites file not found");
+        log<level::ERR>("Missing address for IPv4 assignment");
         elog<InternalFailure>();
     }
-
-    auto data = Json::parse(jsonFile, nullptr, false);
-    if (data.is_discarded())
+    uint8_t fallbackPrefix = AddrFamily<AF_INET>::defaultPrefix;
+    if (ifaddr)
     {
-        log<level::ERR>("Parsing channel cipher suites JSON failed");
+        fallbackPrefix = ifaddr->prefix;
+        deleteObjectIfExists(bus, params.service, ifaddr->path);
+    }
+    createIfAddr<AF_INET>(bus, params, address.value_or(ifaddr->address),
+                          prefix.value_or(fallbackPrefix));
+}
+
+/** @brief Gets the vlan ID configured on the interface
+ *
+ *  @param[in] bus    - The bus object used for lookups
+ *  @param[in] params - The parameters for the channel
+ *  @return VLAN id or the standard 0 for no VLAN
+ */
+uint16_t getVLANProperty(sdbusplus::bus::bus& bus, const ChannelParams& params)
+{
+    // VLAN devices will always have a separate logical object
+    if (params.ifPath == params.logicalPath)
+    {
+        return 0;
+    }
+
+    auto vlan = std::get<uint32_t>(getDbusProperty(
+        bus, params.service, params.logicalPath, INTF_VLAN, "Id"));
+    if ((vlan & VLAN_VALUE_MASK) != vlan)
+    {
+        logWithChannel<level::ERR>(params, "networkd returned an invalid vlan",
+                                   entry("VLAN=%" PRIu32, vlan));
         elog<InternalFailure>();
     }
-
-    // Byte 1 is reserved
-    cipherList.push_back(0x00);
-
-    for (const auto& record : data)
-    {
-        cipherList.push_back(record.value(cipher, 0));
-    }
-
-    return cipherList;
+    return vlan;
 }
 
-} // namespace cipher
-
-ipmi_ret_t ipmi_transport_wildcard(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
-                                   ipmi_request_t request,
-                                   ipmi_response_t response,
-                                   ipmi_data_len_t data_len,
-                                   ipmi_context_t context)
+/** @brief Deletes all of the possible configuration parameters for a channel
+ *
+ *  @param[in] bus    - The bus object used for lookups
+ *  @param[in] params - The parameters for the channel
+ */
+void deconfigureChannel(sdbusplus::bus::bus& bus, ChannelParams& params)
 {
-    // Status code.
-    ipmi_ret_t rc = IPMI_CC_INVALID;
-    *data_len = 0;
-    return rc;
+    // Delete all objects associated with the interface
+    auto objreq = bus.new_method_call(MAPPER_BUS_NAME, MAPPER_OBJ, MAPPER_INTF,
+                                      "GetSubTree");
+    objreq.append(PATH_ROOT, 0, std::vector<std::string>{DELETE_INTERFACE});
+    auto objreply = bus.call(objreq);
+    ObjectTree objs;
+    objreply.read(objs);
+    for (const auto& [path, impls] : objs)
+    {
+        if (path.find(params.ifname) == path.npos)
+        {
+            continue;
+        }
+        for (const auto& [service, intfs] : impls)
+        {
+            deleteObjectIfExists(bus, service, path);
+        }
+        // Update params to reflect the deletion of vlan
+        if (path == params.logicalPath)
+        {
+            params.logicalPath = params.ifPath;
+        }
+    }
+
+    // Clear out any settings on the lower physical interface
+    setDHCPProperty(bus, params, false);
 }
 
-struct set_lan_t
+/** @brief Creates a new VLAN on the specified interface
+ *
+ *  @param[in] bus    - The bus object used for lookups
+ *  @param[in] params - The parameters for the channel
+ *  @param[in] vlan   - The id of the new vlan
+ */
+void createVLAN(sdbusplus::bus::bus& bus, ChannelParams& params, uint16_t vlan)
 {
-    uint8_t channel;
-    uint8_t parameter;
-    uint8_t data[8]; // Per IPMI spec, not expecting more than this size
-} __attribute__((packed));
-
-ipmi_ret_t checkAndUpdateNetwork(int channel)
-{
-    auto channelConf = getChannelConfig(channel);
-    using namespace std::chrono_literals;
-    // time to wait before applying the network changes.
-    constexpr auto networkTimeout = 10000000us; // 10 sec
-
-    // Skip the timer. Expecting more update as we are in SET_IN_PROGRESS
-    if (channelConf->lan_set_in_progress == SET_IN_PROGRESS)
+    if (vlan == 0)
     {
-        return IPMI_CC_OK;
+        return;
     }
 
-    // Start the timer, if it is direct single param update without
-    // SET_IN_PROGRESS or many params updated through SET_IN_PROGRESS to
-    // SET_COMPLETE Note: Even for update with SET_IN_PROGRESS, don't apply the
-    // changes immediately, as ipmitool sends each param individually
-    // through SET_IN_PROGRESS to SET_COMPLETE.
-    channelConf->flush = true;
-    if (!networkTimer)
-    {
-        log<level::ERR>("Network timer is not instantiated");
-        return IPMI_CC_UNSPECIFIED_ERROR;
-    }
-    // start the timer.
-    networkTimer->start(networkTimeout);
-    return IPMI_CC_OK;
+    auto req = bus.new_method_call(params.service.c_str(), PATH_ROOT,
+                                   INTF_VLAN_CREATE, "VLAN");
+    req.append(params.ifname, static_cast<uint32_t>(vlan));
+    auto reply = bus.call(req);
+    sdbusplus::message::object_path newPath;
+    reply.read(newPath);
+    params.logicalPath = std::move(newPath);
 }
 
-ipmi_ret_t ipmi_transport_set_lan(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
-                                  ipmi_request_t request,
-                                  ipmi_response_t response,
-                                  ipmi_data_len_t data_len,
-                                  ipmi_context_t context)
+/** @brief Performs the necessary reconfiguration to change the VLAN
+ *
+ *  @param[in] bus    - The bus object used for lookups
+ *  @param[in] params - The parameters for the channel
+ *  @param[in] vlan   - The new vlan id to use
+ */
+void reconfigureVLAN(sdbusplus::bus::bus& bus, ChannelParams& params,
+                     uint16_t vlan)
 {
-    ipmi_ret_t rc = IPMI_CC_OK;
+    // Unfortunatetly we don't have built-in functions to migrate our interface
+    // customizations to new VLAN interfaces, or have some kind of decoupling.
+    // We therefore must retain all of our old information, setup the new VLAN
+    // configuration, then restore the old info.
 
-    char ipaddr[INET_ADDRSTRLEN];
-    char netmask[INET_ADDRSTRLEN];
-    char gateway[INET_ADDRSTRLEN];
+    // Save info from the old logical interface
+    ObjectLookupCache ips(bus, params, INTF_IP);
+    auto ifaddr4 = findIfAddr<AF_INET>(bus, params, 0, originsV4, ips);
+    auto dhcp = getDHCPProperty(bus, params);
 
-    auto reqptr = reinterpret_cast<const set_lan_t*>(request);
-    sdbusplus::bus::bus bus(ipmid_get_sd_bus_connection());
+    deconfigureChannel(bus, params);
+    createVLAN(bus, params, vlan);
 
-    size_t reqLen = *data_len;
-    *data_len = 0;
-
-    // channel number is the lower nibble
-    int channel = reqptr->channel & CHANNEL_MASK;
-    auto ethdevice = ipmi::getChannelName(channel);
-    ipmi::ChannelInfo chInfo;
-    ipmi::getChannelInfo(channel, chInfo);
-
-    if (ethdevice.empty() ||
-        chInfo.mediumType !=
-            static_cast<uint8_t>(ipmi::EChannelMediumType::lan8032))
+    // Re-establish the saved settings
+    setDHCPProperty(bus, params, dhcp);
+    if (ifaddr4)
     {
-        return IPMI_CC_INVALID_FIELD_REQUEST;
+        createIfAddr<AF_INET>(bus, params, ifaddr4->address, ifaddr4->prefix);
     }
-    auto channelConf = getChannelConfig(channel);
+}
 
-    switch (static_cast<LanParam>(reqptr->parameter))
+/** @brief Turns a prefix into a netmask
+ *
+ *  @param[in] prefix - The prefix length
+ *  @return The netmask
+ */
+in_addr prefixToNetmask(uint8_t prefix)
+{
+    if (prefix > 32)
     {
+        log<level::ERR>("Invalid prefix", entry("PREFIX=%" PRIu8, prefix));
+        elog<InternalFailure>();
+    }
+    if (prefix == 0)
+    {
+        // Avoids 32-bit lshift by 32 UB
+        return {};
+    }
+    return {htobe32(~UINT32_C(0) << (32 - prefix))};
+}
+
+/** @brief Turns a a netmask into a prefix length
+ *
+ *  @param[in] netmask - The netmask in byte form
+ *  @return The prefix length
+ */
+uint8_t netmaskToPrefix(in_addr netmask)
+{
+    uint32_t x = be32toh(netmask.s_addr);
+    if ((~x & (~x + 1)) != 0)
+    {
+        char maskStr[INET_ADDRSTRLEN];
+        inet_ntop(AF_INET, &netmask, maskStr, sizeof(maskStr));
+        log<level::ERR>("Invalid netmask", entry("NETMASK=%s", maskStr));
+        elog<InternalFailure>();
+    }
+    return 32 - __builtin_ctz(x);
+}
+
+// We need to store this value so it can be returned to the client
+// It is volatile so safe to store in daemon memory.
+static std::unordered_map<uint8_t, SetStatus> setStatus;
+
+// Until we have good support for fixed versions of IPMI tool
+// we need to return the VLAN id for disabled VLANs. The value is only
+// used for verification that a disable operation succeeded and will only
+// be sent if our system indicates that vlans are disabled.
+static std::unordered_map<uint8_t, uint16_t> lastDisabledVlan;
+
+/** @brief Gets the set status for the channel if it exists
+ *         Otherise populates and returns the default value.
+ *
+ *  @param[in] channel - The channel id corresponding to an ethernet interface
+ *  @return A reference to the SetStatus for the channel
+ */
+SetStatus& getSetStatus(uint8_t channel)
+{
+    auto it = setStatus.find(channel);
+    if (it != setStatus.end())
+    {
+        return it->second;
+    }
+    return setStatus[channel] = SetStatus::Complete;
+}
+
+RspType<> setLan(uint4_t channelBits, uint4_t, uint8_t parameter,
+                 message::Payload& req)
+{
+    auto channel = static_cast<uint8_t>(channelBits);
+    if (!doesDeviceExist(channel))
+    {
+        req.trailingOk = true;
+        return responseInvalidFieldRequest();
+    }
+
+    switch (static_cast<LanParam>(parameter))
+    {
+        case LanParam::SetStatus:
+        {
+            uint2_t flag;
+            uint6_t rsvd;
+            if (req.unpack(flag, rsvd) != 0 || !req.fullyUnpacked())
+            {
+                return responseReqDataLenInvalid();
+            }
+            auto status = static_cast<SetStatus>(static_cast<uint8_t>(flag));
+            switch (status)
+            {
+                case SetStatus::Complete:
+                {
+                    getSetStatus(channel) = status;
+                    return responseSuccess();
+                }
+                case SetStatus::InProgress:
+                {
+                    auto& storedStatus = getSetStatus(channel);
+                    if (storedStatus == SetStatus::InProgress)
+                    {
+                        return response(ccParamSetLocked);
+                    }
+                    storedStatus = status;
+                    return responseSuccess();
+                }
+                case SetStatus::Commit:
+                    if (getSetStatus(channel) != SetStatus::InProgress)
+                    {
+                        return responseInvalidFieldRequest();
+                    }
+                    return responseSuccess();
+            }
+            return response(ccParamNotSupported);
+        }
+        case LanParam::AuthSupport:
+        {
+            req.trailingOk = true;
+            return response(ccParamReadOnly);
+        }
+        case LanParam::AuthEnables:
+        {
+            req.trailingOk = true;
+            return response(ccParamNotSupported);
+        }
         case LanParam::IP:
         {
-            std::snprintf(ipaddr, INET_ADDRSTRLEN,
-                          ipmi::network::IP_ADDRESS_FORMAT, reqptr->data[0],
-                          reqptr->data[1], reqptr->data[2], reqptr->data[3]);
-
-            channelConf->ipaddr.assign(ipaddr);
+            in_addr ip;
+            std::array<uint8_t, sizeof(ip)> bytes;
+            if (req.unpack(bytes) != 0 || !req.fullyUnpacked())
+            {
+                return responseReqDataLenInvalid();
+            }
+            copyInto(ip, bytes);
+            channelCall<reconfigureIfAddr4>(channel, ip, std::nullopt);
+            return responseSuccess();
         }
-        break;
-
-        case LanParam::IPSRC:
+        case LanParam::IPSrc:
         {
-            uint8_t ipsrc{};
-            std::memcpy(&ipsrc, reqptr->data, ipmi::network::IPSRC_SIZE_BYTE);
-            channelConf->ipsrc = static_cast<ipmi::network::IPOrigin>(ipsrc);
+            uint4_t flag;
+            uint4_t rsvd;
+            if (req.unpack(flag, rsvd) != 0 || !req.fullyUnpacked())
+            {
+                return responseReqDataLenInvalid();
+            }
+            switch (static_cast<IPSrc>(static_cast<uint8_t>(flag)))
+            {
+                case IPSrc::DHCP:
+                {
+                    channelCall<setDHCPProperty>(channel, true);
+                    return responseSuccess();
+                }
+                case IPSrc::Unspecified:
+                case IPSrc::Static:
+                case IPSrc::BIOS:
+                case IPSrc::BMC:
+                {
+                    channelCall<setDHCPProperty>(channel, false);
+                    return responseSuccess();
+                }
+            }
+            return response(ccParamNotSupported);
         }
-        break;
-
         case LanParam::MAC:
         {
-            char mac[SIZE_MAC];
-
-            std::snprintf(mac, SIZE_MAC, ipmi::network::MAC_ADDRESS_FORMAT,
-                          reqptr->data[0], reqptr->data[1], reqptr->data[2],
-                          reqptr->data[3], reqptr->data[4], reqptr->data[5]);
-
-            auto macObjectInfo =
-                ipmi::getDbusObject(bus, ipmi::network::MAC_INTERFACE,
-                                    ipmi::network::ROOT, ethdevice);
-
-            ipmi::setDbusProperty(
-                bus, macObjectInfo.second, macObjectInfo.first,
-                ipmi::network::MAC_INTERFACE, "MACAddress", std::string(mac));
-
-            channelConf->macAddress = mac;
-        }
-        break;
-
-        case LanParam::SUBNET:
-        {
-            std::snprintf(netmask, INET_ADDRSTRLEN,
-                          ipmi::network::IP_ADDRESS_FORMAT, reqptr->data[0],
-                          reqptr->data[1], reqptr->data[2], reqptr->data[3]);
-            channelConf->netmask.assign(netmask);
-        }
-        break;
-
-        case LanParam::GATEWAY:
-        {
-            std::snprintf(gateway, INET_ADDRSTRLEN,
-                          ipmi::network::IP_ADDRESS_FORMAT, reqptr->data[0],
-                          reqptr->data[1], reqptr->data[2], reqptr->data[3]);
-            channelConf->gateway.assign(gateway);
-        }
-        break;
-
-        case LanParam::VLAN:
-        {
-            if (reqLen != lanParamVLANSize)
+            ether_addr mac;
+            std::array<uint8_t, sizeof(mac)> bytes;
+            if (req.unpack(bytes) != 0 || !req.fullyUnpacked())
             {
-                return IPMI_CC_REQ_DATA_LEN_INVALID;
+                return responseReqDataLenInvalid();
             }
-
-            uint16_t vlan{};
-            std::memcpy(&vlan, reqptr->data, ipmi::network::VLAN_SIZE_BYTE);
-            // We are not storing the enable bit
-            // We assume that ipmitool always send enable
-            // bit as 1.
-            vlan = le16toh(vlan);
-            if (vlan == 0 || vlan > maxValidVLANIDValue)
-            {
-                return IPMI_CC_INVALID_FIELD_REQUEST;
-            }
-            channelConf->vlanID = vlan;
+            copyInto(mac, bytes);
+            channelCall<setMACProperty>(channel, mac);
+            return responseSuccess();
         }
-        break;
-
-        case LanParam::INPROGRESS:
+        case LanParam::SubnetMask:
         {
-            if (reqptr->data[0] == SET_COMPLETE)
+            in_addr netmask;
+            std::array<uint8_t, sizeof(netmask)> bytes;
+            if (req.unpack(bytes) != 0 || !req.fullyUnpacked())
             {
-                channelConf->lan_set_in_progress = SET_COMPLETE;
-
-                log<level::INFO>(
-                    "Network data from Cache",
-                    entry("PREFIX=%s", channelConf->netmask.c_str()),
-                    entry("ADDRESS=%s", channelConf->ipaddr.c_str()),
-                    entry("GATEWAY=%s", channelConf->gateway.c_str()),
-                    entry("VLAN=%d", channelConf->vlanID));
+                return responseReqDataLenInvalid();
             }
-            else if (reqptr->data[0] == SET_IN_PROGRESS) // Set In Progress
-            {
-                channelConf->lan_set_in_progress = SET_IN_PROGRESS;
-            }
+            copyInto(netmask, bytes);
+            channelCall<reconfigureIfAddr4>(channel, std::nullopt,
+                                            netmaskToPrefix(netmask));
+            return responseSuccess();
         }
-        break;
-
-        default:
+        case LanParam::Gateway1:
         {
-            rc = IPMI_CC_PARM_NOT_SUPPORTED;
-            return rc;
+            in_addr gateway;
+            std::array<uint8_t, sizeof(gateway)> bytes;
+            if (req.unpack(bytes) != 0 || !req.fullyUnpacked())
+            {
+                return responseReqDataLenInvalid();
+            }
+            copyInto(gateway, bytes);
+            channelCall<setGatewayProperty<AF_INET>>(channel, gateway);
+            return responseSuccess();
+        }
+        case LanParam::VLANId:
+        {
+            uint16_t vlanData;
+            if (req.unpack(vlanData) != 0 || !req.fullyUnpacked())
+            {
+                return responseReqDataLenInvalid();
+            }
+            if ((vlanData & VLAN_ENABLE_FLAG) == 0)
+            {
+                lastDisabledVlan[channel] = vlanData & VLAN_VALUE_MASK;
+                vlanData = 0;
+            }
+            channelCall<reconfigureVLAN>(channel, vlanData & VLAN_VALUE_MASK);
+            return responseSuccess();
+        }
+        case LanParam::CiphersuiteSupport:
+        case LanParam::CiphersuiteEntries:
+        {
+            req.trailingOk = true;
+            return response(ccParamReadOnly);
         }
     }
-    rc = checkAndUpdateNetwork(channel);
 
-    return rc;
+    req.trailingOk = true;
+    return response(ccParamNotSupported);
 }
 
-struct get_lan_t
+RspType<message::Payload> getLan(uint4_t channelBits, uint3_t, bool revOnly,
+                                 uint8_t parameter, uint8_t set, uint8_t block)
 {
-    uint8_t rev_channel;
-    uint8_t parameter;
-    uint8_t parameter_set;
-    uint8_t parameter_block;
-} __attribute__((packed));
+    message::Payload ret;
+    constexpr uint8_t current_revision = 0x11;
+    ret.pack(current_revision);
 
-ipmi_ret_t ipmi_transport_get_lan(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
-                                  ipmi_request_t request,
-                                  ipmi_response_t response,
-                                  ipmi_data_len_t data_len,
-                                  ipmi_context_t context)
-{
-    ipmi_ret_t rc = IPMI_CC_OK;
-    *data_len = 0;
-    const uint8_t current_revision = 0x11; // Current rev per IPMI Spec 2.0
-
-    get_lan_t* reqptr = (get_lan_t*)request;
-    // channel number is the lower nibble
-    int channel = reqptr->rev_channel & CHANNEL_MASK;
-    ipmi::ChannelInfo chInfo;
-    ipmi::getChannelInfo(channel, chInfo);
-    if (chInfo.mediumType !=
-        static_cast<uint8_t>(ipmi::EChannelMediumType::lan8032))
+    if (revOnly)
     {
-        return IPMI_CC_INVALID_FIELD_REQUEST;
+        return responseSuccess(std::move(ret));
     }
 
-    if (reqptr->rev_channel & 0x80) // Revision is bit 7
+    auto channel = static_cast<uint8_t>(channelBits);
+    if (!doesDeviceExist(channel))
     {
-        // Only current revision was requested
-        *data_len = sizeof(current_revision);
-        std::memcpy(response, &current_revision, *data_len);
-        return IPMI_CC_OK;
+        return responseInvalidFieldRequest();
     }
 
-    static std::vector<uint8_t> cipherList;
-    static auto listInit = false;
-
-    if (!listInit)
+    switch (static_cast<LanParam>(parameter))
     {
-        try
+        case LanParam::SetStatus:
         {
-            cipherList = cipher::getCipherList();
-            listInit = true;
+            SetStatus status;
+            try
+            {
+                status = setStatus.at(channel);
+            }
+            catch (const std::out_of_range&)
+            {
+                status = SetStatus::Complete;
+            }
+            ret.pack(static_cast<uint2_t>(status), uint6_t{});
+            return responseSuccess(std::move(ret));
         }
-        catch (const std::exception& e)
+        case LanParam::AuthSupport:
         {
-            return IPMI_CC_UNSPECIFIED_ERROR;
+            std::bitset<6> support;
+            ret.pack(support, uint2_t{});
+            return responseSuccess(std::move(ret));
         }
-    }
-
-    auto ethdevice = ipmi::getChannelName(channel);
-    if (ethdevice.empty())
-    {
-        return IPMI_CC_INVALID_FIELD_REQUEST;
-    }
-    auto channelConf = getChannelConfig(channel);
-
-    LanParam param = static_cast<LanParam>(reqptr->parameter);
-    switch (param)
-    {
-        case LanParam::INPROGRESS:
+        case LanParam::AuthEnables:
         {
-            uint8_t buf[] = {current_revision,
-                             channelConf->lan_set_in_progress};
-            *data_len = sizeof(buf);
-            std::memcpy(response, &buf, *data_len);
-            break;
-        }
-        case LanParam::AUTHSUPPORT:
-        {
-            uint8_t buf[] = {current_revision, 0x04};
-            *data_len = sizeof(buf);
-            std::memcpy(response, &buf, *data_len);
-            break;
-        }
-        case LanParam::AUTHENABLES:
-        {
-            uint8_t buf[] = {current_revision, 0x04, 0x04, 0x04, 0x04, 0x04};
-            *data_len = sizeof(buf);
-            std::memcpy(response, &buf, *data_len);
-            break;
+            std::bitset<6> enables;
+            ret.pack(enables, uint2_t{}); // Callback
+            ret.pack(enables, uint2_t{}); // User
+            ret.pack(enables, uint2_t{}); // Operator
+            ret.pack(enables, uint2_t{}); // Admin
+            ret.pack(enables, uint2_t{}); // OEM
+            return responseSuccess(std::move(ret));
         }
         case LanParam::IP:
-        case LanParam::SUBNET:
-        case LanParam::GATEWAY:
+        {
+            auto ifaddr = channelCall<getIfAddr4>(channel);
+            in_addr addr{};
+            if (ifaddr)
+            {
+                addr = ifaddr->address;
+            }
+            ret.pack(dataRef(addr));
+            return responseSuccess(std::move(ret));
+        }
+        case LanParam::IPSrc:
+        {
+            auto src = IPSrc::Static;
+            if (channelCall<getDHCPProperty>(channel))
+            {
+                src = IPSrc::DHCP;
+            }
+            ret.pack(static_cast<uint4_t>(src), uint4_t{});
+            return responseSuccess(std::move(ret));
+        }
         case LanParam::MAC:
         {
-            uint8_t buf[ipmi::network::MAC_ADDRESS_SIZE_BYTE + 1] = {};
-
-            *data_len = sizeof(current_revision);
-            std::memcpy(buf, &current_revision, *data_len);
-
-            if (getNetworkData(reqptr->parameter, &buf[1], channel) ==
-                IPMI_CC_OK)
+            ether_addr mac = channelCall<getMACProperty>(channel);
+            ret.pack(dataRef(mac));
+            return responseSuccess(std::move(ret));
+        }
+        case LanParam::SubnetMask:
+        {
+            auto ifaddr = channelCall<getIfAddr4>(channel);
+            uint8_t prefix = AddrFamily<AF_INET>::defaultPrefix;
+            if (ifaddr)
             {
-                if (param == LanParam::MAC)
-                {
-                    *data_len = sizeof(buf);
-                }
-                else
-                {
-                    *data_len = ipmi::network::IPV4_ADDRESS_SIZE_BYTE + 1;
-                }
-                std::memcpy(response, &buf, *data_len);
+                prefix = ifaddr->prefix;
+            }
+            in_addr netmask = prefixToNetmask(prefix);
+            ret.pack(dataRef(netmask));
+            return responseSuccess(std::move(ret));
+        }
+        case LanParam::Gateway1:
+        {
+            auto gateway =
+                channelCall<getGatewayProperty<AF_INET>>(channel).value_or(
+                    in_addr{});
+            ret.pack(dataRef(gateway));
+            return responseSuccess(std::move(ret));
+        }
+        case LanParam::VLANId:
+        {
+            uint16_t vlan = channelCall<getVLANProperty>(channel);
+            if (vlan != 0)
+            {
+                vlan |= VLAN_ENABLE_FLAG;
             }
             else
             {
-                rc = IPMI_CC_UNSPECIFIED_ERROR;
+                vlan = lastDisabledVlan[channel];
             }
-            break;
+            ret.pack(vlan);
+            return responseSuccess(std::move(ret));
         }
-        case LanParam::VLAN:
-        {
-            uint8_t buf[ipmi::network::VLAN_SIZE_BYTE + 1] = {};
-
-            *data_len = sizeof(current_revision);
-            std::memcpy(buf, &current_revision, *data_len);
-            if (getNetworkData(reqptr->parameter, &buf[1], channel) ==
-                IPMI_CC_OK)
-            {
-                *data_len = sizeof(buf);
-                std::memcpy(response, &buf, *data_len);
-            }
-            break;
-        }
-        case LanParam::IPSRC:
-        {
-            uint8_t buff[ipmi::network::IPSRC_SIZE_BYTE + 1] = {};
-            *data_len = sizeof(current_revision);
-            std::memcpy(buff, &current_revision, *data_len);
-            if (getNetworkData(reqptr->parameter, &buff[1], channel) ==
-                IPMI_CC_OK)
-            {
-                *data_len = sizeof(buff);
-                std::memcpy(response, &buff, *data_len);
-            }
-            break;
-        }
-        case LanParam::CIPHER_SUITE_COUNT:
-        {
-            *(static_cast<uint8_t*>(response)) = current_revision;
-            // Byte 1 is reserved byte and does not indicate a cipher suite ID,
-            // so no of cipher suite entry count is one less than the size of
-            // the vector
-            auto count = static_cast<uint8_t>(cipherList.size() - 1);
-            *(static_cast<uint8_t*>(response) + 1) = count;
-            *data_len = sizeof(current_revision) + sizeof(count);
-            break;
-        }
-        case LanParam::CIPHER_SUITE_ENTRIES:
-        {
-            *(static_cast<uint8_t*>(response)) = current_revision;
-            // Byte 1 is reserved
-            std::copy_n(cipherList.data(), cipherList.size(),
-                        static_cast<uint8_t*>(response) + 1);
-            *data_len = sizeof(current_revision) +
-                        static_cast<uint8_t>(cipherList.size());
-            break;
-        }
-        default:
-            log<level::ERR>("Unsupported parameter",
-                            entry("PARAMETER=0x%x", reqptr->parameter));
-            rc = IPMI_CC_PARM_NOT_SUPPORTED;
+        case LanParam::CiphersuiteSupport:
+        case LanParam::CiphersuiteEntries:
+            return response(ccParamNotSupported);
     }
 
-    return rc;
+    return response(ccParamNotSupported);
 }
 
-void applyChanges(int channel)
-{
-    std::string ipaddress;
-    std::string gateway;
-    uint8_t prefix{};
-    uint32_t vlanID{};
-    std::string networkInterfacePath;
-    ipmi::DbusObjectInfo ipObject;
-    ipmi::DbusObjectInfo systemObject;
+} // namespace transport
+} // namespace ipmi
 
-    auto ethdevice = ipmi::getChannelName(channel);
-    if (ethdevice.empty())
-    {
-        log<level::ERR>("Unable to get the interface name",
-                        entry("CHANNEL=%d", channel));
-        return;
-    }
-    auto ethIp = ethdevice + "/" + ipmi::network::IP_TYPE;
-    auto channelConf = getChannelConfig(channel);
-
-    try
-    {
-        sdbusplus::bus::bus bus(ipmid_get_sd_bus_connection());
-
-        log<level::INFO>("Network data from Cache",
-                         entry("PREFIX=%s", channelConf->netmask.c_str()),
-                         entry("ADDRESS=%s", channelConf->ipaddr.c_str()),
-                         entry("GATEWAY=%s", channelConf->gateway.c_str()),
-                         entry("VLAN=%d", channelConf->vlanID),
-                         entry("IPSRC=%d", channelConf->ipsrc));
-        if (channelConf->vlanID != ipmi::network::VLAN_ID_MASK)
-        {
-            // get the first twelve bits which is vlan id
-            // not interested in rest of the bits.
-            channelConf->vlanID = le32toh(channelConf->vlanID);
-            vlanID = channelConf->vlanID & ipmi::network::VLAN_ID_MASK;
-        }
-
-        // if the asked ip src is DHCP then not interested in
-        // any given data except vlan.
-        if (channelConf->ipsrc != ipmi::network::IPOrigin::DHCP)
-        {
-            // always get the system object
-            systemObject =
-                ipmi::getDbusObject(bus, ipmi::network::SYSTEMCONFIG_INTERFACE,
-                                    ipmi::network::ROOT);
-
-            // the below code is to determine the mode of the interface
-            // as the handling is same, if the system is configured with
-            // DHCP or user has given all the data.
-            try
-            {
-                ipmi::ObjectTree ancestorMap;
-
-                ipmi::InterfaceList interfaces{
-                    ipmi::network::ETHERNET_INTERFACE};
-
-                // if the system is having ip object,then
-                // get the IP object.
-                ipObject = ipmi::getIPObject(bus, ipmi::network::IP_INTERFACE,
-                                             ipmi::network::ROOT, ethIp);
-
-                // Get the parent interface of the IP object.
-                try
-                {
-                    ancestorMap = ipmi::getAllAncestors(bus, ipObject.first,
-                                                        std::move(interfaces));
-                }
-                catch (InternalFailure& e)
-                {
-                    // if unable to get the parent interface
-                    // then commit the error and return.
-                    log<level::ERR>("Unable to get the parent interface",
-                                    entry("PATH=%s", ipObject.first.c_str()),
-                                    entry("INTERFACE=%s",
-                                          ipmi::network::ETHERNET_INTERFACE));
-                    commit<InternalFailure>();
-                    channelConf->clear();
-                    return;
-                }
-
-                networkInterfacePath = ancestorMap.begin()->first;
-            }
-            catch (InternalFailure& e)
-            {
-                // TODO Currently IPMI supports single interface,need to handle
-                // Multiple interface through
-                // https://github.com/openbmc/openbmc/issues/2138
-
-                // if there is no ip configured on the system,then
-                // get the network interface object.
-                auto networkInterfaceObject =
-                    ipmi::getDbusObject(bus, ipmi::network::ETHERNET_INTERFACE,
-                                        ipmi::network::ROOT, ethdevice);
-
-                networkInterfacePath = std::move(networkInterfaceObject.first);
-            }
-
-            // get the configured mode on the system.
-            auto enableDHCP = std::get<bool>(ipmi::getDbusProperty(
-                bus, ipmi::network::SERVICE, networkInterfacePath,
-                ipmi::network::ETHERNET_INTERFACE, "DHCPEnabled"));
-
-            // if ip address source is not given then get the ip source mode
-            // from the system so that it can be applied later.
-            if (channelConf->ipsrc == ipmi::network::IPOrigin::UNSPECIFIED)
-            {
-                channelConf->ipsrc = (enableDHCP)
-                                         ? ipmi::network::IPOrigin::DHCP
-                                         : ipmi::network::IPOrigin::STATIC;
-            }
-
-            // check whether user has given all the data
-            // or the configured system interface is dhcp enabled,
-            // in both of the cases get the values from the cache.
-            if ((!channelConf->ipaddr.empty() &&
-                 !channelConf->netmask.empty() &&
-                 !channelConf->gateway.empty()) ||
-                (enableDHCP)) // configured system interface mode = DHCP
-            {
-                // convert mask into prefix
-                ipaddress = channelConf->ipaddr;
-                prefix = ipmi::network::toPrefix(AF_INET, channelConf->netmask);
-                gateway = channelConf->gateway;
-            }
-            else // asked ip src = static and configured system src = static
-                 // or partially given data.
-            {
-                // We have partial filled cache so get the remaining
-                // info from the system.
-
-                // Get the network data from the system as user has
-                // not given all the data then use the data fetched from the
-                // system but it is implementation dependent,IPMI spec doesn't
-                // force it.
-
-                // if system is not having any ip object don't throw error,
-                try
-                {
-                    auto properties = ipmi::getAllDbusProperties(
-                        bus, ipObject.second, ipObject.first,
-                        ipmi::network::IP_INTERFACE);
-
-                    ipaddress =
-                        channelConf->ipaddr.empty()
-                            ? std::get<std::string>(properties["Address"])
-                            : channelConf->ipaddr;
-
-                    prefix = channelConf->netmask.empty()
-                                 ? std::get<uint8_t>(properties["PrefixLength"])
-                                 : ipmi::network::toPrefix(
-                                       AF_INET, channelConf->netmask);
-                }
-                catch (InternalFailure& e)
-                {
-                    log<level::INFO>(
-                        "Failed to get IP object which matches",
-                        entry("INTERFACE=%s", ipmi::network::IP_INTERFACE),
-                        entry("MATCH=%s", ethIp.c_str()));
-                }
-
-                auto systemProperties = ipmi::getAllDbusProperties(
-                    bus, systemObject.second, systemObject.first,
-                    ipmi::network::SYSTEMCONFIG_INTERFACE);
-
-                gateway = channelConf->gateway.empty()
-                              ? std::get<std::string>(
-                                    systemProperties["DefaultGateway"])
-                              : channelConf->gateway;
-            }
-        }
-
-        // Currently network manager doesn't support purging of all the
-        // ip addresses and the vlan interfaces from the parent interface,
-        // TODO once the support is there, will make the change here.
-        // https://github.com/openbmc/openbmc/issues/2141.
-
-        // TODO Currently IPMI supports single interface,need to handle
-        // Multiple interface through
-        // https://github.com/openbmc/openbmc/issues/2138
-
-        // instead of deleting all the vlan interfaces and
-        // all the ipv4 address,we will call reset method.
-        // delete all the vlan interfaces
-
-        ipmi::deleteAllDbusObjects(bus, ipmi::network::ROOT,
-                                   ipmi::network::VLAN_INTERFACE);
-
-        // set the interface mode  to static
-        auto networkInterfaceObject =
-            ipmi::getDbusObject(bus, ipmi::network::ETHERNET_INTERFACE,
-                                ipmi::network::ROOT, ethdevice);
-
-        // setting the physical interface mode to static.
-        ipmi::setDbusProperty(
-            bus, ipmi::network::SERVICE, networkInterfaceObject.first,
-            ipmi::network::ETHERNET_INTERFACE, "DHCPEnabled", false);
-
-        networkInterfacePath = networkInterfaceObject.first;
-
-        // delete all the ipv4 addresses
-        ipmi::deleteAllDbusObjects(bus, ipmi::network::ROOT,
-                                   ipmi::network::IP_INTERFACE, ethIp);
-
-        if (vlanID)
-        {
-            ipmi::network::createVLAN(bus, ipmi::network::SERVICE,
-                                      ipmi::network::ROOT, ethdevice, vlanID);
-
-            auto networkInterfaceObject = ipmi::getDbusObject(
-                bus, ipmi::network::VLAN_INTERFACE, ipmi::network::ROOT);
-
-            networkInterfacePath = networkInterfaceObject.first;
-        }
-
-        if (channelConf->ipsrc == ipmi::network::IPOrigin::DHCP)
-        {
-            ipmi::setDbusProperty(
-                bus, ipmi::network::SERVICE, networkInterfacePath,
-                ipmi::network::ETHERNET_INTERFACE, "DHCPEnabled", true);
-        }
-        else
-        {
-            // change the mode to static
-            ipmi::setDbusProperty(
-                bus, ipmi::network::SERVICE, networkInterfacePath,
-                ipmi::network::ETHERNET_INTERFACE, "DHCPEnabled", false);
-
-            if (!ipaddress.empty())
-            {
-                ipmi::network::createIP(bus, ipmi::network::SERVICE,
-                                        networkInterfacePath, ipv4Protocol,
-                                        ipaddress, prefix);
-            }
-
-            if (!gateway.empty())
-            {
-                ipmi::setDbusProperty(bus, systemObject.second,
-                                      systemObject.first,
-                                      ipmi::network::SYSTEMCONFIG_INTERFACE,
-                                      "DefaultGateway", std::string(gateway));
-            }
-        }
-    }
-    catch (sdbusplus::exception::exception& e)
-    {
-        log<level::ERR>(
-            "Failed to set network data", entry("PREFIX=%d", prefix),
-            entry("ADDRESS=%s", ipaddress.c_str()),
-            entry("GATEWAY=%s", gateway.c_str()), entry("VLANID=%d", vlanID),
-            entry("IPSRC=%d", channelConf->ipsrc));
-
-        commit<InternalFailure>();
-    }
-
-    channelConf->clear();
-}
-
-void commitNetworkChanges()
-{
-    for (const auto& channel : channelConfig)
-    {
-        if (channel.second->flush)
-        {
-            applyChanges(channel.first);
-        }
-    }
-}
-
-void createNetworkTimer()
-{
-    if (!networkTimer)
-    {
-        std::function<void()> networkTimerCallback(
-            std::bind(&commitNetworkChanges));
-
-        networkTimer = std::make_unique<phosphor::Timer>(networkTimerCallback);
-    }
-}
+void register_netfn_transport_functions() __attribute__((constructor));
 
 void register_netfn_transport_functions()
 {
-    // As this timer is only for transport handler
-    // so creating it here.
-    createNetworkTimer();
-    // <Wildcard Command>
-    ipmi_register_callback(NETFUN_TRANSPORT, IPMI_CMD_WILDCARD, NULL,
-                           ipmi_transport_wildcard, PRIVILEGE_USER);
-
-    // <Set LAN Configuration Parameters>
-    ipmi_register_callback(NETFUN_TRANSPORT, IPMI_CMD_SET_LAN, NULL,
-                           ipmi_transport_set_lan, PRIVILEGE_ADMIN);
-
-    // <Get LAN Configuration Parameters>
-    ipmi_register_callback(NETFUN_TRANSPORT, IPMI_CMD_GET_LAN, NULL,
-                           ipmi_transport_get_lan, PRIVILEGE_OPERATOR);
-
-    return;
+    ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnTransport,
+                          ipmi::transport::cmdSetLanConfigParameters,
+                          ipmi::Privilege::Admin, ipmi::transport::setLan);
+    ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnTransport,
+                          ipmi::transport::cmdGetLanConfigParameters,
+                          ipmi::Privilege::Admin, ipmi::transport::getLan);
 }