Cleanups to ethernet schemas

Several rounds of cleanup to the ethernet interfaces.  Changes involved:
1. Move schemas to std::shared_ptr for handling responses
2. Remove the use of the "providers" interface.  As there is currently
only one usage, the providers interface doesn't give us any flexibility.
3 Move the ethernet interfaces to flat_set, to allow better sorting
and lookup times.
4. Fix a number of "use after free" bugs with lambda callbacks that
were passing by reference instead of by value.

Change-Id: I3f8ffb10702563b25fda88155f1929b8d8e38bf5
Signed-off-by: Ed Tanous <ed.tanous@intel.com>
diff --git a/redfish-core/lib/ethernet.hpp b/redfish-core/lib/ethernet.hpp
index 85af636..da4df20 100644
--- a/redfish-core/lib/ethernet.hpp
+++ b/redfish-core/lib/ethernet.hpp
@@ -16,6 +16,8 @@
 #pragma once
 
 #include <boost/container/flat_map.hpp>
+#include <boost/container/flat_set.hpp>
+#include <boost/optional.hpp>
 #include <dbus_singleton.hpp>
 #include <error_messages.hpp>
 #include <node.hpp>
@@ -33,38 +35,37 @@
     sdbusplus::message::variant<std::string, bool, uint8_t, int16_t, uint16_t,
                                 int32_t, uint32_t, int64_t, uint64_t, double>>;
 
-using GetManagedObjectsType = boost::container::flat_map<
+using GetManagedObjects = std::vector<std::pair<
     sdbusplus::message::object_path,
-    boost::container::flat_map<
+    std::vector<std::pair<
         std::string,
         boost::container::flat_map<
             std::string, sdbusplus::message::variant<
                              std::string, bool, uint8_t, int16_t, uint16_t,
-                             int32_t, uint32_t, int64_t, uint64_t, double>>>>;
+                             int32_t, uint32_t, int64_t, uint64_t, double>>>>>>;
+
+enum class LinkType
+{
+    Local,
+    Global
+};
 
 /**
  * Structure for keeping IPv4 data required by Redfish
- * TODO(Pawel) consider change everything to ptr, or to non-ptr values.
  */
 struct IPv4AddressData
 {
     std::string id;
-    const std::string *address;
-    const std::string *domain;
-    const std::string *gateway;
+    std::string address;
+    std::string domain;
+    std::string gateway;
     std::string netmask;
     std::string origin;
-    bool global;
-    /**
-     * @brief Operator< to enable sorting
-     *
-     * @param[in] obj   Object to compare with
-     *
-     * @return This object id < supplied object id
-     */
+    LinkType linktype;
+
     bool operator<(const IPv4AddressData &obj) const
     {
-        return (id < obj.id);
+        return id < obj.id;
     }
 };
 
@@ -74,771 +75,695 @@
  */
 struct EthernetInterfaceData
 {
-    const unsigned int *speed;
-    const bool *autoNeg;
-    const std::string *hostname;
-    const std::string *defaultGateway;
-    const std::string *macAddress;
-    const unsigned int *vlanId;
+    uint32_t speed;
+    bool auto_neg;
+    std::string hostname;
+    std::string default_gateway;
+    std::string mac_address;
+    boost::optional<uint32_t> vlan_id;
 };
 
-/**
- * OnDemandEthernetProvider
- * Ethernet provider class that retrieves data directly from dbus, before
- * setting it into JSON output. This does not cache any data.
- *
- * TODO(Pawel)
- * This perhaps shall be different file, which has to be chosen on compile time
- * depending on OEM needs
- */
-class OnDemandEthernetProvider
+// Helper function that changes bits netmask notation (i.e. /24)
+// into full dot notation
+inline std::string getNetmask(unsigned int bits)
 {
-  private:
-    // Consts that may have influence on EthernetProvider performance/memory
-    // usage
-    const size_t maxIpV4AddressesPerInterface = 10;
+    uint32_t value = 0xffffffff << (32 - bits);
+    std::string netmask = std::to_string((value >> 24) & 0xff) + "." +
+                          std::to_string((value >> 16) & 0xff) + "." +
+                          std::to_string((value >> 8) & 0xff) + "." +
+                          std::to_string(value & 0xff);
+    return netmask;
+}
 
-    // Helper function that allows to extract GetAllPropertiesType from
-    // GetManagedObjectsType, based on object path, and interface name
-    const PropertiesMapType *extractInterfaceProperties(
-        const sdbusplus::message::object_path &objpath,
-        const std::string &interface, const GetManagedObjectsType &dbus_data)
+inline std::string
+    translateAddressOriginDbusToRedfish(const std::string &inputOrigin,
+                                        bool isIPv4)
+{
+    if (inputOrigin == "xyz.openbmc_project.Network.IP.AddressOrigin.Static")
     {
-        const auto &dbusObj = dbus_data.find(objpath);
-        if (dbusObj != dbus_data.end())
+        return "Static";
+    }
+    if (inputOrigin == "xyz.openbmc_project.Network.IP.AddressOrigin.LinkLocal")
+    {
+        if (isIPv4)
         {
-            const auto &iface = dbusObj->second.find(interface);
-            if (iface != dbusObj->second.end())
+            return "IPv4LinkLocal";
+        }
+        else
+        {
+            return "LinkLocal";
+        }
+    }
+    if (inputOrigin == "xyz.openbmc_project.Network.IP.AddressOrigin.DHCP")
+    {
+        if (isIPv4)
+        {
+            return "DHCP";
+        }
+        else
+        {
+            return "DHCPv6";
+        }
+    }
+    if (inputOrigin == "xyz.openbmc_project.Network.IP.AddressOrigin.SLAAC")
+    {
+        return "SLAAC";
+    }
+    return "";
+}
+
+inline std::string
+    translateAddressOriginRedfishToDbus(const std::string &inputOrigin)
+{
+    if (inputOrigin == "Static")
+    {
+        return "xyz.openbmc_project.Network.IP.AddressOrigin.Static";
+    }
+    if (inputOrigin == "DHCP" || inputOrigin == "DHCPv6")
+    {
+        return "xyz.openbmc_project.Network.IP.AddressOrigin.DHCP";
+    }
+    if (inputOrigin == "IPv4LinkLocal" || inputOrigin == "LinkLocal")
+    {
+        return "xyz.openbmc_project.Network.IP.AddressOrigin.LinkLocal";
+    }
+    if (inputOrigin == "SLAAC")
+    {
+        return "xyz.openbmc_project.Network.IP.AddressOrigin.SLAAC";
+    }
+    return "";
+}
+
+inline void extractEthernetInterfaceData(const std::string &ethiface_id,
+                                         const GetManagedObjects &dbus_data,
+                                         EthernetInterfaceData &ethData)
+{
+    for (const auto &objpath : dbus_data)
+    {
+        if (objpath.first == "/xyz/openbmc_project/network/" + ethiface_id)
+        {
+            for (const auto &ifacePair : objpath.second)
             {
-                return &iface->second;
-            }
-        }
-        return nullptr;
-    }
-
-    // Helper Wrapper that does inline object_path conversion from string
-    // into sdbusplus::message::object_path type
-    inline const PropertiesMapType *
-        extractInterfaceProperties(const std::string &objpath,
-                                   const std::string &interface,
-                                   const GetManagedObjectsType &dbus_data)
-    {
-        const auto &dbusObj = sdbusplus::message::object_path{objpath};
-        return extractInterfaceProperties(dbusObj, interface, dbus_data);
-    }
-
-    // Helper function that allows to get pointer to the property from
-    // GetAllPropertiesType native, or extracted by GetAllPropertiesType
-    template <typename T>
-    inline T const *const extractProperty(const PropertiesMapType &properties,
-                                          const std::string &name)
-    {
-        const auto &property = properties.find(name);
-        if (property != properties.end())
-        {
-            return mapbox::getPtr<const T>(property->second);
-        }
-        return nullptr;
-    }
-    // TODO(Pawel) Consider to move the above functions to dbus
-    // generic_interfaces.hpp
-
-    // Helper function that extracts data from several dbus objects and several
-    // interfaces required by single ethernet interface instance
-    void extractEthernetInterfaceData(const std::string &ethifaceId,
-                                      const GetManagedObjectsType &dbus_data,
-                                      EthernetInterfaceData &eth_data)
-    {
-        // Extract data that contains MAC Address
-        const PropertiesMapType *macProperties = extractInterfaceProperties(
-            "/xyz/openbmc_project/network/" + ethifaceId,
-            "xyz.openbmc_project.Network.MACAddress", dbus_data);
-
-        if (macProperties != nullptr)
-        {
-            eth_data.macAddress =
-                extractProperty<std::string>(*macProperties, "MACAddress");
-        }
-
-        const PropertiesMapType *vlanProperties = extractInterfaceProperties(
-            "/xyz/openbmc_project/network/" + ethifaceId,
-            "xyz.openbmc_project.Network.VLAN", dbus_data);
-
-        if (vlanProperties != nullptr)
-        {
-            eth_data.vlanId =
-                extractProperty<unsigned int>(*vlanProperties, "Id");
-        }
-
-        // Extract data that contains link information (auto negotiation and
-        // speed)
-        const PropertiesMapType *ethProperties = extractInterfaceProperties(
-            "/xyz/openbmc_project/network/" + ethifaceId,
-            "xyz.openbmc_project.Network.EthernetInterface", dbus_data);
-
-        if (ethProperties != nullptr)
-        {
-            eth_data.autoNeg = extractProperty<bool>(*ethProperties, "AutoNeg");
-            eth_data.speed =
-                extractProperty<unsigned int>(*ethProperties, "Speed");
-        }
-
-        // Extract data that contains network config (HostName and DefaultGW)
-        const PropertiesMapType *configProperties = extractInterfaceProperties(
-            "/xyz/openbmc_project/network/config",
-            "xyz.openbmc_project.Network.SystemConfiguration", dbus_data);
-
-        if (configProperties != nullptr)
-        {
-            eth_data.hostname =
-                extractProperty<std::string>(*configProperties, "HostName");
-            eth_data.defaultGateway = extractProperty<std::string>(
-                *configProperties, "DefaultGateway");
-        }
-    }
-
-    // Helper function that changes bits netmask notation (i.e. /24)
-    // into full dot notation
-    inline std::string getNetmask(unsigned int bits)
-    {
-        uint32_t value = 0xffffffff << (32 - bits);
-        std::string netmask = std::to_string((value >> 24) & 0xff) + "." +
-                              std::to_string((value >> 16) & 0xff) + "." +
-                              std::to_string((value >> 8) & 0xff) + "." +
-                              std::to_string(value & 0xff);
-        return netmask;
-    }
-
-    // Helper function that extracts data for single ethernet ipv4 address
-    void extractIPv4Data(const std::string &ethifaceId,
-                         const GetManagedObjectsType &dbus_data,
-                         std::vector<IPv4AddressData> &ipv4_config)
-    {
-        const std::string pathStart =
-            "/xyz/openbmc_project/network/" + ethifaceId + "/ipv4/";
-
-        // Since there might be several IPv4 configurations aligned with
-        // single ethernet interface, loop over all of them
-        for (auto &objpath : dbus_data)
-        {
-            // Check if proper patter for object path appears
-            if (boost::starts_with(
-                    static_cast<const std::string &>(objpath.first), pathStart))
-            {
-                // and get approrpiate interface
-                const auto &interface =
-                    objpath.second.find("xyz.openbmc_project.Network.IP");
-                if (interface != objpath.second.end())
+                if (ifacePair.first == "xyz.openbmc_project.Network.MACAddress")
                 {
-                    // Make a properties 'shortcut', to make everything more
-                    // readable
-                    const PropertiesMapType &properties = interface->second;
-                    // Instance IPv4AddressData structure, and set as
-                    // appropriate
-                    IPv4AddressData ipv4Address;
-
-                    ipv4Address.id =
-                        static_cast<const std::string &>(objpath.first)
-                            .substr(pathStart.size());
-
-                    // IPv4 address
-                    ipv4Address.address =
-                        extractProperty<std::string>(properties, "Address");
-                    // IPv4 gateway
-                    ipv4Address.gateway =
-                        extractProperty<std::string>(properties, "Gateway");
-
-                    // Origin is kind of DBus object so fetch pointer...
-                    const std::string *origin =
-                        extractProperty<std::string>(properties, "Origin");
-                    if (origin != nullptr)
+                    for (const auto &propertyPair : ifacePair.second)
                     {
-                        ipv4Address.origin =
-                            translateAddressOriginBetweenDBusAndRedfish(
-                                origin, true, true);
-                    }
-
-                    // Netmask is presented as PrefixLength
-                    const auto *mask =
-                        extractProperty<uint8_t>(properties, "PrefixLength");
-                    if (mask != nullptr)
-                    {
-                        // convert it to the string
-                        ipv4Address.netmask = getNetmask(*mask);
-                    }
-
-                    // Attach IPv4 only if address is present
-                    if (ipv4Address.address != nullptr)
-                    {
-                        // Check if given address is local, or global
-                        if (boost::starts_with(*ipv4Address.address, "169.254"))
+                        if (propertyPair.first == "MACAddress")
                         {
-                            ipv4Address.global = false;
+                            const std::string *mac =
+                                mapbox::getPtr<const std::string>(
+                                    propertyPair.second);
+                            if (mac != nullptr)
+                            {
+                                ethData.mac_address = *mac;
+                            }
                         }
-                        else
+                    }
+                }
+                else if (ifacePair.first == "xyz.openbmc_project.Network.VLAN")
+                {
+                    for (const auto &propertyPair : ifacePair.second)
+                    {
+                        if (propertyPair.first == "Id")
                         {
-                            ipv4Address.global = true;
+                            const uint32_t *id = mapbox::getPtr<const uint32_t>(
+                                propertyPair.second);
+                            if (id != nullptr)
+                            {
+                                ethData.vlan_id = *id;
+                            }
                         }
-                        ipv4_config.emplace_back(std::move(ipv4Address));
+                    }
+                }
+                else if (ifacePair.first ==
+                         "xyz.openbmc_project.Network.EthernetInterface")
+                {
+                    for (const auto &propertyPair : ifacePair.second)
+                    {
+                        if (propertyPair.first == "AutoNeg")
+                        {
+                            const bool *auto_neg =
+                                mapbox::getPtr<const bool>(propertyPair.second);
+                            if (auto_neg != nullptr)
+                            {
+                                ethData.auto_neg = *auto_neg;
+                            }
+                        }
+                        else if (propertyPair.first == "Speed")
+                        {
+                            const uint32_t *speed =
+                                mapbox::getPtr<const uint32_t>(
+                                    propertyPair.second);
+                            if (speed != nullptr)
+                            {
+                                ethData.speed = *speed;
+                            }
+                        }
+                    }
+                }
+                else if (ifacePair.first ==
+                         "xyz.openbmc_project.Network.SystemConfiguration")
+                {
+                    for (const auto &propertyPair : ifacePair.second)
+                    {
+                        if (propertyPair.first == "HostName")
+                        {
+                            const std::string *hostname =
+                                mapbox::getPtr<const std::string>(
+                                    propertyPair.second);
+                            if (hostname != nullptr)
+                            {
+                                ethData.hostname = *hostname;
+                            }
+                        }
+                        else if (propertyPair.first == "DefaultGateway")
+                        {
+                            const std::string *defaultGateway =
+                                mapbox::getPtr<const std::string>(
+                                    propertyPair.second);
+                            if (defaultGateway != nullptr)
+                            {
+                                ethData.default_gateway = *defaultGateway;
+                            }
+                        }
                     }
                 }
             }
         }
-
-        /**
-         * We have to sort this vector and ensure that order of IPv4 addresses
-         * is consistent between GETs to allow modification and deletion in
-         * PATCHes
-         */
-        std::sort(ipv4_config.begin(), ipv4_config.end());
     }
+}
+
+// Helper function that extracts data for single ethernet ipv4 address
+inline void
+    extractIPData(const std::string &ethiface_id,
+                  const GetManagedObjects &dbus_data,
+                  boost::container::flat_set<IPv4AddressData> &ipv4_config)
+{
+    const std::string ipv4PathStart =
+        "/xyz/openbmc_project/network/" + ethiface_id + "/ipv4/";
+
+    // Since there might be several IPv4 configurations aligned with
+    // single ethernet interface, loop over all of them
+    for (const auto &objpath : dbus_data)
+    {
+        // Check if proper pattern for object path appears
+        if (boost::starts_with(objpath.first.str, ipv4PathStart))
+        {
+            for (auto &interface : objpath.second)
+            {
+                if (interface.first == "xyz.openbmc_project.Network.IP")
+                {
+                    // Instance IPv4AddressData structure, and set as
+                    // appropriate
+                    std::pair<
+                        boost::container::flat_set<IPv4AddressData>::iterator,
+                        bool>
+                        it = ipv4_config.insert(
+                            {objpath.first.str.substr(ipv4PathStart.size())});
+                    IPv4AddressData &ipv4_address = *it.first;
+                    for (auto &property : interface.second)
+                    {
+                        if (property.first == "Address")
+                        {
+                            const std::string *address =
+                                mapbox::getPtr<const std::string>(
+                                    property.second);
+                            if (address != nullptr)
+                            {
+                                ipv4_address.address = *address;
+                            }
+                        }
+                        else if (property.first == "Gateway")
+                        {
+                            const std::string *gateway =
+                                mapbox::getPtr<const std::string>(
+                                    property.second);
+                            if (gateway != nullptr)
+                            {
+                                ipv4_address.gateway = *gateway;
+                            }
+                        }
+                        else if (property.first == "Origin")
+                        {
+                            const std::string *origin =
+                                mapbox::getPtr<const std::string>(
+                                    property.second);
+                            if (origin != nullptr)
+                            {
+                                ipv4_address.origin =
+                                    translateAddressOriginDbusToRedfish(*origin,
+                                                                        true);
+                            }
+                        }
+                        else if (property.first == "PrefixLength")
+                        {
+                            const uint8_t *mask =
+                                mapbox::getPtr<uint8_t>(property.second);
+                            if (mask != nullptr)
+                            {
+                                // convert it to the string
+                                ipv4_address.netmask = getNetmask(*mask);
+                            }
+                        }
+                        else
+                        {
+                            BMCWEB_LOG_ERROR
+                                << "Got extra property: " << property.first
+                                << " on the " << objpath.first.str << " object";
+                        }
+                    }
+                    // Check if given address is local, or global
+                    ipv4_address.linktype =
+                        boost::starts_with(ipv4_address.address, "169.254.")
+                            ? LinkType::Global
+                            : LinkType::Local;
+                }
+            }
+        }
+    }
+}
+
+/**
+ * @brief Sets given Id on the given VLAN interface through D-Bus
+ *
+ * @param[in] ifaceId       Id of VLAN interface that should be modified
+ * @param[in] inputVlanId   New ID of the VLAN
+ * @param[in] callback      Function that will be called after the operation
+ *
+ * @return None.
+ */
+template <typename CallbackFunc>
+void changeVlanId(const std::string &ifaceId, const uint32_t &inputVlanId,
+                  CallbackFunc &&callback)
+{
+    crow::connections::systemBus->async_method_call(
+        callback, "xyz.openbmc_project.Network",
+        std::string("/xyz/openbmc_project/network/") + ifaceId,
+        "org.freedesktop.DBus.Properties", "Set",
+        "xyz.openbmc_project.Network.VLAN", "Id",
+        sdbusplus::message::variant<uint32_t>(inputVlanId));
+}
+
+/**
+ * @brief Helper function that verifies IP address to check if it is in
+ *        proper format. If bits pointer is provided, also calculates active
+ *        bit count for Subnet Mask.
+ *
+ * @param[in]  ip     IP that will be verified
+ * @param[out] bits   Calculated mask in bits notation
+ *
+ * @return true in case of success, false otherwise
+ */
+inline bool ipv4VerifyIpAndGetBitcount(const std::string &ip,
+                                       uint8_t *bits = nullptr)
+{
+    std::vector<std::string> bytesInMask;
+
+    boost::split(bytesInMask, ip, boost::is_any_of("."));
 
     static const constexpr int ipV4AddressSectionsCount = 4;
-
-  public:
-    /**
-     * @brief Creates VLAN for given interface with given Id through D-Bus
-     *
-     * @param[in] ifaceId       Id of interface for which VLAN will be created
-     * @param[in] inputVlanId   ID of the new VLAN
-     * @param[in] callback      Function that will be called after the operation
-     *
-     * @return None.
-     */
-    template <typename CallbackFunc>
-    void createVlan(const std::string &ifaceId, const uint64_t &inputVlanId,
-                    CallbackFunc &&callback)
+    if (bytesInMask.size() != ipV4AddressSectionsCount)
     {
-        crow::connections::systemBus->async_method_call(
-            callback, "xyz.openbmc_project.Network",
-            "/xyz/openbmc_project/network",
-            "xyz.openbmc_project.Network.VLAN.Create", "VLAN", ifaceId,
-            static_cast<uint32_t>(inputVlanId));
-    };
+        return false;
+    }
 
-    /**
-     * @brief Sets given Id on the given VLAN interface through D-Bus
-     *
-     * @param[in] ifaceId       Id of VLAN interface that should be modified
-     * @param[in] inputVlanId   New ID of the VLAN
-     * @param[in] callback      Function that will be called after the operation
-     *
-     * @return None.
-     */
-    template <typename CallbackFunc>
-    static void changeVlanId(const std::string &ifaceId,
-                             const uint32_t &inputVlanId,
-                             CallbackFunc &&callback)
+    if (bits != nullptr)
     {
-        crow::connections::systemBus->async_method_call(
-            callback, "xyz.openbmc_project.Network",
-            std::string("/xyz/openbmc_project/network/") + ifaceId,
-            "org.freedesktop.DBus.Properties", "Set",
-            "xyz.openbmc_project.Network.VLAN", "Id",
-            sdbusplus::message::variant<uint32_t>(inputVlanId));
-    };
+        *bits = 0;
+    }
 
-    /**
-     * @brief Helper function that verifies IP address to check if it is in
-     *        proper format. If bits pointer is provided, also calculates active
-     *        bit count for Subnet Mask.
-     *
-     * @param[in]  ip     IP that will be verified
-     * @param[out] bits   Calculated mask in bits notation
-     *
-     * @return true in case of success, false otherwise
-     */
-    bool ipv4VerifyIpAndGetBitcount(const std::string &ip,
-                                    uint8_t *bits = nullptr)
+    char *endPtr;
+    long previousValue = 255;
+    bool firstZeroInByteHit;
+    for (const std::string &byte : bytesInMask)
     {
-        std::vector<std::string> bytesInMask;
+        if (byte.empty())
+        {
+            return false;
+        }
 
-        boost::split(bytesInMask, ip, boost::is_any_of("."));
+        // Use strtol instead of stroi to avoid exceptions
+        long value = std::strtol(byte.c_str(), &endPtr, 10);
 
-        if (bytesInMask.size() != ipV4AddressSectionsCount)
+        // endPtr should point to the end of the string, otherwise given string
+        // is not 100% number
+        if (*endPtr != '\0')
+        {
+            return false;
+        }
+
+        // Value should be contained in byte
+        if (value < 0 || value > 255)
         {
             return false;
         }
 
         if (bits != nullptr)
         {
-            *bits = 0;
-        }
-
-        char *endPtr;
-        long previousValue = 255;
-        bool firstZeroInByteHit;
-
-        for (const std::string &byte : bytesInMask)
-        {
-            if (byte.empty())
+            // Mask has to be continuous between bytes
+            if (previousValue != 255 && value != 0)
             {
                 return false;
             }
 
-            // Use strtol instead of stroi to avoid exceptions
-            long value = std::strtol(byte.c_str(), &endPtr, 10);
+            // Mask has to be continuous inside bytes
+            firstZeroInByteHit = false;
 
-            // endPtr should point to the end of the string, otherwise given
-            // string is not 100% number
-            if (*endPtr != '\0')
+            // Count bits
+            for (int bitIdx = 7; bitIdx >= 0; bitIdx--)
             {
-                return false;
-            }
-
-            // Value should be contained in byte
-            if (value < 0 || value > 255)
-            {
-                return false;
-            }
-
-            if (bits != nullptr)
-            {
-                // Mask has to be continuous between bytes
-                if (previousValue != 255 && value != 0)
+                if (value & (1 << bitIdx))
                 {
-                    return false;
-                }
-
-                // Mask has to be continuous inside bytes
-                firstZeroInByteHit = false;
-
-                // Count bits
-                for (int bitIdx = 7; bitIdx >= 0; bitIdx--)
-                {
-                    if (value & (1 << bitIdx))
+                    if (firstZeroInByteHit)
                     {
-                        if (firstZeroInByteHit)
-                        {
-                            // Continuity not preserved
-                            return false;
-                        }
-                        else
-                        {
-                            (*bits)++;
-                        }
+                        // Continuity not preserved
+                        return false;
                     }
                     else
                     {
-                        firstZeroInByteHit = true;
+                        (*bits)++;
                     }
                 }
-            }
-
-            previousValue = value;
-        }
-
-        return true;
-    }
-
-    /**
-     * @brief Changes IPv4 address type property (Address, Gateway)
-     *
-     * @param[in] ifaceId     Id of interface whose IP should be modified
-     * @param[in] ipIdx       index of IP in input array that should be modified
-     * @param[in] ipHash      DBus Hash id of modified IP
-     * @param[in] name        Name of field in JSON representation
-     * @param[in] newValue    New value that should be written
-     * @param[io] asyncResp   Response object that will be returned to client
-     *
-     * @return true if give IP is valid and has been sent do D-Bus, false
-     * otherwise
-     */
-    void changeIPv4AddressProperty(const std::string &ifaceId, int ipIdx,
-                                   const std::string &ipHash,
-                                   const std::string &name,
-                                   const std::string &newValue,
-                                   const std::shared_ptr<AsyncResp> &asyncResp)
-    {
-        auto callback = [asyncResp, ipIdx{std::move(ipIdx)},
-                         name{std::move(name)}, newValue{std::move(newValue)}](
-                            const boost::system::error_code ec) {
-            if (ec)
-            {
-                messages::addMessageToJson(
-                    asyncResp->res.jsonValue, messages::internalError(),
-                    "/IPv4Addresses/" + std::to_string(ipIdx) + "/" + name);
-            }
-            else
-            {
-                asyncResp->res.jsonValue["IPv4Addresses"][ipIdx][name] =
-                    newValue;
-            }
-        };
-
-        crow::connections::systemBus->async_method_call(
-            std::move(callback), "xyz.openbmc_project.Network",
-            "/xyz/openbmc_project/network/" + ifaceId + "/ipv4/" + ipHash,
-            "org.freedesktop.DBus.Properties", "Set",
-            "xyz.openbmc_project.Network.IP", name,
-            sdbusplus::message::variant<std::string>(newValue));
-    };
-
-    /**
-     * @brief Changes IPv4 address origin property
-     *
-     * @param[in] ifaceId       Id of interface whose IP should be modified
-     * @param[in] ipIdx         index of IP in input array that should be
-     * modified
-     * @param[in] ipHash        DBus Hash id of modified IP
-     * @param[in] newValue      New value in Redfish format
-     * @param[in] newValueDbus  New value in D-Bus format
-     * @param[io] asyncResp     Response object that will be returned to client
-     *
-     * @return true if give IP is valid and has been sent do D-Bus, false
-     * otherwise
-     */
-    void changeIPv4Origin(const std::string &ifaceId, int ipIdx,
-                          const std::string &ipHash,
-                          const std::string &newValue,
-                          const std::string &newValueDbus,
-                          const std::shared_ptr<AsyncResp> &asyncResp)
-    {
-        auto callback = [asyncResp, ipIdx{std::move(ipIdx)},
-                         newValue{std::move(newValue)}](
-                            const boost::system::error_code ec) {
-            if (ec)
-            {
-                messages::addMessageToJson(
-                    asyncResp->res.jsonValue, messages::internalError(),
-                    "/IPv4Addresses/" + std::to_string(ipIdx) +
-                        "/AddressOrigin");
-            }
-            else
-            {
-                asyncResp->res
-                    .jsonValue["IPv4Addresses"][ipIdx]["AddressOrigin"] =
-                    newValue;
-            }
-        };
-
-        crow::connections::systemBus->async_method_call(
-            std::move(callback), "xyz.openbmc_project.Network",
-            "/xyz/openbmc_project/network/" + ifaceId + "/ipv4/" + ipHash,
-            "org.freedesktop.DBus.Properties", "Set",
-            "xyz.openbmc_project.Network.IP", "Origin",
-            sdbusplus::message::variant<std::string>(newValueDbus));
-    };
-
-    /**
-     * @brief Modifies SubnetMask for given IP
-     *
-     * @param[in] ifaceId      Id of interface whose IP should be modified
-     * @param[in] ipIdx        index of IP in input array that should be
-     * modified
-     * @param[in] ipHash       DBus Hash id of modified IP
-     * @param[in] newValueStr  Mask in dot notation as string
-     * @param[in] newValue     Mask as PrefixLength in bitcount
-     * @param[io] asyncResp   Response object that will be returned to client
-     *
-     * @return None
-     */
-    void changeIPv4SubnetMaskProperty(
-        const std::string &ifaceId, int ipIdx, const std::string &ipHash,
-        const std::string &newValueStr, uint8_t &newValue,
-        const std::shared_ptr<AsyncResp> &asyncResp)
-    {
-        auto callback = [asyncResp, ipIdx{std::move(ipIdx)},
-                         newValueStr{std::move(newValueStr)}](
-                            const boost::system::error_code ec) {
-            if (ec)
-            {
-                messages::addMessageToJson(
-                    asyncResp->res.jsonValue, messages::internalError(),
-                    "/IPv4Addresses/" + std::to_string(ipIdx) + "/SubnetMask");
-            }
-            else
-            {
-                asyncResp->res.jsonValue["IPv4Addresses"][ipIdx]["SubnetMask"] =
-                    newValueStr;
-            }
-        };
-
-        crow::connections::systemBus->async_method_call(
-            std::move(callback), "xyz.openbmc_project.Network",
-            "/xyz/openbmc_project/network/" + ifaceId + "/ipv4/" + ipHash,
-            "org.freedesktop.DBus.Properties", "Set",
-            "xyz.openbmc_project.Network.IP", "PrefixLength",
-            sdbusplus::message::variant<uint8_t>(newValue));
-    };
-
-    /**
-     * @brief Disables VLAN with given ifaceId
-     *
-     * @param[in] ifaceId   Id of VLAN interface that should be disabled
-     * @param[in] callback  Function that will be called after the operation
-     *
-     * @return None.
-     */
-    template <typename CallbackFunc>
-    static void disableVlan(const std::string &ifaceId, CallbackFunc &&callback)
-    {
-        crow::connections::systemBus->async_method_call(
-            callback, "xyz.openbmc_project.Network",
-            std::string("/xyz/openbmc_project/network/") + ifaceId,
-            "xyz.openbmc_project.Object.Delete", "Delete");
-    };
-
-    /**
-     * @brief Sets given HostName of the machine through D-Bus
-     *
-     * @param[in] newHostname   New name that HostName will be changed to
-     * @param[in] callback      Function that will be called after the operation
-     *
-     * @return None.
-     */
-    template <typename CallbackFunc>
-    void setHostName(const std::string &newHostname, CallbackFunc &&callback)
-    {
-        crow::connections::systemBus->async_method_call(
-            callback, "xyz.openbmc_project.Network",
-            "/xyz/openbmc_project/network/config",
-            "org.freedesktop.DBus.Properties", "Set",
-            "xyz.openbmc_project.Network.SystemConfiguration", "HostName",
-            sdbusplus::message::variant<std::string>(newHostname));
-    };
-
-    /**
-     * @brief Deletes given IPv4
-     *
-     * @param[in] ifaceId     Id of interface whose IP should be deleted
-     * @param[in] ipIdx       index of IP in input array that should be deleted
-     * @param[in] ipHash      DBus Hash id of IP that should be deleted
-     * @param[io] asyncResp   Response object that will be returned to client
-     *
-     * @return None
-     */
-    void deleteIPv4(const std::string &ifaceId, const std::string &ipHash,
-                    unsigned int ipIdx,
-                    const std::shared_ptr<AsyncResp> &asyncResp)
-    {
-        crow::connections::systemBus->async_method_call(
-            [ipIdx{std::move(ipIdx)}, asyncResp{std::move(asyncResp)}](
-                const boost::system::error_code ec) {
-                if (ec)
-                {
-                    messages::addMessageToJson(
-                        asyncResp->res.jsonValue, messages::internalError(),
-                        "/IPv4Addresses/" + std::to_string(ipIdx) + "/");
-                }
                 else
                 {
-                    asyncResp->res.jsonValue["IPv4Addresses"][ipIdx] = nullptr;
+                    firstZeroInByteHit = true;
                 }
-            },
-            "xyz.openbmc_project.Network",
-            "/xyz/openbmc_project/network/" + ifaceId + "/ipv4/" + ipHash,
-            "xyz.openbmc_project.Object.Delete", "Delete");
+            }
+        }
+
+        previousValue = value;
     }
 
-    /**
-     * @brief Creates IPv4 with given data
-     *
-     * @param[in] ifaceId     Id of interface whose IP should be deleted
-     * @param[in] ipIdx       index of IP in input array that should be deleted
-     * @param[in] ipHash      DBus Hash id of IP that should be deleted
-     * @param[io] asyncResp   Response object that will be returned to client
-     *
-     * @return None
-     */
-    void createIPv4(const std::string &ifaceId, unsigned int ipIdx,
-                    uint8_t subnetMask, const std::string &gateway,
-                    const std::string &address,
-                    const std::shared_ptr<AsyncResp> &asyncResp)
-    {
-        auto createIpHandler = [ipIdx{std::move(ipIdx)},
-                                asyncResp{std::move(asyncResp)}](
-                                   const boost::system::error_code ec) {
+    return true;
+}
+
+/**
+ * @brief Changes IPv4 address type property (Address, Gateway)
+ *
+ * @param[in] ifaceId     Id of interface whose IP should be modified
+ * @param[in] ipIdx       Index of IP in input array that should be modified
+ * @param[in] ipHash      DBus Hash id of modified IP
+ * @param[in] name        Name of field in JSON representation
+ * @param[in] newValue    New value that should be written
+ * @param[io] asyncResp   Response object that will be returned to client
+ *
+ * @return true if give IP is valid and has been sent do D-Bus, false
+ * otherwise
+ */
+inline void changeIPv4AddressProperty(
+    const std::string &ifaceId, int ipIdx, const std::string &ipHash,
+    const std::string &name, const std::string &newValue,
+    const std::shared_ptr<AsyncResp> asyncResp)
+{
+    auto callback = [asyncResp, ipIdx, name{std::string(name)},
+                     newValue{std::move(newValue)}](
+                        const boost::system::error_code ec) {
+        if (ec)
+        {
+            messages::addMessageToJson(
+                asyncResp->res.jsonValue, messages::internalError(),
+                "/IPv4Addresses/" + std::to_string(ipIdx) + "/" + name);
+        }
+        else
+        {
+            asyncResp->res.jsonValue["IPv4Addresses"][ipIdx][name] = newValue;
+        }
+    };
+
+    crow::connections::systemBus->async_method_call(
+        std::move(callback), "xyz.openbmc_project.Network",
+        "/xyz/openbmc_project/network/" + ifaceId + "/ipv4/" + ipHash,
+        "org.freedesktop.DBus.Properties", "Set",
+        "xyz.openbmc_project.Network.IP", name,
+        sdbusplus::message::variant<std::string>(newValue));
+}
+
+/**
+ * @brief Changes IPv4 address origin property
+ *
+ * @param[in] ifaceId       Id of interface whose IP should be modified
+ * @param[in] ipIdx         Index of IP in input array that should be
+ * modified
+ * @param[in] ipHash        DBus Hash id of modified IP
+ * @param[in] newValue      New value in Redfish format
+ * @param[in] newValueDbus  New value in D-Bus format
+ * @param[io] asyncResp     Response object that will be returned to client
+ *
+ * @return true if give IP is valid and has been sent do D-Bus, false
+ * otherwise
+ */
+inline void changeIPv4Origin(const std::string &ifaceId, int ipIdx,
+                             const std::string &ipHash,
+                             const std::string &newValue,
+                             const std::string &newValueDbus,
+                             const std::shared_ptr<AsyncResp> asyncResp)
+{
+    auto callback = [asyncResp, ipIdx, newValue{std::move(newValue)}](
+                        const boost::system::error_code ec) {
+        if (ec)
+        {
+            messages::addMessageToJson(
+                asyncResp->res.jsonValue, messages::internalError(),
+                "/IPv4Addresses/" + std::to_string(ipIdx) + "/AddressOrigin");
+        }
+        else
+        {
+            asyncResp->res.jsonValue["IPv4Addresses"][ipIdx]["AddressOrigin"] =
+                newValue;
+        }
+    };
+
+    crow::connections::systemBus->async_method_call(
+        std::move(callback), "xyz.openbmc_project.Network",
+        "/xyz/openbmc_project/network/" + ifaceId + "/ipv4/" + ipHash,
+        "org.freedesktop.DBus.Properties", "Set",
+        "xyz.openbmc_project.Network.IP", "Origin",
+        sdbusplus::message::variant<std::string>(newValueDbus));
+}
+
+/**
+ * @brief Modifies SubnetMask for given IP
+ *
+ * @param[in] ifaceId      Id of interface whose IP should be modified
+ * @param[in] ipIdx        Index of IP in input array that should be
+ * modified
+ * @param[in] ipHash       DBus Hash id of modified IP
+ * @param[in] newValueStr  Mask in dot notation as string
+ * @param[in] newValue     Mask as PrefixLength in bitcount
+ * @param[io] asyncResp   Response object that will be returned to client
+ *
+ * @return None
+ */
+inline void changeIPv4SubnetMaskProperty(const std::string &ifaceId, int ipIdx,
+                                         const std::string &ipHash,
+                                         const std::string &newValueStr,
+                                         uint8_t &newValue,
+                                         std::shared_ptr<AsyncResp> asyncResp)
+{
+    auto callback = [asyncResp, ipIdx, newValueStr{std::move(newValueStr)}](
+                        const boost::system::error_code ec) {
+        if (ec)
+        {
+            messages::addMessageToJson(
+                asyncResp->res.jsonValue, messages::internalError(),
+                "/IPv4Addresses/" + std::to_string(ipIdx) + "/SubnetMask");
+        }
+        else
+        {
+            asyncResp->res.jsonValue["IPv4Addresses"][ipIdx]["SubnetMask"] =
+                newValueStr;
+        }
+    };
+
+    crow::connections::systemBus->async_method_call(
+        std::move(callback), "xyz.openbmc_project.Network",
+        "/xyz/openbmc_project/network/" + ifaceId + "/ipv4/" + ipHash,
+        "org.freedesktop.DBus.Properties", "Set",
+        "xyz.openbmc_project.Network.IP", "PrefixLength",
+        sdbusplus::message::variant<uint8_t>(newValue));
+}
+
+/**
+ * @brief Sets given HostName of the machine through D-Bus
+ *
+ * @param[in] newHostname   New name that HostName will be changed to
+ * @param[in] callback      Function that will be called after the operation
+ *
+ * @return None.
+ */
+template <typename CallbackFunc>
+void setHostName(const std::string &newHostname, CallbackFunc &&callback)
+{
+    crow::connections::systemBus->async_method_call(
+        callback, "xyz.openbmc_project.Network",
+        "/xyz/openbmc_project/network/config",
+        "org.freedesktop.DBus.Properties", "Set",
+        "xyz.openbmc_project.Network.SystemConfiguration", "HostName",
+        sdbusplus::message::variant<std::string>(newHostname));
+}
+
+/**
+ * @brief Deletes given IPv4
+ *
+ * @param[in] ifaceId     Id of interface whose IP should be deleted
+ * @param[in] ipIdx       Index of IP in input array that should be deleted
+ * @param[in] ipHash      DBus Hash id of IP that should be deleted
+ * @param[io] asyncResp   Response object that will be returned to client
+ *
+ * @return None
+ */
+inline void deleteIPv4(const std::string &ifaceId, const std::string &ipHash,
+                       unsigned int ipIdx,
+                       const std::shared_ptr<AsyncResp> asyncResp)
+{
+    crow::connections::systemBus->async_method_call(
+        [ipIdx, asyncResp](const boost::system::error_code ec) {
             if (ec)
             {
                 messages::addMessageToJson(
                     asyncResp->res.jsonValue, messages::internalError(),
                     "/IPv4Addresses/" + std::to_string(ipIdx) + "/");
             }
-        };
+            else
+            {
+                asyncResp->res.jsonValue["IPv4Addresses"][ipIdx] = nullptr;
+            }
+        },
+        "xyz.openbmc_project.Network",
+        "/xyz/openbmc_project/network/" + ifaceId + "/ipv4/" + ipHash,
+        "xyz.openbmc_project.Object.Delete", "Delete");
+}
 
-        crow::connections::systemBus->async_method_call(
-            std::move(createIpHandler), "xyz.openbmc_project.Network",
-            "/xyz/openbmc_project/network/" + ifaceId,
-            "xyz.openbmc_project.Network.IP.Create", "IP",
-            "xyz.openbmc_project.Network.IP.Protocol.IPv4", address, subnetMask,
-            gateway);
-    }
-
-    /**
-     * @brief Translates Address Origin value from D-Bus to Redfish format and
-     *        vice-versa
-     *
-     * @param[in] inputOrigin Input value that should be translated
-     * @param[in] isIPv4      True for IPv4 origins, False for IPv6
-     * @param[in] isFromDBus  True for DBus->Redfish conversion, false for
-     * reverse
-     *
-     * @return Empty string in case of failure, translated value otherwise
-     */
-    std::string translateAddressOriginBetweenDBusAndRedfish(
-        const std::string *inputOrigin, bool isIPv4, bool isFromDBus)
-    {
-        // Invalid pointer
-        if (inputOrigin == nullptr)
+/**
+ * @brief Creates IPv4 with given data
+ *
+ * @param[in] ifaceId     Id of interface whose IP should be deleted
+ * @param[in] ipIdx       Index of IP in input array that should be deleted
+ * @param[in] ipHash      DBus Hash id of IP that should be deleted
+ * @param[io] asyncResp   Response object that will be returned to client
+ *
+ * @return None
+ */
+inline void createIPv4(const std::string &ifaceId, unsigned int ipIdx,
+                       uint8_t subnetMask, const std::string &gateway,
+                       const std::string &address,
+                       std::shared_ptr<AsyncResp> asyncResp)
+{
+    auto createIpHandler = [ipIdx,
+                            asyncResp](const boost::system::error_code ec) {
+        if (ec)
         {
-            return "";
+            messages::addMessageToJson(
+                asyncResp->res.jsonValue, messages::internalError(),
+                "/IPv4Addresses/" + std::to_string(ipIdx) + "/");
         }
-
-        static const constexpr unsigned int firstIPv4OnlyIdx = 1;
-        static const constexpr unsigned int firstIPv6OnlyIdx = 3;
-
-        std::array<std::pair<const char *, const char *>, 6> translationTable{
-            {{"xyz.openbmc_project.Network.IP.AddressOrigin.Static", "Static"},
-             {"xyz.openbmc_project.Network.IP.AddressOrigin.DHCP", "DHCP"},
-             {"xyz.openbmc_project.Network.IP.AddressOrigin.LinkLocal",
-              "IPv4LinkLocal"},
-             {"xyz.openbmc_project.Network.IP.AddressOrigin.DHCP", "DHCPv6"},
-             {"xyz.openbmc_project.Network.IP.AddressOrigin.LinkLocal",
-              "LinkLocal"},
-             {"xyz.openbmc_project.Network.IP.AddressOrigin.SLAAC", "SLAAC"}}};
-
-        for (unsigned int i = 0; i < translationTable.size(); i++)
-        {
-            // Skip unrelated
-            if (isIPv4 && i >= firstIPv6OnlyIdx)
-                break;
-            if (!isIPv4 && i >= firstIPv4OnlyIdx && i < firstIPv6OnlyIdx)
-                continue;
-
-            // When translating D-Bus to Redfish compare input to first element
-            if (isFromDBus && translationTable[i].first == *inputOrigin)
-                return translationTable[i].second;
-
-            // When translating Redfish to D-Bus compare input to second element
-            if (!isFromDBus && translationTable[i].second == *inputOrigin)
-                return translationTable[i].first;
-        }
-
-        // If we are still here, that means that value has not been found
-        return "";
-    }
-
-    /**
-     * Function that retrieves all properties for given Ethernet Interface
-     * Object
-     * from EntityManager Network Manager
-     * @param ethifaceId a eth interface id to query on DBus
-     * @param callback a function that shall be called to convert Dbus output
-     * into JSON
-     */
-    template <typename CallbackFunc>
-    void getEthernetIfaceData(const std::string &ethifaceId,
-                              CallbackFunc &&callback)
-    {
-        crow::connections::systemBus->async_method_call(
-            [this, ethifaceId{std::move(ethifaceId)},
-             callback{std::move(callback)}](
-                const boost::system::error_code error_code,
-                const GetManagedObjectsType &resp) {
-                EthernetInterfaceData ethData{};
-                std::vector<IPv4AddressData> ipv4Data;
-                ipv4Data.reserve(maxIpV4AddressesPerInterface);
-
-                if (error_code)
-                {
-                    // Something wrong on DBus, the error_code is not important
-                    // at this moment, just return success=false, and empty
-                    // output. Since size of vector may vary depending on
-                    // information from Network Manager, and empty output could
-                    // not be treated same way as error.
-                    callback(false, ethData, ipv4Data);
-                    return;
-                }
-
-                // Find interface
-                if (resp.find("/xyz/openbmc_project/network/" + ethifaceId) ==
-                    resp.end())
-                {
-                    // Interface has not been found
-                    callback(false, ethData, ipv4Data);
-                    return;
-                }
-
-                extractEthernetInterfaceData(ethifaceId, resp, ethData);
-                extractIPv4Data(ethifaceId, resp, ipv4Data);
-
-                // Fix global GW
-                for (IPv4AddressData &ipv4 : ipv4Data)
-                {
-                    if ((ipv4.global) && ((ipv4.gateway == nullptr) ||
-                                          (*ipv4.gateway == "0.0.0.0")))
-                    {
-                        ipv4.gateway = ethData.defaultGateway;
-                    }
-                }
-
-                // Finally make a callback with useful data
-                callback(true, ethData, ipv4Data);
-            },
-            "xyz.openbmc_project.Network", "/xyz/openbmc_project/network",
-            "org.freedesktop.DBus.ObjectManager", "GetManagedObjects");
     };
 
-    /**
-     * Function that retrieves all Ethernet Interfaces available through Network
-     * Manager
-     * @param callback a function that shall be called to convert Dbus output
-     * into JSON.
-     */
-    template <typename CallbackFunc>
-    void getEthernetIfaceList(CallbackFunc &&callback)
-    {
-        crow::connections::systemBus->async_method_call(
-            [this, callback{std::move(callback)}](
-                const boost::system::error_code error_code,
-                GetManagedObjectsType &resp) {
-                // Callback requires vector<string> to retrieve all available
-                // ethernet interfaces
-                std::vector<std::string> ifaceList;
-                ifaceList.reserve(resp.size());
-                if (error_code)
-                {
-                    // Something wrong on DBus, the error_code is not important
-                    // at this moment, just return success=false, and empty
-                    // output. Since size of vector may vary depending on
-                    // information from Network Manager, and empty output could
-                    // not be treated same way as error.
-                    callback(false, ifaceList);
-                    return;
-                }
+    crow::connections::systemBus->async_method_call(
+        std::move(createIpHandler), "xyz.openbmc_project.Network",
+        "/xyz/openbmc_project/network/" + ifaceId,
+        "xyz.openbmc_project.Network.IP.Create", "IP",
+        "xyz.openbmc_project.Network.IP.Protocol.IPv4", address, subnetMask,
+        gateway);
+}
 
-                // Iterate over all retrieved ObjectPaths.
-                for (auto &objpath : resp)
+/**
+ * Function that retrieves all properties for given Ethernet Interface
+ * Object
+ * from EntityManager Network Manager
+ * @param ethiface_id a eth interface id to query on DBus
+ * @param callback a function that shall be called to convert Dbus output
+ * into JSON
+ */
+template <typename CallbackFunc>
+void getEthernetIfaceData(const std::string &ethiface_id,
+                          CallbackFunc &&callback)
+{
+    crow::connections::systemBus->async_method_call(
+        [ethiface_id{std::string{ethiface_id}}, callback{std::move(callback)}](
+            const boost::system::error_code error_code,
+            const GetManagedObjects &resp) {
+            EthernetInterfaceData ethData{};
+            boost::container::flat_set<IPv4AddressData> ipv4Data;
+
+            if (error_code)
+            {
+                callback(false, ethData, ipv4Data);
+                return;
+            }
+
+            extractEthernetInterfaceData(ethiface_id, resp, ethData);
+            extractIPData(ethiface_id, resp, ipv4Data);
+
+            // Fix global GW
+            for (IPv4AddressData &ipv4 : ipv4Data)
+            {
+                if ((ipv4.linktype == LinkType::Global) &&
+                    (ipv4.gateway == "0.0.0.0"))
                 {
-                    // And all interfaces available for certain ObjectPath.
-                    for (auto &interface : objpath.second)
+                    ipv4.gateway = ethData.default_gateway;
+                }
+            }
+
+            // Finally make a callback with usefull data
+            callback(true, ethData, ipv4Data);
+        },
+        "xyz.openbmc_project.Network", "/xyz/openbmc_project/network",
+        "org.freedesktop.DBus.ObjectManager", "GetManagedObjects");
+};
+
+/**
+ * Function that retrieves all Ethernet Interfaces available through Network
+ * Manager
+ * @param callback a function that shall be called to convert Dbus output
+ * into JSON.
+ */
+template <typename CallbackFunc>
+void getEthernetIfaceList(CallbackFunc &&callback)
+{
+    crow::connections::systemBus->async_method_call(
+        [callback{std::move(callback)}](
+            const boost::system::error_code error_code,
+            GetManagedObjects &resp) {
+            // Callback requires vector<string> to retrieve all available
+            // ethernet interfaces
+            std::vector<std::string> iface_list;
+            iface_list.reserve(resp.size());
+            if (error_code)
+            {
+                callback(false, iface_list);
+                return;
+            }
+
+            // Iterate over all retrieved ObjectPaths.
+            for (const auto &objpath : resp)
+            {
+                // And all interfaces available for certain ObjectPath.
+                for (const auto &interface : objpath.second)
+                {
+                    // If interface is
+                    // xyz.openbmc_project.Network.EthernetInterface, this is
+                    // what we're looking for.
+                    if (interface.first ==
+                        "xyz.openbmc_project.Network.EthernetInterface")
                     {
-                        // If interface is
-                        // xyz.openbmc_project.Network.EthernetInterface, this
-                        // is what we're looking for.
-                        if (interface.first ==
-                            "xyz.openbmc_project.Network.EthernetInterface")
+                        // Cut out everyting until last "/", ...
+                        const std::string &iface_id = objpath.first.str;
+                        std::size_t last_pos = iface_id.rfind("/");
+                        if (last_pos != std::string::npos)
                         {
-                            // Cut out everything until last "/", ...
-                            const std::string &ifaceId =
-                                static_cast<const std::string &>(objpath.first);
-                            std::size_t lastPos = ifaceId.rfind("/");
-                            if (lastPos != std::string::npos)
-                            {
-                                // and put it into output vector.
-                                ifaceList.emplace_back(
-                                    ifaceId.substr(lastPos + 1));
-                            }
+                            // and put it into output vector.
+                            iface_list.emplace_back(
+                                iface_id.substr(last_pos + 1));
                         }
                     }
                 }
-                // Finally make a callback with useful data
-                callback(true, ifaceList);
-            },
-            "xyz.openbmc_project.Network", "/xyz/openbmc_project/network",
-            "org.freedesktop.DBus.ObjectManager", "GetManagedObjects");
-    };
+            }
+            // Finally make a callback with useful data
+            callback(true, iface_list);
+        },
+        "xyz.openbmc_project.Network", "/xyz/openbmc_project/network",
+        "org.freedesktop.DBus.ObjectManager", "GetManagedObjects");
 };
 
 /**
@@ -847,19 +772,16 @@
 class EthernetCollection : public Node
 {
   public:
-    // TODO(Pawel) Remove line from below, where we assume that there is only
-    // one manager called openbmc This shall be generic, but requires to update
-    // GetSubroutes method
+    template <typename CrowApp>
     EthernetCollection(CrowApp &app) :
-        Node(app, "/redfish/v1/Managers/openbmc/EthernetInterfaces/")
+        Node(app, "/redfish/v1/Managers/bmc/EthernetInterfaces/")
     {
         Node::json["@odata.type"] =
             "#EthernetInterfaceCollection.EthernetInterfaceCollection";
         Node::json["@odata.context"] =
             "/redfish/v1/"
             "$metadata#EthernetInterfaceCollection.EthernetInterfaceCollection";
-        Node::json["@odata.id"] =
-            "/redfish/v1/Managers/openbmc/EthernetInterfaces";
+        Node::json["@odata.id"] = "/redfish/v1/Managers/bmc/EthernetInterfaces";
         Node::json["Name"] = "Ethernet Network Interface Collection";
         Node::json["Description"] =
             "Collection of EthernetInterfaces for this Manager";
@@ -880,45 +802,35 @@
     void doGet(crow::Response &res, const crow::Request &req,
                const std::vector<std::string> &params) override
     {
-        // TODO(Pawel) this shall be parametrized call to get EthernetInterfaces
-        // for any Manager, not only hardcoded 'openbmc'.
-        std::string managerId = "openbmc";
-
-        // get eth interface list, and call the below callback for JSON
+        res.jsonValue = Node::json;
+        // Get eth interface list, and call the below callback for JSON
         // preparation
-        ethernetProvider.getEthernetIfaceList(
-            [&, managerId{std::move(managerId)}](
-                const bool &success,
-                const std::vector<std::string> &iface_list) {
-                if (success)
-                {
-                    nlohmann::json ifaceArray = nlohmann::json::array();
-                    for (const std::string &ifaceItem : iface_list)
-                    {
-                        ifaceArray.push_back(
-                            {{"@odata.id", "/redfish/v1/Managers/" + managerId +
-                                               "/EthernetInterfaces/" +
-                                               ifaceItem}});
-                    }
-                    Node::json["Members"] = ifaceArray;
-                    Node::json["Members@odata.count"] = ifaceArray.size();
-                    Node::json["@odata.id"] = "/redfish/v1/Managers/" +
-                                              managerId + "/EthernetInterfaces";
-                    res.jsonValue = Node::json;
-                }
-                else
-                {
-                    // No success, best what we can do is return INTERNALL ERROR
-                    res.result(
-                        boost::beast::http::status::internal_server_error);
-                }
+        getEthernetIfaceList([&res](
+                                 const bool &success,
+                                 const std::vector<std::string> &iface_list) {
+            if (!success)
+            {
+                res.result(boost::beast::http::status::internal_server_error);
                 res.end();
-            });
-    }
+                return;
+            }
 
-    // Ethernet Provider object
-    // TODO(Pawel) consider move it to singleton
-    OnDemandEthernetProvider ethernetProvider;
+            nlohmann::json &iface_array = res.jsonValue["Members"];
+            iface_array = nlohmann::json::array();
+            for (const std::string &iface_item : iface_list)
+            {
+                iface_array.push_back(
+                    {{"@odata.id",
+                      "/redfish/v1/Managers/bmc/EthernetInterfaces/" +
+                          iface_item}});
+            }
+
+            res.jsonValue["Members@odata.count"] = iface_array.size();
+            res.jsonValue["@odata.id"] =
+                "/redfish/v1/Managers/bmc/EthernetInterfaces";
+            res.end();
+        });
+    }
 };
 
 /**
@@ -930,11 +842,9 @@
     /*
      * Default Constructor
      */
-    // TODO(Pawel) Remove line from below, where we assume that there is only
-    // one manager called openbmc This shall be generic, but requires to update
-    // GetSubroutes method
+    template <typename CrowApp>
     EthernetInterface(CrowApp &app) :
-        Node(app, "/redfish/v1/Managers/openbmc/EthernetInterfaces/<str>/",
+        Node(app, "/redfish/v1/Managers/bmc/EthernetInterfaces/<str>/",
              std::string())
     {
         Node::json["@odata.type"] =
@@ -956,160 +866,141 @@
     // TODO(kkowalsk) Find a suitable class/namespace for this
     static void handleVlanPatch(const std::string &ifaceId,
                                 const nlohmann::json &input,
-                                const EthernetInterfaceData &eth_data,
-                                const std::string &pathPrefix,
-                                const std::shared_ptr<AsyncResp> &asyncResp)
+                                const EthernetInterfaceData &ethData,
+                                const std::shared_ptr<AsyncResp> asyncResp)
     {
         if (!input.is_object())
         {
             messages::addMessageToJson(
                 asyncResp->res.jsonValue,
-                messages::propertyValueTypeError(input.dump(), "VLAN"),
-                pathPrefix);
+                messages::propertyValueTypeError(input.dump(), "VLAN"), "/");
             return;
         }
 
-        const std::string pathStart = (pathPrefix == "/") ? "" : pathPrefix;
-        nlohmann::json &paramsJson =
-            (pathPrefix == "/")
-                ? asyncResp->res.jsonValue
-                : asyncResp->res
-                      .jsonValue[nlohmann::json_pointer<nlohmann::json>(
-                          pathPrefix)];
-        bool inputVlanEnabled;
-        uint64_t inputVlanId;
-
-        json_util::Result inputVlanEnabledState = json_util::getBool(
-            "VLANEnable", input, inputVlanEnabled,
-            static_cast<int>(json_util::MessageSetting::TYPE_ERROR),
-            asyncResp->res.jsonValue, std::string(pathStart + "/VLANEnable"));
-        json_util::Result inputVlanIdState = json_util::getUnsigned(
-            "VLANId", input, inputVlanId,
-            static_cast<int>(json_util::MessageSetting::TYPE_ERROR),
-            asyncResp->res.jsonValue, std::string(pathStart + "/VLANId"));
-        bool inputInvalid = false;
-
-        // Do not proceed if fields in VLAN object were of wrong type
-        if (inputVlanEnabledState == json_util::Result::WRONG_TYPE ||
-            inputVlanIdState == json_util::Result::WRONG_TYPE)
+        nlohmann::json::const_iterator vlanEnable = input.find("VLANEnable");
+        if (vlanEnable == input.end())
         {
+            messages::addMessageToJson(asyncResp->res.jsonValue,
+                                       messages::propertyMissing("VLANEnable"),
+                                       "/VLANEnable");
+            return;
+        }
+        const bool *vlanEnableBool = vlanEnable->get_ptr<const bool *>();
+        if (vlanEnableBool == nullptr)
+        {
+            messages::addMessageToJson(asyncResp->res.jsonValue,
+                                       messages::propertyValueTypeError(
+                                           vlanEnable->dump(), "VLANEnable"),
+                                       "/VLANEnable");
             return;
         }
 
-        // Verify input
-        if (eth_data.vlanId == nullptr)
+        nlohmann::json::const_iterator vlanId = input.find("VLANId");
+        if (vlanId == input.end())
+        {
+            messages::addMessageToJson(asyncResp->res.jsonValue,
+                                       messages::propertyMissing("VLANId"),
+                                       "/VLANId");
+            return;
+        }
+        const uint64_t *vlanIdUint = vlanId->get_ptr<const uint64_t *>();
+        if (vlanIdUint == nullptr)
+        {
+            messages::addMessageToJson(
+                asyncResp->res.jsonValue,
+                messages::propertyValueTypeError(vlanId->dump(), "VLANId"),
+                "/VLANId");
+            return;
+        }
+
+        if (!ethData.vlan_id)
         {
             // This interface is not a VLAN. Cannot do anything with it
             // TODO(kkowalsk) Change this message
-            messages::addMessageToJson(asyncResp->res.jsonValue,
-                                       messages::propertyMissing("VLANEnable"),
-                                       pathPrefix);
+            messages::addMessageToJson(
+                asyncResp->res.jsonValue,
+                messages::propertyNotWritable("VLANEnable"), "/VLANEnable");
 
-            inputInvalid = true;
-        }
-        else
-        {
-            // Load actual data into field values if they were not provided
-            if (inputVlanEnabledState == json_util::Result::NOT_EXIST)
-            {
-                inputVlanEnabled = true;
-            }
-
-            if (inputVlanIdState == json_util::Result::NOT_EXIST)
-            {
-                inputVlanId = *eth_data.vlanId;
-            }
-        }
-
-        // Do not proceed if input has not been valid
-        if (inputInvalid)
-        {
             return;
         }
 
         // VLAN is configured on the interface
-        if (inputVlanEnabled == true && inputVlanId != *eth_data.vlanId)
+        if (*vlanEnableBool == true)
         {
             // Change VLAN Id
-            paramsJson["VLANId"] = inputVlanId;
-            OnDemandEthernetProvider::changeVlanId(
-                ifaceId, static_cast<uint32_t>(inputVlanId),
-                [&, asyncResp, pathPrefx{std::move(pathPrefix)}](
-                    const boost::system::error_code ec) {
-                    if (ec)
-                    {
-                        messages::addMessageToJson(asyncResp->res.jsonValue,
-                                                   messages::internalError(),
-                                                   pathPrefix);
-                    }
-                    else
-                    {
-                        paramsJson["VLANEnable"] = true;
-                    }
-                });
+            asyncResp->res.jsonValue["VLANId"] = *vlanIdUint;
+            auto callback = [asyncResp](const boost::system::error_code ec) {
+                if (ec)
+                {
+                    messages::addMessageToJson(asyncResp->res.jsonValue,
+                                               messages::internalError(), "/");
+                }
+                else
+                {
+                    asyncResp->res.jsonValue["VLANEnable"] = true;
+                }
+            };
+            crow::connections::systemBus->async_method_call(
+                std::move(callback), "xyz.openbmc_project.Network",
+                "/xyz/openbmc_project/network/" + ifaceId,
+                "org.freedesktop.DBus.Properties", "Set",
+                "xyz.openbmc_project.Network.VLAN", "Id",
+                sdbusplus::message::variant<uint32_t>(*vlanIdUint));
         }
-        else if (inputVlanEnabled == false)
+        else
         {
-            // Disable VLAN
-            OnDemandEthernetProvider::disableVlan(
-                ifaceId, [&, asyncResp, pathPrefx{std::move(pathPrefix)}](
-                             const boost::system::error_code ec) {
-                    if (ec)
-                    {
-                        messages::addMessageToJson(asyncResp->res.jsonValue,
-                                                   messages::internalError(),
-                                                   pathPrefix);
-                    }
-                    else
-                    {
-                        paramsJson["VLANEnable"] = false;
-                    }
-                });
+            auto callback = [asyncResp](const boost::system::error_code ec) {
+                if (ec)
+                {
+                    messages::addMessageToJson(asyncResp->res.jsonValue,
+                                               messages::internalError(), "/");
+                    return;
+                }
+                asyncResp->res.jsonValue["VLANEnable"] = false;
+            };
+
+            crow::connections::systemBus->async_method_call(
+                std::move(callback), "xyz.openbmc_project.Network",
+                "/xyz/openbmc_project/network/" + ifaceId,
+                "xyz.openbmc_project.Object.Delete", "Delete");
         }
     }
 
   private:
     void handleHostnamePatch(const nlohmann::json &input,
-                             const EthernetInterfaceData &eth_data,
-                             const std::shared_ptr<AsyncResp> &asyncResp)
+                             const std::shared_ptr<AsyncResp> asyncResp)
     {
-        if (input.is_string())
-        {
-            std::string newHostname = input.get<std::string>();
-
-            if (eth_data.hostname == nullptr ||
-                newHostname != *eth_data.hostname)
-            {
-                // Change hostname
-                ethernetProvider.setHostName(
-                    newHostname, [asyncResp, newHostname](
-                                     const boost::system::error_code ec) {
-                        if (ec)
-                        {
-                            messages::addMessageToJson(
-                                asyncResp->res.jsonValue,
-                                messages::internalError(), "/HostName");
-                        }
-                        else
-                        {
-                            asyncResp->res.jsonValue["HostName"] = newHostname;
-                        }
-                    });
-            }
-        }
-        else
+        const std::string *newHostname = input.get_ptr<const std::string *>();
+        if (newHostname == nullptr)
         {
             messages::addMessageToJson(
                 asyncResp->res.jsonValue,
                 messages::propertyValueTypeError(input.dump(), "HostName"),
                 "/HostName");
+            return;
         }
+
+        // Change hostname
+        setHostName(
+            *newHostname, [asyncResp, newHostname{std::string(*newHostname)}](
+                              const boost::system::error_code ec) {
+                if (ec)
+                {
+                    messages::addMessageToJson(asyncResp->res.jsonValue,
+                                               messages::internalError(),
+                                               "/HostName");
+                }
+                else
+                {
+                    asyncResp->res.jsonValue["HostName"] = newHostname;
+                }
+            });
     }
 
-    void handleIPv4Patch(const std::string &ifaceId,
-                         const nlohmann::json &input,
-                         const std::vector<IPv4AddressData> &ipv4_data,
-                         const std::shared_ptr<AsyncResp> &asyncResp)
+    void handleIPv4Patch(
+        const std::string &ifaceId, const nlohmann::json &input,
+        const boost::container::flat_set<IPv4AddressData> &ipv4Data,
+        const std::shared_ptr<AsyncResp> asyncResp)
     {
         if (!input.is_array())
         {
@@ -1121,7 +1012,7 @@
         }
 
         // According to Redfish PATCH definition, size must be at least equal
-        if (input.size() < ipv4_data.size())
+        if (input.size() < ipv4Data.size())
         {
             // TODO(kkowalsk) This should be a message indicating that not
             // enough data has been provided
@@ -1131,305 +1022,338 @@
             return;
         }
 
-        json_util::Result addressFieldState;
-        json_util::Result subnetMaskFieldState;
-        json_util::Result addressOriginFieldState;
-        json_util::Result gatewayFieldState;
-        const std::string *addressFieldValue;
-        const std::string *subnetMaskFieldValue;
-        const std::string *addressOriginFieldValue = nullptr;
-        const std::string *gatewayFieldValue;
-        uint8_t subnetMaskAsPrefixLength;
-        std::string addressOriginInDBusFormat;
-
-        bool errorDetected = false;
-        for (unsigned int entryIdx = 0; entryIdx < input.size(); entryIdx++)
+        int entryIdx = 0;
+        boost::container::flat_set<IPv4AddressData>::const_iterator thisData =
+            ipv4Data.begin();
+        for (const nlohmann::json &thisJson : input)
         {
+            std::string pathString =
+                "/IPv4Addresses/" + std::to_string(entryIdx);
             // Check that entry is not of some unexpected type
-            if (!input[entryIdx].is_object() && !input[entryIdx].is_null())
+            if (!thisJson.is_object() && !thisJson.is_null())
             {
-                // Invalid object type
-                messages::addMessageToJson(
-                    asyncResp->res.jsonValue,
-                    messages::propertyValueTypeError(input[entryIdx].dump(),
-                                                     "IPv4Address"),
-                    "/IPv4Addresses/" + std::to_string(entryIdx));
+                messages::addMessageToJson(asyncResp->res.jsonValue,
+                                           messages::propertyValueTypeError(
+                                               thisJson.dump(), "IPv4Address"),
+                                           pathString);
 
                 continue;
             }
 
-            // Try to load fields
-            addressFieldState = json_util::getString(
-                "Address", input[entryIdx], addressFieldValue,
-                static_cast<uint8_t>(json_util::MessageSetting::TYPE_ERROR),
-                asyncResp->res.jsonValue,
-                "/IPv4Addresses/" + std::to_string(entryIdx) + "/Address");
-            subnetMaskFieldState = json_util::getString(
-                "SubnetMask", input[entryIdx], subnetMaskFieldValue,
-                static_cast<uint8_t>(json_util::MessageSetting::TYPE_ERROR),
-                asyncResp->res.jsonValue,
-                "/IPv4Addresses/" + std::to_string(entryIdx) + "/SubnetMask");
-            addressOriginFieldState = json_util::getString(
-                "AddressOrigin", input[entryIdx], addressOriginFieldValue,
-                static_cast<uint8_t>(json_util::MessageSetting::TYPE_ERROR),
-                asyncResp->res.jsonValue,
-                "/IPv4Addresses/" + std::to_string(entryIdx) +
-                    "/AddressOrigin");
-            gatewayFieldState = json_util::getString(
-                "Gateway", input[entryIdx], gatewayFieldValue,
-                static_cast<uint8_t>(json_util::MessageSetting::TYPE_ERROR),
-                asyncResp->res.jsonValue,
-                "/IPv4Addresses/" + std::to_string(entryIdx) + "/Gateway");
-
-            if (addressFieldState == json_util::Result::WRONG_TYPE ||
-                subnetMaskFieldState == json_util::Result::WRONG_TYPE ||
-                addressOriginFieldState == json_util::Result::WRONG_TYPE ||
-                gatewayFieldState == json_util::Result::WRONG_TYPE)
+            nlohmann::json::const_iterator addressFieldIt =
+                thisJson.find("Address");
+            const std::string *addressField = nullptr;
+            if (addressFieldIt != thisJson.end())
             {
-                return;
-            }
-
-            if (addressFieldState == json_util::Result::SUCCESS &&
-                !ethernetProvider.ipv4VerifyIpAndGetBitcount(
-                    *addressFieldValue))
-            {
-                errorDetected = true;
-                messages::addMessageToJson(
-                    asyncResp->res.jsonValue,
-                    messages::propertyValueFormatError(*addressFieldValue,
-                                                       "Address"),
-                    "/IPv4Addresses/" + std::to_string(entryIdx) + "/Address");
-            }
-
-            if (subnetMaskFieldState == json_util::Result::SUCCESS &&
-                !ethernetProvider.ipv4VerifyIpAndGetBitcount(
-                    *subnetMaskFieldValue, &subnetMaskAsPrefixLength))
-            {
-                errorDetected = true;
-                messages::addMessageToJson(
-                    asyncResp->res.jsonValue,
-                    messages::propertyValueFormatError(*subnetMaskFieldValue,
-                                                       "SubnetMask"),
-                    "/IPv4Addresses/" + std::to_string(entryIdx) +
-                        "/SubnetMask");
-            }
-
-            // get Address origin in proper format
-            addressOriginInDBusFormat =
-                ethernetProvider.translateAddressOriginBetweenDBusAndRedfish(
-                    addressOriginFieldValue, true, false);
-
-            if (addressOriginFieldState == json_util::Result::SUCCESS &&
-                addressOriginInDBusFormat.empty())
-            {
-                errorDetected = true;
-                messages::addMessageToJson(
-                    asyncResp->res.jsonValue,
-                    messages::propertyValueNotInList(*addressOriginFieldValue,
-                                                     "AddressOrigin"),
-                    "/IPv4Addresses/" + std::to_string(entryIdx) +
-                        "/AddressOrigin");
-            }
-
-            if (gatewayFieldState == json_util::Result::SUCCESS &&
-                !ethernetProvider.ipv4VerifyIpAndGetBitcount(
-                    *gatewayFieldValue))
-            {
-                errorDetected = true;
-                messages::addMessageToJson(
-                    asyncResp->res.jsonValue,
-                    messages::propertyValueFormatError(*gatewayFieldValue,
-                                                       "Gateway"),
-                    "/IPv4Addresses/" + std::to_string(entryIdx) + "/Gateway");
-            }
-
-            // If any error occured do not proceed with current entry, but do
-            // not end loop
-            if (errorDetected)
-            {
-                errorDetected = false;
-                continue;
-            }
-
-            if (entryIdx >= ipv4_data.size())
-            {
-                asyncResp->res.jsonValue["IPv4Addresses"][entryIdx] =
-                    input[entryIdx];
-
-                // Verify that all field were provided
-                if (addressFieldState == json_util::Result::NOT_EXIST)
+                addressField = addressFieldIt->get_ptr<const std::string *>();
+                if (addressField == nullptr)
                 {
-                    errorDetected = true;
                     messages::addMessageToJson(
                         asyncResp->res.jsonValue,
-                        messages::propertyMissing("Address"),
-                        "/IPv4Addresses/" + std::to_string(entryIdx) +
-                            "/Address");
-                }
-
-                if (subnetMaskFieldState == json_util::Result::NOT_EXIST)
-                {
-                    errorDetected = true;
-                    messages::addMessageToJson(
-                        asyncResp->res.jsonValue,
-                        messages::propertyMissing("SubnetMask"),
-                        "/IPv4Addresses/" + std::to_string(entryIdx) +
-                            "/SubnetMask");
-                }
-
-                if (addressOriginFieldState == json_util::Result::NOT_EXIST)
-                {
-                    errorDetected = true;
-                    messages::addMessageToJson(
-                        asyncResp->res.jsonValue,
-                        messages::propertyMissing("AddressOrigin"),
-                        "/IPv4Addresses/" + std::to_string(entryIdx) +
-                            "/AddressOrigin");
-                }
-
-                if (gatewayFieldState == json_util::Result::NOT_EXIST)
-                {
-                    errorDetected = true;
-                    messages::addMessageToJson(
-                        asyncResp->res.jsonValue,
-                        messages::propertyMissing("Gateway"),
-                        "/IPv4Addresses/" + std::to_string(entryIdx) +
-                            "/Gateway");
-                }
-
-                // If any error occured do not proceed with current entry, but
-                // do not end loop
-                if (errorDetected)
-                {
-                    errorDetected = false;
+                        messages::propertyValueFormatError(
+                            addressFieldIt->dump(), "Address"),
+                        pathString + "/Address");
                     continue;
                 }
-
-                // Create IPv4 with provided data
-                ethernetProvider.createIPv4(
-                    ifaceId, entryIdx, subnetMaskAsPrefixLength,
-                    *gatewayFieldValue, *addressFieldValue, asyncResp);
+                else
+                {
+                    if (!ipv4VerifyIpAndGetBitcount(*addressField))
+                    {
+                        messages::addMessageToJson(
+                            asyncResp->res.jsonValue,
+                            messages::propertyValueFormatError(*addressField,
+                                                               "Address"),
+                            pathString + "/Address");
+                        continue;
+                    }
+                }
             }
-            else
+
+            boost::optional<uint8_t> prefixLength;
+            const std::string *subnetField = nullptr;
+            nlohmann::json::const_iterator subnetFieldIt =
+                thisJson.find("SubnetMask");
+            if (subnetFieldIt != thisJson.end())
+            {
+                subnetField = subnetFieldIt->get_ptr<const std::string *>();
+                if (subnetField == nullptr)
+                {
+                    messages::addMessageToJson(
+                        asyncResp->res.jsonValue,
+                        messages::propertyValueFormatError(*subnetField,
+                                                           "SubnetMask"),
+                        pathString + "/SubnetMask");
+                    continue;
+                }
+                else
+                {
+                    prefixLength = 0;
+                    if (!ipv4VerifyIpAndGetBitcount(*subnetField,
+                                                    &*prefixLength))
+                    {
+                        messages::addMessageToJson(
+                            asyncResp->res.jsonValue,
+                            messages::propertyValueFormatError(*subnetField,
+                                                               "SubnetMask"),
+                            pathString + "/SubnetMask");
+                        continue;
+                    }
+                }
+            }
+
+            std::string addressOriginInDBusFormat;
+            const std::string *addressOriginField = nullptr;
+            nlohmann::json::const_iterator addressOriginFieldIt =
+                thisJson.find("AddressOrigin");
+            if (addressOriginFieldIt != thisJson.end())
+            {
+                const std::string *addressOriginField =
+                    addressOriginFieldIt->get_ptr<const std::string *>();
+                if (addressOriginField == nullptr)
+                {
+                    messages::addMessageToJson(
+                        asyncResp->res.jsonValue,
+                        messages::propertyValueFormatError(*addressOriginField,
+                                                           "AddressOrigin"),
+                        pathString + "/AddressOrigin");
+                    continue;
+                }
+                else
+                {
+                    // Get Address origin in proper format
+                    addressOriginInDBusFormat =
+                        translateAddressOriginRedfishToDbus(
+                            *addressOriginField);
+                    if (addressOriginInDBusFormat.empty())
+                    {
+                        messages::addMessageToJson(
+                            asyncResp->res.jsonValue,
+                            messages::propertyValueNotInList(
+                                *addressOriginField, "AddressOrigin"),
+                            pathString + "/AddressOrigin");
+                        continue;
+                    }
+                }
+            }
+
+            nlohmann::json::const_iterator gatewayFieldIt =
+                thisJson.find("Gateway");
+            const std::string *gatewayField = nullptr;
+            if (gatewayFieldIt != thisJson.end())
+            {
+                const std::string *gatewayField =
+                    gatewayFieldIt->get_ptr<const std::string *>();
+                if (gatewayField == nullptr ||
+                    !ipv4VerifyIpAndGetBitcount(*gatewayField))
+                {
+                    messages::addMessageToJson(
+                        asyncResp->res.jsonValue,
+                        messages::propertyValueFormatError(*gatewayField,
+                                                           "Gateway"),
+                        pathString + "/Gateway");
+                    continue;
+                }
+            }
+
+            // if a vlan already exists, modify the existing
+            if (thisData != ipv4Data.end())
             {
                 // Existing object that should be modified/deleted/remain
                 // unchanged
-                if (input[entryIdx].is_null())
+                if (thisJson.is_null())
                 {
-                    // Object should be deleted
-                    ethernetProvider.deleteIPv4(ifaceId, ipv4_data[entryIdx].id,
-                                                entryIdx, asyncResp);
+                    auto callback = [entryIdx{std::to_string(entryIdx)},
+                                     asyncResp](
+                                        const boost::system::error_code ec) {
+                        if (ec)
+                        {
+                            messages::addMessageToJson(
+                                asyncResp->res.jsonValue,
+                                messages::internalError(),
+                                "/IPv4Addresses/" + entryIdx + "/");
+                            return;
+                        }
+                        asyncResp->res.jsonValue["IPv4Addresses"][entryIdx] =
+                            nullptr;
+                    };
+                    crow::connections::systemBus->async_method_call(
+                        std::move(callback), "xyz.openbmc_project.Network",
+                        "/xyz/openbmc_project/network/" + ifaceId + "/ipv4/" +
+                            thisData->id,
+                        "xyz.openbmc_project.Object.Delete", "Delete");
                 }
-                else if (input[entryIdx].is_object())
+                else if (thisJson.is_object())
                 {
-                    if (input[entryIdx].size() == 0)
-                    {
-                        // Object shall remain unchanged
-                        continue;
-                    }
-
                     // Apply changes
-                    if (addressFieldState == json_util::Result::SUCCESS &&
-                        ipv4_data[entryIdx].address != nullptr &&
-                        *ipv4_data[entryIdx].address != *addressFieldValue)
+                    if (addressField != nullptr)
                     {
-                        ethernetProvider.changeIPv4AddressProperty(
-                            ifaceId, entryIdx, ipv4_data[entryIdx].id,
-                            "Address", *addressFieldValue, asyncResp);
+                        auto callback =
+                            [asyncResp, entryIdx,
+                             addressField{std::string(*addressField)}](
+                                const boost::system::error_code ec) {
+                                if (ec)
+                                {
+                                    messages::addMessageToJson(
+                                        asyncResp->res.jsonValue,
+                                        messages::internalError(),
+                                        "/IPv4Addresses/" +
+                                            std::to_string(entryIdx) +
+                                            "/Address");
+                                    return;
+                                }
+                                asyncResp->res
+                                    .jsonValue["IPv4Addresses"][std::to_string(
+                                        entryIdx)]["Address"] = addressField;
+                            };
+
+                        crow::connections::systemBus->async_method_call(
+                            std::move(callback), "xyz.openbmc_project.Network",
+                            "/xyz/openbmc_project/network/" + ifaceId +
+                                "/ipv4/" + thisData->id,
+                            "org.freedesktop.DBus.Properties", "Set",
+                            "xyz.openbmc_project.Network.IP", "Address",
+                            sdbusplus::message::variant<std::string>(
+                                *addressField));
                     }
 
-                    if (subnetMaskFieldState == json_util::Result::SUCCESS &&
-                        ipv4_data[entryIdx].netmask != *subnetMaskFieldValue)
+                    if (prefixLength && subnetField != nullptr)
                     {
-                        ethernetProvider.changeIPv4SubnetMaskProperty(
-                            ifaceId, entryIdx, ipv4_data[entryIdx].id,
-                            *subnetMaskFieldValue, subnetMaskAsPrefixLength,
-                            asyncResp);
+                        changeIPv4SubnetMaskProperty(ifaceId, entryIdx,
+                                                     thisData->id, *subnetField,
+                                                     *prefixLength, asyncResp);
                     }
 
-                    if (addressOriginFieldState == json_util::Result::SUCCESS &&
-                        ipv4_data[entryIdx].origin != *addressFieldValue)
+                    if (!addressOriginInDBusFormat.empty() &&
+                        addressOriginField != nullptr)
                     {
-                        ethernetProvider.changeIPv4Origin(
-                            ifaceId, entryIdx, ipv4_data[entryIdx].id,
-                            *addressOriginFieldValue, addressOriginInDBusFormat,
-                            asyncResp);
+                        changeIPv4Origin(ifaceId, entryIdx, thisData->id,
+                                         *addressOriginField,
+                                         addressOriginInDBusFormat, asyncResp);
                     }
 
-                    if (gatewayFieldState == json_util::Result::SUCCESS &&
-                        ipv4_data[entryIdx].gateway != nullptr &&
-                        *ipv4_data[entryIdx].gateway != *gatewayFieldValue)
+                    if (gatewayField != nullptr)
                     {
-                        ethernetProvider.changeIPv4AddressProperty(
-                            ifaceId, entryIdx, ipv4_data[entryIdx].id,
-                            "Gateway", *gatewayFieldValue, asyncResp);
+                        auto callback =
+                            [asyncResp, entryIdx,
+                             gatewayField{std::string(*gatewayField)}](
+                                const boost::system::error_code ec) {
+                                if (ec)
+                                {
+                                    messages::addMessageToJson(
+                                        asyncResp->res.jsonValue,
+                                        messages::internalError(),
+                                        "/IPv4Addresses/" +
+                                            std::to_string(entryIdx) +
+                                            "/Gateway");
+                                    return;
+                                }
+                                asyncResp->res
+                                    .jsonValue["IPv4Addresses"][std::to_string(
+                                        entryIdx)]["Gateway"] =
+                                    std::move(gatewayField);
+                            };
+
+                        crow::connections::systemBus->async_method_call(
+                            std::move(callback), "xyz.openbmc_project.Network",
+                            "/xyz/openbmc_project/network/" + ifaceId +
+                                "/ipv4/" + thisData->id,
+                            "org.freedesktop.DBus.Properties", "Set",
+                            "xyz.openbmc_project.Network.IP", "Gateway",
+                            sdbusplus::message::variant<std::string>(
+                                *gatewayField));
                     }
                 }
+                thisData++;
             }
+            else
+            {
+                // Create IPv4 with provided data
+                if (gatewayField == nullptr)
+                {
+                    messages::addMessageToJson(
+                        asyncResp->res.jsonValue,
+                        messages::propertyMissing("Gateway"),
+                        pathString + "/Gateway");
+                    continue;
+                }
+
+                if (addressField == nullptr)
+                {
+                    messages::addMessageToJson(
+                        asyncResp->res.jsonValue,
+                        messages::propertyMissing("Address"),
+                        pathString + "/Address");
+                    continue;
+                }
+
+                if (!prefixLength)
+                {
+                    messages::addMessageToJson(
+                        asyncResp->res.jsonValue,
+                        messages::propertyMissing("SubnetMask"),
+                        pathString + "/SubnetMask");
+                    continue;
+                }
+
+                createIPv4(ifaceId, entryIdx, *prefixLength, *gatewayField,
+                           *addressField, asyncResp);
+                asyncResp->res.jsonValue["IPv4Addresses"][entryIdx] = thisJson;
+            }
+            entryIdx++;
         }
     }
 
-    nlohmann::json
-        parseInterfaceData(const std::string &ifaceId,
-                           const EthernetInterfaceData &eth_data,
-                           const std::vector<IPv4AddressData> &ipv4_data)
+    nlohmann::json parseInterfaceData(
+        const std::string &iface_id, const EthernetInterfaceData &ethData,
+        const boost::container::flat_set<IPv4AddressData> &ipv4Data)
     {
         // Copy JSON object to avoid race condition
-        nlohmann::json jsonResponse(Node::json);
+        nlohmann::json json_response(Node::json);
 
-        // Fill out obvious data...
-        jsonResponse["Id"] = ifaceId;
-        jsonResponse["@odata.id"] =
-            "/redfish/v1/Managers/openbmc/EthernetInterfaces/" + ifaceId;
+        json_response["Id"] = iface_id;
+        json_response["@odata.id"] =
+            "/redfish/v1/Managers/bmc/EthernetInterfaces/" + iface_id;
 
-        // ... then the one from DBus, regarding eth iface...
-        if (eth_data.speed != nullptr)
-            jsonResponse["SpeedMbps"] = *eth_data.speed;
-
-        if (eth_data.macAddress != nullptr)
-            jsonResponse["MACAddress"] = *eth_data.macAddress;
-
-        if (eth_data.hostname != nullptr)
-            jsonResponse["HostName"] = *eth_data.hostname;
-
-        if (eth_data.vlanId != nullptr)
+        json_response["SpeedMbps"] = ethData.speed;
+        json_response["MACAddress"] = ethData.mac_address;
+        if (!ethData.hostname.empty())
         {
-            nlohmann::json &vlanObj = jsonResponse["VLAN"];
+            json_response["HostName"] = ethData.hostname;
+        }
+
+        nlohmann::json &vlanObj = json_response["VLAN"];
+        if (ethData.vlan_id)
+        {
             vlanObj["VLANEnable"] = true;
-            vlanObj["VLANId"] = *eth_data.vlanId;
+            vlanObj["VLANId"] = *ethData.vlan_id;
         }
         else
         {
-            nlohmann::json &vlanObj = jsonResponse["VLANs"];
-            vlanObj["@odata.id"] =
-                "/redfish/v1/Managers/openbmc/EthernetInterfaces/" + ifaceId +
-                "/VLANs";
+            vlanObj["VLANEnable"] = false;
+            vlanObj["VLANId"] = 0;
         }
 
-        // ... at last, check if there are IPv4 data and prepare appropriate
-        // collection
-        if (ipv4_data.size() > 0)
+        if (ipv4Data.size() > 0)
         {
-            nlohmann::json ipv4Array = nlohmann::json::array();
-            for (auto &ipv4Config : ipv4_data)
+            nlohmann::json &ipv4_array = json_response["IPv4Addresses"];
+            ipv4_array = nlohmann::json::array();
+            for (auto &ipv4_config : ipv4Data)
             {
-                nlohmann::json jsonIpv4;
-                if (ipv4Config.address != nullptr)
+                if (!ipv4_config.address.empty())
                 {
-                    jsonIpv4["Address"] = *ipv4Config.address;
-                    if (ipv4Config.gateway != nullptr)
-                        jsonIpv4["Gateway"] = *ipv4Config.gateway;
+                    ipv4_array.push_back({{"AddressOrigin", ipv4_config.origin},
+                                          {"SubnetMask", ipv4_config.netmask},
+                                          {"Address", ipv4_config.address}});
 
-                    jsonIpv4["AddressOrigin"] = ipv4Config.origin;
-                    jsonIpv4["SubnetMask"] = ipv4Config.netmask;
-
-                    ipv4Array.push_back(std::move(jsonIpv4));
+                    if (!ipv4_config.gateway.empty())
+                    {
+                        ipv4_array.back()["Gateway"] = ipv4_config.gateway;
+                    }
                 }
             }
-            jsonResponse["IPv4Addresses"] = std::move(ipv4Array);
         }
 
-        return jsonResponse;
+        return json_response;
     }
 
     /**
@@ -1438,153 +1362,132 @@
     void doGet(crow::Response &res, const crow::Request &req,
                const std::vector<std::string> &params) override
     {
-        // TODO(Pawel) this shall be parametrized call (two params) to get
-        // EthernetInterfaces for any Manager, not only hardcoded 'openbmc'.
-        // Check if there is required param, truly entering this shall be
-        // impossible.
+        std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
         if (params.size() != 1)
         {
-            res.result(boost::beast::http::status::internal_server_error);
-            res.end();
+            asyncResp->res.result(
+                boost::beast::http::status::internal_server_error);
             return;
         }
 
-        const std::string &ifaceId = params[0];
-
-        // get single eth interface data, and call the below callback for JSON
-        // preparation
-        ethernetProvider.getEthernetIfaceData(
-            ifaceId,
-            [&, ifaceId](const bool &success,
-                         const EthernetInterfaceData &eth_data,
-                         const std::vector<IPv4AddressData> &ipv4_data) {
-                if (success)
+        getEthernetIfaceData(
+            params[0],
+            [this, asyncResp, iface_id{std::string(params[0])}](
+                const bool &success, const EthernetInterfaceData &ethData,
+                const boost::container::flat_set<IPv4AddressData> &ipv4Data) {
+                if (!success)
                 {
-                    res.jsonValue =
-                        parseInterfaceData(ifaceId, eth_data, ipv4_data);
-                }
-                else
-                {
-                    // ... otherwise return error
                     // TODO(Pawel)consider distinguish between non existing
                     // object, and other errors
                     messages::addMessageToErrorJson(
-                        res.jsonValue, messages::resourceNotFound(
-                                           "EthernetInterface", ifaceId));
-                    res.result(boost::beast::http::status::not_found);
+                        asyncResp->res.jsonValue,
+                        messages::resourceNotFound("EthernetInterface",
+                                                   iface_id));
+                    asyncResp->res.result(
+                        boost::beast::http::status::not_found);
+                    return;
                 }
-                res.end();
+                asyncResp->res.jsonValue =
+                    parseInterfaceData(iface_id, ethData, ipv4Data);
             });
     }
 
     void doPatch(crow::Response &res, const crow::Request &req,
                  const std::vector<std::string> &params) override
     {
-        // TODO(Pawel) this shall be parametrized call (two params) to get
-        // EthernetInterfaces for any Manager, not only hardcoded 'openbmc'.
-        // Check if there is required param, truly entering this shall be
-        // impossible.
+        std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
         if (params.size() != 1)
         {
             res.result(boost::beast::http::status::internal_server_error);
-            res.end();
             return;
         }
 
-        const std::string &ifaceId = params[0];
+        const std::string &iface_id = params[0];
 
         nlohmann::json patchReq;
-
         if (!json_util::processJsonFromRequest(res, req, patchReq))
         {
             return;
         }
 
-        // get single eth interface data, and call the below callback for JSON
+        // Get single eth interface data, and call the below callback for JSON
         // preparation
-        ethernetProvider.getEthernetIfaceData(
-            ifaceId,
-            [&, ifaceId, patchReq = std::move(patchReq)](
-                const bool &success, const EthernetInterfaceData &eth_data,
-                const std::vector<IPv4AddressData> &ipv4_data) {
+        getEthernetIfaceData(
+            iface_id,
+            [this, asyncResp, iface_id, patchReq = std::move(patchReq)](
+                const bool &success, const EthernetInterfaceData &ethData,
+                const boost::container::flat_set<IPv4AddressData> &ipv4Data) {
                 if (!success)
                 {
                     // ... otherwise return error
                     // TODO(Pawel)consider distinguish between non existing
                     // object, and other errors
                     messages::addMessageToErrorJson(
-                        res.jsonValue, messages::resourceNotFound(
-                                           "VLAN Network Interface", ifaceId));
-                    res.result(boost::beast::http::status::not_found);
-                    res.end();
+                        asyncResp->res.jsonValue,
+                        messages::resourceNotFound("VLAN Network Interface",
+                                                   iface_id));
+                    asyncResp->res.result(
+                        boost::beast::http::status::not_found);
+                    asyncResp->res.end();
 
                     return;
                 }
 
-                res.jsonValue =
-                    parseInterfaceData(ifaceId, eth_data, ipv4_data);
+                asyncResp->res.jsonValue =
+                    parseInterfaceData(iface_id, ethData, ipv4Data);
 
-                std::shared_ptr<AsyncResp> asyncResp =
-                    std::make_shared<AsyncResp>(res);
-
-                for (auto propertyIt = patchReq.begin();
-                     propertyIt != patchReq.end(); ++propertyIt)
+                for (auto propertyIt : patchReq.items())
                 {
                     if (propertyIt.key() == "VLAN")
                     {
-                        handleVlanPatch(ifaceId, propertyIt.value(), eth_data,
-                                        "/VLAN", asyncResp);
+                        handleVlanPatch(iface_id, propertyIt.value(), ethData,
+                                        asyncResp);
                     }
                     else if (propertyIt.key() == "HostName")
                     {
-                        handleHostnamePatch(propertyIt.value(), eth_data,
-                                            asyncResp);
+                        handleHostnamePatch(propertyIt.value(), asyncResp);
                     }
                     else if (propertyIt.key() == "IPv4Addresses")
                     {
-                        handleIPv4Patch(ifaceId, propertyIt.value(), ipv4_data,
+                        handleIPv4Patch(iface_id, propertyIt.value(), ipv4Data,
                                         asyncResp);
                     }
                     else if (propertyIt.key() == "IPv6Addresses")
                     {
                         // TODO(kkowalsk) IPv6 Not supported on D-Bus yet
                         messages::addMessageToJsonRoot(
-                            res.jsonValue,
+                            asyncResp->res.jsonValue,
                             messages::propertyNotWritable(propertyIt.key()));
                     }
                     else
                     {
                         auto fieldInJsonIt =
-                            res.jsonValue.find(propertyIt.key());
+                            asyncResp->res.jsonValue.find(propertyIt.key());
 
-                        if (fieldInJsonIt == res.jsonValue.end())
+                        if (fieldInJsonIt == asyncResp->res.jsonValue.end())
                         {
                             // Field not in scope of defined fields
                             messages::addMessageToJsonRoot(
-                                res.jsonValue,
+                                asyncResp->res.jsonValue,
                                 messages::propertyUnknown(propertyIt.key()));
                         }
-                        else if (*fieldInJsonIt != *propertyIt)
+                        else
                         {
                             // User attempted to modify non-writable field
                             messages::addMessageToJsonRoot(
-                                res.jsonValue, messages::propertyNotWritable(
-                                                   propertyIt.key()));
+                                asyncResp->res.jsonValue,
+                                messages::propertyNotWritable(
+                                    propertyIt.key()));
                         }
                     }
                 }
             });
     }
-
-    // Ethernet Provider object
-    // TODO(Pawel) consider move it to singleton
-    OnDemandEthernetProvider ethernetProvider;
 };
 
-class VlanNetworkInterfaceCollection;
-
 /**
- * VlanNetworkInterface derived class for delivering VLANNetworkInterface Schema
+ * VlanNetworkInterface derived class for delivering VLANNetworkInterface
+ * Schema
  */
 class VlanNetworkInterface : public Node
 {
@@ -1594,13 +1497,13 @@
      */
     template <typename CrowApp>
     // TODO(Pawel) Remove line from below, where we assume that there is only
-    // one manager called openbmc This shall be generic, but requires to update
-    // GetSubroutes method
+    // one manager called openbmc.  This shall be generic, but requires to
+    // update GetSubroutes method
     VlanNetworkInterface(CrowApp &app) :
-        Node(
-            app,
-            "/redfish/v1/Managers/openbmc/EthernetInterfaces/<str>/VLANs/<str>",
-            std::string(), std::string())
+        Node(app,
+             "/redfish/v1/Managers/bmc/EthernetInterfaces/<str>>/VLANs/"
+             "<str>",
+             std::string(), std::string())
     {
         Node::json["@odata.type"] =
             "#VLanNetworkInterface.v1_1_0.VLanNetworkInterface";
@@ -1618,25 +1521,26 @@
     }
 
   private:
-    nlohmann::json
-        parseInterfaceData(const std::string &parent_ifaceId,
-                           const std::string &ifaceId,
-                           const EthernetInterfaceData &eth_data,
-                           const std::vector<IPv4AddressData> &ipv4_data)
+    nlohmann::json parseInterfaceData(
+        const std::string &parent_iface_id, const std::string &iface_id,
+        const EthernetInterfaceData &ethData,
+        const boost::container::flat_set<IPv4AddressData> &ipv4Data)
     {
         // Copy JSON object to avoid race condition
-        nlohmann::json jsonResponse(Node::json);
+        nlohmann::json json_response(Node::json);
 
         // Fill out obvious data...
-        jsonResponse["Id"] = ifaceId;
-        jsonResponse["@odata.id"] =
-            "/redfish/v1/Managers/openbmc/EthernetInterfaces/" +
-            parent_ifaceId + "/VLANs/" + ifaceId;
+        json_response["Id"] = iface_id;
+        json_response["@odata.id"] =
+            "/redfish/v1/Managers/bmc/EthernetInterfaces/" + parent_iface_id +
+            "/VLANs/" + iface_id;
 
-        jsonResponse["VLANEnable"] = true;
-        jsonResponse["VLANId"] = *eth_data.vlanId;
-
-        return jsonResponse;
+        json_response["VLANEnable"] = true;
+        if (ethData.vlan_id)
+        {
+            json_response["VLANId"] = *ethData.vlan_id;
+        }
+        return json_response;
     }
 
     bool verifyNames(crow::Response &res, const std::string &parent,
@@ -1664,7 +1568,8 @@
     void doGet(crow::Response &res, const crow::Request &req,
                const std::vector<std::string> &params) override
     {
-        // TODO(Pawel) this shall be parametrized call (two params) to get
+        std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
+        // TODO(Pawel) this shall be parameterized call (two params) to get
         // EthernetInterfaces for any Manager, not only hardcoded 'openbmc'.
         // Check if there is required param, truly entering this shall be
         // impossible.
@@ -1675,25 +1580,25 @@
             return;
         }
 
-        const std::string &parentIfaceId = params[0];
-        const std::string &ifaceId = params[1];
+        const std::string &parent_iface_id = params[0];
+        const std::string &iface_id = params[1];
 
-        if (!verifyNames(res, parentIfaceId, ifaceId))
+        if (!verifyNames(res, parent_iface_id, iface_id))
         {
             return;
         }
 
         // Get single eth interface data, and call the below callback for JSON
         // preparation
-        ethernetProvider.getEthernetIfaceData(
-            ifaceId, [&, parentIfaceId,
-                      ifaceId](const bool &success,
-                               const EthernetInterfaceData &eth_data,
-                               const std::vector<IPv4AddressData> &ipv4_data) {
-                if (success && eth_data.vlanId != nullptr)
+        getEthernetIfaceData(
+            iface_id,
+            [this, asyncResp, parent_iface_id, iface_id](
+                const bool &success, const EthernetInterfaceData &ethData,
+                const boost::container::flat_set<IPv4AddressData> &ipv4Data) {
+                if (success && ethData.vlan_id)
                 {
-                    res.jsonValue = parseInterfaceData(parentIfaceId, ifaceId,
-                                                       eth_data, ipv4_data);
+                    asyncResp->res.jsonValue = parseInterfaceData(
+                        parent_iface_id, iface_id, ethData, ipv4Data);
                 }
                 else
                 {
@@ -1701,21 +1606,23 @@
                     // TODO(Pawel)consider distinguish between non existing
                     // object, and other errors
                     messages::addMessageToErrorJson(
-                        res.jsonValue, messages::resourceNotFound(
-                                           "VLAN Network Interface", ifaceId));
-                    res.result(boost::beast::http::status::not_found);
+                        asyncResp->res.jsonValue,
+                        messages::resourceNotFound("VLAN Network Interface",
+                                                   iface_id));
+                    asyncResp->res.result(
+                        boost::beast::http::status::not_found);
                 }
-                res.end();
             });
     }
 
     void doPatch(crow::Response &res, const crow::Request &req,
                  const std::vector<std::string> &params) override
     {
+        std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
         if (params.size() != 2)
         {
-            res.result(boost::beast::http::status::internal_server_error);
-            res.end();
+            asyncResp->res.result(
+                boost::beast::http::status::internal_server_error);
             return;
         }
 
@@ -1728,7 +1635,6 @@
         }
 
         nlohmann::json patchReq;
-
         if (!json_util::processJsonFromRequest(res, req, patchReq))
         {
             return;
@@ -1736,131 +1642,125 @@
 
         // Get single eth interface data, and call the below callback for JSON
         // preparation
-        ethernetProvider.getEthernetIfaceData(
+        getEthernetIfaceData(
             ifaceId,
-            [&, parentIfaceId, ifaceId, patchReq = std::move(patchReq)](
-                const bool &success, const EthernetInterfaceData &eth_data,
-                const std::vector<IPv4AddressData> &ipv4_data) {
+            [this, asyncResp, parentIfaceId, ifaceId,
+             patchReq{std::move(patchReq)}](
+                const bool &success, const EthernetInterfaceData &ethData,
+                const boost::container::flat_set<IPv4AddressData> &ipv4Data) {
                 if (!success)
                 {
-                    // ... otherwise return error
                     // TODO(Pawel)consider distinguish between non existing
                     // object, and other errors
                     messages::addMessageToErrorJson(
-                        res.jsonValue, messages::resourceNotFound(
-                                           "VLAN Network Interface", ifaceId));
-                    res.result(boost::beast::http::status::not_found);
-                    res.end();
+                        asyncResp->res.jsonValue,
+                        messages::resourceNotFound("VLAN Network Interface",
+                                                   ifaceId));
+                    asyncResp->res.result(
+                        boost::beast::http::status::not_found);
+                    asyncResp->res.end();
 
                     return;
                 }
 
-                res.jsonValue = parseInterfaceData(parentIfaceId, ifaceId,
-                                                   eth_data, ipv4_data);
+                asyncResp->res.jsonValue = parseInterfaceData(
+                    parentIfaceId, ifaceId, ethData, ipv4Data);
 
-                std::shared_ptr<AsyncResp> asyncResp =
-                    std::make_shared<AsyncResp>(res);
-
-                for (auto propertyIt = patchReq.begin();
-                     propertyIt != patchReq.end(); ++propertyIt)
+                for (auto propertyIt : patchReq.items())
                 {
                     if (propertyIt.key() != "VLANEnable" &&
                         propertyIt.key() != "VLANId")
                     {
                         auto fieldInJsonIt =
-                            res.jsonValue.find(propertyIt.key());
-
-                        if (fieldInJsonIt == res.jsonValue.end())
+                            asyncResp->res.jsonValue.find(propertyIt.key());
+                        if (fieldInJsonIt == asyncResp->res.jsonValue.end())
                         {
                             // Field not in scope of defined fields
                             messages::addMessageToJsonRoot(
-                                res.jsonValue,
+                                asyncResp->res.jsonValue,
                                 messages::propertyUnknown(propertyIt.key()));
                         }
-                        else if (*fieldInJsonIt != *propertyIt)
+                        else
                         {
                             // User attempted to modify non-writable field
                             messages::addMessageToJsonRoot(
-                                res.jsonValue, messages::propertyNotWritable(
-                                                   propertyIt.key()));
+                                asyncResp->res.jsonValue,
+                                messages::propertyNotWritable(
+                                    propertyIt.key()));
                         }
                     }
                 }
 
-                EthernetInterface::handleVlanPatch(ifaceId, patchReq, eth_data,
-                                                   "/", asyncResp);
+                EthernetInterface::handleVlanPatch(ifaceId, patchReq, ethData,
+                                                   asyncResp);
             });
     }
 
     void doDelete(crow::Response &res, const crow::Request &req,
                   const std::vector<std::string> &params) override
     {
+        std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
         if (params.size() != 2)
         {
-            res.result(boost::beast::http::status::internal_server_error);
-            res.end();
+            asyncResp->res.result(
+                boost::beast::http::status::internal_server_error);
             return;
         }
 
         const std::string &parentIfaceId = params[0];
         const std::string &ifaceId = params[1];
 
-        if (!verifyNames(res, parentIfaceId, ifaceId))
+        if (!verifyNames(asyncResp->res, parentIfaceId, ifaceId))
         {
             return;
         }
 
         // Get single eth interface data, and call the below callback for JSON
         // preparation
-        ethernetProvider.getEthernetIfaceData(
-            ifaceId, [&, parentIfaceId,
-                      ifaceId](const bool &success,
-                               const EthernetInterfaceData &eth_data,
-                               const std::vector<IPv4AddressData> &ipv4_data) {
-                if (success && eth_data.vlanId != nullptr)
-                {
-                    res.jsonValue = parseInterfaceData(parentIfaceId, ifaceId,
-                                                       eth_data, ipv4_data);
+        getEthernetIfaceData(ifaceId, [this, asyncResp,
+                                       parentIfaceId{
+                                           std::string(parentIfaceId)},
+                                       ifaceId{std::string(ifaceId)}](
+                                          const bool &success,
+                                          const EthernetInterfaceData &ethData,
+                                          const boost::container::flat_set<
+                                              IPv4AddressData> &ipv4Data) {
+            if (success && ethData.vlan_id)
+            {
+                asyncResp->res.jsonValue = parseInterfaceData(
+                    parentIfaceId, ifaceId, ethData, ipv4Data);
 
-                    // Disable VLAN
-                    OnDemandEthernetProvider::disableVlan(
-                        ifaceId, [&](const boost::system::error_code ec) {
-                            if (ec)
-                            {
-                                res.jsonValue = nlohmann::json::object();
-                                messages::addMessageToErrorJson(
-                                    res.jsonValue, messages::internalError());
-                                res.result(boost::beast::http::status::
-                                               internal_server_error);
-                            }
-                            res.end();
-                        });
-                }
-                else
-                {
-                    // ... otherwise return error
-                    // TODO(Pawel)consider distinguish between non existing
-                    // object, and other errors
-                    messages::addMessageToErrorJson(
-                        res.jsonValue, messages::resourceNotFound(
-                                           "VLAN Network Interface", ifaceId));
-                    res.result(boost::beast::http::status::not_found);
-                    res.end();
-                }
-            });
+                auto callback = [asyncResp](
+                                    const boost::system::error_code ec) {
+                    if (ec)
+                    {
+                        messages::addMessageToErrorJson(
+                            asyncResp->res.jsonValue,
+                            messages::internalError());
+                        asyncResp->res.result(
+                            boost::beast::http::status::internal_server_error);
+                    }
+                };
+                crow::connections::systemBus->async_method_call(
+                    std::move(callback), "xyz.openbmc_project.Network",
+                    std::string("/xyz/openbmc_project/network/") + ifaceId,
+                    "xyz.openbmc_project.Object.Delete", "Delete");
+            }
+            else
+            {
+                // ... otherwise return error
+                // TODO(Pawel)consider distinguish between non existing object,
+                // and
+                // other errors
+                messages::addMessageToErrorJson(
+                    asyncResp->res.jsonValue,
+                    messages::resourceNotFound("VLAN Network Interface",
+                                               ifaceId));
+                asyncResp->res.result(boost::beast::http::status::not_found);
+                asyncResp->res.end();
+            }
+        });
     }
-
-    /**
-     * This allows VlanNetworkInterfaceCollection to reuse this class' doGet
-     * method, to maintain consistency of returned data, as Collection's doPost
-     * should return data for created member which should match member's doGet
-     * result in 100%.
-     */
-    friend VlanNetworkInterfaceCollection;
-
-    // Ethernet Provider object
-    // TODO(Pawel) consider move it to singleton
-    OnDemandEthernetProvider ethernetProvider;
 };
 
 /**
@@ -1872,13 +1772,11 @@
   public:
     template <typename CrowApp>
     // TODO(Pawel) Remove line from below, where we assume that there is only
-    // one manager called openbmc This shall be generic, but requires to update
-    // GetSubroutes method
+    // one manager called openbmc.  This shall be generic, but requires to
+    // update GetSubroutes method
     VlanNetworkInterfaceCollection(CrowApp &app) :
-        Node(app,
-             "/redfish/v1/Managers/openbmc/EthernetInterfaces/<str>/VLANs/",
-             std::string()),
-        memberVlan(app)
+        Node(app, "/redfish/v1/Managers/bmc/EthernetInterfaces/<str>/VLANs/",
+             std::string())
     {
         Node::json["@odata.type"] =
             "#VLanNetworkInterfaceCollection.VLanNetworkInterfaceCollection";
@@ -1903,182 +1801,118 @@
     void doGet(crow::Response &res, const crow::Request &req,
                const std::vector<std::string> &params) override
     {
+        std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
         if (params.size() != 1)
         {
             // This means there is a problem with the router
-            res.result(boost::beast::http::status::internal_server_error);
-            res.end();
-
+            asyncResp->res.result(
+                boost::beast::http::status::internal_server_error);
             return;
         }
 
-        // TODO(Pawel) this shall be parametrized call to get EthernetInterfaces
-        // for any Manager, not only hardcoded 'openbmc'.
-        std::string managerId = "openbmc";
-        std::string rootInterfaceName = params[0];
+        const std::string &rootInterfaceName = params[0];
 
-        // get eth interface list, and call the below callback for JSON
+        // Get eth interface list, and call the below callback for JSON
         // preparation
-        ethernetProvider.getEthernetIfaceList(
-            [&, managerId{std::move(managerId)},
-             rootInterfaceName{std::move(rootInterfaceName)}](
-                const bool &success,
-                const std::vector<std::string> &iface_list) {
-                if (success)
-                {
-                    bool rootInterfaceFound = false;
-                    nlohmann::json ifaceArray = nlohmann::json::array();
+        getEthernetIfaceList([this, asyncResp,
+                              rootInterfaceName{
+                                  std::string(rootInterfaceName)}](
+                                 const bool &success,
+                                 const std::vector<std::string> &iface_list) {
+            if (!success)
+            {
+                asyncResp->res.result(
+                    boost::beast::http::status::internal_server_error);
+                return;
+            }
+            asyncResp->res.jsonValue = Node::json;
 
-                    for (const std::string &ifaceItem : iface_list)
-                    {
-                        if (ifaceItem == rootInterfaceName)
-                        {
-                            rootInterfaceFound = true;
-                        }
-                        else if (boost::starts_with(ifaceItem,
-                                                    rootInterfaceName + "_"))
-                        {
-                            ifaceArray.push_back(
-                                {{"@odata.id", "/redfish/v1/Managers/" +
-                                                   managerId +
-                                                   "/EthernetInterfaces/" +
-                                                   rootInterfaceName +
-                                                   "/VLANs/" + ifaceItem}});
-                        }
-                    }
+            nlohmann::json iface_array = nlohmann::json::array();
 
-                    if (rootInterfaceFound)
-                    {
-                        Node::json["Members"] = ifaceArray;
-                        Node::json["Members@odata.count"] = ifaceArray.size();
-                        Node::json["@odata.id"] = "/redfish/v1/Managers/" +
-                                                  managerId +
-                                                  "/EthernetInterfaces/" +
-                                                  rootInterfaceName + "/VLANs";
-                        res.jsonValue = Node::json;
-                    }
-                    else
-                    {
-                        messages::addMessageToErrorJson(
-                            res.jsonValue,
-                            messages::resourceNotFound("EthernetInterface",
-                                                       rootInterfaceName));
-                        res.result(boost::beast::http::status::not_found);
-                        res.end();
-                    }
-                }
-                else
+            for (const std::string &iface_item : iface_list)
+            {
+                if (boost::starts_with(iface_item, rootInterfaceName + "_"))
                 {
-                    // No success, best what we can do is return INTERNALL ERROR
-                    res.result(
-                        boost::beast::http::status::internal_server_error);
+                    iface_array.push_back(
+                        {{"@odata.id",
+                          "/redfish/v1/Managers/bmc/EthernetInterfaces/" +
+                              rootInterfaceName + "/VLANs/" + iface_item}});
                 }
-                res.end();
-            });
+            }
+
+            if (iface_array.empty())
+            {
+                messages::addMessageToErrorJson(
+                    asyncResp->res.jsonValue,
+                    messages::resourceNotFound("EthernetInterface",
+                                               rootInterfaceName));
+                asyncResp->res.result(boost::beast::http::status::not_found);
+                return;
+            }
+            asyncResp->res.jsonValue["Members@odata.count"] =
+                iface_array.size();
+            asyncResp->res.jsonValue["Members"] = std::move(iface_array);
+            asyncResp->res.jsonValue["@odata.id"] =
+                "/redfish/v1/Managers/bmc/EthernetInterfaces/" +
+                rootInterfaceName + "/VLANs";
+        });
     }
 
     void doPost(crow::Response &res, const crow::Request &req,
                 const std::vector<std::string> &params) override
     {
+        std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
         if (params.size() != 1)
         {
-            // This means there is a problem with the router
-            res.result(boost::beast::http::status::internal_server_error);
-            res.end();
+            asyncResp->res.result(
+                boost::beast::http::status::internal_server_error);
             return;
         }
 
         nlohmann::json postReq;
-
         if (!json_util::processJsonFromRequest(res, req, postReq))
         {
             return;
         }
 
-        // TODO(Pawel) this shall be parametrized call to get EthernetInterfaces
-        // for any Manager, not only hardcoded 'openbmc'.
-        std::string managerId = "openbmc";
-        std::string rootInterfaceName = params[0];
-        uint64_t vlanId;
-        bool errorDetected;
-
-        if (json_util::getUnsigned(
-                "VLANId", postReq, vlanId,
-                static_cast<uint8_t>(json_util::MessageSetting::MISSING) |
-                    static_cast<uint8_t>(json_util::MessageSetting::TYPE_ERROR),
-                res.jsonValue, "/VLANId") != json_util::Result::SUCCESS)
+        auto vlanIdJson = json.find("VLANId");
+        if (vlanIdJson == json.end())
         {
-            res.end();
+            messages::addMessageToJson(asyncResp->res.jsonValue,
+                                       messages::propertyMissing("VLANId"),
+                                       "/VLANId");
             return;
         }
 
-        // get eth interface list, and call the below callback for JSON
-        // preparation
-        ethernetProvider.getEthernetIfaceList(
-            [&, managerId{std::move(managerId)},
-             rootInterfaceName{std::move(rootInterfaceName)}](
-                const bool &success,
-                const std::vector<std::string> &iface_list) {
-                if (success)
-                {
-                    bool rootInterfaceFound = false;
+        const uint64_t *vlanId = vlanIdJson->get_ptr<const uint64_t *>();
+        if (vlanId == nullptr)
+        {
+            messages::addMessageToJson(
+                asyncResp->res.jsonValue,
+                messages::propertyValueTypeError(vlanIdJson->dump(), "VLANId"),
+                "/VLANId");
+            return;
+        }
+        const std::string &rootInterfaceName = params[0];
 
-                    for (const std::string &ifaceItem : iface_list)
-                    {
-                        if (ifaceItem == rootInterfaceName)
-                        {
-                            rootInterfaceFound = true;
-                            break;
-                        }
-                    }
-
-                    if (rootInterfaceFound)
-                    {
-                        ethernetProvider.createVlan(
-                            rootInterfaceName, vlanId,
-                            [&, vlanId, rootInterfaceName, req{std::move(req)}](
-                                const boost::system::error_code ec) {
-                                if (ec)
-                                {
-                                    messages::addMessageToErrorJson(
-                                        res.jsonValue,
-                                        messages::internalError());
-                                    res.end();
-                                }
-                                else
-                                {
-                                    memberVlan.doGet(
-                                        res, req,
-                                        {rootInterfaceName,
-                                         rootInterfaceName + "_" +
-                                             std::to_string(vlanId)});
-                                }
-                            });
-                    }
-                    else
-                    {
-                        messages::addMessageToErrorJson(
-                            res.jsonValue,
-                            messages::resourceNotFound("EthernetInterface",
-                                                       rootInterfaceName));
-                        res.result(boost::beast::http::status::not_found);
-                        res.end();
-                    }
-                }
-                else
-                {
-                    // No success, best what we can do is return INTERNALL ERROR
-                    res.result(
-                        boost::beast::http::status::internal_server_error);
-                    res.end();
-                }
-            });
+        auto callback = [asyncResp](const boost::system::error_code ec) {
+            if (ec)
+            {
+                // TODO(ed) make more consistent error messages based on
+                // phosphor-network responses
+                messages::addMessageToErrorJson(asyncResp->res.jsonValue,
+                                                messages::internalError());
+                return;
+            }
+            asyncResp->res.result(boost::beast::http::status::created);
+            messages::addMessageToErrorJson(asyncResp->res.jsonValue,
+                                            messages::created());
+        };
+        crow::connections::systemBus->async_method_call(
+            std::move(callback), "xyz.openbmc_project.Network",
+            "/xyz/openbmc_project/network",
+            "xyz.openbmc_project.Network.VLAN.Create", "VLAN",
+            rootInterfaceName, static_cast<uint32_t>(*vlanId));
     }
-
-    // Ethernet Provider object
-    // TODO(Pawel) consider move it to singleton
-    OnDemandEthernetProvider ethernetProvider;
-    VlanNetworkInterface memberVlan;
 };
-
 } // namespace redfish