build: Split c++ sources into a subdirectory

Change-Id: Iedea50c688189ae4953195105e323f7173d17a4b
Signed-off-by: William A. Kennington III <wak@google.com>
diff --git a/src/argument.cpp b/src/argument.cpp
new file mode 100644
index 0000000..bcd5a13
--- /dev/null
+++ b/src/argument.cpp
@@ -0,0 +1,99 @@
+/**
+ * Copyright © 2018 IBM Corporation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#include "argument.hpp"
+
+#include <algorithm>
+#include <iostream>
+#include <iterator>
+
+namespace phosphor
+{
+namespace network
+{
+namespace ncsi
+{
+
+ArgumentParser::ArgumentParser(int argc, char** argv)
+{
+    int option = 0;
+    while (-1 != (option = getopt_long(argc, argv, optionStr, options, NULL)))
+    {
+        if ((option == '?') || (option == 'h'))
+        {
+            usage(argv);
+            exit(-1);
+        }
+
+        auto i = &options[0];
+        while ((i->val != option) && (i->val != 0))
+        {
+            ++i;
+        }
+
+        if (i->val)
+        {
+            arguments[i->name] = (i->has_arg ? optarg : trueString);
+        }
+    }
+}
+
+const std::string& ArgumentParser::operator[](const std::string& opt)
+{
+    auto i = arguments.find(opt);
+    if (i == arguments.end())
+    {
+        return emptyString;
+    }
+    else
+    {
+        return i->second;
+    }
+}
+
+void ArgumentParser::usage(char** argv)
+{
+    std::cerr << "Usage: " << argv[0] << " [options]\n";
+    std::cerr << "Options:\n";
+    std::cerr << "    --help            Print this menu.\n";
+    std::cerr << "    --info=<info>     Retrieve info about NCSI topology.\n";
+    std::cerr << "    --set=<set>       Set a specific package/channel.\n";
+    std::cerr
+        << "    --clear=<clear>   Clear all the settings on the interface.\n";
+    std::cerr << "    --package=<package>  Specify a package.\n";
+    std::cerr << "    --channel=<channel> Specify a channel.\n";
+    std::cerr << "    --index=<device index> Specify device ifindex.\n";
+    std::cerr << std::flush;
+}
+
+const option ArgumentParser::options[] = {
+    {"info", no_argument, NULL, 'i'},
+    {"set", no_argument, NULL, 's'},
+    {"clear", no_argument, NULL, 'r'},
+    {"package", required_argument, NULL, 'p'},
+    {"channel", required_argument, NULL, 'c'},
+    {"index", required_argument, NULL, 'x'},
+    {"help", no_argument, NULL, 'h'},
+    {0, 0, 0, 0},
+};
+
+const char* ArgumentParser::optionStr = "i:s:r:p:c:x:h?";
+
+const std::string ArgumentParser::trueString = "true";
+const std::string ArgumentParser::emptyString = "";
+
+} // namespace ncsi
+} // namespace network
+} // namespace phosphor
diff --git a/src/argument.hpp b/src/argument.hpp
new file mode 100644
index 0000000..28e5ce3
--- /dev/null
+++ b/src/argument.hpp
@@ -0,0 +1,68 @@
+#pragma once
+
+#include <getopt.h>
+
+#include <map>
+#include <string>
+
+namespace phosphor
+{
+namespace network
+{
+namespace ncsi
+{
+/** @brief Class - Encapsulates parsing command line options and
+ *                 populating arguments
+ */
+class ArgumentParser
+{
+  public:
+    ArgumentParser() = delete;
+    ~ArgumentParser() = default;
+    ArgumentParser(const ArgumentParser&) = delete;
+    ArgumentParser& operator=(const ArgumentParser&) = delete;
+    ArgumentParser(ArgumentParser&&) = default;
+    ArgumentParser& operator=(ArgumentParser&&) = default;
+
+    /** @brief Constructs Argument object
+     *
+     *  @param argc - the main function's argc passed as is
+     *  @param argv - the main function's argv passed as is
+     *  @return Object constructed
+     */
+    ArgumentParser(int argc, char** argv);
+
+    /** @brief Given an option, returns its argument(optarg)
+     *
+     *  @param opt - command line option string
+     *
+     *  @return argument which is a standard optarg
+     */
+    const std::string& operator[](const std::string& opt);
+
+    /** @brief Displays usage
+     *
+     *  @param argv - the main function's argv passed as is
+     */
+    static void usage(char** argv);
+
+    /** @brief Set to 'true' when an option is passed */
+    static const std::string trueString;
+
+    /** @brief Set to '' when an option is not passed */
+    static const std::string emptyString;
+
+  private:
+    /** @brief Option to argument mapping */
+    std::map<const std::string, std::string> arguments;
+
+    /** @brief Array of struct options as needed by getopt_long */
+    static const option options[];
+
+    /** @brief optstring as needed by getopt_long */
+    static const char* optionStr;
+};
+
+} // namespace ncsi
+} // namespace network
+} // namespace phosphor
diff --git a/src/config_parser.cpp b/src/config_parser.cpp
new file mode 100644
index 0000000..c4af404
--- /dev/null
+++ b/src/config_parser.cpp
@@ -0,0 +1,164 @@
+#include "config_parser.hpp"
+
+#include <algorithm>
+#include <fstream>
+#include <list>
+#include <phosphor-logging/log.hpp>
+#include <regex>
+#include <string>
+#include <unordered_map>
+
+namespace phosphor
+{
+namespace network
+{
+namespace config
+{
+
+using namespace phosphor::logging;
+
+Parser::Parser(const fs::path& filePath)
+{
+    setFile(filePath);
+}
+
+std::tuple<ReturnCode, KeyValueMap>
+    Parser::getSection(const std::string& section)
+{
+    auto it = sections.find(section);
+    if (it == sections.end())
+    {
+        KeyValueMap keyValues;
+        return std::make_tuple(ReturnCode::SECTION_NOT_FOUND,
+                               std::move(keyValues));
+    }
+
+    return std::make_tuple(ReturnCode::SUCCESS, it->second);
+}
+
+std::tuple<ReturnCode, ValueList> Parser::getValues(const std::string& section,
+                                                    const std::string& key)
+{
+    ValueList values;
+    KeyValueMap keyValues{};
+    auto rc = ReturnCode::SUCCESS;
+
+    std::tie(rc, keyValues) = getSection(section);
+    if (rc != ReturnCode::SUCCESS)
+    {
+        return std::make_tuple(rc, std::move(values));
+    }
+
+    auto it = keyValues.find(key);
+    if (it == keyValues.end())
+    {
+        return std::make_tuple(ReturnCode::KEY_NOT_FOUND, std::move(values));
+    }
+
+    for (; it != keyValues.end() && key == it->first; it++)
+    {
+        values.push_back(it->second);
+    }
+
+    return std::make_tuple(ReturnCode::SUCCESS, std::move(values));
+}
+
+bool Parser::isValueExist(const std::string& section, const std::string& key,
+                          const std::string& value)
+{
+    auto rc = ReturnCode::SUCCESS;
+    ValueList values;
+    std::tie(rc, values) = getValues(section, key);
+
+    if (rc != ReturnCode::SUCCESS)
+    {
+        return false;
+    }
+    auto it = std::find(values.begin(), values.end(), value);
+    return it != std::end(values) ? true : false;
+}
+
+void Parser::setValue(const std::string& section, const std::string& key,
+                      const std::string& value)
+{
+    KeyValueMap values;
+    auto it = sections.find(section);
+    if (it != sections.end())
+    {
+        values = std::move(it->second);
+    }
+    values.insert(std::make_pair(key, value));
+
+    if (it != sections.end())
+    {
+        it->second = std::move(values);
+    }
+    else
+    {
+        sections.insert(std::make_pair(section, std::move(values)));
+    }
+}
+
+#if 0
+void Parser::print()
+{
+    for (auto section : sections)
+    {
+        std::cout << "[" << section.first << "]\n\n";
+        for (auto keyValue : section.second)
+        {
+            std::cout << keyValue.first << "=" << keyValue.second << "\n";
+        }
+    }
+}
+#endif
+
+void Parser::setFile(const fs::path& filePath)
+{
+    this->filePath = filePath;
+    std::fstream stream;
+    stream.open(filePath.string(), std::fstream::in);
+
+    if (!stream.is_open())
+    {
+        return;
+    }
+    // clear all the section data.
+    sections.clear();
+    parse(stream);
+    stream.close();
+}
+
+void Parser::parse(std::istream& in)
+{
+    static const std::regex commentRegex{R"x(\s*[;#])x"};
+    static const std::regex sectionRegex{R"x(\s*\[([^\]]+)\])x"};
+    static const std::regex valueRegex{R"x(\s*(\S[^ \t=]*)\s*=\s*(\S+)\s*$)x"};
+    std::string section;
+    std::smatch pieces;
+    for (std::string line; std::getline(in, line);)
+    {
+        if (line.empty() || std::regex_match(line, pieces, commentRegex))
+        {
+            // skip comment lines and blank lines
+        }
+        else if (std::regex_match(line, pieces, sectionRegex))
+        {
+            if (pieces.size() == 2)
+            {
+                section = pieces[1].str();
+            }
+        }
+        else if (std::regex_match(line, pieces, valueRegex))
+        {
+            if (pieces.size() == 3)
+            {
+                setValue(section, pieces[1].str(), pieces[2].str());
+            }
+        }
+    }
+}
+
+} // namespace config
+} // namespace network
+} // namespace phosphor
diff --git a/src/config_parser.hpp b/src/config_parser.hpp
new file mode 100644
index 0000000..8af4a34
--- /dev/null
+++ b/src/config_parser.hpp
@@ -0,0 +1,97 @@
+#pragma once
+
+#include <filesystem>
+#include <map>
+#include <string>
+#include <tuple>
+#include <unordered_map>
+#include <vector>
+
+namespace phosphor
+{
+namespace network
+{
+namespace config
+{
+
+using Section = std::string;
+using KeyValueMap = std::multimap<std::string, std::string>;
+using ValueList = std::vector<std::string>;
+
+namespace fs = std::filesystem;
+
+enum class ReturnCode
+{
+    SUCCESS = 0x0,
+    SECTION_NOT_FOUND = 0x1,
+    KEY_NOT_FOUND = 0x2,
+};
+
+class Parser
+{
+  public:
+    Parser() = default;
+
+    /** @brief Constructor
+     *  @param[in] fileName - Absolute path of the file which will be parsed.
+     */
+
+    Parser(const fs::path& fileName);
+
+    /** @brief Get the values of the given key and section.
+     *  @param[in] section - section name.
+     *  @param[in] key - key to look for.
+     *  @returns the tuple of return code and the
+     *           values associated with the key.
+     */
+
+    std::tuple<ReturnCode, ValueList> getValues(const std::string& section,
+                                                const std::string& key);
+
+    /** @brief Set the value of the given key and section.
+     *  @param[in] section - section name.
+     *  @param[in] key - key name.
+     *  @param[in] value - value.
+     */
+
+    void setValue(const std::string& section, const std::string& key,
+                  const std::string& value);
+
+    /** @brief Set the file name and parse it.
+     *  @param[in] fileName - Absolute path of the file.
+     */
+
+    void setFile(const fs::path& fileName);
+
+  private:
+    /** @brief Parses the given file and fills the data.
+     *  @param[in] stream - inputstream.
+     */
+
+    void parse(std::istream& stream);
+
+    /** @brief Get all the key values of the given section.
+     *  @param[in] section - section name.
+     *  @returns the tuple of return code and the map of (key,value).
+     */
+
+    std::tuple<ReturnCode, KeyValueMap> getSection(const std::string& section);
+
+    /** @brief checks that whether the value exist in the
+     *         given section.
+     *  @param[in] section - section name.
+     *  @param[in] key - key name.
+     *  @param[in] value - value.
+     *  @returns true if exist otherwise false.
+     */
+
+    bool isValueExist(const std::string& section, const std::string& key,
+                      const std::string& value);
+
+    std::unordered_map<Section, KeyValueMap> sections;
+    fs::path filePath;
+};
+
+} // namespace config
+} // namespace network
+} // namespace phosphor
diff --git a/src/dhcp_configuration.cpp b/src/dhcp_configuration.cpp
new file mode 100644
index 0000000..690ae47
--- /dev/null
+++ b/src/dhcp_configuration.cpp
@@ -0,0 +1,111 @@
+#include "config.h"
+
+#include "dhcp_configuration.hpp"
+
+#include "network_manager.hpp"
+
+#include <phosphor-logging/elog-errors.hpp>
+#include <phosphor-logging/log.hpp>
+#include <xyz/openbmc_project/Common/error.hpp>
+
+namespace phosphor
+{
+namespace network
+{
+namespace dhcp
+{
+
+using namespace phosphor::network;
+using namespace phosphor::logging;
+using namespace sdbusplus::xyz::openbmc_project::Common::Error;
+bool Configuration::sendHostNameEnabled(bool value)
+{
+    if (value == sendHostNameEnabled())
+    {
+        return value;
+    }
+
+    auto name = ConfigIntf::sendHostNameEnabled(value);
+    manager.writeToConfigurationFile();
+
+    return name;
+}
+
+bool Configuration::hostNameEnabled(bool value)
+{
+    if (value == hostNameEnabled())
+    {
+        return value;
+    }
+
+    auto name = ConfigIntf::hostNameEnabled(value);
+    manager.writeToConfigurationFile();
+    manager.restartSystemdUnit(phosphor::network::networkdService);
+
+    return name;
+}
+
+bool Configuration::ntpEnabled(bool value)
+{
+    if (value == ntpEnabled())
+    {
+        return value;
+    }
+
+    auto ntp = ConfigIntf::ntpEnabled(value);
+    manager.writeToConfigurationFile();
+    manager.restartSystemdUnit(phosphor::network::networkdService);
+    manager.restartSystemdUnit(phosphor::network::timeSynchdService);
+
+    return ntp;
+}
+
+bool Configuration::dnsEnabled(bool value)
+{
+    if (value == dnsEnabled())
+    {
+        return value;
+    }
+
+    auto dns = ConfigIntf::dnsEnabled(value);
+    manager.writeToConfigurationFile();
+    manager.restartSystemdUnit(phosphor::network::networkdService);
+
+    return dns;
+}
+
+bool Configuration::getDHCPPropFromConf(const std::string& prop)
+{
+    fs::path confPath = manager.getConfDir();
+    auto interfaceStrList = getInterfaces();
+    // get the first interface name, we need it to know config file name.
+    auto interface = *interfaceStrList.begin();
+    auto fileName = systemd::config::networkFilePrefix + interface +
+                    systemd::config::networkFileSuffix;
+
+    confPath /= fileName;
+    // systemd default behaviour is all DHCP fields should be enabled by
+    // default.
+    auto propValue = true;
+    config::Parser parser(confPath);
+
+    auto rc = config::ReturnCode::SUCCESS;
+    config::ValueList values{};
+    std::tie(rc, values) = parser.getValues("DHCP", prop);
+
+    if (rc != config::ReturnCode::SUCCESS)
+    {
+        log<level::DEBUG>("Unable to get the value from section DHCP",
+                          entry("PROP=%s", prop.c_str()), entry("RC=%d", rc));
+        return propValue;
+    }
+
+    if (values[0] == "false")
+    {
+        propValue = false;
+    }
+    return propValue;
+}
+} // namespace dhcp
+} // namespace network
+} // namespace phosphor
diff --git a/src/dhcp_configuration.hpp b/src/dhcp_configuration.hpp
new file mode 100644
index 0000000..441c7b2
--- /dev/null
+++ b/src/dhcp_configuration.hpp
@@ -0,0 +1,117 @@
+#pragma once
+
+#include "config_parser.hpp"
+
+#include <sdbusplus/bus.hpp>
+#include <sdbusplus/server/object.hpp>
+#include <string>
+#include <xyz/openbmc_project/Network/DHCPConfiguration/server.hpp>
+
+#ifndef SDBUSPP_NEW_CAMELCASE
+#define dnsEnabled dNSEnabled
+#define ntpEnabled nTPEnabled
+#endif
+
+namespace phosphor
+{
+namespace network
+{
+
+class Manager; // forward declaration of network manager.
+
+namespace dhcp
+{
+
+using ConfigIntf =
+    sdbusplus::xyz::openbmc_project::Network::server::DHCPConfiguration;
+
+using Iface = sdbusplus::server::object::object<ConfigIntf>;
+
+/** @class Configuration
+ *  @brief DHCP configuration.
+ *  @details A concrete implementation for the
+ *  xyz.openbmc_project.Network.DHCP DBus interface.
+ */
+class Configuration : public Iface
+{
+  public:
+    Configuration() = default;
+    Configuration(const Configuration&) = delete;
+    Configuration& operator=(const Configuration&) = delete;
+    Configuration(Configuration&&) = delete;
+    Configuration& operator=(Configuration&&) = delete;
+    virtual ~Configuration() = default;
+
+    /** @brief Constructor to put object onto bus at a dbus path.
+     *  @param[in] bus - Bus to attach to.
+     *  @param[in] objPath - Path to attach at.
+     *  @param[in] parent - Parent object.
+     */
+    Configuration(sdbusplus::bus::bus& bus, const std::string& objPath,
+                  Manager& parent) :
+        Iface(bus, objPath.c_str(), true),
+        bus(bus), manager(parent)
+    {
+        ConfigIntf::dnsEnabled(getDHCPPropFromConf("UseDNS"));
+        ConfigIntf::ntpEnabled(getDHCPPropFromConf("UseNTP"));
+        ConfigIntf::hostNameEnabled(getDHCPPropFromConf("UseHostname"));
+        ConfigIntf::sendHostNameEnabled(getDHCPPropFromConf("SendHostname"));
+        emit_object_added();
+    }
+
+    /** @brief If true then DNS servers received from the DHCP server
+     *         will be used and take precedence over any statically
+     *         configured ones.
+     *  @param[in] value - true if DNS server needed from DHCP server
+     *                     else false.
+     */
+    bool dnsEnabled(bool value) override;
+
+    /** @brief If true then NTP servers received from the DHCP server
+               will be used by systemd-timesyncd.
+     *  @param[in] value - true if NTP server needed from DHCP server
+     *                     else false.
+     */
+    bool ntpEnabled(bool value) override;
+
+    /** @brief If true then Hostname received from the DHCP server will
+     *         be set as the hostname of the system
+     *  @param[in] value - true if hostname needed from the DHCP server
+     *                     else false.
+     *
+     */
+    bool hostNameEnabled(bool value) override;
+
+    /** @brief if true then it will cause an Option 12 field, i.e machine's
+     *         hostname, will be included in the DHCP packet.
+     *  @param[in] value - true if machine's host name needs to be included
+     *         in the DHCP packet.
+     */
+    bool sendHostNameEnabled(bool value) override;
+
+    /** @brief read the DHCP Prop value from the configuration file
+     *  @param[in] prop - DHCP Prop name.
+     */
+    bool getDHCPPropFromConf(const std::string& prop);
+
+    /* @brief Network Manager needed the below function to know the
+     *        value of the properties (ntpEnabled,dnsEnabled,hostnameEnabled
+              sendHostNameEnabled).
+     *
+     */
+    using ConfigIntf::dnsEnabled;
+    using ConfigIntf::hostNameEnabled;
+    using ConfigIntf::ntpEnabled;
+    using ConfigIntf::sendHostNameEnabled;
+
+  private:
+    /** @brief sdbusplus DBus bus connection. */
+    sdbusplus::bus::bus& bus;
+
+    /** @brief Network Manager object. */
+    phosphor::network::Manager& manager;
+};
+
+} // namespace dhcp
+} // namespace network
+} // namespace phosphor
diff --git a/src/dns_updater.cpp b/src/dns_updater.cpp
new file mode 100644
index 0000000..bf29fff
--- /dev/null
+++ b/src/dns_updater.cpp
@@ -0,0 +1,58 @@
+#include "config.h"
+
+#include "dns_updater.hpp"
+
+#include <fstream>
+#include <phosphor-logging/elog-errors.hpp>
+#include <phosphor-logging/log.hpp>
+#include <sdbusplus/bus.hpp>
+#include <xyz/openbmc_project/Common/error.hpp>
+
+namespace phosphor
+{
+namespace network
+{
+namespace dns
+{
+namespace updater
+{
+
+void updateDNSEntries(const fs::path& inFile, const fs::path& outFile)
+{
+    using namespace phosphor::logging;
+    using namespace sdbusplus::xyz::openbmc_project::Common::Error;
+
+    std::fstream outStream(outFile, std::fstream::out);
+    if (!outStream.is_open())
+    {
+        log<level::ERR>("Unable to open output file",
+                        entry("FILE=%s", outFile.c_str()));
+        elog<InternalFailure>();
+    }
+
+    std::fstream inStream(inFile, std::fstream::in);
+    if (!inStream.is_open())
+    {
+        log<level::ERR>("Unable to open the input file",
+                        entry("FILE=%s", inFile.c_str()));
+        elog<InternalFailure>();
+    }
+
+    outStream << "### Generated by phosphor-networkd ###\n";
+
+    for (std::string line; std::getline(inStream, line);)
+    {
+        auto index = line.find("DNS=");
+        if (index != std::string::npos)
+        {
+            auto dns = line.substr(index + 4);
+            outStream << "nameserver " << dns << "\n";
+        }
+    }
+    return;
+}
+
+} // namespace updater
+} // namespace dns
+} // namespace network
+} // namespace phosphor
diff --git a/src/dns_updater.hpp b/src/dns_updater.hpp
new file mode 100644
index 0000000..5d23b29
--- /dev/null
+++ b/src/dns_updater.hpp
@@ -0,0 +1,40 @@
+#pragma once
+
+#include <filesystem>
+
+namespace phosphor
+{
+namespace network
+{
+namespace dns
+{
+namespace updater
+{
+
+namespace fs = std::filesystem;
+
+constexpr auto RESOLV_CONF = "/etc/resolv.conf";
+
+/** @brief Reads DNS entries supplied by DHCP and updates specified file
+ *
+ *  @param[in] inFile  - File having DNS entries supplied by DHCP
+ *  @param[in] outFile - File to write the nameserver entries to
+ */
+void updateDNSEntries(const fs::path& inFile, const fs::path& outFile);
+
+/** @brief User callback handler invoked by inotify watcher
+ *
+ *  Needed to enable production and test code so that the right
+ *  callback functions could be implemented
+ *
+ *  @param[in] inFile - File having DNS entries supplied by DHCP
+ */
+inline void processDNSEntries(const fs::path& inFile)
+{
+    return updateDNSEntries(inFile, RESOLV_CONF);
+}
+
+} // namespace updater
+} // namespace dns
+} // namespace network
+} // namespace phosphor
diff --git a/src/ethernet_interface.cpp b/src/ethernet_interface.cpp
new file mode 100644
index 0000000..ce424ff
--- /dev/null
+++ b/src/ethernet_interface.cpp
@@ -0,0 +1,1179 @@
+#include "config.h"
+
+#include "ethernet_interface.hpp"
+
+#include "config_parser.hpp"
+#include "neighbor.hpp"
+#include "network_manager.hpp"
+#include "routing_table.hpp"
+#include "vlan_interface.hpp"
+
+#include <arpa/inet.h>
+#include <linux/ethtool.h>
+#include <linux/rtnetlink.h>
+#include <linux/sockios.h>
+#include <net/if.h>
+#include <netinet/in.h>
+#include <sys/ioctl.h>
+#include <sys/socket.h>
+#include <unistd.h>
+
+#include <algorithm>
+#include <filesystem>
+#include <fstream>
+#include <phosphor-logging/elog-errors.hpp>
+#include <phosphor-logging/log.hpp>
+#include <sstream>
+#include <stdplus/raw.hpp>
+#include <string>
+#include <string_view>
+#include <xyz/openbmc_project/Common/error.hpp>
+
+namespace phosphor
+{
+namespace network
+{
+
+using namespace phosphor::logging;
+using namespace sdbusplus::xyz::openbmc_project::Common::Error;
+using NotAllowed = sdbusplus::xyz::openbmc_project::Common::Error::NotAllowed;
+using NotAllowedArgument = xyz::openbmc_project::Common::NotAllowed;
+using Argument = xyz::openbmc_project::Common::InvalidArgument;
+constexpr auto RESOLVED_SERVICE = "org.freedesktop.resolve1";
+constexpr auto RESOLVED_INTERFACE = "org.freedesktop.resolve1.Link";
+constexpr auto PROPERTY_INTERFACE = "org.freedesktop.DBus.Properties";
+constexpr auto RESOLVED_SERVICE_PATH = "/org/freedesktop/resolve1/link/";
+constexpr auto METHOD_GET = "Get";
+
+struct EthernetIntfSocket
+{
+    EthernetIntfSocket(int domain, int type, int protocol)
+    {
+        if ((sock = socket(domain, type, protocol)) < 0)
+        {
+            log<level::ERR>("socket creation failed:",
+                            entry("ERROR=%s", strerror(errno)));
+        }
+    }
+
+    ~EthernetIntfSocket()
+    {
+        if (sock >= 0)
+        {
+            close(sock);
+        }
+    }
+
+    int sock{-1};
+};
+
+std::map<EthernetInterface::DHCPConf, std::string> mapDHCPToSystemd = {
+    {EthernetInterface::DHCPConf::both, "true"},
+    {EthernetInterface::DHCPConf::v4, "ipv4"},
+    {EthernetInterface::DHCPConf::v6, "ipv6"},
+    {EthernetInterface::DHCPConf::none, "false"}};
+
+EthernetInterface::EthernetInterface(sdbusplus::bus::bus& bus,
+                                     const std::string& objPath,
+                                     DHCPConf dhcpEnabled, Manager& parent,
+                                     bool emitSignal) :
+    Ifaces(bus, objPath.c_str(), true),
+    bus(bus), manager(parent), objPath(objPath)
+{
+    auto intfName = objPath.substr(objPath.rfind("/") + 1);
+    std::replace(intfName.begin(), intfName.end(), '_', '.');
+    interfaceName(intfName);
+    EthernetInterfaceIntf::dhcpEnabled(dhcpEnabled);
+    EthernetInterfaceIntf::ipv6AcceptRA(getIPv6AcceptRAFromConf());
+    route::Table routingTable;
+    auto gatewayList = routingTable.getDefaultGateway();
+    auto gateway6List = routingTable.getDefaultGateway6();
+    std::string defaultGateway;
+    std::string defaultGateway6;
+
+    for (auto& gateway : gatewayList)
+    {
+        if (gateway.first == intfName)
+        {
+            defaultGateway = gateway.second;
+            break;
+        }
+    }
+
+    for (auto& gateway6 : gateway6List)
+    {
+        if (gateway6.first == intfName)
+        {
+            defaultGateway6 = gateway6.second;
+            break;
+        }
+    }
+
+    EthernetInterfaceIntf::defaultGateway(defaultGateway);
+    EthernetInterfaceIntf::defaultGateway6(defaultGateway6);
+    // Don't get the mac address from the system as the mac address
+    // would be same as parent interface.
+    if (intfName.find(".") == std::string::npos)
+    {
+        MacAddressIntf::macAddress(getMACAddress(intfName));
+    }
+    EthernetInterfaceIntf::ntpServers(getNTPServersFromConf());
+
+    EthernetInterfaceIntf::linkUp(linkUp());
+    EthernetInterfaceIntf::nicEnabled(nicEnabled());
+
+#ifdef NIC_SUPPORTS_ETHTOOL
+    InterfaceInfo ifInfo = EthernetInterface::getInterfaceInfo();
+
+    EthernetInterfaceIntf::autoNeg(std::get<2>(ifInfo));
+    EthernetInterfaceIntf::speed(std::get<0>(ifInfo));
+#endif
+
+    // Emit deferred signal.
+    if (emitSignal)
+    {
+        this->emit_object_added();
+    }
+}
+
+static IP::Protocol convertFamily(int family)
+{
+    switch (family)
+    {
+        case AF_INET:
+            return IP::Protocol::IPv4;
+        case AF_INET6:
+            return IP::Protocol::IPv6;
+    }
+
+    throw std::invalid_argument("Bad address family");
+}
+
+void EthernetInterface::disableDHCP(IP::Protocol protocol)
+{
+    DHCPConf dhcpState = EthernetInterfaceIntf::dhcpEnabled();
+    if (dhcpState == EthernetInterface::DHCPConf::both)
+    {
+        if (protocol == IP::Protocol::IPv4)
+        {
+            dhcpEnabled(EthernetInterface::DHCPConf::v6);
+        }
+        else if (protocol == IP::Protocol::IPv6)
+        {
+            dhcpEnabled(EthernetInterface::DHCPConf::v4);
+        }
+    }
+    else if ((dhcpState == EthernetInterface::DHCPConf::v4) &&
+             (protocol == IP::Protocol::IPv4))
+    {
+        dhcpEnabled(EthernetInterface::DHCPConf::none);
+    }
+    else if ((dhcpState == EthernetInterface::DHCPConf::v6) &&
+             (protocol == IP::Protocol::IPv6))
+    {
+        dhcpEnabled(EthernetInterface::DHCPConf::none);
+    }
+}
+
+bool EthernetInterface::dhcpIsEnabled(IP::Protocol family, bool ignoreProtocol)
+{
+    return ((EthernetInterfaceIntf::dhcpEnabled() ==
+             EthernetInterface::DHCPConf::both) ||
+            ((EthernetInterfaceIntf::dhcpEnabled() ==
+              EthernetInterface::DHCPConf::v6) &&
+             ((family == IP::Protocol::IPv6) || ignoreProtocol)) ||
+            ((EthernetInterfaceIntf::dhcpEnabled() ==
+              EthernetInterface::DHCPConf::v4) &&
+             ((family == IP::Protocol::IPv4) || ignoreProtocol)));
+}
+
+bool EthernetInterface::dhcpToBeEnabled(IP::Protocol family,
+                                        const std::string& nextDHCPState)
+{
+    return ((nextDHCPState == "true") ||
+            ((nextDHCPState == "ipv6") && (family == IP::Protocol::IPv6)) ||
+            ((nextDHCPState == "ipv4") && (family == IP::Protocol::IPv4)));
+}
+
+bool EthernetInterface::originIsManuallyAssigned(IP::AddressOrigin origin)
+{
+    return (
+#ifdef LINK_LOCAL_AUTOCONFIGURATION
+        (origin == IP::AddressOrigin::Static)
+#else
+        (origin == IP::AddressOrigin::Static ||
+         origin == IP::AddressOrigin::LinkLocal)
+#endif
+
+    );
+}
+
+void EthernetInterface::createIPAddressObjects()
+{
+    addrs.clear();
+
+    auto addrs = getInterfaceAddrs()[interfaceName()];
+
+    for (auto& addr : addrs)
+    {
+        IP::Protocol addressType = convertFamily(addr.addrType);
+        IP::AddressOrigin origin = IP::AddressOrigin::Static;
+        if (dhcpIsEnabled(addressType))
+        {
+            origin = IP::AddressOrigin::DHCP;
+        }
+        if (isLinkLocalIP(addr.ipaddress))
+        {
+            origin = IP::AddressOrigin::LinkLocal;
+        }
+        // Obsolete parameter
+        std::string gateway = "";
+
+        std::string ipAddressObjectPath = generateObjectPath(
+            addressType, addr.ipaddress, addr.prefix, gateway);
+
+        this->addrs.insert_or_assign(
+            addr.ipaddress,
+            std::make_shared<phosphor::network::IPAddress>(
+                bus, ipAddressObjectPath.c_str(), *this, addressType,
+                addr.ipaddress, origin, addr.prefix, gateway));
+    }
+}
+
+void EthernetInterface::createStaticNeighborObjects()
+{
+    staticNeighbors.clear();
+
+    NeighborFilter filter;
+    filter.interface = ifIndex();
+    filter.state = NUD_PERMANENT;
+    auto neighbors = getCurrentNeighbors(filter);
+    for (const auto& neighbor : neighbors)
+    {
+        if (!neighbor.mac)
+        {
+            continue;
+        }
+        std::string ip = toString(neighbor.address);
+        std::string mac = mac_address::toString(*neighbor.mac);
+        std::string objectPath = generateStaticNeighborObjectPath(ip, mac);
+        staticNeighbors.emplace(ip,
+                                std::make_shared<phosphor::network::Neighbor>(
+                                    bus, objectPath.c_str(), *this, ip, mac,
+                                    Neighbor::State::Permanent));
+    }
+}
+
+unsigned EthernetInterface::ifIndex() const
+{
+    unsigned idx = if_nametoindex(interfaceName().c_str());
+    if (idx == 0)
+    {
+        throw std::system_error(errno, std::generic_category(),
+                                "if_nametoindex");
+    }
+    return idx;
+}
+
+ObjectPath EthernetInterface::ip(IP::Protocol protType, std::string ipaddress,
+                                 uint8_t prefixLength, std::string gateway)
+{
+    if (dhcpIsEnabled(protType))
+    {
+        log<level::INFO>("DHCP enabled on the interface"),
+            entry("INTERFACE=%s", interfaceName().c_str());
+        disableDHCP(protType);
+    }
+
+    IP::AddressOrigin origin = IP::AddressOrigin::Static;
+
+    int addressFamily = (protType == IP::Protocol::IPv4) ? AF_INET : AF_INET6;
+
+    if (!isValidIP(addressFamily, ipaddress))
+    {
+        log<level::ERR>("Not a valid IP address"),
+            entry("ADDRESS=%s", ipaddress.c_str());
+        elog<InvalidArgument>(Argument::ARGUMENT_NAME("ipaddress"),
+                              Argument::ARGUMENT_VALUE(ipaddress.c_str()));
+    }
+
+    // Gateway is an obsolete parameter
+    gateway = "";
+
+    if (!isValidPrefix(addressFamily, prefixLength))
+    {
+        log<level::ERR>("PrefixLength is not correct "),
+            entry("PREFIXLENGTH=%" PRIu8, prefixLength);
+        elog<InvalidArgument>(
+            Argument::ARGUMENT_NAME("prefixLength"),
+            Argument::ARGUMENT_VALUE(std::to_string(prefixLength).c_str()));
+    }
+
+    std::string objectPath =
+        generateObjectPath(protType, ipaddress, prefixLength, gateway);
+    this->addrs.insert_or_assign(ipaddress,
+                                 std::make_shared<phosphor::network::IPAddress>(
+                                     bus, objectPath.c_str(), *this, protType,
+                                     ipaddress, origin, prefixLength, gateway));
+
+    manager.writeToConfigurationFile();
+    return objectPath;
+}
+
+ObjectPath EthernetInterface::neighbor(std::string ipAddress,
+                                       std::string macAddress)
+{
+    if (!isValidIP(AF_INET, ipAddress) && !isValidIP(AF_INET6, ipAddress))
+    {
+        log<level::ERR>("Not a valid IP address",
+                        entry("ADDRESS=%s", ipAddress.c_str()));
+        elog<InvalidArgument>(Argument::ARGUMENT_NAME("ipAddress"),
+                              Argument::ARGUMENT_VALUE(ipAddress.c_str()));
+    }
+    if (!mac_address::isUnicast(mac_address::fromString(macAddress)))
+    {
+        log<level::ERR>("Not a valid MAC address",
+                        entry("MACADDRESS=%s", ipAddress.c_str()));
+        elog<InvalidArgument>(Argument::ARGUMENT_NAME("macAddress"),
+                              Argument::ARGUMENT_VALUE(macAddress.c_str()));
+    }
+
+    std::string objectPath =
+        generateStaticNeighborObjectPath(ipAddress, macAddress);
+    staticNeighbors.emplace(ipAddress,
+                            std::make_shared<phosphor::network::Neighbor>(
+                                bus, objectPath.c_str(), *this, ipAddress,
+                                macAddress, Neighbor::State::Permanent));
+    manager.writeToConfigurationFile();
+    return objectPath;
+}
+
+#ifdef NIC_SUPPORTS_ETHTOOL
+/*
+  Enable this code if your NIC driver supports the ETHTOOL features.
+  Do this by adding the following to your phosphor-network*.bbappend file.
+     EXTRA_OECONF_append = " --enable-nic-ethtool=yes"
+  The default compile mode is to omit getInterfaceInfo()
+*/
+InterfaceInfo EthernetInterface::getInterfaceInfo() const
+{
+    ifreq ifr = {};
+    ethtool_cmd edata = {};
+    LinkSpeed speed = {};
+    Autoneg autoneg = {};
+    DuplexMode duplex = {};
+    LinkUp linkState = {};
+    NICEnabled enabled = {};
+    EthernetIntfSocket eifSocket(PF_INET, SOCK_DGRAM, IPPROTO_IP);
+
+    if (eifSocket.sock < 0)
+    {
+        return std::make_tuple(speed, duplex, autoneg, linkState, enabled);
+    }
+
+    std::strncpy(ifr.ifr_name, interfaceName().c_str(), IFNAMSIZ - 1);
+    ifr.ifr_data = reinterpret_cast<char*>(&edata);
+
+    edata.cmd = ETHTOOL_GSET;
+    if (ioctl(eifSocket.sock, SIOCETHTOOL, &ifr) >= 0)
+    {
+        speed = edata.speed;
+        duplex = edata.duplex;
+        autoneg = edata.autoneg;
+    }
+
+    enabled = nicEnabled();
+    linkState = linkUp();
+
+    return std::make_tuple(speed, duplex, autoneg, linkState, enabled);
+}
+#endif
+
+/** @brief get the mac address of the interface.
+ *  @return macaddress on success
+ */
+
+std::string
+    EthernetInterface::getMACAddress(const std::string& interfaceName) const
+{
+    std::string activeMACAddr = MacAddressIntf::macAddress();
+    EthernetIntfSocket eifSocket(PF_INET, SOCK_DGRAM, IPPROTO_IP);
+
+    if (eifSocket.sock < 0)
+    {
+        return activeMACAddr;
+    }
+
+    ifreq ifr = {};
+    std::strncpy(ifr.ifr_name, interfaceName.c_str(), IFNAMSIZ - 1);
+    if (ioctl(eifSocket.sock, SIOCGIFHWADDR, &ifr) != 0)
+    {
+        log<level::ERR>("ioctl failed for SIOCGIFHWADDR:",
+                        entry("ERROR=%s", strerror(errno)));
+        elog<InternalFailure>();
+    }
+
+    static_assert(sizeof(ifr.ifr_hwaddr.sa_data) >= sizeof(ether_addr));
+    std::string_view hwaddr(reinterpret_cast<char*>(ifr.ifr_hwaddr.sa_data),
+                            sizeof(ifr.ifr_hwaddr.sa_data));
+    return mac_address::toString(stdplus::raw::copyFrom<ether_addr>(hwaddr));
+}
+
+std::string EthernetInterface::generateId(const std::string& ipaddress,
+                                          uint8_t prefixLength,
+                                          const std::string& gateway)
+{
+    std::stringstream hexId;
+    std::string hashString = ipaddress;
+    hashString += std::to_string(prefixLength);
+    hashString += gateway;
+
+    // Only want 8 hex digits.
+    hexId << std::hex << ((std::hash<std::string>{}(hashString)) & 0xFFFFFFFF);
+    return hexId.str();
+}
+
+std::string EthernetInterface::generateNeighborId(const std::string& ipAddress,
+                                                  const std::string& macAddress)
+{
+    std::stringstream hexId;
+    std::string hashString = ipAddress + macAddress;
+
+    // Only want 8 hex digits.
+    hexId << std::hex << ((std::hash<std::string>{}(hashString)) & 0xFFFFFFFF);
+    return hexId.str();
+}
+
+void EthernetInterface::deleteObject(const std::string& ipaddress)
+{
+    auto it = addrs.find(ipaddress);
+    if (it == addrs.end())
+    {
+        log<level::ERR>("DeleteObject:Unable to find the object.");
+        return;
+    }
+    this->addrs.erase(it);
+    manager.writeToConfigurationFile();
+}
+
+void EthernetInterface::deleteStaticNeighborObject(const std::string& ipAddress)
+{
+    auto it = staticNeighbors.find(ipAddress);
+    if (it == staticNeighbors.end())
+    {
+        log<level::ERR>(
+            "DeleteStaticNeighborObject:Unable to find the object.");
+        return;
+    }
+    staticNeighbors.erase(it);
+    manager.writeToConfigurationFile();
+}
+
+void EthernetInterface::deleteVLANFromSystem(const std::string& interface)
+{
+    auto confDir = manager.getConfDir();
+    fs::path networkFile = confDir;
+    networkFile /= systemd::config::networkFilePrefix + interface +
+                   systemd::config::networkFileSuffix;
+
+    fs::path deviceFile = confDir;
+    deviceFile /= interface + systemd::config::deviceFileSuffix;
+
+    // delete the vlan network file
+    if (fs::is_regular_file(networkFile))
+    {
+        fs::remove(networkFile);
+    }
+
+    // delete the vlan device file
+    if (fs::is_regular_file(deviceFile))
+    {
+        fs::remove(deviceFile);
+    }
+
+    // TODO  systemd doesn't delete the virtual network interface
+    // even after deleting all the related configuartion.
+    // https://github.com/systemd/systemd/issues/6600
+    try
+    {
+        deleteInterface(interface);
+    }
+    catch (InternalFailure& e)
+    {
+        commit<InternalFailure>();
+    }
+}
+
+void EthernetInterface::deleteVLANObject(const std::string& interface)
+{
+    auto it = vlanInterfaces.find(interface);
+    if (it == vlanInterfaces.end())
+    {
+        log<level::ERR>("DeleteVLANObject:Unable to find the object",
+                        entry("INTERFACE=%s", interface.c_str()));
+        return;
+    }
+
+    deleteVLANFromSystem(interface);
+    // delete the interface
+    vlanInterfaces.erase(it);
+
+    manager.writeToConfigurationFile();
+}
+
+std::string EthernetInterface::generateObjectPath(
+    IP::Protocol addressType, const std::string& ipaddress,
+    uint8_t prefixLength, const std::string& gateway) const
+{
+    std::string type = convertForMessage(addressType);
+    type = type.substr(type.rfind('.') + 1);
+    std::transform(type.begin(), type.end(), type.begin(), ::tolower);
+
+    std::filesystem::path objectPath;
+    objectPath /= objPath;
+    objectPath /= type;
+    objectPath /= generateId(ipaddress, prefixLength, gateway);
+    return objectPath.string();
+}
+
+std::string EthernetInterface::generateStaticNeighborObjectPath(
+    const std::string& ipAddress, const std::string& macAddress) const
+{
+    std::filesystem::path objectPath;
+    objectPath /= objPath;
+    objectPath /= "static_neighbor";
+    objectPath /= generateNeighborId(ipAddress, macAddress);
+    return objectPath.string();
+}
+
+bool EthernetInterface::ipv6AcceptRA(bool value)
+{
+    if (value == EthernetInterfaceIntf::ipv6AcceptRA())
+    {
+        return value;
+    }
+    EthernetInterfaceIntf::ipv6AcceptRA(value);
+    manager.writeToConfigurationFile();
+    return value;
+}
+
+EthernetInterface::DHCPConf EthernetInterface::dhcpEnabled(DHCPConf value)
+{
+    if (value == EthernetInterfaceIntf::dhcpEnabled())
+    {
+        return value;
+    }
+
+    EthernetInterfaceIntf::dhcpEnabled(value);
+    manager.writeToConfigurationFile();
+    return value;
+}
+
+bool EthernetInterface::linkUp() const
+{
+    EthernetIntfSocket eifSocket(PF_INET, SOCK_DGRAM, IPPROTO_IP);
+    bool value = EthernetInterfaceIntf::linkUp();
+
+    if (eifSocket.sock < 0)
+    {
+        return value;
+    }
+
+    ifreq ifr = {};
+    std::strncpy(ifr.ifr_name, interfaceName().c_str(), IF_NAMESIZE - 1);
+    if (ioctl(eifSocket.sock, SIOCGIFFLAGS, &ifr) == 0)
+    {
+        value = static_cast<bool>(ifr.ifr_flags & IFF_RUNNING);
+    }
+    else
+    {
+        log<level::ERR>("ioctl failed for SIOCGIFFLAGS:",
+                        entry("ERROR=%s", strerror(errno)));
+    }
+    return value;
+}
+
+bool EthernetInterface::nicEnabled() const
+{
+    EthernetIntfSocket eifSocket(PF_INET, SOCK_DGRAM, IPPROTO_IP);
+    bool value = EthernetInterfaceIntf::nicEnabled();
+
+    if (eifSocket.sock < 0)
+    {
+        return value;
+    }
+
+    ifreq ifr = {};
+    std::strncpy(ifr.ifr_name, interfaceName().c_str(), IF_NAMESIZE - 1);
+    if (ioctl(eifSocket.sock, SIOCGIFFLAGS, &ifr) == 0)
+    {
+        value = static_cast<bool>(ifr.ifr_flags & IFF_UP);
+    }
+    else
+    {
+        log<level::ERR>("ioctl failed for SIOCGIFFLAGS:",
+                        entry("ERROR=%s", strerror(errno)));
+    }
+    return value;
+}
+
+bool EthernetInterface::nicEnabled(bool value)
+{
+    if (value == EthernetInterfaceIntf::nicEnabled())
+    {
+        return value;
+    }
+
+    EthernetIntfSocket eifSocket(PF_INET, SOCK_DGRAM, IPPROTO_IP);
+    if (eifSocket.sock < 0)
+    {
+        return EthernetInterfaceIntf::nicEnabled();
+    }
+
+    ifreq ifr = {};
+    std::strncpy(ifr.ifr_name, interfaceName().c_str(), IF_NAMESIZE - 1);
+    if (ioctl(eifSocket.sock, SIOCGIFFLAGS, &ifr) != 0)
+    {
+        log<level::ERR>("ioctl failed for SIOCGIFFLAGS:",
+                        entry("ERROR=%s", strerror(errno)));
+        return EthernetInterfaceIntf::nicEnabled();
+    }
+
+    ifr.ifr_flags &= ~IFF_UP;
+    ifr.ifr_flags |= value ? IFF_UP : 0;
+
+    if (ioctl(eifSocket.sock, SIOCSIFFLAGS, &ifr) != 0)
+    {
+        log<level::ERR>("ioctl failed for SIOCSIFFLAGS:",
+                        entry("ERROR=%s", strerror(errno)));
+        return EthernetInterfaceIntf::nicEnabled();
+    }
+    EthernetInterfaceIntf::nicEnabled(value);
+    writeConfigurationFile();
+
+    return value;
+}
+
+ServerList EthernetInterface::nameservers(ServerList /*value*/)
+{
+    elog<NotAllowed>(NotAllowedArgument::REASON("ReadOnly Property"));
+    return EthernetInterfaceIntf::nameservers();
+}
+
+ServerList EthernetInterface::staticNameServers(ServerList value)
+{
+    for (const auto& nameserverip : value)
+    {
+        if (!isValidIP(AF_INET, nameserverip) &&
+            !isValidIP(AF_INET6, nameserverip))
+        {
+            log<level::ERR>("Not a valid IP address"),
+                entry("ADDRESS=%s", nameserverip.c_str());
+            elog<InvalidArgument>(
+                Argument::ARGUMENT_NAME("StaticNameserver"),
+                Argument::ARGUMENT_VALUE(nameserverip.c_str()));
+        }
+    }
+    try
+    {
+        EthernetInterfaceIntf::staticNameServers(value);
+        writeConfigurationFile();
+        // resolved reads the DNS server configuration from the
+        // network file.
+        manager.restartSystemdUnit(networkdService);
+    }
+    catch (InternalFailure& e)
+    {
+        log<level::ERR>("Exception processing DNS entries");
+    }
+    return EthernetInterfaceIntf::staticNameServers();
+}
+
+void EthernetInterface::loadNameServers()
+{
+    EthernetInterfaceIntf::nameservers(getNameServerFromResolvd());
+    EthernetInterfaceIntf::staticNameServers(getstaticNameServerFromConf());
+}
+
+ServerList EthernetInterface::getstaticNameServerFromConf()
+{
+    fs::path confPath = manager.getConfDir();
+
+    std::string fileName = systemd::config::networkFilePrefix +
+                           interfaceName() + systemd::config::networkFileSuffix;
+    confPath /= fileName;
+    ServerList servers;
+    config::Parser parser(confPath.string());
+    auto rc = config::ReturnCode::SUCCESS;
+
+    std::tie(rc, servers) = parser.getValues("Network", "DNS");
+    if (rc != config::ReturnCode::SUCCESS)
+    {
+        log<level::DEBUG>("Unable to get the value for network[DNS]",
+                          entry("RC=%d", rc));
+    }
+    return servers;
+}
+
+ServerList EthernetInterface::getNameServerFromResolvd()
+{
+    ServerList servers;
+    std::string OBJ_PATH = RESOLVED_SERVICE_PATH + std::to_string(ifIndex());
+
+    /*
+      The DNS property under org.freedesktop.resolve1.Link interface contains
+      an array containing all DNS servers currently used by resolved. It
+      contains similar information as the DNS server data written to
+      /run/systemd/resolve/resolv.conf.
+
+      Each structure in the array consists of a numeric network interface index,
+      an address family, and a byte array containing the DNS server address
+      (either 4 bytes in length for IPv4 or 16 bytes in lengths for IPv6).
+      The array contains DNS servers configured system-wide, including those
+      possibly read from a foreign /etc/resolv.conf or the DNS= setting in
+      /etc/systemd/resolved.conf, as well as per-interface DNS server
+      information either retrieved from systemd-networkd or configured by
+      external software via SetLinkDNS().
+    */
+
+    using type = std::vector<std::tuple<int32_t, std::vector<uint8_t>>>;
+    std::variant<type> name; // Variable to capture the DNS property
+    auto method = bus.new_method_call(RESOLVED_SERVICE, OBJ_PATH.c_str(),
+                                      PROPERTY_INTERFACE, METHOD_GET);
+
+    method.append(RESOLVED_INTERFACE, "DNS");
+    auto reply = bus.call(method);
+
+    try
+    {
+        reply.read(name);
+    }
+    catch (const sdbusplus::exception::SdBusError& e)
+    {
+        log<level::ERR>("Failed to get DNS information from Systemd-Resolved");
+    }
+    auto tupleVector = std::get_if<type>(&name);
+    for (auto i = tupleVector->begin(); i != tupleVector->end(); ++i)
+    {
+        int addressFamily = std::get<0>(*i);
+        std::vector<uint8_t>& ipaddress = std::get<1>(*i);
+
+        switch (addressFamily)
+        {
+            case AF_INET:
+                if (ipaddress.size() == sizeof(struct in_addr))
+                {
+                    servers.push_back(toString(
+                        *reinterpret_cast<struct in_addr*>(ipaddress.data())));
+                }
+                else
+                {
+                    log<level::ERR>(
+                        "Invalid data recived from Systemd-Resolved");
+                }
+                break;
+
+            case AF_INET6:
+                if (ipaddress.size() == sizeof(struct in6_addr))
+                {
+                    servers.push_back(toString(
+                        *reinterpret_cast<struct in6_addr*>(ipaddress.data())));
+                }
+                else
+                {
+                    log<level::ERR>(
+                        "Invalid data recived from Systemd-Resolved");
+                }
+                break;
+
+            default:
+                log<level::ERR>(
+                    "Unsupported address family in DNS from Systemd-Resolved");
+                break;
+        }
+    }
+    return servers;
+}
+
+void EthernetInterface::loadVLAN(VlanId id)
+{
+    std::string vlanInterfaceName = interfaceName() + "." + std::to_string(id);
+    std::string path = objPath;
+    path += "_" + std::to_string(id);
+
+    DHCPConf dhcpEnabled =
+        getDHCPValue(manager.getConfDir().string(), vlanInterfaceName);
+    auto vlanIntf = std::make_unique<phosphor::network::VlanInterface>(
+        bus, path.c_str(), dhcpEnabled, EthernetInterfaceIntf::nicEnabled(), id,
+        *this, manager);
+
+    // Fetch the ip address from the system
+    // and create the dbus object.
+    vlanIntf->createIPAddressObjects();
+    vlanIntf->createStaticNeighborObjects();
+
+    this->vlanInterfaces.emplace(std::move(vlanInterfaceName),
+                                 std::move(vlanIntf));
+}
+
+ObjectPath EthernetInterface::createVLAN(VlanId id)
+{
+    std::string vlanInterfaceName = interfaceName() + "." + std::to_string(id);
+    std::string path = objPath;
+    path += "_" + std::to_string(id);
+
+    // Pass the parents nicEnabled property, so that the child
+    // VLAN interface can inherit.
+
+    auto vlanIntf = std::make_unique<phosphor::network::VlanInterface>(
+        bus, path.c_str(), EthernetInterface::DHCPConf::none,
+        EthernetInterfaceIntf::nicEnabled(), id, *this, manager);
+
+    // write the device file for the vlan interface.
+    vlanIntf->writeDeviceFile();
+
+    this->vlanInterfaces.emplace(vlanInterfaceName, std::move(vlanIntf));
+    // write the new vlan device entry to the configuration(network) file.
+    manager.writeToConfigurationFile();
+
+    return path;
+}
+
+bool EthernetInterface::getIPv6AcceptRAFromConf()
+{
+    fs::path confPath = manager.getConfDir();
+
+    std::string fileName = systemd::config::networkFilePrefix +
+                           interfaceName() + systemd::config::networkFileSuffix;
+    confPath /= fileName;
+    config::ValueList values;
+    config::Parser parser(confPath.string());
+    auto rc = config::ReturnCode::SUCCESS;
+    std::tie(rc, values) = parser.getValues("Network", "IPv6AcceptRA");
+    if (rc != config::ReturnCode::SUCCESS)
+    {
+        log<level::DEBUG>("Unable to get the value for Network[IPv6AcceptRA]",
+                          entry("rc=%d", rc));
+        return false;
+    }
+    return (values[0] == "true");
+}
+
+ServerList EthernetInterface::getNTPServersFromConf()
+{
+    fs::path confPath = manager.getConfDir();
+
+    std::string fileName = systemd::config::networkFilePrefix +
+                           interfaceName() + systemd::config::networkFileSuffix;
+    confPath /= fileName;
+
+    ServerList servers;
+    config::Parser parser(confPath.string());
+    auto rc = config::ReturnCode::SUCCESS;
+
+    std::tie(rc, servers) = parser.getValues("Network", "NTP");
+    if (rc != config::ReturnCode::SUCCESS)
+    {
+        log<level::DEBUG>("Unable to get the value for Network[NTP]",
+                          entry("rc=%d", rc));
+    }
+
+    return servers;
+}
+
+ServerList EthernetInterface::ntpServers(ServerList servers)
+{
+    auto ntpServers = EthernetInterfaceIntf::ntpServers(servers);
+
+    writeConfigurationFile();
+    // timesynchd reads the NTP server configuration from the
+    // network file.
+    manager.restartSystemdUnit(networkdService);
+    return ntpServers;
+}
+// Need to merge the below function with the code which writes the
+// config file during factory reset.
+// TODO openbmc/openbmc#1751
+
+void EthernetInterface::writeConfigurationFile()
+{
+    // write all the static ip address in the systemd-network conf file
+
+    using namespace std::string_literals;
+    namespace fs = std::filesystem;
+
+    // if there is vlan interafce then write the configuration file
+    // for vlan also.
+
+    for (const auto& intf : vlanInterfaces)
+    {
+        intf.second->writeConfigurationFile();
+    }
+
+    fs::path confPath = manager.getConfDir();
+
+    std::string fileName = systemd::config::networkFilePrefix +
+                           interfaceName() + systemd::config::networkFileSuffix;
+    confPath /= fileName;
+    std::fstream stream;
+
+    stream.open(confPath.c_str(), std::fstream::out);
+    if (!stream.is_open())
+    {
+        log<level::ERR>("Unable to open the file",
+                        entry("FILE=%s", confPath.c_str()));
+        elog<InternalFailure>();
+    }
+
+    // Write the device
+    stream << "[Match]\n";
+    stream << "Name=" << interfaceName() << "\n";
+
+    auto addrs = getAddresses();
+
+    // Write the link section
+    stream << "[Link]\n";
+    auto mac = MacAddressIntf::macAddress();
+    if (!mac.empty())
+    {
+        stream << "MACAddress=" << mac << "\n";
+    }
+
+    if (!EthernetInterfaceIntf::nicEnabled())
+    {
+        stream << "Unmanaged=yes\n";
+    }
+
+    // write the network section
+    stream << "[Network]\n";
+#ifdef LINK_LOCAL_AUTOCONFIGURATION
+    stream << "LinkLocalAddressing=yes\n";
+#else
+    stream << "LinkLocalAddressing=no\n";
+#endif
+    stream << std::boolalpha
+           << "IPv6AcceptRA=" << EthernetInterfaceIntf::ipv6AcceptRA() << "\n";
+
+    // Add the VLAN entry
+    for (const auto& intf : vlanInterfaces)
+    {
+        stream << "VLAN=" << intf.second->EthernetInterface::interfaceName()
+               << "\n";
+    }
+    // Add the NTP server
+    for (const auto& ntp : EthernetInterfaceIntf::ntpServers())
+    {
+        stream << "NTP=" << ntp << "\n";
+    }
+
+    // Add the DNS entry
+    for (const auto& dns : EthernetInterfaceIntf::staticNameServers())
+    {
+        stream << "DNS=" << dns << "\n";
+    }
+
+    // Add the DHCP entry
+    stream << "DHCP="s +
+                  mapDHCPToSystemd[EthernetInterfaceIntf::dhcpEnabled()] + "\n";
+
+    // Static IP addresses
+    for (const auto& addr : addrs)
+    {
+        if (originIsManuallyAssigned(addr.second->origin()) &&
+            !dhcpIsEnabled(addr.second->type()))
+        {
+            // Process all static addresses
+            std::string address = addr.second->address() + "/" +
+                                  std::to_string(addr.second->prefixLength());
+
+            // build the address entries. Do not use [Network] shortcuts to
+            // insert address entries.
+            stream << "[Address]\n";
+            stream << "Address=" << address << "\n";
+        }
+    }
+
+    auto gateway = EthernetInterfaceIntf::defaultGateway();
+    if (!gateway.empty())
+    {
+        stream << "[Route]\n";
+        stream << "Gateway=" << gateway << "\n";
+    }
+
+    auto gateway6 = EthernetInterfaceIntf::defaultGateway6();
+    if (!gateway6.empty())
+    {
+        stream << "[Route]\n";
+        stream << "Gateway=" << gateway6 << "\n";
+    }
+
+    if (manager.getSystemConf())
+    {
+        const auto& gateway = manager.getSystemConf()->defaultGateway();
+        if (!gateway.empty())
+        {
+            stream << "[Route]\n";
+            stream << "Gateway=" << gateway << "\n";
+        }
+        const auto& gateway6 = manager.getSystemConf()->defaultGateway6();
+        if (!gateway6.empty())
+        {
+            stream << "[Route]\n";
+            stream << "Gateway=" << gateway6 << "\n";
+        }
+    }
+
+    // Write the neighbor sections
+    for (const auto& neighbor : staticNeighbors)
+    {
+        stream << "[Neighbor]"
+               << "\n";
+        stream << "Address=" << neighbor.second->ipAddress() << "\n";
+        stream << "MACAddress=" << neighbor.second->macAddress() << "\n";
+    }
+
+    // Write the dhcp section irrespective of whether DHCP is enabled or not
+    writeDHCPSection(stream);
+
+    stream.close();
+}
+
+void EthernetInterface::writeDHCPSection(std::fstream& stream)
+{
+    using namespace std::string_literals;
+    // write the dhcp section
+    stream << "[DHCP]\n";
+
+    // Hardcoding the client identifier to mac, to address below issue
+    // https://github.com/openbmc/openbmc/issues/1280
+    stream << "ClientIdentifier=mac\n";
+    if (manager.getDHCPConf())
+    {
+        auto value = manager.getDHCPConf()->dnsEnabled() ? "true"s : "false"s;
+        stream << "UseDNS="s + value + "\n";
+
+        value = manager.getDHCPConf()->ntpEnabled() ? "true"s : "false"s;
+        stream << "UseNTP="s + value + "\n";
+
+        value = manager.getDHCPConf()->hostNameEnabled() ? "true"s : "false"s;
+        stream << "UseHostname="s + value + "\n";
+
+        value =
+            manager.getDHCPConf()->sendHostNameEnabled() ? "true"s : "false"s;
+        stream << "SendHostname="s + value + "\n";
+    }
+}
+
+std::string EthernetInterface::macAddress(std::string value)
+{
+    ether_addr newMAC;
+    try
+    {
+        newMAC = mac_address::fromString(value);
+    }
+    catch (std::invalid_argument&)
+    {
+        log<level::ERR>("MACAddress is not valid.",
+                        entry("MAC=%s", value.c_str()));
+        elog<InvalidArgument>(Argument::ARGUMENT_NAME("MACAddress"),
+                              Argument::ARGUMENT_VALUE(value.c_str()));
+    }
+    if (!mac_address::isUnicast(newMAC))
+    {
+        log<level::ERR>("MACAddress is not valid.",
+                        entry("MAC=%s", value.c_str()));
+        elog<InvalidArgument>(Argument::ARGUMENT_NAME("MACAddress"),
+                              Argument::ARGUMENT_VALUE(value.c_str()));
+    }
+
+    auto interface = interfaceName();
+    std::string validMAC = mac_address::toString(newMAC);
+
+    // We don't need to update the system if the address is unchanged
+    ether_addr oldMAC = mac_address::fromString(MacAddressIntf::macAddress());
+    if (!stdplus::raw::equal(newMAC, oldMAC))
+    {
+        // Update everything that depends on the MAC value
+        for (const auto& [name, intf] : vlanInterfaces)
+        {
+            intf->MacAddressIntf::macAddress(validMAC);
+        }
+        MacAddressIntf::macAddress(validMAC);
+
+        // TODO: would remove the call below and
+        //      just restart systemd-netwokd
+        //      through https://github.com/systemd/systemd/issues/6696
+        execute("/sbin/ip", "ip", "link", "set", "dev", interface.c_str(),
+                "down");
+        manager.writeToConfigurationFile();
+    }
+
+#ifdef HAVE_UBOOT_ENV
+    // Ensure that the valid address is stored in the u-boot-env
+    auto envVar = interfaceToUbootEthAddr(interface.c_str());
+    if (envVar)
+    {
+        // Trimming MAC addresses that are out of range. eg: AA:FF:FF:FF:FF:100;
+        // and those having more than 6 bytes. eg: AA:AA:AA:AA:AA:AA:BB
+        execute("/sbin/fw_setenv", "fw_setenv", envVar->c_str(),
+                validMAC.c_str());
+    }
+#endif // HAVE_UBOOT_ENV
+
+    return value;
+}
+
+void EthernetInterface::deleteAll()
+{
+    if (dhcpIsEnabled(IP::Protocol::IPv4, true))
+    {
+        log<level::INFO>("DHCP enabled on the interface"),
+            entry("INTERFACE=%s", interfaceName().c_str());
+    }
+
+    // clear all the ip on the interface
+    addrs.clear();
+    manager.writeToConfigurationFile();
+}
+
+std::string EthernetInterface::defaultGateway(std::string gateway)
+{
+    auto gw = EthernetInterfaceIntf::defaultGateway();
+    if (gw == gateway)
+    {
+        return gw;
+    }
+
+    if (!isValidIP(AF_INET, gateway))
+    {
+        log<level::ERR>("Not a valid v4 Gateway",
+                        entry("GATEWAY=%s", gateway.c_str()));
+        elog<InvalidArgument>(Argument::ARGUMENT_NAME("GATEWAY"),
+                              Argument::ARGUMENT_VALUE(gateway.c_str()));
+    }
+    gw = EthernetInterfaceIntf::defaultGateway(gateway);
+    manager.writeToConfigurationFile();
+    return gw;
+}
+
+std::string EthernetInterface::defaultGateway6(std::string gateway)
+{
+    auto gw = EthernetInterfaceIntf::defaultGateway6();
+    if (gw == gateway)
+    {
+        return gw;
+    }
+
+    if (!isValidIP(AF_INET6, gateway))
+    {
+        log<level::ERR>("Not a valid v6 Gateway",
+                        entry("GATEWAY=%s", gateway.c_str()));
+        elog<InvalidArgument>(Argument::ARGUMENT_NAME("GATEWAY"),
+                              Argument::ARGUMENT_VALUE(gateway.c_str()));
+    }
+    gw = EthernetInterfaceIntf::defaultGateway6(gateway);
+    manager.writeToConfigurationFile();
+    return gw;
+}
+} // namespace network
+} // namespace phosphor
diff --git a/src/ethernet_interface.hpp b/src/ethernet_interface.hpp
new file mode 100644
index 0000000..8e28b51
--- /dev/null
+++ b/src/ethernet_interface.hpp
@@ -0,0 +1,372 @@
+#pragma once
+
+#include "types.hpp"
+#include "util.hpp"
+#include "xyz/openbmc_project/Network/IP/Create/server.hpp"
+#include "xyz/openbmc_project/Network/Neighbor/CreateStatic/server.hpp"
+
+#include <filesystem>
+#include <sdbusplus/bus.hpp>
+#include <sdbusplus/server/object.hpp>
+#include <string>
+#include <xyz/openbmc_project/Collection/DeleteAll/server.hpp>
+#include <xyz/openbmc_project/Network/EthernetInterface/server.hpp>
+#include <xyz/openbmc_project/Network/MACAddress/server.hpp>
+
+#ifndef SDBUSPP_NEW_CAMELCASE
+#define dhcpEnabled dHCPEnabled
+#define ip iP
+#define ipAddress iPAddress
+#define ipv6AcceptRA iPv6AcceptRA
+#define macAddress mACAddress
+#define nicEnabled nICEnabled
+#define ntpServers nTPServers
+#endif
+
+namespace phosphor
+{
+namespace network
+{
+
+using Ifaces = sdbusplus::server::object::object<
+    sdbusplus::xyz::openbmc_project::Network::server::EthernetInterface,
+    sdbusplus::xyz::openbmc_project::Network::server::MACAddress,
+    sdbusplus::xyz::openbmc_project::Network::IP::server::Create,
+    sdbusplus::xyz::openbmc_project::Network::Neighbor::server::CreateStatic,
+    sdbusplus::xyz::openbmc_project::Collection::server::DeleteAll>;
+
+using IP = sdbusplus::xyz::openbmc_project::Network::server::IP;
+
+using EthernetInterfaceIntf =
+    sdbusplus::xyz::openbmc_project::Network::server::EthernetInterface;
+using MacAddressIntf =
+    sdbusplus::xyz::openbmc_project::Network::server::MACAddress;
+
+using ServerList = std::vector<std::string>;
+using ObjectPath = sdbusplus::message::object_path;
+
+namespace fs = std::filesystem;
+
+class Manager; // forward declaration of network manager.
+
+class TestEthernetInterface;
+
+class VlanInterface;
+
+class IPAddress;
+
+class Neighbor;
+
+using LinkSpeed = uint16_t;
+using DuplexMode = uint8_t;
+using Autoneg = uint8_t;
+using LinkUp = bool;
+using NICEnabled = bool;
+using VlanId = uint32_t;
+using InterfaceName = std::string;
+using InterfaceInfo =
+    std::tuple<LinkSpeed, DuplexMode, Autoneg, LinkUp, NICEnabled>;
+using AddressMap = std::map<std::string, std::shared_ptr<IPAddress>>;
+using NeighborMap = std::map<std::string, std::shared_ptr<Neighbor>>;
+using VlanInterfaceMap =
+    std::map<InterfaceName, std::unique_ptr<VlanInterface>>;
+
+/** @class EthernetInterface
+ *  @brief OpenBMC Ethernet Interface implementation.
+ *  @details A concrete implementation for the
+ *  xyz.openbmc_project.Network.EthernetInterface DBus API.
+ */
+class EthernetInterface : public Ifaces
+{
+  public:
+    EthernetInterface() = delete;
+    EthernetInterface(const EthernetInterface&) = delete;
+    EthernetInterface& operator=(const EthernetInterface&) = delete;
+    EthernetInterface(EthernetInterface&&) = delete;
+    EthernetInterface& operator=(EthernetInterface&&) = delete;
+    virtual ~EthernetInterface() = default;
+
+    /** @brief Constructor to put object onto bus at a dbus path.
+     *  @param[in] bus - Bus to attach to.
+     *  @param[in] objPath - Path to attach at.
+     *  @param[in] dhcpEnabled - is dhcp enabled(true/false).
+     *  @param[in] parent - parent object.
+     *  @param[in] emitSignal - true if the object added signal needs to be
+     *                          send.
+     */
+    EthernetInterface(sdbusplus::bus::bus& bus, const std::string& objPath,
+                      DHCPConf dhcpEnabled, Manager& parent,
+                      bool emitSignal = true);
+
+    /** @brief Function used to load the nameservers.
+     */
+    virtual void loadNameServers();
+
+    /** @brief Function to create ipAddress dbus object.
+     *  @param[in] addressType - Type of ip address.
+     *  @param[in] ipAddress- IP address.
+     *  @param[in] prefixLength - Length of prefix.
+     *  @param[in] gateway - Gateway ip address.
+     */
+
+    ObjectPath ip(IP::Protocol addressType, std::string ipAddress,
+                  uint8_t prefixLength, std::string gateway) override;
+
+    /** @brief Function to create static neighbor dbus object.
+     *  @param[in] ipAddress - IP address.
+     *  @param[in] macAddress - Low level MAC address.
+     */
+    ObjectPath neighbor(std::string ipAddress, std::string macAddress) override;
+
+    /* @brief delete the dbus object of the given ipAddress.
+     * @param[in] ipAddress - IP address.
+     */
+    void deleteObject(const std::string& ipAddress);
+
+    /* @brief delete the dbus object of the given ipAddress.
+     * @param[in] ipAddress - IP address.
+     */
+    void deleteStaticNeighborObject(const std::string& ipAddress);
+
+    /* @brief delete the vlan dbus object of the given interface.
+     *        Also deletes the device file and the network file.
+     * @param[in] interface - VLAN Interface.
+     */
+    void deleteVLANObject(const std::string& interface);
+
+    /* @brief creates the dbus object(IPaddres) given in the address list.
+     * @param[in] addrs - address list for which dbus objects needs
+     *                    to create.
+     */
+    void createIPAddressObjects();
+
+    /* @brief creates the dbus object(Neighbor) given in the neighbor list.
+     */
+    void createStaticNeighborObjects();
+
+    /* @brief Gets the index of the interface on the system
+     */
+    unsigned ifIndex() const;
+
+    /* @brief Gets all the ip addresses.
+     * @returns the list of ipAddress.
+     */
+    const AddressMap& getAddresses() const
+    {
+        return addrs;
+    }
+
+    /* @brief Gets all the static neighbor entries.
+     * @returns Static neighbor map.
+     */
+    const NeighborMap& getStaticNeighbors() const
+    {
+        return staticNeighbors;
+    }
+
+    /** Set value of DHCPEnabled */
+    DHCPConf dhcpEnabled(DHCPConf value) override;
+
+    /** @brief Selectively disables DHCP
+     *  @param[in] protocol - The IPv4 or IPv6 protocol to return to static
+     *                        addressing mode
+     */
+    void disableDHCP(IP::Protocol protocol);
+
+    /** Retrieve Link State */
+    bool linkUp() const override;
+
+    /** Retrieve NIC State */
+    bool nicEnabled() const override;
+
+    /** Set value of NICEnabled */
+    bool nicEnabled(bool value) override;
+
+    /** @brief sets the MAC address.
+     *  @param[in] value - MAC address which needs to be set on the system.
+     *  @returns macAddress of the interface or throws an error.
+     */
+    std::string macAddress(std::string value) override;
+
+    /** @brief get the IPv6AcceptRA flag from the network configuration file
+     *
+     */
+    bool getIPv6AcceptRAFromConf();
+
+    /** @brief check conf file for Router Advertisements
+     *
+     */
+    bool ipv6AcceptRA(bool value) override;
+
+    /** @brief sets the NTP servers.
+     *  @param[in] value - vector of NTP servers.
+     */
+    ServerList ntpServers(ServerList value) override;
+
+    /** @brief sets the DNS/nameservers.
+     *  @param[in] value - vector of DNS servers.
+     */
+    ServerList nameservers(ServerList value) override;
+
+    /** @brief sets the Static DNS/nameservers.
+     *  @param[in] value - vector of DNS servers.
+     */
+
+    ServerList staticNameServers(ServerList value) override;
+
+    /** @brief create Vlan interface.
+     *  @param[in] id- VLAN identifier.
+     */
+    ObjectPath createVLAN(VlanId id);
+
+    /** @brief load the vlan info from the system
+     *         and creates the ip address dbus objects.
+     *  @param[in] vlanID- VLAN identifier.
+     */
+    void loadVLAN(VlanId vlanID);
+
+    /** @brief write the network conf file with the in-memory objects.
+     */
+    void writeConfigurationFile();
+
+    /** @brief delete all dbus objects.
+     */
+    void deleteAll();
+
+    /** @brief set the default v4 gateway of the interface.
+     *  @param[in] gateway - default v4 gateway of the interface.
+     */
+    std::string defaultGateway(std::string gateway) override;
+
+    /** @brief set the default v6 gateway of the interface.
+     *  @param[in] gateway - default v6 gateway of the interface.
+     */
+    std::string defaultGateway6(std::string gateway) override;
+
+    using EthernetInterfaceIntf::dhcpEnabled;
+    using EthernetInterfaceIntf::interfaceName;
+    using EthernetInterfaceIntf::linkUp;
+    using EthernetInterfaceIntf::nicEnabled;
+    using MacAddressIntf::macAddress;
+
+    using EthernetInterfaceIntf::defaultGateway;
+    using EthernetInterfaceIntf::defaultGateway6;
+    /** @brief Absolute path of the resolv conf file */
+    static constexpr auto resolvConfFile = "/etc/resolv.conf";
+
+  protected:
+    /** @brief get the info of the ethernet interface.
+     *  @return tuple having the link speed,autonegotiation,duplexmode .
+     */
+    InterfaceInfo getInterfaceInfo() const;
+
+    /* @brief delete the vlan interface from system.
+     * @param[in] interface - vlan Interface.
+     */
+    void deleteVLANFromSystem(const std::string& interface);
+
+    /** @brief get the mac address of the interface.
+     *  @param[in] interfaceName - Network interface name.
+     *  @return macaddress on success
+     */
+
+    std::string getMACAddress(const std::string& interfaceName) const;
+
+    /** @brief construct the ip address dbus object path.
+     *  @param[in] addressType - Type of ip address.
+     *  @param[in] ipAddress - IP address.
+     *  @param[in] prefixLength - Length of prefix.
+     *  @param[in] gateway - Gateway address.
+
+     *  @return path of the address object.
+     */
+
+    std::string generateObjectPath(IP::Protocol addressType,
+                                   const std::string& ipAddress,
+                                   uint8_t prefixLength,
+                                   const std::string& gateway) const;
+
+    std::string
+        generateStaticNeighborObjectPath(const std::string& ipAddress,
+                                         const std::string& macAddress) const;
+
+    /** @brief generates the id by doing hash of ipAddress,
+     *         prefixlength and the gateway.
+     *  @param[in] ipAddress - IP address.
+     *  @param[in] prefixLength - Length of prefix.
+     *  @param[in] gateway - Gateway address.
+     *  @return hash string.
+     */
+
+    static std::string generateId(const std::string& ipAddress,
+                                  uint8_t prefixLength,
+                                  const std::string& gateway);
+
+    /** @brief generates the id by doing hash of ipAddress
+     *         and the mac address.
+     *  @param[in] ipAddress  - IP address.
+     *  @param[in] macAddress - Gateway address.
+     *  @return hash string.
+     */
+    static std::string generateNeighborId(const std::string& ipAddress,
+                                          const std::string& macAddress);
+
+    /** @brief write the dhcp section **/
+    void writeDHCPSection(std::fstream& stream);
+
+    /** @brief get the NTP server list from the network conf
+     *
+     */
+    ServerList getNTPServersFromConf();
+
+    /** @brief get the name server details from the network conf
+     *
+     */
+    virtual ServerList getNameServerFromResolvd();
+    ServerList getstaticNameServerFromConf();
+
+    /** @brief Persistent sdbusplus DBus bus connection. */
+    sdbusplus::bus::bus& bus;
+
+    /** @brief Network Manager object. */
+    Manager& manager;
+
+    /** @brief Persistent map of IPAddress dbus objects and their names */
+    AddressMap addrs;
+
+    /** @brief Persistent map of Neighbor dbus objects and their names */
+    NeighborMap staticNeighbors;
+
+    /** @brief Persistent map of VLAN interface dbus objects and their names */
+    VlanInterfaceMap vlanInterfaces;
+
+    /** @brief Dbus object path */
+    std::string objPath;
+
+    friend class TestEthernetInterface;
+
+  private:
+    /** @brief Determines if DHCP is active for the IP::Protocol supplied.
+     *  @param[in] protocol - Either IPv4 or IPv6
+     *  @param[in] ignoreProtocol - Allows IPv4 and IPv6 to be checked using a
+     *                              single call.
+     *  @returns true/false value if DHCP is active for the input protocol
+     */
+    bool dhcpIsEnabled(IP::Protocol protocol, bool ignoreProtocol = false);
+
+    /** @brief Determines if DHCP will be active following next reconfig
+     *  @param[in] protocol - Either IPv4 or IPv6
+     *  @param[in] nextDHCPState - The new DHCP mode to take affect
+     *  @returns true/false value if DHCP is active for the input protocol
+     */
+    bool dhcpToBeEnabled(IP::Protocol family, const std::string& nextDHCPState);
+
+    /** @brief Determines if the address is manually assigned
+     *  @param[in] origin - The origin entry of the IP::Address
+     *  @returns true/false value if the address is static
+     */
+    bool originIsManuallyAssigned(IP::AddressOrigin origin);
+};
+
+} // namespace network
+} // namespace phosphor
diff --git a/src/ipaddress.cpp b/src/ipaddress.cpp
new file mode 100644
index 0000000..f30cf34
--- /dev/null
+++ b/src/ipaddress.cpp
@@ -0,0 +1,73 @@
+#include "config.h"
+
+#include "ipaddress.hpp"
+
+#include "ethernet_interface.hpp"
+#include "util.hpp"
+
+#include <phosphor-logging/elog-errors.hpp>
+#include <phosphor-logging/log.hpp>
+#include <xyz/openbmc_project/Common/error.hpp>
+namespace phosphor
+{
+namespace network
+{
+
+using namespace phosphor::logging;
+using namespace sdbusplus::xyz::openbmc_project::Common::Error;
+using NotAllowed = sdbusplus::xyz::openbmc_project::Common::Error::NotAllowed;
+using Reason = xyz::openbmc_project::Common::NotAllowed::REASON;
+
+IPAddress::IPAddress(sdbusplus::bus::bus& bus, const char* objPath,
+                     EthernetInterface& parent, IP::Protocol type,
+                     const std::string& ipaddress, IP::AddressOrigin origin,
+                     uint8_t prefixLength, const std::string& gateway) :
+    IPIfaces(bus, objPath, true),
+    parent(parent)
+{
+
+    IP::address(ipaddress);
+    IP::prefixLength(prefixLength);
+    IP::gateway(gateway);
+    IP::type(type);
+    IP::origin(origin);
+
+    // Emit deferred signal.
+    emit_object_added();
+}
+std::string IPAddress::address(std::string /*ipAddress*/)
+{
+    elog<NotAllowed>(Reason("Property update is not allowed"));
+}
+uint8_t IPAddress::prefixLength(uint8_t /*value*/)
+{
+    elog<NotAllowed>(Reason("Property update is not allowed"));
+}
+std::string IPAddress::gateway(std::string /*gateway*/)
+{
+    elog<NotAllowed>(Reason("Property update is not allowed"));
+}
+IP::Protocol IPAddress::type(IP::Protocol /*type*/)
+{
+    elog<NotAllowed>(Reason("Property update is not allowed"));
+}
+IP::AddressOrigin IPAddress::origin(IP::AddressOrigin /*origin*/)
+{
+    elog<NotAllowed>(Reason("Property update is not allowed"));
+}
+void IPAddress::delete_()
+{
+    if (origin() != IP::AddressOrigin::Static)
+    {
+        log<level::ERR>("Tried to delete a non-static address"),
+            entry("ADDRESS=%s", address().c_str()),
+            entry("PREFIX=%" PRIu8, prefixLength()),
+            entry("INTERFACE=%s", parent.interfaceName().c_str());
+        elog<InternalFailure>();
+    }
+
+    parent.deleteObject(address());
+}
+
+} // namespace network
+} // namespace phosphor
diff --git a/src/ipaddress.hpp b/src/ipaddress.hpp
new file mode 100644
index 0000000..3ed551a
--- /dev/null
+++ b/src/ipaddress.hpp
@@ -0,0 +1,75 @@
+#pragma once
+
+#include <sdbusplus/bus.hpp>
+#include <sdbusplus/server/object.hpp>
+#include <string>
+#include <xyz/openbmc_project/Network/IP/server.hpp>
+#include <xyz/openbmc_project/Object/Delete/server.hpp>
+
+namespace phosphor
+{
+namespace network
+{
+
+using IPIfaces = sdbusplus::server::object::object<
+    sdbusplus::xyz::openbmc_project::Network::server::IP,
+    sdbusplus::xyz::openbmc_project::Object::server::Delete>;
+
+using IP = sdbusplus::xyz::openbmc_project::Network::server::IP;
+
+class EthernetInterface;
+
+/** @class IPAddress
+ *  @brief OpenBMC IPAddress implementation.
+ *  @details A concrete implementation for the
+ *  xyz.openbmc_project.Network.IPProtocol
+ *  xyz.openbmc_project.Network.IP Dbus interfaces.
+ */
+class IPAddress : public IPIfaces
+{
+  public:
+    IPAddress() = delete;
+    IPAddress(const IPAddress&) = delete;
+    IPAddress& operator=(const IPAddress&) = delete;
+    IPAddress(IPAddress&&) = delete;
+    IPAddress& operator=(IPAddress&&) = delete;
+    virtual ~IPAddress() = default;
+
+    /** @brief Constructor to put object onto bus at a dbus path.
+     *  @param[in] bus - Bus to attach to.
+     *  @param[in] objPath - Path to attach at.
+     *  @param[in] parent - Parent object.
+     *  @param[in] type - ipaddress type(v4/v6).
+     *  @param[in] ipAddress - ipadress.
+     *  @param[in] origin - origin of ipaddress(dhcp/static/SLAAC/LinkLocal).
+     *  @param[in] prefixLength - Length of prefix.
+     *  @param[in] gateway - gateway address.
+     */
+    IPAddress(sdbusplus::bus::bus& bus, const char* objPath,
+              EthernetInterface& parent, IP::Protocol type,
+              const std::string& ipAddress, IP::AddressOrigin origin,
+              uint8_t prefixLength, const std::string& gateway);
+
+    std::string address(std::string ipAddress) override;
+    uint8_t prefixLength(uint8_t) override;
+    std::string gateway(std::string gateway) override;
+    IP::Protocol type(IP::Protocol type) override;
+    IP::AddressOrigin origin(IP::AddressOrigin origin) override;
+
+    /** @brief Delete this d-bus object.
+     */
+    void delete_() override;
+
+    using IP::address;
+    using IP::gateway;
+    using IP::origin;
+    using IP::prefixLength;
+    using IP::type;
+
+  private:
+    /** @brief Parent Object. */
+    EthernetInterface& parent;
+};
+
+} // namespace network
+} // namespace phosphor
diff --git a/src/meson.build b/src/meson.build
new file mode 100644
index 0000000..31371f9
--- /dev/null
+++ b/src/meson.build
@@ -0,0 +1,94 @@
+phosphor_dbus_interfaces_dep = dependency('phosphor-dbus-interfaces')
+phosphor_logging_dep = dependency('phosphor-logging')
+
+src_includes = include_directories('.')
+
+executable(
+  'ncsi-netlink',
+  'argument.cpp',
+  'ncsi_netlink_main.cpp',
+  'ncsi_util.cpp',
+  implicit_include_directories: false,
+  include_directories: src_includes,
+  dependencies: [
+    dependency('libnl-3.0'),
+    dependency('libnl-genl-3.0'),
+    phosphor_dbus_interfaces_dep,
+    phosphor_logging_dep,
+  ],
+  install: true,
+  install_dir: get_option('bindir'))
+
+json_dep = declare_dependency()
+if get_option('sync-mac')
+  # nlohmann_json might not have a pkg-config. It is header only so just make
+  # sure we can access the needed symbols from the header.
+  has_json = meson.get_compiler('cpp').has_header_symbol(
+    'nlohmann/json.hpp',
+    'nlohmann::json::string_t',
+    required: false)
+  if not has_json
+    json_dep = dependency(
+      'nlohmann_json',
+      fallback: ['nlohmann_json', 'nlohmann_json_dep'],
+      required: true)
+  endif
+endif
+
+networkd_deps = [
+  json_dep,
+  phosphor_dbus_interfaces_dep,
+  phosphor_logging_dep,
+  sdbusplus_dep,
+  dependency('sdeventplus', fallback: ['sdeventplus', 'sdeventplus_dep']),
+  dependency('stdplus', fallback: ['stdplus', 'stdplus_dep']),
+]
+
+conf_header = configure_file(
+  output: 'config.h',
+  configuration: conf_data)
+
+networkd_generated = [
+  conf_header,
+] + generated_sources
+
+networkd_includes = [
+  src_includes,
+  generated_includes,
+]
+
+networkd_lib = static_library(
+  'networkd',
+  networkd_generated,
+  'ethernet_interface.cpp',
+  'neighbor.cpp',
+  'ipaddress.cpp',
+  'netlink.cpp',
+  'network_config.cpp',
+  'network_manager.cpp',
+  'system_configuration.cpp',
+  'util.cpp',
+  'routing_table.cpp',
+  'config_parser.cpp',
+  'dhcp_configuration.cpp',
+  'vlan_interface.cpp',
+  'rtnetlink_server.cpp',
+  'dns_updater.cpp',
+  'watch.cpp',
+  implicit_include_directories: false,
+  include_directories: networkd_includes,
+  dependencies: networkd_deps)
+
+networkd_dep = declare_dependency(
+  sources: networkd_generated,
+  dependencies: networkd_deps,
+  include_directories: networkd_includes,
+  link_with: networkd_lib)
+
+executable(
+  'phosphor-network-manager',
+  'network_manager_main.cpp',
+  implicit_include_directories: false,
+  dependencies: networkd_dep,
+  install: true,
+  install_dir: get_option('bindir'))
diff --git a/src/ncsi_netlink_main.cpp b/src/ncsi_netlink_main.cpp
new file mode 100644
index 0000000..9db624f
--- /dev/null
+++ b/src/ncsi_netlink_main.cpp
@@ -0,0 +1,111 @@
+/**
+ * Copyright © 2018 IBM Corporation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#include "argument.hpp"
+#include "ncsi_util.hpp"
+
+#include <iostream>
+#include <string>
+
+static void exitWithError(const char* err, char** argv)
+{
+    phosphor::network::ncsi::ArgumentParser::usage(argv);
+    std::cerr << "ERROR: " << err << "\n";
+    exit(EXIT_FAILURE);
+}
+
+int main(int argc, char** argv)
+{
+    using namespace phosphor::network;
+    using namespace phosphor::network::ncsi;
+    // Read arguments.
+    auto options = ArgumentParser(argc, argv);
+    int packageInt{};
+    int channelInt{};
+    int indexInt{};
+
+    // Parse out interface argument.
+    auto ifIndex = (options)["index"];
+    try
+    {
+        indexInt = stoi(ifIndex, nullptr);
+    }
+    catch (const std::exception& e)
+    {
+        exitWithError("Interface not specified.", argv);
+    }
+
+    if (indexInt < 0)
+    {
+        exitWithError("Interface value should be greater than equal to 0",
+                      argv);
+    }
+
+    // Parse out package argument.
+    auto package = (options)["package"];
+    try
+    {
+        packageInt = stoi(package, nullptr);
+    }
+    catch (const std::exception& e)
+    {
+        packageInt = DEFAULT_VALUE;
+    }
+
+    if (packageInt < 0)
+    {
+        packageInt = DEFAULT_VALUE;
+    }
+
+    // Parse out channel argument.
+    auto channel = (options)["channel"];
+    try
+    {
+        channelInt = stoi(channel, nullptr);
+    }
+    catch (const std::exception& e)
+    {
+        channelInt = DEFAULT_VALUE;
+    }
+
+    if (channelInt < 0)
+    {
+        channelInt = DEFAULT_VALUE;
+    }
+
+    auto setCmd = (options)["set"];
+    if (setCmd == "true")
+    {
+        // Can not perform set operation without package.
+        if (packageInt == DEFAULT_VALUE)
+        {
+            exitWithError("Package not specified.", argv);
+        }
+        return ncsi::setChannel(indexInt, packageInt, channelInt);
+    }
+    else if ((options)["info"] == "true")
+    {
+        return ncsi::getInfo(indexInt, packageInt);
+    }
+    else if ((options)["clear"] == "true")
+    {
+        return ncsi::clearInterface(indexInt);
+    }
+    else
+    {
+        exitWithError("No Command specified", argv);
+    }
+    return 0;
+}
diff --git a/src/ncsi_util.cpp b/src/ncsi_util.cpp
new file mode 100644
index 0000000..902911d
--- /dev/null
+++ b/src/ncsi_util.cpp
@@ -0,0 +1,301 @@
+#include "ncsi_util.hpp"
+
+#include <linux/ncsi.h>
+#include <netlink/genl/ctrl.h>
+#include <netlink/genl/genl.h>
+#include <netlink/netlink.h>
+
+#include <iostream>
+#include <phosphor-logging/elog-errors.hpp>
+#include <phosphor-logging/log.hpp>
+#include <xyz/openbmc_project/Common/error.hpp>
+
+namespace phosphor
+{
+namespace network
+{
+namespace ncsi
+{
+
+using namespace phosphor::logging;
+using namespace sdbusplus::xyz::openbmc_project::Common::Error;
+
+using CallBack = int (*)(struct nl_msg* msg, void* arg);
+
+namespace internal
+{
+
+using nlMsgPtr = std::unique_ptr<nl_msg, decltype(&::nlmsg_free)>;
+using nlSocketPtr = std::unique_ptr<nl_sock, decltype(&::nl_socket_free)>;
+
+CallBack infoCallBack = [](struct nl_msg* msg, void* /*arg*/) {
+    using namespace phosphor::network::ncsi;
+    auto nlh = nlmsg_hdr(msg);
+
+    struct nlattr* tb[NCSI_ATTR_MAX + 1] = {nullptr};
+    struct nla_policy ncsiPolicy[NCSI_ATTR_MAX + 1] = {
+        {NLA_UNSPEC, 0, 0}, {NLA_U32, 0, 0}, {NLA_NESTED, 0, 0},
+        {NLA_U32, 0, 0},    {NLA_U32, 0, 0},
+    };
+
+    struct nlattr* packagetb[NCSI_PKG_ATTR_MAX + 1] = {nullptr};
+    struct nla_policy packagePolicy[NCSI_PKG_ATTR_MAX + 1] = {
+        {NLA_UNSPEC, 0, 0}, {NLA_NESTED, 0, 0}, {NLA_U32, 0, 0},
+        {NLA_FLAG, 0, 0},   {NLA_NESTED, 0, 0},
+    };
+
+    struct nlattr* channeltb[NCSI_CHANNEL_ATTR_MAX + 1] = {nullptr};
+    struct nla_policy channelPolicy[NCSI_CHANNEL_ATTR_MAX + 1] = {
+        {NLA_UNSPEC, 0, 0}, {NLA_NESTED, 0, 0}, {NLA_U32, 0, 0},
+        {NLA_FLAG, 0, 0},   {NLA_NESTED, 0, 0}, {NLA_UNSPEC, 0, 0},
+    };
+
+    auto ret = genlmsg_parse(nlh, 0, tb, NCSI_ATTR_MAX, ncsiPolicy);
+    if (!tb[NCSI_ATTR_PACKAGE_LIST])
+    {
+        std::cerr << "No Packages" << std::endl;
+        return -1;
+    }
+
+    auto attrTgt = static_cast<nlattr*>(nla_data(tb[NCSI_ATTR_PACKAGE_LIST]));
+    if (!attrTgt)
+    {
+        std::cerr << "Package list attribute is null" << std::endl;
+        return -1;
+    }
+
+    auto rem = nla_len(tb[NCSI_ATTR_PACKAGE_LIST]);
+    nla_for_each_nested(attrTgt, tb[NCSI_ATTR_PACKAGE_LIST], rem)
+    {
+        ret = nla_parse_nested(packagetb, NCSI_PKG_ATTR_MAX, attrTgt,
+                               packagePolicy);
+        if (ret < 0)
+        {
+            std::cerr << "Failed to parse package nested" << std::endl;
+            return -1;
+        }
+
+        if (packagetb[NCSI_PKG_ATTR_ID])
+        {
+            auto attrID = nla_get_u32(packagetb[NCSI_PKG_ATTR_ID]);
+            std::cout << "Package has id : " << std::hex << attrID << std::endl;
+        }
+        else
+        {
+            std::cout << "Package with no id" << std::endl;
+        }
+
+        if (packagetb[NCSI_PKG_ATTR_FORCED])
+        {
+            std::cout << "This package is forced" << std::endl;
+        }
+
+        auto channelListTarget = static_cast<nlattr*>(
+            nla_data(packagetb[NCSI_PKG_ATTR_CHANNEL_LIST]));
+
+        auto channelrem = nla_len(packagetb[NCSI_PKG_ATTR_CHANNEL_LIST]);
+        nla_for_each_nested(channelListTarget,
+                            packagetb[NCSI_PKG_ATTR_CHANNEL_LIST], channelrem)
+        {
+            ret = nla_parse_nested(channeltb, NCSI_CHANNEL_ATTR_MAX,
+                                   channelListTarget, channelPolicy);
+            if (ret < 0)
+            {
+                std::cerr << "Failed to parse channel nested" << std::endl;
+                return -1;
+            }
+
+            if (channeltb[NCSI_CHANNEL_ATTR_ID])
+            {
+                auto channel = nla_get_u32(channeltb[NCSI_CHANNEL_ATTR_ID]);
+                if (channeltb[NCSI_CHANNEL_ATTR_ACTIVE])
+                {
+                    std::cout << "Channel Active : " << std::hex << channel
+                              << std::endl;
+                }
+                else
+                {
+                    std::cout << "Channel Not Active : " << std::hex << channel
+                              << std::endl;
+                }
+
+                if (channeltb[NCSI_CHANNEL_ATTR_FORCED])
+                {
+                    std::cout << "Channel is forced" << std::endl;
+                }
+            }
+            else
+            {
+                std::cout << "Channel with no ID" << std::endl;
+            }
+
+            if (channeltb[NCSI_CHANNEL_ATTR_VERSION_MAJOR])
+            {
+                auto major =
+                    nla_get_u32(channeltb[NCSI_CHANNEL_ATTR_VERSION_MAJOR]);
+                std::cout << "Channel Major Version : " << std::hex << major
+                          << std::endl;
+            }
+            if (channeltb[NCSI_CHANNEL_ATTR_VERSION_MINOR])
+            {
+                auto minor =
+                    nla_get_u32(channeltb[NCSI_CHANNEL_ATTR_VERSION_MINOR]);
+                std::cout << "Channel Minor Version : " << std::hex << minor
+                          << std::endl;
+            }
+            if (channeltb[NCSI_CHANNEL_ATTR_VERSION_STR])
+            {
+                auto str =
+                    nla_get_string(channeltb[NCSI_CHANNEL_ATTR_VERSION_STR]);
+                std::cout << "Channel Version Str :" << str << std::endl;
+            }
+            if (channeltb[NCSI_CHANNEL_ATTR_LINK_STATE])
+            {
+
+                auto link =
+                    nla_get_u32(channeltb[NCSI_CHANNEL_ATTR_LINK_STATE]);
+                std::cout << "Channel Link State : " << std::hex << link
+                          << std::endl;
+            }
+            if (channeltb[NCSI_CHANNEL_ATTR_VLAN_LIST])
+            {
+                std::cout << "Active Vlan ids" << std::endl;
+                auto vids = channeltb[NCSI_CHANNEL_ATTR_VLAN_LIST];
+                auto vid = static_cast<nlattr*>(nla_data(vids));
+                auto len = nla_len(vids);
+                while (nla_ok(vid, len))
+                {
+                    auto id = nla_get_u16(vid);
+                    std::cout << "VID : " << id << std::endl;
+                    vid = nla_next(vid, &len);
+                }
+            }
+        }
+    }
+    return (int)NL_SKIP;
+};
+
+int applyCmd(int ifindex, int cmd, int package = DEFAULT_VALUE,
+             int channel = DEFAULT_VALUE, int flags = NONE,
+             CallBack function = nullptr)
+{
+    nlSocketPtr socket(nl_socket_alloc(), &::nl_socket_free);
+    auto ret = genl_connect(socket.get());
+    if (ret < 0)
+    {
+        std::cerr << "Failed to open the socket , RC : " << ret << std::endl;
+        return ret;
+    }
+
+    auto driverID = genl_ctrl_resolve(socket.get(), "NCSI");
+    if (driverID < 0)
+    {
+        std::cerr << "Failed to resolve, RC : " << ret << std::endl;
+        return driverID;
+    }
+
+    nlMsgPtr msg(nlmsg_alloc(), &::nlmsg_free);
+
+    auto msgHdr = genlmsg_put(msg.get(), 0, 0, driverID, 0, flags, cmd, 0);
+    if (!msgHdr)
+    {
+        std::cerr << "Unable to add the netlink headers , COMMAND : " << cmd
+                  << std::endl;
+        return -1;
+    }
+
+    if (package != DEFAULT_VALUE)
+    {
+        ret = nla_put_u32(msg.get(), ncsi_nl_attrs::NCSI_ATTR_PACKAGE_ID,
+                          package);
+        if (ret < 0)
+        {
+            std::cerr << "Failed to set the attribute , RC : " << ret
+                      << "PACKAGE " << std::hex << package << std::endl;
+            return ret;
+        }
+    }
+
+    if (channel != DEFAULT_VALUE)
+    {
+        ret = nla_put_u32(msg.get(), ncsi_nl_attrs::NCSI_ATTR_CHANNEL_ID,
+                          channel);
+        if (ret < 0)
+        {
+            std::cerr << "Failed to set the attribute , RC : " << ret
+                      << "CHANNEL : " << std::hex << channel << std::endl;
+            return ret;
+        }
+    }
+
+    ret = nla_put_u32(msg.get(), ncsi_nl_attrs::NCSI_ATTR_IFINDEX, ifindex);
+    if (ret < 0)
+    {
+        std::cerr << "Failed to set the attribute , RC : " << ret
+                  << "INTERFACE : " << std::hex << ifindex << std::endl;
+        return ret;
+    }
+
+    if (function)
+    {
+        // Add a callback function to the socket
+        nl_socket_modify_cb(socket.get(), NL_CB_VALID, NL_CB_CUSTOM, function,
+                            nullptr);
+    }
+
+    ret = nl_send_auto(socket.get(), msg.get());
+    if (ret < 0)
+    {
+        std::cerr << "Failed to send the message , RC : " << ret << std::endl;
+        return ret;
+    }
+
+    ret = nl_recvmsgs_default(socket.get());
+    if (ret < 0)
+    {
+        std::cerr << "Failed to receive the message , RC : " << ret
+                  << std::endl;
+    }
+    return ret;
+}
+
+} // namespace internal
+
+int setChannel(int ifindex, int package, int channel)
+{
+    std::cout << "Set Channel : " << std::hex << channel
+              << ", PACKAGE : " << std::hex << package
+              << ", IFINDEX :  " << std::hex << ifindex << std::endl;
+    return internal::applyCmd(ifindex, ncsi_nl_commands::NCSI_CMD_SET_INTERFACE,
+                              package, channel);
+}
+
+int clearInterface(int ifindex)
+{
+    std::cout << "ClearInterface , IFINDEX :" << std::hex << ifindex
+              << std::endl;
+    return internal::applyCmd(ifindex,
+                              ncsi_nl_commands::NCSI_CMD_CLEAR_INTERFACE);
+}
+
+int getInfo(int ifindex, int package)
+{
+    std::cout << "Get Info , PACKAGE :  " << std::hex << package
+              << ", IFINDEX :  " << std::hex << ifindex << std::endl;
+    if (package == DEFAULT_VALUE)
+    {
+        return internal::applyCmd(ifindex, ncsi_nl_commands::NCSI_CMD_PKG_INFO,
+                                  package, DEFAULT_VALUE, NLM_F_DUMP,
+                                  internal::infoCallBack);
+    }
+    else
+    {
+        return internal::applyCmd(ifindex, ncsi_nl_commands::NCSI_CMD_PKG_INFO,
+                                  package, DEFAULT_VALUE, NONE,
+                                  internal::infoCallBack);
+    }
+}
+
+} // namespace ncsi
+} // namespace network
+} // namespace phosphor
diff --git a/src/ncsi_util.hpp b/src/ncsi_util.hpp
new file mode 100644
index 0000000..db754fe
--- /dev/null
+++ b/src/ncsi_util.hpp
@@ -0,0 +1,44 @@
+namespace phosphor
+{
+namespace network
+{
+namespace ncsi
+{
+
+constexpr auto DEFAULT_VALUE = -1;
+constexpr auto NONE = 0;
+
+/* @brief  This function will ask underlying NCSI driver
+ *         to set a specific  package or package/channel
+ *         combination as the preferred choice.
+ *         This function talks with the NCSI driver over
+ *         netlink messages.
+ * @param[in] ifindex - Interface Index.
+ * @param[in] package - NCSI Package.
+ * @param[in] channel - Channel number with in the package.
+ * @returns 0 on success and negative value for failure.
+ */
+int setChannel(int ifindex, int package, int channel);
+
+/* @brief  This function will ask underlying NCSI driver
+ *         to clear any preferred setting from the given
+ *         interface.
+ *         This function talks with the NCSI driver over
+ *         netlink messages.
+ * @param[in] ifindex - Interface Index.
+ * @returns 0 on success and negative value for failure.
+ */
+int clearInterface(int ifindex);
+
+/* @brief  This function is used to dump all the info
+ *         of the package and the channels underlying
+ *         the package.
+ * @param[in] ifindex - Interface Index.
+ * @param[in] package - NCSI Package.
+ * @returns 0 on success and negative value for failure.
+ */
+int getInfo(int ifindex, int package);
+
+} // namespace ncsi
+} // namespace network
+} // namespace phosphor
diff --git a/src/neighbor.cpp b/src/neighbor.cpp
new file mode 100644
index 0000000..11b0666
--- /dev/null
+++ b/src/neighbor.cpp
@@ -0,0 +1,108 @@
+#include "config.h"
+
+#include "neighbor.hpp"
+
+#include "ethernet_interface.hpp"
+#include "netlink.hpp"
+#include "util.hpp"
+
+#include <linux/neighbour.h>
+#include <linux/netlink.h>
+#include <linux/rtnetlink.h>
+#include <net/if.h>
+#include <sys/socket.h>
+#include <sys/types.h>
+
+#include <stdexcept>
+#include <stdplus/raw.hpp>
+#include <string_view>
+#include <utility>
+#include <vector>
+
+namespace phosphor
+{
+namespace network
+{
+namespace detail
+{
+
+void parseNeighbor(const NeighborFilter& filter, const nlmsghdr& hdr,
+                   std::string_view msg, std::vector<NeighborInfo>& neighbors)
+{
+    if (hdr.nlmsg_type != RTM_NEWNEIGH)
+    {
+        throw std::runtime_error("Not a neighbor msg");
+    }
+    auto ndm = stdplus::raw::extract<ndmsg>(msg);
+
+    // Filter out neighbors we don't care about
+    unsigned ifindex = ndm.ndm_ifindex;
+    if (filter.interface != 0 && filter.interface != ifindex)
+    {
+        return;
+    }
+    if ((ndm.ndm_state & filter.state) == 0)
+    {
+        return;
+    }
+
+    // Build the neighbor info for our valid neighbor
+    NeighborInfo neighbor;
+    neighbor.interface = ifindex;
+    neighbor.state = ndm.ndm_state;
+    bool set_addr = false;
+    while (!msg.empty())
+    {
+        auto [hdr, data] = netlink::extractRtAttr(msg);
+        if (hdr.rta_type == NDA_LLADDR)
+        {
+            neighbor.mac = stdplus::raw::copyFrom<ether_addr>(data);
+        }
+        else if (hdr.rta_type == NDA_DST)
+        {
+            neighbor.address = addrFromBuf(ndm.ndm_family, data);
+            set_addr = true;
+        }
+    }
+    if (!set_addr)
+    {
+        throw std::runtime_error("Missing address");
+    }
+    neighbors.push_back(std::move(neighbor));
+}
+
+} // namespace detail
+
+std::vector<NeighborInfo> getCurrentNeighbors(const NeighborFilter& filter)
+{
+    std::vector<NeighborInfo> neighbors;
+    auto cb = [&filter, &neighbors](const nlmsghdr& hdr, std::string_view msg) {
+        detail::parseNeighbor(filter, hdr, msg, neighbors);
+    };
+    ndmsg msg{};
+    msg.ndm_ifindex = filter.interface;
+    netlink::performRequest(NETLINK_ROUTE, RTM_GETNEIGH, NLM_F_DUMP, msg, cb);
+    return neighbors;
+}
+
+Neighbor::Neighbor(sdbusplus::bus::bus& bus, const char* objPath,
+                   EthernetInterface& parent, const std::string& ipAddress,
+                   const std::string& macAddress, State state) :
+    NeighborObj(bus, objPath, true),
+    parent(parent)
+{
+    this->ipAddress(ipAddress);
+    this->macAddress(macAddress);
+    this->state(state);
+
+    // Emit deferred signal.
+    emit_object_added();
+}
+
+void Neighbor::delete_()
+{
+    parent.deleteStaticNeighborObject(ipAddress());
+}
+
+} // namespace network
+} // namespace phosphor
diff --git a/src/neighbor.hpp b/src/neighbor.hpp
new file mode 100644
index 0000000..cca9c11
--- /dev/null
+++ b/src/neighbor.hpp
@@ -0,0 +1,105 @@
+#pragma once
+
+#include "types.hpp"
+
+#include <linux/netlink.h>
+#include <net/ethernet.h>
+
+#include <cstdint>
+#include <optional>
+#include <sdbusplus/bus.hpp>
+#include <sdbusplus/server/object.hpp>
+#include <string>
+#include <string_view>
+#include <vector>
+#include <xyz/openbmc_project/Network/Neighbor/server.hpp>
+#include <xyz/openbmc_project/Object/Delete/server.hpp>
+
+namespace phosphor
+{
+namespace network
+{
+
+using NeighborIntf = sdbusplus::xyz::openbmc_project::Network::server::Neighbor;
+
+using NeighborObj = sdbusplus::server::object::object<
+    NeighborIntf, sdbusplus::xyz::openbmc_project::Object::server::Delete>;
+
+class EthernetInterface;
+
+/* @class NeighborFilter
+ */
+struct NeighborFilter
+{
+    unsigned interface;
+    uint16_t state;
+
+    /* @brief Creates an empty filter */
+    NeighborFilter() : interface(0), state(~UINT16_C(0))
+    {
+    }
+};
+
+/** @class NeighborInfo
+ *  @brief Information about a neighbor from the kernel
+ */
+struct NeighborInfo
+{
+    unsigned interface;
+    InAddrAny address;
+    std::optional<ether_addr> mac;
+    uint16_t state;
+};
+
+/** @brief Returns a list of the current system neighbor table
+ */
+std::vector<NeighborInfo> getCurrentNeighbors(const NeighborFilter& filter);
+
+/** @class Neighbor
+ *  @brief OpenBMC network neighbor implementation.
+ *  @details A concrete implementation for the
+ *  xyz.openbmc_project.Network.Neighbor dbus interface.
+ */
+class Neighbor : public NeighborObj
+{
+  public:
+    using State = NeighborIntf::State;
+
+    Neighbor() = delete;
+    Neighbor(const Neighbor&) = delete;
+    Neighbor& operator=(const Neighbor&) = delete;
+    Neighbor(Neighbor&&) = delete;
+    Neighbor& operator=(Neighbor&&) = delete;
+    virtual ~Neighbor() = default;
+
+    /** @brief Constructor to put object onto bus at a dbus path.
+     *  @param[in] bus - Bus to attach to.
+     *  @param[in] objPath - Path to attach at.
+     *  @param[in] parent - Parent object.
+     *  @param[in] ipAddress - IP address.
+     *  @param[in] macAddress - Low level MAC address.
+     *  @param[in] state - The state of the neighbor entry.
+     */
+    Neighbor(sdbusplus::bus::bus& bus, const char* objPath,
+             EthernetInterface& parent, const std::string& ipAddress,
+             const std::string& macAddress, State state);
+
+    /** @brief Delete this d-bus object.
+     */
+    void delete_() override;
+
+  private:
+    /** @brief Parent Object. */
+    EthernetInterface& parent;
+};
+
+namespace detail
+{
+
+void parseNeighbor(const NeighborFilter& filter, const nlmsghdr& hdr,
+                   std::string_view msg, std::vector<NeighborInfo>& neighbors);
+
+} // namespace detail
+
+} // namespace network
+} // namespace phosphor
diff --git a/src/netlink.cpp b/src/netlink.cpp
new file mode 100644
index 0000000..9039575
--- /dev/null
+++ b/src/netlink.cpp
@@ -0,0 +1,195 @@
+#include "netlink.hpp"
+
+#include "util.hpp"
+
+#include <linux/netlink.h>
+#include <linux/rtnetlink.h>
+#include <sys/socket.h>
+#include <unistd.h>
+
+#include <array>
+#include <stdexcept>
+#include <stdplus/raw.hpp>
+#include <system_error>
+
+namespace phosphor
+{
+namespace network
+{
+namespace netlink
+{
+namespace detail
+{
+
+void processMsg(std::string_view& msgs, bool& done, const ReceiveCallback& cb)
+{
+    // Parse and update the message buffer
+    auto hdr = stdplus::raw::copyFrom<nlmsghdr>(msgs);
+    if (hdr.nlmsg_len < sizeof(hdr))
+    {
+        throw std::runtime_error("Invalid nlmsg length");
+    }
+    if (msgs.size() < hdr.nlmsg_len)
+    {
+        throw std::runtime_error("Bad nlmsg payload");
+    }
+    auto msg = msgs.substr(NLMSG_HDRLEN, hdr.nlmsg_len - NLMSG_HDRLEN);
+    msgs.remove_prefix(NLMSG_ALIGN(hdr.nlmsg_len));
+
+    // Figure out how to handle the individual message
+    bool doCallback = true;
+    if (hdr.nlmsg_flags & NLM_F_MULTI)
+    {
+        done = false;
+    }
+    if (hdr.nlmsg_type == NLMSG_NOOP)
+    {
+        doCallback = false;
+    }
+    else if (hdr.nlmsg_type == NLMSG_DONE)
+    {
+        if (done)
+        {
+            throw std::runtime_error("Got done for non-multi msg");
+        }
+        done = true;
+        doCallback = false;
+    }
+    else if (hdr.nlmsg_type == NLMSG_ERROR)
+    {
+        auto err = stdplus::raw::copyFrom<nlmsgerr>(msg);
+        // This is just an ACK so don't do the callback
+        if (err.error <= 0)
+        {
+            doCallback = false;
+        }
+    }
+    // All multi-msg headers must have the multi flag
+    if (!done && !(hdr.nlmsg_flags & NLM_F_MULTI))
+    {
+        throw std::runtime_error("Got non-multi msg before done");
+    }
+    if (doCallback)
+    {
+        cb(hdr, msg);
+    }
+}
+
+static void receive(int sock, const ReceiveCallback& cb)
+{
+    // We need to make sure we have enough room for an entire packet otherwise
+    // it gets truncated. The netlink docs guarantee packets will not exceed 8K
+    std::array<char, 8192> buf;
+
+    iovec iov{};
+    iov.iov_base = buf.data();
+    iov.iov_len = buf.size();
+
+    sockaddr_nl from{};
+    from.nl_family = AF_NETLINK;
+
+    msghdr hdr{};
+    hdr.msg_name = &from;
+    hdr.msg_namelen = sizeof(from);
+    hdr.msg_iov = &iov;
+    hdr.msg_iovlen = 1;
+
+    // We only do multiple recvs if we have a MULTI type message
+    bool done = true;
+    do
+    {
+        ssize_t recvd = recvmsg(sock, &hdr, 0);
+        if (recvd < 0)
+        {
+            throw std::system_error(errno, std::generic_category(),
+                                    "netlink recvmsg");
+        }
+        if (recvd == 0)
+        {
+            throw std::runtime_error("netlink recvmsg: Got empty payload");
+        }
+
+        std::string_view msgs(buf.data(), recvd);
+        do
+        {
+            processMsg(msgs, done, cb);
+        } while (!done && !msgs.empty());
+
+        if (done && !msgs.empty())
+        {
+            throw std::runtime_error("Extra unprocessed netlink messages");
+        }
+    } while (!done);
+}
+
+static void requestSend(int sock, void* data, size_t size)
+{
+    sockaddr_nl dst{};
+    dst.nl_family = AF_NETLINK;
+
+    iovec iov{};
+    iov.iov_base = data;
+    iov.iov_len = size;
+
+    msghdr hdr{};
+    hdr.msg_name = reinterpret_cast<sockaddr*>(&dst);
+    hdr.msg_namelen = sizeof(dst);
+    hdr.msg_iov = &iov;
+    hdr.msg_iovlen = 1;
+
+    if (sendmsg(sock, &hdr, 0) < 0)
+    {
+        throw std::system_error(errno, std::generic_category(),
+                                "netlink sendmsg");
+    }
+}
+
+static int newRequestSocket(int protocol)
+{
+    int sock = socket(AF_NETLINK, SOCK_RAW, protocol);
+    if (sock < 0)
+    {
+        throw std::system_error(errno, std::generic_category(), "netlink open");
+    }
+
+    sockaddr_nl local{};
+    local.nl_family = AF_NETLINK;
+    int r = bind(sock, reinterpret_cast<sockaddr*>(&local), sizeof(local));
+    if (r < 0)
+    {
+        close(sock);
+        throw std::system_error(errno, std::generic_category(), "netlink bind");
+    }
+
+    return sock;
+}
+
+void performRequest(int protocol, void* data, size_t size,
+                    const ReceiveCallback& cb)
+{
+    Descriptor sock(newRequestSocket(protocol));
+    requestSend(sock(), data, size);
+    receive(sock(), cb);
+}
+
+} // namespace detail
+
+std::tuple<rtattr, std::string_view> extractRtAttr(std::string_view& data)
+{
+    auto hdr = stdplus::raw::copyFrom<rtattr>(data);
+    if (hdr.rta_len < RTA_LENGTH(0))
+    {
+        throw std::runtime_error("Invalid rtattr length");
+    }
+    if (data.size() < hdr.rta_len)
+    {
+        throw std::runtime_error("Not enough data for rtattr");
+    }
+    auto attr = data.substr(RTA_LENGTH(0), hdr.rta_len - RTA_LENGTH(0));
+    data.remove_prefix(RTA_ALIGN(hdr.rta_len));
+    return {hdr, attr};
+}
+
+} // namespace netlink
+} // namespace network
+} // namespace phosphor
diff --git a/src/netlink.hpp b/src/netlink.hpp
new file mode 100644
index 0000000..e1a8253
--- /dev/null
+++ b/src/netlink.hpp
@@ -0,0 +1,69 @@
+#pragma once
+#include <linux/netlink.h>
+#include <linux/rtnetlink.h>
+
+#include <functional>
+#include <string_view>
+#include <tuple>
+#include <type_traits>
+
+namespace phosphor
+{
+namespace network
+{
+namespace netlink
+{
+
+/* @brief Called on each nlmsg received on the socket
+ */
+using ReceiveCallback = std::function<void(const nlmsghdr&, std::string_view)>;
+
+namespace detail
+{
+
+void processMsg(std::string_view& msgs, bool& done, const ReceiveCallback& cb);
+
+void performRequest(int protocol, void* data, size_t size,
+                    const ReceiveCallback& cb);
+
+} // namespace detail
+
+/* @brief Call on a block of rtattrs to parse a single one out
+ *        Updates the input to remove the attr parsed out.
+ *
+ * @param[in,out] attrs - The buffer holding rtattrs to parse
+ * @return A tuple of rtattr header + data buffer for the attr
+ */
+std::tuple<rtattr, std::string_view> extractRtAttr(std::string_view& data);
+
+/** @brief Performs a netlink request of the specified type with the given
+ *  message Calls the callback upon receiving
+ *
+ *  @param[in] protocol - The netlink protocol to use when opening the socket
+ *  @param[in] type     - The netlink message type
+ *  @param[in] flags    - Additional netlink flags for the request
+ *  @param[in] msg      - The message payload for the request
+ *  @param[in] cb       - Called for each response message payload
+ */
+template <typename T>
+void performRequest(int protocol, uint16_t type, uint16_t flags, const T& msg,
+                    const ReceiveCallback& cb)
+{
+    static_assert(std::is_trivially_copyable_v<T>);
+
+    struct
+    {
+        nlmsghdr hdr;
+        T msg;
+    } data{};
+    data.hdr.nlmsg_len = sizeof(data);
+    data.hdr.nlmsg_type = type;
+    data.hdr.nlmsg_flags = NLM_F_REQUEST | NLM_F_ACK | flags;
+    data.msg = msg;
+
+    detail::performRequest(protocol, &data, sizeof(data), cb);
+}
+
+} // namespace netlink
+} // namespace network
+} // namespace phosphor
diff --git a/src/network_config.cpp b/src/network_config.cpp
new file mode 100644
index 0000000..94c8eae
--- /dev/null
+++ b/src/network_config.cpp
@@ -0,0 +1,47 @@
+#include "config.h"
+
+#include "network_config.hpp"
+
+#include <fstream>
+#include <string>
+
+namespace phosphor
+{
+namespace network
+{
+
+namespace bmc
+{
+void writeDHCPDefault(const std::string& filename, const std::string& interface)
+{
+    std::ofstream filestream;
+
+    filestream.open(filename);
+    // Add the following line to your phosphor-network bbappend file
+    // to control IPV6_ACCEPT_RA
+    //   EXTRA_OECONF_append = " --enable-ipv6-accept-ra=yes"
+    // If this switch is not present or set to 'no'
+    // ENABLE_IPV6_ACCEPT_RA will be undefined.
+    // The new value is only assigned on first boot, when the default
+    // file is not present, or after the default file has been
+    // manually removed.
+    filestream << "[Match]\nName=" << interface <<
+                "\n[Network]\nDHCP=true\n"
+#ifdef LINK_LOCAL_AUTOCONFIGURATION
+                "LinkLocalAddressing=yes\n"
+#else
+                "LinkLocalAddressing=no\n"
+#endif
+#ifdef ENABLE_IPV6_ACCEPT_RA
+                "IPv6AcceptRA=true\n"
+#else
+                "IPv6AcceptRA=false\n"
+
+#endif
+                "[DHCP]\nClientIdentifier=mac\n";
+    filestream.close();
+}
+} // namespace bmc
+
+} // namespace network
+} // namespace phosphor
diff --git a/src/network_config.hpp b/src/network_config.hpp
new file mode 100644
index 0000000..7260ca2
--- /dev/null
+++ b/src/network_config.hpp
@@ -0,0 +1,15 @@
+#include <string>
+
+namespace phosphor
+{
+namespace network
+{
+
+namespace bmc
+{
+void writeDHCPDefault(const std::string& filename,
+                      const std::string& interface);
+}
+
+} // namespace network
+} // namespace phosphor
diff --git a/src/network_manager.cpp b/src/network_manager.cpp
new file mode 100644
index 0000000..751d1a1
--- /dev/null
+++ b/src/network_manager.cpp
@@ -0,0 +1,289 @@
+#include "config.h"
+
+#include "network_manager.hpp"
+
+#include "ipaddress.hpp"
+#include "network_config.hpp"
+#include "types.hpp"
+#include "util.hpp"
+
+#include <arpa/inet.h>
+#include <dirent.h>
+#include <net/if.h>
+
+#include <algorithm>
+#include <bitset>
+#include <filesystem>
+#include <fstream>
+#include <map>
+#include <phosphor-logging/elog-errors.hpp>
+#include <phosphor-logging/log.hpp>
+#include <string>
+#include <xyz/openbmc_project/Common/error.hpp>
+
+constexpr char SYSTEMD_BUSNAME[] = "org.freedesktop.systemd1";
+constexpr char SYSTEMD_PATH[] = "/org/freedesktop/systemd1";
+constexpr char SYSTEMD_INTERFACE[] = "org.freedesktop.systemd1.Manager";
+constexpr auto FirstBootFile = "/var/lib/network/firstBoot_";
+
+namespace phosphor
+{
+namespace network
+{
+
+extern std::unique_ptr<Timer> refreshObjectTimer;
+extern std::unique_ptr<Timer> restartTimer;
+using namespace phosphor::logging;
+using namespace sdbusplus::xyz::openbmc_project::Common::Error;
+
+Manager::Manager(sdbusplus::bus::bus& bus, const char* objPath,
+                 const std::string& path) :
+    details::VLANCreateIface(bus, objPath, true),
+    bus(bus), objectPath(objPath)
+{
+    fs::path confDir(path);
+    setConfDir(confDir);
+}
+
+bool Manager::createDefaultNetworkFiles(bool force)
+{
+    auto isCreated = false;
+    try
+    {
+        // Directory would have created before with
+        // setConfDir function.
+        if (force)
+        {
+            // Factory Reset case
+            // we need to forcefully write the files
+            // so delete the existing ones.
+            if (fs::is_directory(confDir))
+            {
+                for (const auto& file : fs::directory_iterator(confDir))
+                {
+                    fs::remove(file.path());
+                }
+            }
+        }
+
+        auto interfaceStrList = getInterfaces();
+        for (const auto& interface : interfaceStrList)
+        {
+            // if the interface has '.' in the name, it means that this is a
+            // VLAN - don't create the network file.
+            if (interface.find(".") != std::string::npos)
+            {
+                continue;
+            }
+
+            auto fileName = systemd::config::networkFilePrefix + interface +
+                            systemd::config::networkFileSuffix;
+
+            fs::path filePath = confDir;
+            filePath /= fileName;
+
+            // create the interface specific network file
+            // if not exist or we forcefully wants to write
+            // the network file.
+
+            if (force || !fs::is_regular_file(filePath.string()))
+            {
+                bmc::writeDHCPDefault(filePath.string(), interface);
+                log<level::INFO>("Created the default network file.",
+                                 entry("INTERFACE=%s", interface.c_str()));
+                isCreated = true;
+            }
+        }
+    }
+    catch (std::exception& e)
+    {
+        log<level::ERR>("Unable to create the default network file");
+    }
+    return isCreated;
+}
+
+void Manager::setConfDir(const fs::path& dir)
+{
+    confDir = dir;
+
+    if (!fs::exists(confDir))
+    {
+        if (!fs::create_directories(confDir))
+        {
+            log<level::ERR>("Unable to create the network conf dir",
+                            entry("DIR=%s", confDir.c_str()));
+            elog<InternalFailure>();
+        }
+    }
+}
+
+void Manager::createInterfaces()
+{
+    // clear all the interfaces first
+    interfaces.clear();
+
+    auto interfaceStrList = getInterfaces();
+
+    for (auto& interface : interfaceStrList)
+    {
+        fs::path objPath = objectPath;
+        auto index = interface.find(".");
+
+        // interface can be of vlan type or normal ethernet interface.
+        // vlan interface looks like "interface.vlanid",so here by looking
+        // at the interface name we decide that we need
+        // to create the vlaninterface or normal physical interface.
+        if (index != std::string::npos)
+        {
+            // it is vlan interface
+            auto interfaceName = interface.substr(0, index);
+            auto vlanid = interface.substr(index + 1);
+            uint32_t vlanInt = std::stoul(vlanid);
+
+            interfaces[interfaceName]->loadVLAN(vlanInt);
+            continue;
+        }
+        // normal ethernet interface
+        objPath /= interface;
+
+        auto dhcp = getDHCPValue(confDir, interface);
+
+        auto intf = std::make_shared<phosphor::network::EthernetInterface>(
+            bus, objPath.string(), dhcp, *this);
+
+        intf->createIPAddressObjects();
+        intf->createStaticNeighborObjects();
+        intf->loadNameServers();
+
+        this->interfaces.emplace(
+            std::make_pair(std::move(interface), std::move(intf)));
+    }
+}
+
+void Manager::createChildObjects()
+{
+    // creates the ethernet interface dbus object.
+    createInterfaces();
+
+    systemConf.reset(nullptr);
+    dhcpConf.reset(nullptr);
+
+    fs::path objPath = objectPath;
+    objPath /= "config";
+
+    // create the system conf object.
+    systemConf = std::make_unique<phosphor::network::SystemConfiguration>(
+        bus, objPath.string(), *this);
+    // create the dhcp conf object.
+    objPath /= "dhcp";
+    dhcpConf = std::make_unique<phosphor::network::dhcp::Configuration>(
+        bus, objPath.string(), *this);
+}
+
+ObjectPath Manager::vlan(IntfName interfaceName, uint32_t id)
+{
+    return interfaces[interfaceName]->createVLAN(id);
+}
+
+void Manager::reset()
+{
+    if (!createDefaultNetworkFiles(true))
+    {
+        log<level::ERR>("Network Factory Reset failed.");
+        return;
+        // TODO: openbmc/openbmc#1721 - Log ResetFailed error here.
+    }
+
+    log<level::INFO>("Network Factory Reset done.");
+}
+
+// Need to merge the below function with the code which writes the
+// config file during factory reset.
+// TODO openbmc/openbmc#1751
+void Manager::writeToConfigurationFile()
+{
+    // write all the static ip address in the systemd-network conf file
+    for (const auto& intf : interfaces)
+    {
+        intf.second->writeConfigurationFile();
+    }
+    restartTimers();
+}
+
+#ifdef SYNC_MAC_FROM_INVENTORY
+void Manager::setFistBootMACOnInterface(
+    const std::pair<std::string, std::string>& inventoryEthPair)
+{
+    for (const auto& interface : interfaces)
+    {
+        if (interface.first == inventoryEthPair.first)
+        {
+            auto returnMAC =
+                interface.second->macAddress(inventoryEthPair.second);
+            if (returnMAC == inventoryEthPair.second)
+            {
+                log<level::INFO>("Set the MAC on "),
+                    entry("interface : ", interface.first.c_str()),
+                    entry("MAC : ", inventoryEthPair.second.c_str());
+                std::error_code ec;
+                if (std::filesystem::is_directory("/var/lib/network", ec))
+                {
+                    std::ofstream persistentFile(FirstBootFile +
+                                                 interface.first);
+                }
+                break;
+            }
+            else
+            {
+                log<level::INFO>("MAC is Not Set on ethernet Interface");
+            }
+        }
+    }
+}
+
+#endif
+
+void Manager::restartTimers()
+{
+    using namespace std::chrono;
+    if (refreshObjectTimer && restartTimer)
+    {
+        restartTimer->restartOnce(restartTimeout);
+        refreshObjectTimer->restartOnce(refreshTimeout);
+    }
+}
+
+void Manager::restartSystemdUnit(const std::string& unit)
+{
+    try
+    {
+        auto method = bus.new_method_call(SYSTEMD_BUSNAME, SYSTEMD_PATH,
+                                          SYSTEMD_INTERFACE, "ResetFailedUnit");
+        method.append(unit);
+        bus.call_noreply(method);
+    }
+    catch (const sdbusplus::exception::SdBusError& ex)
+    {
+        log<level::ERR>("Failed to reset failed unit",
+                        entry("UNIT=%s", unit.c_str()),
+                        entry("ERR=%s", ex.what()));
+        elog<InternalFailure>();
+    }
+
+    try
+    {
+        auto method = bus.new_method_call(SYSTEMD_BUSNAME, SYSTEMD_PATH,
+                                          SYSTEMD_INTERFACE, "RestartUnit");
+        method.append(unit.c_str(), "replace");
+        bus.call_noreply(method);
+    }
+    catch (const sdbusplus::exception::SdBusError& ex)
+    {
+        log<level::ERR>("Failed to restart service", entry("ERR=%s", ex.what()),
+                        entry("UNIT=%s", unit.c_str()));
+        elog<InternalFailure>();
+    }
+}
+
+} // namespace network
+} // namespace phosphor
diff --git a/src/network_manager.hpp b/src/network_manager.hpp
new file mode 100644
index 0000000..227955c
--- /dev/null
+++ b/src/network_manager.hpp
@@ -0,0 +1,183 @@
+#pragma once
+
+#include "dhcp_configuration.hpp"
+#include "ethernet_interface.hpp"
+#include "system_configuration.hpp"
+#include "vlan_interface.hpp"
+#include "xyz/openbmc_project/Network/VLAN/Create/server.hpp"
+
+#include <filesystem>
+#include <list>
+#include <memory>
+#include <sdbusplus/bus.hpp>
+#include <string>
+#include <vector>
+#include <xyz/openbmc_project/Common/FactoryReset/server.hpp>
+
+#ifndef SDBUSPP_NEW_CAMELCASE
+#define vlan vLAN
+#endif
+
+namespace phosphor
+{
+namespace network
+{
+
+using SystemConfPtr = std::unique_ptr<SystemConfiguration>;
+using DHCPConfPtr = std::unique_ptr<dhcp::Configuration>;
+
+namespace fs = std::filesystem;
+namespace details
+{
+
+template <typename T, typename U>
+using ServerObject = typename sdbusplus::server::object::object<T, U>;
+
+using VLANCreateIface = details::ServerObject<
+    sdbusplus::xyz::openbmc_project::Network::VLAN::server::Create,
+    sdbusplus::xyz::openbmc_project::Common::server::FactoryReset>;
+
+} // namespace details
+
+/** @class Manager
+ *  @brief OpenBMC network manager implementation.
+ */
+class Manager : public details::VLANCreateIface
+{
+  public:
+    Manager() = delete;
+    Manager(const Manager&) = delete;
+    Manager& operator=(const Manager&) = delete;
+    Manager(Manager&&) = delete;
+    Manager& operator=(Manager&&) = delete;
+    virtual ~Manager() = default;
+
+    /** @brief Constructor to put object onto bus at a dbus path.
+     *  @param[in] bus - Bus to attach to.
+     *  @param[in] objPath - Path to attach at.
+     *  @param[in] dir - Network Configuration directory path.
+     */
+    Manager(sdbusplus::bus::bus& bus, const char* objPath,
+            const std::string& dir);
+
+    ObjectPath vlan(IntfName interfaceName, uint32_t id) override;
+
+    /** @brief write the network conf file with the in-memory objects.
+     */
+    void writeToConfigurationFile();
+
+    /** @brief Fetch the interface and the ipaddress details
+     *         from the system and create the ethernet interraces
+     *         dbus object.
+     */
+    virtual void createInterfaces();
+
+    /** @brief create child interface object and the system conf object.
+     */
+    void createChildObjects();
+
+    /** @brief sets the network conf directory.
+     *  @param[in] dirName - Absolute path of the directory.
+     */
+    void setConfDir(const fs::path& dir);
+
+    /** @brief gets the network conf directory.
+     */
+    fs::path getConfDir()
+    {
+        return confDir;
+    }
+
+    /** @brief gets the system conf object.
+     *
+     */
+    const SystemConfPtr& getSystemConf()
+    {
+        return systemConf;
+    }
+
+    /** @brief gets the dhcp conf object.
+     *
+     */
+    const DHCPConfPtr& getDHCPConf()
+    {
+        return dhcpConf;
+    }
+
+    /** @brief create the default network files for each interface
+     *  @detail if force param is true then forcefully create the network
+     *          files otherwise if network file doesn't exist then
+     *          create it.
+     *  @param[in] force - forcefully create the file
+     *  @return true if network file created else false
+     */
+    bool createDefaultNetworkFiles(bool force);
+
+    /** @brief restart the network timers. */
+    void restartTimers();
+
+    /** @brief This function gets the MAC address from the VPD and
+     *  sets it on the corresponding ethernet interface during first
+     *  Boot, once it sets the MAC from VPD, it creates a file named
+     *  firstBoot under /var/lib to make sure we dont run this function
+     *  again.
+     *
+     *  @param[in] ethPair - Its a pair of ethernet interface name & the
+     * corresponding MAC Address from the VPD
+     *
+     *  return - NULL
+     */
+    void setFistBootMACOnInterface(
+        const std::pair<std::string, std::string>& ethPair);
+
+    /** @brief Restart the systemd unit
+     *  @param[in] unit - systemd unit name which needs to be
+     *                    restarted.
+     */
+    virtual void restartSystemdUnit(const std::string& unit);
+
+    /** @brief Returns the number of interfaces under this manager.
+     *
+     * @return the number of interfaces managed by this manager.
+     */
+    int getInterfaceCount()
+    {
+        return interfaces.size();
+    }
+
+    /** @brief Does the requested interface exist under this manager?
+     *
+     * @param[in] intf - the interface name to check.
+     * @return true if found, false otherwise.
+     */
+    bool hasInterface(const std::string& intf)
+    {
+        return (interfaces.find(intf) != interfaces.end());
+    }
+
+  protected:
+    /** @brief Persistent sdbusplus DBus bus connection. */
+    sdbusplus::bus::bus& bus;
+
+    /** @brief Persistent map of EthernetInterface dbus objects and their names
+     */
+    std::map<IntfName, std::shared_ptr<EthernetInterface>> interfaces;
+
+    /** @brief BMC network reset - resets network configuration for BMC. */
+    void reset() override;
+
+    /** @brief Path of Object. */
+    std::string objectPath;
+
+    /** @brief pointer to system conf object. */
+    SystemConfPtr systemConf = nullptr;
+
+    /** @brief pointer to dhcp conf object. */
+    DHCPConfPtr dhcpConf = nullptr;
+
+    /** @brief Network Configuration directory. */
+    fs::path confDir;
+};
+
+} // namespace network
+} // namespace phosphor
diff --git a/src/network_manager_main.cpp b/src/network_manager_main.cpp
new file mode 100644
index 0000000..b0d6d2e
--- /dev/null
+++ b/src/network_manager_main.cpp
@@ -0,0 +1,342 @@
+#include "config.h"
+
+#include "network_manager.hpp"
+#include "rtnetlink_server.hpp"
+#include "types.hpp"
+#include "watch.hpp"
+
+#include <linux/netlink.h>
+
+#include <filesystem>
+#include <fstream>
+#include <functional>
+#include <memory>
+#ifdef SYNC_MAC_FROM_INVENTORY
+#include <nlohmann/json.hpp>
+#endif
+#include <phosphor-logging/elog-errors.hpp>
+#include <phosphor-logging/log.hpp>
+#include <sdbusplus/bus.hpp>
+#include <sdbusplus/bus/match.hpp>
+#include <sdbusplus/server/manager.hpp>
+#include <sdeventplus/event.hpp>
+#include <xyz/openbmc_project/Common/error.hpp>
+
+using phosphor::logging::elog;
+using phosphor::logging::entry;
+using phosphor::logging::level;
+using phosphor::logging::log;
+using sdbusplus::xyz::openbmc_project::Common::Error::InternalFailure;
+using DbusObjectPath = std::string;
+using DbusInterface = std::string;
+using PropertyValue = std::string;
+
+constexpr char NETWORK_CONF_DIR[] = "/etc/systemd/network";
+
+constexpr char DEFAULT_OBJPATH[] = "/xyz/openbmc_project/network";
+
+constexpr auto firstBootPath = "/var/lib/network/firstBoot_";
+constexpr auto configFile = "/usr/share/network/config.json";
+
+constexpr auto invNetworkIntf =
+    "xyz.openbmc_project.Inventory.Item.NetworkInterface";
+
+namespace phosphor
+{
+namespace network
+{
+
+std::unique_ptr<phosphor::network::Manager> manager = nullptr;
+std::unique_ptr<Timer> refreshObjectTimer = nullptr;
+std::unique_ptr<Timer> restartTimer = nullptr;
+
+#ifdef SYNC_MAC_FROM_INVENTORY
+std::unique_ptr<sdbusplus::bus::match::match> EthInterfaceMatch = nullptr;
+std::vector<std::string> first_boot_status;
+
+bool setInventoryMACOnSystem(sdbusplus::bus::bus& bus,
+                             const nlohmann::json& configJson,
+                             const std::string& intfname)
+{
+    try
+    {
+        auto inventoryMAC = mac_address::getfromInventory(bus, intfname);
+        if (!mac_address::toString(inventoryMAC).empty())
+        {
+            log<level::INFO>("Mac Address in Inventory on "),
+                entry("Interface : ", intfname.c_str()),
+                entry("MAC Address :",
+                      (mac_address::toString(inventoryMAC)).c_str());
+            manager->setFistBootMACOnInterface(std::make_pair(
+                intfname.c_str(), mac_address::toString(inventoryMAC)));
+            first_boot_status.push_back(intfname.c_str());
+            bool status = true;
+            for (const auto& keys : configJson.items())
+            {
+                if (!(std::find(first_boot_status.begin(),
+                                first_boot_status.end(),
+                                keys.key()) != first_boot_status.end()))
+                {
+                    log<level::INFO>("Interface MAC is NOT set from VPD"),
+                        entry("INTERFACE", keys.key().c_str());
+                    status = false;
+                }
+            }
+            if (status)
+            {
+                log<level::INFO>("Removing the match for ethernet interfaces");
+                phosphor::network::EthInterfaceMatch = nullptr;
+            }
+        }
+        else
+        {
+            log<level::INFO>("Nothing is present in Inventory");
+            return false;
+        }
+    }
+    catch (const std::exception& e)
+    {
+        log<level::ERR>("Exception occurred during getting of MAC "
+                        "address from Inventory");
+        return false;
+    }
+    return true;
+}
+
+// register the macthes to be monitored from inventory manager
+void registerSignals(sdbusplus::bus::bus& bus, const nlohmann::json& configJson)
+{
+    log<level::INFO>("Registering the Inventory Signals Matcher");
+
+    static std::unique_ptr<sdbusplus::bus::match::match> MacAddressMatch;
+
+    auto callback = [&](sdbusplus::message::message& m) {
+        std::map<DbusObjectPath,
+                 std::map<DbusInterface, std::variant<PropertyValue>>>
+            interfacesProperties;
+
+        sdbusplus::message::object_path objPath;
+        std::pair<std::string, std::string> ethPair;
+        m.read(objPath, interfacesProperties);
+
+        for (const auto& pattern : configJson.items())
+        {
+            if (objPath.str.find(pattern.value()) != std::string::npos)
+            {
+                for (auto& interface : interfacesProperties)
+                {
+                    if (interface.first == invNetworkIntf)
+                    {
+                        for (const auto& property : interface.second)
+                        {
+                            if (property.first == "MACAddress")
+                            {
+                                ethPair = std::make_pair(
+                                    pattern.key(),
+                                    std::get<std::string>(property.second));
+                                break;
+                            }
+                        }
+                        break;
+                    }
+                }
+                if (!(ethPair.first.empty() || ethPair.second.empty()))
+                {
+                    manager->setFistBootMACOnInterface(ethPair);
+                }
+            }
+        }
+    };
+
+    MacAddressMatch = std::make_unique<sdbusplus::bus::match::match>(
+        bus,
+        "interface='org.freedesktop.DBus.ObjectManager',type='signal',"
+        "member='InterfacesAdded',path='/xyz/openbmc_project/"
+        "inventory'",
+        callback);
+}
+
+void watchEthernetInterface(sdbusplus::bus::bus& bus,
+                            const nlohmann::json& configJson)
+{
+    auto mycallback = [&](sdbusplus::message::message& m) {
+        std::map<DbusObjectPath,
+                 std::map<DbusInterface, std::variant<PropertyValue>>>
+            interfacesProperties;
+
+        sdbusplus::message::object_path objPath;
+        std::pair<std::string, std::string> ethPair;
+        m.read(objPath, interfacesProperties);
+        for (const auto& interfaces : interfacesProperties)
+        {
+            if (interfaces.first ==
+                "xyz.openbmc_project.Network.EthernetInterface")
+            {
+                for (const auto& property : interfaces.second)
+                {
+                    if (property.first == "InterfaceName")
+                    {
+                        std::string infname =
+                            std::get<std::string>(property.second);
+
+                        if (configJson.find(infname) == configJson.end())
+                        {
+                            // ethernet interface not found in configJSON
+                            // check if it is not sit0 interface, as it is
+                            // expected.
+                            if (infname != "sit0")
+                            {
+                                log<level::ERR>(
+                                    "Wrong Interface Name in Config Json");
+                            }
+                        }
+                        else
+                        {
+                            if (!phosphor::network::setInventoryMACOnSystem(
+                                    bus, configJson, infname))
+                            {
+                                phosphor::network::registerSignals(bus,
+                                                                   configJson);
+                                phosphor::network::EthInterfaceMatch = nullptr;
+                            }
+                        }
+                        break;
+                    }
+                }
+                break;
+            }
+        }
+    };
+    // Incase if phosphor-inventory-manager started early and the VPD is already
+    // collected by the time network service has come up, better to check the
+    // VPD directly and set the MAC Address on the respective Interface.
+
+    bool registeredSignals = false;
+    for (const auto& interfaceString : configJson.items())
+    {
+        if (!std::filesystem::exists(firstBootPath + interfaceString.key()) &&
+            !registeredSignals)
+        {
+
+            log<level::INFO>(
+                "First boot file is not present, check VPD for MAC");
+            phosphor::network::EthInterfaceMatch = std::make_unique<
+                sdbusplus::bus::match::match>(
+                bus,
+                "interface='org.freedesktop.DBus.ObjectManager',type='signal',"
+                "member='InterfacesAdded',path='/xyz/openbmc_project/network'",
+                mycallback);
+            registeredSignals = true;
+        }
+    }
+}
+
+#endif
+
+/** @brief refresh the network objects. */
+void refreshObjects()
+{
+    if (manager)
+    {
+        log<level::INFO>("Refreshing the objects.");
+        manager->createChildObjects();
+        log<level::INFO>("Refreshing complete.");
+    }
+}
+
+/** @brief restart the systemd networkd. */
+void restartNetwork()
+{
+    if (manager)
+    {
+        manager->restartSystemdUnit("systemd-networkd.service");
+    }
+}
+
+void initializeTimers()
+{
+    auto event = sdeventplus::Event::get_default();
+    refreshObjectTimer =
+        std::make_unique<Timer>(event, std::bind(refreshObjects));
+    restartTimer = std::make_unique<Timer>(event, std::bind(restartNetwork));
+}
+
+} // namespace network
+} // namespace phosphor
+
+void createNetLinkSocket(phosphor::Descriptor& smartSock)
+{
+    // RtnetLink socket
+    auto fd = socket(PF_NETLINK, SOCK_RAW | SOCK_NONBLOCK, NETLINK_ROUTE);
+    if (fd < 0)
+    {
+        log<level::ERR>("Unable to create the net link socket",
+                        entry("ERRNO=%d", errno));
+        elog<InternalFailure>();
+    }
+    smartSock.set(fd);
+}
+
+int main(int /*argc*/, char** /*argv*/)
+{
+    phosphor::network::initializeTimers();
+
+    auto bus = sdbusplus::bus::new_default();
+
+    // Need sd_event to watch for OCC device errors
+    sd_event* event = nullptr;
+    auto r = sd_event_default(&event);
+    if (r < 0)
+    {
+        log<level::ERR>("Error creating a default sd_event handler");
+        return r;
+    }
+
+    phosphor::network::EventPtr eventPtr{event};
+    event = nullptr;
+
+    // Attach the bus to sd_event to service user requests
+    bus.attach_event(eventPtr.get(), SD_EVENT_PRIORITY_NORMAL);
+
+    // Add sdbusplus Object Manager for the 'root' path of the network manager.
+    sdbusplus::server::manager::manager objManager(bus, DEFAULT_OBJPATH);
+    bus.request_name(DEFAULT_BUSNAME);
+
+    phosphor::network::manager = std::make_unique<phosphor::network::Manager>(
+        bus, DEFAULT_OBJPATH, NETWORK_CONF_DIR);
+
+    // create the default network files if the network file
+    // is not there for any interface.
+    // Parameter false means don't create the network
+    // files forcefully.
+    if (phosphor::network::manager->createDefaultNetworkFiles(false))
+    {
+        // if files created restart the network.
+        // don't need to call the create child objects as eventhandler
+        // will create it.
+        phosphor::network::restartNetwork();
+    }
+    else
+    {
+        // this will add the additional fixes which is needed
+        // in the existing network file.
+        phosphor::network::manager->writeToConfigurationFile();
+        // whenever the configuration file gets written it restart
+        // the network which creates the network objects
+    }
+
+    // RtnetLink socket
+    phosphor::Descriptor smartSock;
+    createNetLinkSocket(smartSock);
+
+    // RTNETLINK event handler
+    phosphor::network::rtnetlink::Server svr(eventPtr, smartSock);
+
+#ifdef SYNC_MAC_FROM_INVENTORY
+    std::ifstream in(configFile);
+    nlohmann::json configJson;
+    in >> configJson;
+    phosphor::network::watchEthernetInterface(bus, configJson);
+#endif
+    sd_event_loop(eventPtr.get());
+}
diff --git a/src/routing_table.cpp b/src/routing_table.cpp
new file mode 100644
index 0000000..68a9ee3
--- /dev/null
+++ b/src/routing_table.cpp
@@ -0,0 +1,232 @@
+#include "routing_table.hpp"
+
+#include "util.hpp"
+
+#include <arpa/inet.h>
+#include <linux/rtnetlink.h>
+#include <net/if.h>
+#include <netinet/in.h>
+#include <sys/ioctl.h>
+#include <sys/socket.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include <optional>
+#include <phosphor-logging/elog-errors.hpp>
+#include <phosphor-logging/log.hpp>
+#include <stdexcept>
+#include <string_view>
+#include <xyz/openbmc_project/Common/error.hpp>
+
+namespace phosphor
+{
+namespace network
+{
+namespace route
+{
+
+using namespace phosphor::logging;
+using namespace sdbusplus::xyz::openbmc_project::Common::Error;
+
+Table::Table()
+{
+    try
+    {
+        getRoutes();
+    }
+    catch (InternalFailure& e)
+    {
+        commit<InternalFailure>();
+    }
+}
+
+int Table::readNetLinkSock(int sockFd, std::array<char, BUFSIZE>& buf)
+{
+    struct nlmsghdr* nlHdr = nullptr;
+    int readLen{};
+    int msgLen{};
+    uint8_t seqNum = 1;
+    uint8_t pID = getpid();
+    char* bufPtr = buf.data();
+
+    do
+    {
+        // Receive response from the kernel
+        if ((readLen = recv(sockFd, bufPtr, BUFSIZE - msgLen, 0)) < 0)
+        {
+            auto error = errno;
+            log<level::ERR>("Socket recv failed:",
+                            entry("ERROR=%s", strerror(error)));
+            elog<InternalFailure>();
+        }
+
+        nlHdr = reinterpret_cast<nlmsghdr*>(bufPtr);
+
+        // Check if the header is valid
+
+        if ((NLMSG_OK(nlHdr, readLen) == 0) ||
+            (nlHdr->nlmsg_type == NLMSG_ERROR))
+        {
+
+            auto error = errno;
+            log<level::ERR>("Error validating header",
+                            entry("NLMSGTYPE=%d", nlHdr->nlmsg_type),
+                            entry("ERROR=%s", strerror(error)));
+            elog<InternalFailure>();
+        }
+
+        // Check if the its the last message
+        if (nlHdr->nlmsg_type == NLMSG_DONE)
+        {
+            break;
+        }
+        else
+        {
+            // Else move the pointer to buffer appropriately
+            bufPtr += readLen;
+            msgLen += readLen;
+        }
+
+        // Check if its a multi part message
+        if ((nlHdr->nlmsg_flags & NLM_F_MULTI) == 0)
+        {
+            break;
+        }
+    } while ((nlHdr->nlmsg_seq != seqNum) || (nlHdr->nlmsg_pid != pID));
+    return msgLen;
+}
+
+void Table::parseRoutes(const nlmsghdr* nlHdr)
+{
+    rtmsg* rtMsg = nullptr;
+    rtattr* rtAttr = nullptr;
+    int rtLen{};
+    std::optional<InAddrAny> dstAddr;
+    std::optional<InAddrAny> gateWayAddr;
+    char ifName[IF_NAMESIZE] = {};
+
+    rtMsg = reinterpret_cast<rtmsg*>(NLMSG_DATA(nlHdr));
+
+    // If the route is not for AF_INET{,6} or does not belong to main routing
+    // table then return.
+    if ((rtMsg->rtm_family != AF_INET && rtMsg->rtm_family != AF_INET6) ||
+        rtMsg->rtm_table != RT_TABLE_MAIN)
+    {
+        return;
+    }
+
+    // get the rtattr field
+    rtAttr = reinterpret_cast<rtattr*>(RTM_RTA(rtMsg));
+
+    rtLen = RTM_PAYLOAD(nlHdr);
+
+    for (; RTA_OK(rtAttr, rtLen); rtAttr = RTA_NEXT(rtAttr, rtLen))
+    {
+        std::string_view attrData(reinterpret_cast<char*>(RTA_DATA(rtAttr)),
+                                  RTA_PAYLOAD(rtAttr));
+        switch (rtAttr->rta_type)
+        {
+            case RTA_OIF:
+                if_indextoname(*reinterpret_cast<int*>(RTA_DATA(rtAttr)),
+                               ifName);
+                break;
+            case RTA_GATEWAY:
+                gateWayAddr = addrFromBuf(rtMsg->rtm_family, attrData);
+                break;
+            case RTA_DST:
+                dstAddr = addrFromBuf(rtMsg->rtm_family, attrData);
+                break;
+        }
+    }
+
+    std::string dstStr;
+    if (dstAddr)
+    {
+        dstStr = toString(*dstAddr);
+    }
+    std::string gatewayStr;
+    if (gateWayAddr)
+    {
+        gatewayStr = toString(*gateWayAddr);
+    }
+    if (!dstAddr && gateWayAddr)
+    {
+        std::string ifNameStr(ifName);
+        if (rtMsg->rtm_family == AF_INET)
+        {
+            defaultGateway[ifNameStr] = gatewayStr;
+        }
+        else if (rtMsg->rtm_family == AF_INET6)
+        {
+            defaultGateway6[ifNameStr] = gatewayStr;
+        }
+    }
+    Entry route(dstStr, gatewayStr, ifName);
+    // if there is already existing route for this network
+    // then ignore the next one as it would not be used by the
+    // routing policy
+    // So don't update the route entry for the network for which
+    // there is already a route exist.
+    if (routeList.find(dstStr) == routeList.end())
+    {
+        routeList.emplace(std::make_pair(dstStr, std::move(route)));
+    }
+}
+
+Map Table::getRoutes()
+{
+    nlmsghdr* nlMsg = nullptr;
+    std::array<char, BUFSIZE> msgBuf = {0};
+
+    int sock = -1;
+    int len{0};
+
+    uint8_t msgSeq{0};
+
+    // Create Socket
+    if ((sock = socket(PF_NETLINK, SOCK_DGRAM, NETLINK_ROUTE)) < 0)
+    {
+        auto error = errno;
+        log<level::ERR>("Error occurred during socket creation",
+                        entry("ERRNO=%s", strerror(error)));
+        elog<InternalFailure>();
+    }
+
+    phosphor::Descriptor smartSock(sock);
+    sock = -1;
+
+    // point the header and the msg structure pointers into the buffer.
+    nlMsg = reinterpret_cast<nlmsghdr*>(msgBuf.data());
+    // Length of message
+    nlMsg->nlmsg_len = NLMSG_LENGTH(sizeof(rtmsg));
+    // Get the routes from kernel routing table
+    nlMsg->nlmsg_type = RTM_GETROUTE;
+    // The message is a request for dump
+    nlMsg->nlmsg_flags = NLM_F_DUMP | NLM_F_REQUEST;
+
+    nlMsg->nlmsg_seq = msgSeq;
+    nlMsg->nlmsg_pid = getpid();
+
+    // Send the request
+    if (send(smartSock(), nlMsg, nlMsg->nlmsg_len, 0) < 0)
+    {
+        auto error = errno;
+        log<level::ERR>("Error occurred during send on netlink socket",
+                        entry("ERRNO=%s", strerror(error)));
+        elog<InternalFailure>();
+    }
+
+    // Read the response
+    len = readNetLinkSock(smartSock(), msgBuf);
+
+    // Parse and print the response
+    for (; NLMSG_OK(nlMsg, len); nlMsg = NLMSG_NEXT(nlMsg, len))
+    {
+        parseRoutes(nlMsg);
+    }
+    return routeList;
+}
+
+} // namespace route
+} // namespace network
+} // namespace phosphor
diff --git a/src/routing_table.hpp b/src/routing_table.hpp
new file mode 100644
index 0000000..8b634c6
--- /dev/null
+++ b/src/routing_table.hpp
@@ -0,0 +1,105 @@
+#pragma once
+
+#include <asm/types.h>
+#include <linux/netlink.h>
+#include <sys/socket.h>
+
+#include <iostream>
+#include <list>
+#include <map>
+#include <string>
+#include <vector>
+
+namespace phosphor
+{
+namespace network
+{
+namespace route
+{
+constexpr auto BUFSIZE = 4096;
+
+struct Entry
+{
+    // destination network
+    std::string destination;
+    // gateway for this network.
+    std::string gateway;
+    // interface for this route
+    std::string interface;
+    Entry(const std::string& dest, const std::string& gtw,
+          const std::string& intf) :
+        destination(dest),
+        gateway(gtw), interface(intf)
+    {
+    }
+
+    bool operator==(const Entry& rhs)
+    {
+        return this->destination == rhs.destination &&
+               this->gateway == rhs.gateway && this->interface == rhs.interface;
+    }
+};
+
+// Map of network address and the route entry
+using Map = std::map<std::string, struct Entry>;
+
+class Table
+{
+  public:
+    Table();
+    ~Table() = default;
+    Table(const Table&) = default;
+    Table& operator=(const Table&) = default;
+    Table(Table&&) = default;
+    Table& operator=(Table&&) = default;
+
+    /**
+     * @brief gets the list of routes.
+     *
+     * @returns list of routes.
+     */
+    Map getRoutes();
+
+    /**
+     * @brief gets the default v4 gateway.
+     *
+     * @returns the default v4 gateway list.
+     */
+    std::map<std::string, std::string> getDefaultGateway() const
+    {
+        return defaultGateway;
+    };
+
+    /**
+     * @brief gets the default v6 gateway.
+     *
+     * @returns the default v6 gateway list.
+     */
+    std::map<std::string, std::string> getDefaultGateway6() const
+    {
+        return defaultGateway6;
+    };
+
+  private:
+    /**
+     * @brief read the routing data from the socket and fill the buffer.
+     *
+     * @param[in] bufPtr - unique pointer to confidentiality algorithm
+     *                     instance
+     */
+    int readNetLinkSock(int sockFd, std::array<char, BUFSIZE>& buff);
+    /**
+     * @brief Parse the route and add it to the route list.
+     *
+     * @param[in] nlHdr - net link message header.
+     */
+    void parseRoutes(const struct nlmsghdr* nlHdr);
+
+    std::map<std::string, std::string> defaultGateway;  // default gateway list
+    std::map<std::string, std::string> defaultGateway6; // default gateway list
+    Map routeList;                                      // List of routes
+};
+
+} // namespace route
+} // namespace network
+} // namespace phosphor
diff --git a/src/rtnetlink_server.cpp b/src/rtnetlink_server.cpp
new file mode 100644
index 0000000..07ca08c
--- /dev/null
+++ b/src/rtnetlink_server.cpp
@@ -0,0 +1,171 @@
+#include "rtnetlink_server.hpp"
+
+#include "types.hpp"
+#include "util.hpp"
+
+#include <linux/netlink.h>
+#include <linux/rtnetlink.h>
+#include <net/if.h>
+#include <netinet/in.h>
+#include <sys/types.h>
+#include <systemd/sd-daemon.h>
+#include <unistd.h>
+
+#include <memory>
+#include <phosphor-logging/elog-errors.hpp>
+#include <phosphor-logging/log.hpp>
+#include <string_view>
+#include <xyz/openbmc_project/Common/error.hpp>
+
+namespace phosphor
+{
+namespace network
+{
+
+extern std::unique_ptr<Timer> refreshObjectTimer;
+
+namespace rtnetlink
+{
+
+static bool shouldRefresh(const struct nlmsghdr& hdr, std::string_view data)
+{
+    switch (hdr.nlmsg_type)
+    {
+        case RTM_NEWADDR:
+        case RTM_DELADDR:
+        case RTM_NEWROUTE:
+        case RTM_DELROUTE:
+        {
+            return true;
+        }
+        case RTM_NEWNEIGH:
+        case RTM_DELNEIGH:
+        {
+            struct ndmsg ndm;
+            if (data.size() < sizeof(ndm))
+            {
+                return false;
+            }
+            memcpy(&ndm, data.data(), sizeof(ndm));
+            // We only want to refresh for static neighbors
+            return ndm.ndm_state & NUD_PERMANENT;
+        }
+    }
+
+    return false;
+}
+
+/* Call Back for the sd event loop */
+static int eventHandler(sd_event_source* /*es*/, int fd, uint32_t /*revents*/,
+                        void* /*userdata*/)
+{
+    char buffer[phosphor::network::rtnetlink::BUFSIZE]{};
+    int len{};
+
+    auto netLinkHeader = reinterpret_cast<struct nlmsghdr*>(buffer);
+    while ((len = recv(fd, netLinkHeader, phosphor::network::rtnetlink::BUFSIZE,
+                       0)) > 0)
+    {
+        for (; (NLMSG_OK(netLinkHeader, len)) &&
+               (netLinkHeader->nlmsg_type != NLMSG_DONE);
+             netLinkHeader = NLMSG_NEXT(netLinkHeader, len))
+        {
+            std::string_view data(
+                reinterpret_cast<const char*>(NLMSG_DATA(netLinkHeader)),
+                netLinkHeader->nlmsg_len - NLMSG_HDRLEN);
+            if (shouldRefresh(*netLinkHeader, data))
+            {
+                // starting the timer here to make sure that we don't want
+                // create the child objects multiple times.
+                if (!refreshObjectTimer->isEnabled())
+                {
+                    // if start timer throws exception then let the application
+                    // crash
+                    refreshObjectTimer->restartOnce(refreshTimeout);
+                } // end if
+            }     // end if
+
+        } // end for
+
+    } // end while
+
+    return 0;
+}
+
+Server::Server(EventPtr& eventPtr, const phosphor::Descriptor& smartSock)
+{
+    using namespace phosphor::logging;
+    using InternalFailure =
+        sdbusplus::xyz::openbmc_project::Common::Error::InternalFailure;
+    struct sockaddr_nl addr
+    {
+    };
+    int r{};
+
+    sigset_t ss{};
+    // check that the given socket is valid or not.
+    if (smartSock() < 0)
+    {
+        r = -EBADF;
+        goto finish;
+    }
+
+    if (sigemptyset(&ss) < 0 || sigaddset(&ss, SIGTERM) < 0 ||
+        sigaddset(&ss, SIGINT) < 0)
+    {
+        r = -errno;
+        goto finish;
+    }
+    /* Block SIGTERM first, so that the event loop can handle it */
+    if (sigprocmask(SIG_BLOCK, &ss, NULL) < 0)
+    {
+        r = -errno;
+        goto finish;
+    }
+
+    /* Let's make use of the default handler and "floating"
+       reference features of sd_event_add_signal() */
+
+    r = sd_event_add_signal(eventPtr.get(), NULL, SIGTERM, NULL, NULL);
+    if (r < 0)
+    {
+        goto finish;
+    }
+
+    r = sd_event_add_signal(eventPtr.get(), NULL, SIGINT, NULL, NULL);
+    if (r < 0)
+    {
+        goto finish;
+    }
+
+    std::memset(&addr, 0, sizeof(addr));
+    addr.nl_family = AF_NETLINK;
+    addr.nl_groups = RTMGRP_IPV4_IFADDR | RTMGRP_IPV6_IFADDR |
+                     RTMGRP_IPV4_ROUTE | RTMGRP_IPV6_ROUTE | RTMGRP_NEIGH;
+
+    if (bind(smartSock(), (struct sockaddr*)&addr, sizeof(addr)) < 0)
+    {
+        r = -errno;
+        goto finish;
+    }
+
+    r = sd_event_add_io(eventPtr.get(), nullptr, smartSock(), EPOLLIN,
+                        eventHandler, nullptr);
+    if (r < 0)
+    {
+        goto finish;
+    }
+
+finish:
+
+    if (r < 0)
+    {
+        log<level::ERR>("Failure Occurred in starting of server:",
+                        entry("ERRNO=%d", errno));
+        elog<InternalFailure>();
+    }
+}
+
+} // namespace rtnetlink
+} // namespace network
+} // namespace phosphor
diff --git a/src/rtnetlink_server.hpp b/src/rtnetlink_server.hpp
new file mode 100644
index 0000000..e62f51c
--- /dev/null
+++ b/src/rtnetlink_server.hpp
@@ -0,0 +1,46 @@
+#pragma once
+
+#include "types.hpp"
+#include "util.hpp"
+
+#include <systemd/sd-event.h>
+
+namespace phosphor
+{
+namespace network
+{
+namespace rtnetlink
+{
+
+constexpr auto BUFSIZE = 4096;
+
+/** General rtnetlink server which waits for the POLLIN event
+    and calls the  call back once it gets the event.
+    Usage would be create the server with the  call back
+    and call the run method.
+ */
+
+class Server
+{
+
+  public:
+    /** @brief Constructor
+     *
+     *  @details Sets up the server to handle incoming RTNETLINK events
+     *
+     *  @param[in] eventPtr - Unique ptr reference to sd_event.
+     *  @param[in] socket - netlink socket.
+     */
+    Server(EventPtr& eventPtr, const phosphor::Descriptor& socket);
+
+    Server() = delete;
+    ~Server() = default;
+    Server(const Server&) = delete;
+    Server& operator=(const Server&) = delete;
+    Server(Server&&) = default;
+    Server& operator=(Server&&) = default;
+};
+
+} // namespace rtnetlink
+} // namespace network
+} // namespace phosphor
diff --git a/src/system_configuration.cpp b/src/system_configuration.cpp
new file mode 100644
index 0000000..4fbf601
--- /dev/null
+++ b/src/system_configuration.cpp
@@ -0,0 +1,151 @@
+#include "config.h"
+
+#include "system_configuration.hpp"
+
+#include "network_manager.hpp"
+#include "routing_table.hpp"
+
+#include <phosphor-logging/elog-errors.hpp>
+#include <phosphor-logging/log.hpp>
+#include <xyz/openbmc_project/Common/error.hpp>
+
+namespace phosphor
+{
+namespace network
+{
+
+// systemd service to kick start a target.
+constexpr auto HOSTNAMED_SERVICE = "org.freedesktop.hostname1";
+constexpr auto HOSTNAMED_SERVICE_PATH = "/org/freedesktop/hostname1";
+constexpr auto HOSTNAMED_INTERFACE = "org.freedesktop.hostname1";
+constexpr auto PROPERTY_INTERFACE = "org.freedesktop.DBus.Properties";
+constexpr auto METHOD_GET = "Get";
+constexpr auto METHOD_SET = "SetStaticHostname";
+
+using namespace phosphor::logging;
+using namespace sdbusplus::xyz::openbmc_project::Common::Error;
+using InvalidArgumentMetadata = xyz::openbmc_project::Common::InvalidArgument;
+
+using SystemConfigIntf =
+    sdbusplus::xyz::openbmc_project::Network::server::SystemConfiguration;
+
+SystemConfiguration::SystemConfiguration(sdbusplus::bus::bus& bus,
+                                         const std::string& objPath,
+                                         Manager& parent) :
+    Iface(bus, objPath.c_str(), true),
+    bus(bus), manager(parent)
+{
+    auto name = getHostNameFromSystem();
+    route::Table routingTable;
+
+    SystemConfigIntf::hostName(name);
+    auto gatewayList = routingTable.getDefaultGateway();
+    auto gateway6List = routingTable.getDefaultGateway6();
+    // Assign first entry of gateway list
+    std::string gateway;
+    std::string gateway6;
+    if (!gatewayList.empty())
+    {
+        gateway = gatewayList.begin()->second;
+    }
+    if (!gateway6List.empty())
+    {
+        gateway6 = gateway6List.begin()->second;
+    }
+
+    SystemConfigIntf::defaultGateway(gateway);
+    SystemConfigIntf::defaultGateway6(gateway6);
+
+    this->emit_object_added();
+}
+
+std::string SystemConfiguration::hostName(std::string name)
+{
+    if (SystemConfigIntf::hostName() == name)
+    {
+        return name;
+    }
+    auto method = bus.new_method_call(HOSTNAMED_SERVICE, HOSTNAMED_SERVICE_PATH,
+                                      HOSTNAMED_INTERFACE, METHOD_SET);
+
+    method.append(name, true);
+
+    if (!bus.call(method))
+    {
+        log<level::ERR>("Failed to set the hostname");
+        report<InternalFailure>();
+        return SystemConfigIntf::hostName();
+    }
+
+    return SystemConfigIntf::hostName(name);
+}
+
+std::string SystemConfiguration::getHostNameFromSystem() const
+{
+    try
+    {
+        std::variant<std::string> name;
+        auto method =
+            bus.new_method_call(HOSTNAMED_SERVICE, HOSTNAMED_SERVICE_PATH,
+                                PROPERTY_INTERFACE, METHOD_GET);
+
+        method.append(HOSTNAMED_INTERFACE, "Hostname");
+
+        auto reply = bus.call(method);
+
+        reply.read(name);
+        return std::get<std::string>(name);
+    }
+    catch (const sdbusplus::exception::SdBusError& ex)
+    {
+        log<level::ERR>(
+            "Failed to get the hostname from systemd-hostnamed service",
+            entry("ERR=%s", ex.what()));
+    }
+    return "";
+}
+
+std::string SystemConfiguration::defaultGateway(std::string gateway)
+{
+    auto gw = SystemConfigIntf::defaultGateway();
+    if (gw == gateway)
+    {
+        return gw;
+    }
+
+    if (!isValidIP(AF_INET, gateway))
+    {
+        log<level::ERR>("Not a valid v4 Gateway",
+                        entry("GATEWAY=%s", gateway.c_str()));
+        elog<InvalidArgument>(
+            InvalidArgumentMetadata::ARGUMENT_NAME("GATEWAY"),
+            InvalidArgumentMetadata::ARGUMENT_VALUE(gateway.c_str()));
+    }
+    gw = SystemConfigIntf::defaultGateway(gateway);
+    manager.writeToConfigurationFile();
+    return gw;
+}
+
+std::string SystemConfiguration::defaultGateway6(std::string gateway)
+{
+    auto gw = SystemConfigIntf::defaultGateway6();
+    if (gw == gateway)
+    {
+        return gw;
+    }
+
+    if (!isValidIP(AF_INET6, gateway))
+    {
+        log<level::ERR>("Not a valid v6 Gateway",
+                        entry("GATEWAY=%s", gateway.c_str()));
+        elog<InvalidArgument>(
+            InvalidArgumentMetadata::ARGUMENT_NAME("GATEWAY"),
+            InvalidArgumentMetadata::ARGUMENT_VALUE(gateway.c_str()));
+    }
+    gw = SystemConfigIntf::defaultGateway6(gateway);
+    manager.writeToConfigurationFile();
+    return gw;
+}
+
+} // namespace network
+} // namespace phosphor
diff --git a/src/system_configuration.hpp b/src/system_configuration.hpp
new file mode 100644
index 0000000..a29309c
--- /dev/null
+++ b/src/system_configuration.hpp
@@ -0,0 +1,76 @@
+#pragma once
+
+#include <sdbusplus/bus.hpp>
+#include <sdbusplus/server/object.hpp>
+#include <string>
+#include <xyz/openbmc_project/Network/SystemConfiguration/server.hpp>
+
+namespace phosphor
+{
+namespace network
+{
+
+using SystemConfigIntf =
+    sdbusplus::xyz::openbmc_project::Network::server::SystemConfiguration;
+
+using Iface = sdbusplus::server::object::object<SystemConfigIntf>;
+
+class Manager; // forward declaration of network manager.
+
+/** @class SystemConfiguration
+ *  @brief Network system configuration.
+ *  @details A concrete implementation for the
+ *  xyz.openbmc_project.Network.SystemConfiguration DBus API.
+ */
+class SystemConfiguration : public Iface
+{
+  public:
+    SystemConfiguration() = default;
+    SystemConfiguration(const SystemConfiguration&) = delete;
+    SystemConfiguration& operator=(const SystemConfiguration&) = delete;
+    SystemConfiguration(SystemConfiguration&&) = delete;
+    SystemConfiguration& operator=(SystemConfiguration&&) = delete;
+    virtual ~SystemConfiguration() = default;
+
+    /** @brief Constructor to put object onto bus at a dbus path.
+     *  @param[in] bus - Bus to attach to.
+     *  @param[in] objPath - Path to attach at.
+     *  @param[in] parent - Parent object.
+     */
+    SystemConfiguration(sdbusplus::bus::bus& bus, const std::string& objPath,
+                        Manager& parent);
+
+    /** @brief set the hostname of the system.
+     *  @param[in] name - host name of the system.
+     */
+    std::string hostName(std::string name) override;
+
+    /** @brief set the default v4 gateway of the system.
+     *  @param[in] gateway - default v4 gateway of the system.
+     */
+    std::string defaultGateway(std::string gateway) override;
+
+    using SystemConfigIntf::defaultGateway;
+
+    /** @brief set the default v6 gateway of the system.
+     *  @param[in] gateway - default v6 gateway of the system.
+     */
+    std::string defaultGateway6(std::string gateway) override;
+
+    using SystemConfigIntf::defaultGateway6;
+
+  private:
+    /** @brief get the hostname from the system by doing
+     *         dbus call to hostnamed service.
+     */
+    std::string getHostNameFromSystem() const;
+
+    /** @brief Persistent sdbusplus DBus bus connection. */
+    sdbusplus::bus::bus& bus;
+
+    /** @brief Network Manager object. */
+    Manager& manager;
+};
+
+} // namespace network
+} // namespace phosphor
diff --git a/src/types.hpp b/src/types.hpp
new file mode 100644
index 0000000..082d588
--- /dev/null
+++ b/src/types.hpp
@@ -0,0 +1,93 @@
+#pragma once
+
+#include "ipaddress.hpp"
+
+#include <ifaddrs.h>
+#include <netinet/in.h>
+#include <systemd/sd-event.h>
+
+#include <array>
+#include <chrono>
+#include <cstddef>
+#include <functional>
+#include <list>
+#include <map>
+#include <memory>
+#include <sdeventplus/clock.hpp>
+#include <sdeventplus/utility/timer.hpp>
+#include <set>
+#include <string>
+#include <variant>
+#include <vector>
+
+namespace phosphor
+{
+namespace network
+{
+
+using namespace std::chrono_literals;
+
+// wait for three seconds before restarting the networkd
+constexpr auto restartTimeout = 3s;
+
+// refresh the objets after five seconds as network
+// configuration takes 3-4 sec after systemd-networkd restart.
+constexpr auto refreshTimeout = restartTimeout + 7s;
+
+namespace systemd
+{
+namespace config
+{
+
+constexpr auto networkFilePrefix = "00-bmc-";
+constexpr auto networkFileSuffix = ".network";
+constexpr auto deviceFileSuffix = ".netdev";
+
+} // namespace config
+} // namespace systemd
+
+using IntfName = std::string;
+
+struct AddrInfo
+{
+    uint8_t addrType;
+    std::string ipaddress;
+    uint16_t prefix;
+};
+
+using Addr_t = ifaddrs*;
+
+struct AddrDeleter
+{
+    void operator()(Addr_t ptr) const
+    {
+        freeifaddrs(ptr);
+    }
+};
+
+using AddrPtr = std::unique_ptr<ifaddrs, AddrDeleter>;
+
+/* Need a custom deleter for freeing up sd_event */
+struct EventDeleter
+{
+    void operator()(sd_event* event) const
+    {
+        sd_event_unref(event);
+    }
+};
+using EventPtr = std::unique_ptr<sd_event, EventDeleter>;
+
+template <typename T>
+using UniquePtr = std::unique_ptr<T, std::function<void(T*)>>;
+
+// Byte representations for common address types in network byte order
+using InAddrAny = std::variant<struct in_addr, struct in6_addr>;
+
+using AddrList = std::list<AddrInfo>;
+using IntfAddrMap = std::map<IntfName, AddrList>;
+using InterfaceList = std::set<IntfName>;
+
+using Timer = sdeventplus::utility::Timer<sdeventplus::ClockId::Monotonic>;
+
+} // namespace network
+} // namespace phosphor
diff --git a/src/util.cpp b/src/util.cpp
new file mode 100644
index 0000000..5c5e427
--- /dev/null
+++ b/src/util.cpp
@@ -0,0 +1,673 @@
+#include "util.hpp"
+
+#include "config_parser.hpp"
+#include "types.hpp"
+
+#include <arpa/inet.h>
+#include <dirent.h>
+#include <net/if.h>
+#include <sys/wait.h>
+
+#include <algorithm>
+#include <cctype>
+#include <cstdlib>
+#include <cstring>
+#include <filesystem>
+#include <fstream>
+#include <list>
+#ifdef SYNC_MAC_FROM_INVENTORY
+#include <nlohmann/json.hpp>
+#endif
+#include <phosphor-logging/elog-errors.hpp>
+#include <phosphor-logging/log.hpp>
+#include <stdexcept>
+#include <stdplus/raw.hpp>
+#include <string>
+#include <variant>
+#include <xyz/openbmc_project/Common/error.hpp>
+
+namespace phosphor
+{
+namespace network
+{
+
+using namespace phosphor::logging;
+using namespace sdbusplus::xyz::openbmc_project::Common::Error;
+namespace fs = std::filesystem;
+
+namespace internal
+{
+
+void executeCommandinChildProcess(const char* path, char** args)
+{
+    using namespace std::string_literals;
+    pid_t pid = fork();
+    int status{};
+
+    if (pid == 0)
+    {
+        execv(path, args);
+        auto error = errno;
+        // create the command from var args.
+        std::string command = path + " "s;
+
+        for (int i = 0; args[i]; i++)
+        {
+            command += args[i] + " "s;
+        }
+
+        log<level::ERR>("Couldn't exceute the command",
+                        entry("ERRNO=%d", error),
+                        entry("CMD=%s", command.c_str()));
+        elog<InternalFailure>();
+    }
+    else if (pid < 0)
+    {
+        auto error = errno;
+        log<level::ERR>("Error occurred during fork", entry("ERRNO=%d", error));
+        elog<InternalFailure>();
+    }
+    else if (pid > 0)
+    {
+        while (waitpid(pid, &status, 0) == -1)
+        {
+            if (errno != EINTR)
+            { // Error other than EINTR
+                status = -1;
+                break;
+            }
+        }
+
+        if (status < 0)
+        {
+            std::string command = path + " "s;
+            for (int i = 0; args[i]; i++)
+            {
+                command += args[i] + " "s;
+            }
+
+            log<level::ERR>("Unable to execute the command",
+                            entry("CMD=%s", command.c_str()),
+                            entry("STATUS=%d", status));
+            elog<InternalFailure>();
+        }
+    }
+}
+
+/** @brief Get ignored interfaces from environment */
+std::string_view getIgnoredInterfacesEnv()
+{
+    auto r = std::getenv("IGNORED_INTERFACES");
+    if (r == nullptr)
+    {
+        return "";
+    }
+    return r;
+}
+
+/** @brief Parse the comma separated interface names */
+std::set<std::string_view> parseInterfaces(std::string_view interfaces)
+{
+    std::set<std::string_view> result;
+    while (true)
+    {
+        auto sep = interfaces.find(',');
+        auto interface = interfaces.substr(0, sep);
+        while (!interface.empty() && std::isspace(interface.front()))
+        {
+            interface.remove_prefix(1);
+        }
+        while (!interface.empty() && std::isspace(interface.back()))
+        {
+            interface.remove_suffix(1);
+        }
+        if (!interface.empty())
+        {
+            result.insert(interface);
+        }
+        if (sep == interfaces.npos)
+        {
+            break;
+        }
+        interfaces = interfaces.substr(sep + 1);
+    }
+    return result;
+}
+
+/** @brief Get the ignored interfaces */
+const std::set<std::string_view>& getIgnoredInterfaces()
+{
+    static auto ignoredInterfaces = parseInterfaces(getIgnoredInterfacesEnv());
+    return ignoredInterfaces;
+}
+
+} // namespace internal
+
+uint8_t toCidr(int addressFamily, const std::string& subnetMask)
+{
+    uint32_t subnet[sizeof(in6_addr) / sizeof(uint32_t)];
+    if (inet_pton(addressFamily, subnetMask.c_str(), &subnet) != 1)
+    {
+        log<level::ERR>("inet_pton failed:",
+                        entry("SUBNETMASK=%s", subnetMask.c_str()));
+        return 0;
+    }
+
+    static_assert(sizeof(in6_addr) % sizeof(uint32_t) == 0);
+    static_assert(sizeof(in_addr) % sizeof(uint32_t) == 0);
+    auto i = (addressFamily == AF_INET ? sizeof(in_addr) : sizeof(in6_addr)) /
+             sizeof(uint32_t);
+    while (i > 0)
+    {
+        if (subnet[--i] != 0)
+        {
+            auto v = be32toh(subnet[i]);
+            static_assert(sizeof(unsigned) == sizeof(uint32_t));
+            auto trailing = __builtin_ctz(v);
+            auto ret = (i + 1) * 32 - trailing;
+            bool valid = ~v == 0 || 32 == trailing + __builtin_clz(~v);
+            while (i > 0 && (valid = (~subnet[--i] == 0) && valid))
+                ;
+            if (!valid)
+            {
+                log<level::ERR>("Invalid netmask",
+                                entry("SUBNETMASK=%s", subnetMask.c_str()));
+                return 0;
+            }
+            return ret;
+        }
+    }
+    return 0;
+}
+
+std::string toMask(int addressFamily, uint8_t prefix)
+{
+    if (addressFamily == AF_INET6)
+    {
+        // TODO:- conversion for v6
+        return "";
+    }
+
+    if (prefix < 1 || prefix > 30)
+    {
+        log<level::ERR>("Invalid Prefix", entry("PREFIX=%d", prefix));
+        return "";
+    }
+    /* Create the netmask from the number of bits */
+    unsigned long mask = 0;
+    for (auto i = 0; i < prefix; i++)
+    {
+        mask |= 1 << (31 - i);
+    }
+    struct in_addr netmask;
+    netmask.s_addr = htonl(mask);
+    return inet_ntoa(netmask);
+}
+
+InAddrAny addrFromBuf(int addressFamily, std::string_view buf)
+{
+    if (addressFamily == AF_INET)
+    {
+        struct in_addr ret;
+        if (buf.size() != sizeof(ret))
+        {
+            throw std::runtime_error("Buf not in_addr sized");
+        }
+        memcpy(&ret, buf.data(), sizeof(ret));
+        return ret;
+    }
+    else if (addressFamily == AF_INET6)
+    {
+        struct in6_addr ret;
+        if (buf.size() != sizeof(ret))
+        {
+            throw std::runtime_error("Buf not in6_addr sized");
+        }
+        memcpy(&ret, buf.data(), sizeof(ret));
+        return ret;
+    }
+
+    throw std::runtime_error("Unsupported address family");
+}
+
+std::string toString(const struct in_addr& addr)
+{
+    std::string ip(INET_ADDRSTRLEN, '\0');
+    if (inet_ntop(AF_INET, &addr, ip.data(), ip.size()) == nullptr)
+    {
+        throw std::runtime_error("Failed to convert IP4 to string");
+    }
+
+    ip.resize(strlen(ip.c_str()));
+    return ip;
+}
+
+std::string toString(const struct in6_addr& addr)
+{
+    std::string ip(INET6_ADDRSTRLEN, '\0');
+    if (inet_ntop(AF_INET6, &addr, ip.data(), ip.size()) == nullptr)
+    {
+        throw std::runtime_error("Failed to convert IP6 to string");
+    }
+
+    ip.resize(strlen(ip.c_str()));
+    return ip;
+}
+
+std::string toString(const InAddrAny& addr)
+{
+    if (std::holds_alternative<struct in_addr>(addr))
+    {
+        const auto& v = std::get<struct in_addr>(addr);
+        return toString(v);
+    }
+    else if (std::holds_alternative<struct in6_addr>(addr))
+    {
+        const auto& v = std::get<struct in6_addr>(addr);
+        return toString(v);
+    }
+
+    throw std::runtime_error("Invalid addr type");
+}
+
+bool isLinkLocalIP(const std::string& address)
+{
+    return address.find(IPV4_PREFIX) == 0 || address.find(IPV6_PREFIX) == 0;
+}
+
+bool isValidIP(int addressFamily, const std::string& address)
+{
+    unsigned char buf[sizeof(struct in6_addr)];
+
+    return inet_pton(addressFamily, address.c_str(), buf) > 0;
+}
+
+bool isValidPrefix(int addressFamily, uint8_t prefixLength)
+{
+    if (addressFamily == AF_INET)
+    {
+        if (prefixLength < IPV4_MIN_PREFIX_LENGTH ||
+            prefixLength > IPV4_MAX_PREFIX_LENGTH)
+        {
+            return false;
+        }
+    }
+
+    if (addressFamily == AF_INET6)
+    {
+        if (prefixLength < IPV4_MIN_PREFIX_LENGTH ||
+            prefixLength > IPV6_MAX_PREFIX_LENGTH)
+        {
+            return false;
+        }
+    }
+
+    return true;
+}
+
+IntfAddrMap getInterfaceAddrs()
+{
+    IntfAddrMap intfMap{};
+    struct ifaddrs* ifaddr = nullptr;
+
+    // attempt to fill struct with ifaddrs
+    if (getifaddrs(&ifaddr) == -1)
+    {
+        auto error = errno;
+        log<level::ERR>("Error occurred during the getifaddrs call",
+                        entry("ERRNO=%s", strerror(error)));
+        elog<InternalFailure>();
+    }
+
+    AddrPtr ifaddrPtr(ifaddr);
+    ifaddr = nullptr;
+
+    std::string intfName{};
+
+    for (ifaddrs* ifa = ifaddrPtr.get(); ifa != nullptr; ifa = ifa->ifa_next)
+    {
+        // walk interfaces
+        if (ifa->ifa_addr == nullptr)
+        {
+            continue;
+        }
+
+        // get only INET interfaces not ipv6
+        if (ifa->ifa_addr->sa_family == AF_INET ||
+            ifa->ifa_addr->sa_family == AF_INET6)
+        {
+            // if loopback, or not running ignore
+            if ((ifa->ifa_flags & IFF_LOOPBACK) ||
+                !(ifa->ifa_flags & IFF_RUNNING))
+            {
+                continue;
+            }
+            intfName = ifa->ifa_name;
+            AddrInfo info{};
+            char ip[INET6_ADDRSTRLEN] = {0};
+            char subnetMask[INET6_ADDRSTRLEN] = {0};
+
+            if (ifa->ifa_addr->sa_family == AF_INET)
+            {
+
+                inet_ntop(ifa->ifa_addr->sa_family,
+                          &(((struct sockaddr_in*)(ifa->ifa_addr))->sin_addr),
+                          ip, sizeof(ip));
+
+                inet_ntop(
+                    ifa->ifa_addr->sa_family,
+                    &(((struct sockaddr_in*)(ifa->ifa_netmask))->sin_addr),
+                    subnetMask, sizeof(subnetMask));
+            }
+            else
+            {
+                inet_ntop(ifa->ifa_addr->sa_family,
+                          &(((struct sockaddr_in6*)(ifa->ifa_addr))->sin6_addr),
+                          ip, sizeof(ip));
+
+                inet_ntop(
+                    ifa->ifa_addr->sa_family,
+                    &(((struct sockaddr_in6*)(ifa->ifa_netmask))->sin6_addr),
+                    subnetMask, sizeof(subnetMask));
+            }
+
+            info.addrType = ifa->ifa_addr->sa_family;
+            info.ipaddress = ip;
+            info.prefix = toCidr(info.addrType, std::string(subnetMask));
+            intfMap[intfName].push_back(info);
+        }
+    }
+    return intfMap;
+}
+
+InterfaceList getInterfaces()
+{
+    InterfaceList interfaces{};
+    struct ifaddrs* ifaddr = nullptr;
+
+    // attempt to fill struct with ifaddrs
+    if (getifaddrs(&ifaddr) == -1)
+    {
+        auto error = errno;
+        log<level::ERR>("Error occurred during the getifaddrs call",
+                        entry("ERRNO=%d", error));
+        elog<InternalFailure>();
+    }
+
+    AddrPtr ifaddrPtr(ifaddr);
+    ifaddr = nullptr;
+    const auto& ignoredInterfaces = internal::getIgnoredInterfaces();
+
+    for (ifaddrs* ifa = ifaddrPtr.get(); ifa != nullptr; ifa = ifa->ifa_next)
+    {
+        // walk interfaces
+        // if loopback ignore
+        if (ifa->ifa_flags & IFF_LOOPBACK ||
+            ignoredInterfaces.find(ifa->ifa_name) != ignoredInterfaces.end())
+        {
+            continue;
+        }
+        interfaces.emplace(ifa->ifa_name);
+    }
+    return interfaces;
+}
+
+void deleteInterface(const std::string& intf)
+{
+    pid_t pid = fork();
+    int status{};
+
+    if (pid == 0)
+    {
+
+        execl("/sbin/ip", "ip", "link", "delete", "dev", intf.c_str(), nullptr);
+        auto error = errno;
+        log<level::ERR>("Couldn't delete the device", entry("ERRNO=%d", error),
+                        entry("INTF=%s", intf.c_str()));
+        elog<InternalFailure>();
+    }
+    else if (pid < 0)
+    {
+        auto error = errno;
+        log<level::ERR>("Error occurred during fork", entry("ERRNO=%d", error));
+        elog<InternalFailure>();
+    }
+    else if (pid > 0)
+    {
+        while (waitpid(pid, &status, 0) == -1)
+        {
+            if (errno != EINTR)
+            { /* Error other than EINTR */
+                status = -1;
+                break;
+            }
+        }
+
+        if (status < 0)
+        {
+            log<level::ERR>("Unable to delete the interface",
+                            entry("INTF=%s", intf.c_str()),
+                            entry("STATUS=%d", status));
+            elog<InternalFailure>();
+        }
+    }
+}
+
+std::optional<std::string> interfaceToUbootEthAddr(const char* intf)
+{
+    constexpr char ethPrefix[] = "eth";
+    constexpr size_t ethPrefixLen = sizeof(ethPrefix) - 1;
+    if (strncmp(ethPrefix, intf, ethPrefixLen) != 0)
+    {
+        return std::nullopt;
+    }
+    const auto intfSuffix = intf + ethPrefixLen;
+    if (intfSuffix[0] == '\0')
+    {
+        return std::nullopt;
+    }
+    char* end;
+    unsigned long idx = strtoul(intfSuffix, &end, 10);
+    if (end[0] != '\0')
+    {
+        return std::nullopt;
+    }
+    if (idx == 0)
+    {
+        return "ethaddr";
+    }
+    return "eth" + std::to_string(idx) + "addr";
+}
+
+EthernetInterfaceIntf::DHCPConf getDHCPValue(const std::string& confDir,
+                                             const std::string& intf)
+{
+    EthernetInterfaceIntf::DHCPConf dhcp =
+        EthernetInterfaceIntf::DHCPConf::none;
+    // Get the interface mode value from systemd conf
+    // using namespace std::string_literals;
+    fs::path confPath = confDir;
+    std::string fileName = systemd::config::networkFilePrefix + intf +
+                           systemd::config::networkFileSuffix;
+    confPath /= fileName;
+
+    auto rc = config::ReturnCode::SUCCESS;
+    config::ValueList values;
+    config::Parser parser(confPath.string());
+
+    std::tie(rc, values) = parser.getValues("Network", "DHCP");
+    if (rc != config::ReturnCode::SUCCESS)
+    {
+        log<level::DEBUG>("Unable to get the value for Network[DHCP]",
+                          entry("RC=%d", rc));
+        return dhcp;
+    }
+    // There will be only single value for DHCP key.
+    if (values[0] == "true")
+    {
+        dhcp = EthernetInterfaceIntf::DHCPConf::both;
+    }
+    else if (values[0] == "ipv4")
+    {
+        dhcp = EthernetInterfaceIntf::DHCPConf::v4;
+    }
+    else if (values[0] == "ipv6")
+    {
+        dhcp = EthernetInterfaceIntf::DHCPConf::v6;
+    }
+    return dhcp;
+}
+
+namespace mac_address
+{
+
+constexpr auto mapperBus = "xyz.openbmc_project.ObjectMapper";
+constexpr auto mapperObj = "/xyz/openbmc_project/object_mapper";
+constexpr auto mapperIntf = "xyz.openbmc_project.ObjectMapper";
+constexpr auto propIntf = "org.freedesktop.DBus.Properties";
+constexpr auto methodGet = "Get";
+constexpr auto configFile = "/usr/share/network/config.json";
+
+using DbusObjectPath = std::string;
+using DbusService = std::string;
+using DbusInterface = std::string;
+using ObjectTree =
+    std::map<DbusObjectPath, std::map<DbusService, std::vector<DbusInterface>>>;
+
+constexpr auto invBus = "xyz.openbmc_project.Inventory.Manager";
+constexpr auto invNetworkIntf =
+    "xyz.openbmc_project.Inventory.Item.NetworkInterface";
+constexpr auto invRoot = "/xyz/openbmc_project/inventory";
+
+ether_addr getfromInventory(sdbusplus::bus::bus& bus,
+                            const std::string& intfName)
+{
+
+    std::string interfaceName = intfName;
+
+#ifdef SYNC_MAC_FROM_INVENTORY
+    // load the config JSON from the Read Only Path
+    std::ifstream in(configFile);
+    nlohmann::json configJson;
+    in >> configJson;
+    interfaceName = configJson[intfName];
+#endif
+
+    std::vector<DbusInterface> interfaces;
+    interfaces.emplace_back(invNetworkIntf);
+
+    auto depth = 0;
+
+    auto mapperCall =
+        bus.new_method_call(mapperBus, mapperObj, mapperIntf, "GetSubTree");
+
+    mapperCall.append(invRoot, depth, interfaces);
+
+    auto mapperReply = bus.call(mapperCall);
+    if (mapperReply.is_method_error())
+    {
+        log<level::ERR>("Error in mapper call");
+        elog<InternalFailure>();
+    }
+
+    ObjectTree objectTree;
+    mapperReply.read(objectTree);
+
+    if (objectTree.empty())
+    {
+        log<level::ERR>("No Object has implemented the interface",
+                        entry("INTERFACE=%s", invNetworkIntf));
+        elog<InternalFailure>();
+    }
+
+    DbusObjectPath objPath;
+    DbusService service;
+
+    if (1 == objectTree.size())
+    {
+        objPath = objectTree.begin()->first;
+        service = objectTree.begin()->second.begin()->first;
+    }
+    else
+    {
+        // If there are more than 2 objects, object path must contain the
+        // interface name
+        for (auto const& object : objectTree)
+        {
+            log<level::INFO>("interface",
+                             entry("INT=%s", interfaceName.c_str()));
+            log<level::INFO>("object", entry("OBJ=%s", object.first.c_str()));
+
+            if (std::string::npos != object.first.find(interfaceName.c_str()))
+            {
+                objPath = object.first;
+                service = object.second.begin()->first;
+                break;
+            }
+        }
+
+        if (objPath.empty())
+        {
+            log<level::ERR>("Can't find the object for the interface",
+                            entry("intfName=%s", interfaceName.c_str()));
+            elog<InternalFailure>();
+        }
+    }
+
+    auto method = bus.new_method_call(service.c_str(), objPath.c_str(),
+                                      propIntf, methodGet);
+
+    method.append(invNetworkIntf, "MACAddress");
+
+    auto reply = bus.call(method);
+    if (reply.is_method_error())
+    {
+        log<level::ERR>("Failed to get MACAddress",
+                        entry("PATH=%s", objPath.c_str()),
+                        entry("INTERFACE=%s", invNetworkIntf));
+        elog<InternalFailure>();
+    }
+
+    std::variant<std::string> value;
+    reply.read(value);
+    return fromString(std::get<std::string>(value));
+}
+
+ether_addr fromString(const char* str)
+{
+    struct ether_addr* mac = ether_aton(str);
+    if (mac == nullptr)
+    {
+        throw std::invalid_argument("Invalid MAC Address");
+    }
+    return *mac;
+}
+
+std::string toString(const ether_addr& mac)
+{
+    char buf[18] = {0};
+    snprintf(buf, 18, "%02x:%02x:%02x:%02x:%02x:%02x", mac.ether_addr_octet[0],
+             mac.ether_addr_octet[1], mac.ether_addr_octet[2],
+             mac.ether_addr_octet[3], mac.ether_addr_octet[4],
+             mac.ether_addr_octet[5]);
+    return buf;
+}
+
+bool isEmpty(const ether_addr& mac)
+{
+    return stdplus::raw::equal(mac, ether_addr{});
+}
+
+bool isMulticast(const ether_addr& mac)
+{
+    return mac.ether_addr_octet[0] & 0b1;
+}
+
+bool isUnicast(const ether_addr& mac)
+{
+    return !isEmpty(mac) && !isMulticast(mac);
+}
+
+} // namespace mac_address
+} // namespace network
+} // namespace phosphor
diff --git a/src/util.hpp b/src/util.hpp
new file mode 100644
index 0000000..804d492
--- /dev/null
+++ b/src/util.hpp
@@ -0,0 +1,250 @@
+#pragma once
+
+#include "config.h"
+
+#include "ethernet_interface.hpp"
+#include "types.hpp"
+
+#include <netinet/ether.h>
+#include <unistd.h>
+
+#include <cstring>
+#include <optional>
+#include <sdbusplus/bus.hpp>
+#include <string>
+#include <string_view>
+#include <xyz/openbmc_project/Network/EthernetInterface/server.hpp>
+
+namespace phosphor
+{
+namespace network
+{
+
+using EthernetInterfaceIntf =
+    sdbusplus::xyz::openbmc_project::Network::server::EthernetInterface;
+
+constexpr auto IPV4_MIN_PREFIX_LENGTH = 1;
+constexpr auto IPV4_MAX_PREFIX_LENGTH = 32;
+constexpr auto IPV6_MAX_PREFIX_LENGTH = 128;
+constexpr auto IPV4_PREFIX = "169.254";
+constexpr auto IPV6_PREFIX = "fe80";
+
+namespace mac_address
+{
+
+/** @brief gets the MAC address from the Inventory.
+ *  @param[in] bus - DBUS Bus Object.
+ *  @param[in] intfName - Interface name
+ */
+ether_addr getfromInventory(sdbusplus::bus::bus& bus,
+                            const std::string& intfName);
+
+/** @brief Converts the given mac address into byte form
+ *  @param[in] str - The mac address in human readable form
+ *  @returns A mac address in network byte order
+ *  @throws std::runtime_error for bad mac
+ */
+ether_addr fromString(const char* str);
+inline ether_addr fromString(const std::string& str)
+{
+    return fromString(str.c_str());
+}
+
+/** @brief Converts the given mac address bytes into a string
+ *  @param[in] mac - The mac address
+ *  @returns A valid mac address string
+ */
+std::string toString(const ether_addr& mac);
+
+/** @brief Determines if the mac address is empty
+ *  @param[in] mac - The mac address
+ *  @return True if 00:00:00:00:00:00
+ */
+bool isEmpty(const ether_addr& mac);
+
+/** @brief Determines if the mac address is a multicast address
+ *  @param[in] mac - The mac address
+ *  @return True if multicast bit is set
+ */
+bool isMulticast(const ether_addr& mac);
+
+/** @brief Determines if the mac address is a unicast address
+ *  @param[in] mac - The mac address
+ *  @return True if not multicast or empty
+ */
+bool isUnicast(const ether_addr& mac);
+
+} // namespace mac_address
+
+constexpr auto networkdService = "systemd-networkd.service";
+constexpr auto timeSynchdService = "systemd-timesyncd.service";
+
+/* @brief converts the given subnet into prefix notation.
+ * @param[in] addressFamily - IP address family(AF_INET/AF_INET6).
+ * @param[in] mask - Subnet Mask.
+ * @returns prefix.
+ */
+uint8_t toCidr(int addressFamily, const std::string& mask);
+
+/* @brief converts a sockaddr for the specified address family into
+ *        a type_safe InAddrAny.
+ * @param[in] addressFamily - The address family of the buf
+ * @param[in] buf - The network byte order address
+ */
+InAddrAny addrFromBuf(int addressFamily, std::string_view buf);
+
+/* @brief converts the ip bytes into a string representation
+ * @param[in] addr - input ip address to convert.
+ * @returns String representation of the ip.
+ */
+std::string toString(const InAddrAny& addr);
+std::string toString(const struct in_addr& addr);
+std::string toString(const struct in6_addr& addr);
+
+/* @brief converts the prefix into subnetmask.
+ * @param[in] addressFamily - IP address family(AF_INET/AF_INET6).
+ * @param[in] prefix - prefix length.
+ * @returns subnet mask.
+ */
+std::string toMask(int addressFamily, uint8_t prefix);
+
+/* @brief checks that the given ip address is link local or not.
+ * @param[in] address - IP address.
+ * @returns true if it is linklocal otherwise false.
+ */
+bool isLinkLocalIP(const std::string& address);
+
+/* @brief checks that the given ip address valid or not.
+ * @param[in] addressFamily - IP address family(AF_INET/AF_INET6).
+ * @param[in] address - IP address.
+ * @returns true if it is valid otherwise false.
+ */
+bool isValidIP(int addressFamily, const std::string& address);
+
+/* @brief checks that the given prefix is valid or not.
+ * @param[in] addressFamily - IP address family(AF_INET/AF_INET6).
+ * @param[in] prefix - prefix length.
+ * @returns true if it is valid otherwise false.
+ */
+bool isValidPrefix(int addressFamily, uint8_t prefixLength);
+
+/** @brief Gets the map of interface and the associated
+ *         address.
+ *  @returns map of interface and the address.
+ */
+IntfAddrMap getInterfaceAddrs();
+
+/** @brief Get all the interfaces from the system.
+ *  @returns list of interface names.
+ */
+InterfaceList getInterfaces();
+
+/** @brief Delete the given interface.
+ *  @param[in] intf - interface name.
+ */
+void deleteInterface(const std::string& intf);
+
+/** @brief Converts the interface name into a u-boot environment
+ *         variable that would hold its ethernet address.
+ *
+ *  @param[in] intf - interface name
+ *  @return The name of th environment key
+ */
+std::optional<std::string> interfaceToUbootEthAddr(const char* intf);
+
+/** @brief read the DHCP value from the configuration file
+ *  @param[in] confDir - Network configuration directory.
+ *  @param[in] intf - Interface name.
+ */
+EthernetInterfaceIntf::DHCPConf getDHCPValue(const std::string& confDir,
+                                             const std::string& intf);
+
+namespace internal
+{
+
+/* @brief runs the given command in child process.
+ * @param[in] path - path of the binary file which needs to be execeuted.
+ * @param[in] args - arguments of the command.
+ */
+void executeCommandinChildProcess(const char* path, char** args);
+
+/** @brief Get ignored interfaces from environment */
+std::string_view getIgnoredInterfacesEnv();
+
+/** @brief Parse the comma separated interface names */
+std::set<std::string_view> parseInterfaces(std::string_view interfaces);
+
+/** @brief Get the ignored interfaces */
+const std::set<std::string_view>& getIgnoredInterfaces();
+
+} // namespace internal
+
+/* @brief runs the given command in child process.
+ * @param[in] path -path of the binary file which needs to be execeuted.
+ * @param[in] tArgs - arguments of the command.
+ */
+template <typename... ArgTypes>
+void execute(const char* path, ArgTypes&&... tArgs)
+{
+    using expandType = char*[];
+
+    expandType args = {const_cast<char*>(tArgs)..., nullptr};
+
+    internal::executeCommandinChildProcess(path, args);
+}
+
+} // namespace network
+
+class Descriptor
+{
+  private:
+    /** default value */
+    int fd = -1;
+
+  public:
+    Descriptor() = default;
+    Descriptor(const Descriptor&) = delete;
+    Descriptor& operator=(const Descriptor&) = delete;
+    Descriptor(Descriptor&&) = delete;
+    Descriptor& operator=(Descriptor&&) = delete;
+
+    explicit Descriptor(int fd) : fd(fd)
+    {
+    }
+
+    /* @brief sets the internal file descriptor with the given descriptor
+     *        and closes the old descriptor.
+     * @param[in] descriptor - File/Socket descriptor.
+     */
+    void set(int descriptor)
+    {
+        // same descriptor given
+        if (fd == descriptor)
+        {
+            return;
+        }
+
+        // close the old descriptor
+        if (fd >= 0)
+        {
+            close(fd);
+        }
+
+        fd = descriptor;
+    }
+
+    ~Descriptor()
+    {
+        if (fd >= 0)
+        {
+            close(fd);
+        }
+    }
+
+    int operator()() const
+    {
+        return fd;
+    }
+};
+
+} // namespace phosphor
diff --git a/src/vlan_interface.cpp b/src/vlan_interface.cpp
new file mode 100644
index 0000000..4920c77
--- /dev/null
+++ b/src/vlan_interface.cpp
@@ -0,0 +1,80 @@
+#include "config.h"
+
+#include "vlan_interface.hpp"
+
+#include "ethernet_interface.hpp"
+#include "network_manager.hpp"
+
+#include <algorithm>
+#include <filesystem>
+#include <fstream>
+#include <phosphor-logging/elog-errors.hpp>
+#include <phosphor-logging/log.hpp>
+#include <string>
+#include <xyz/openbmc_project/Common/error.hpp>
+
+namespace phosphor
+{
+namespace network
+{
+
+using namespace phosphor::logging;
+using namespace sdbusplus::xyz::openbmc_project::Common::Error;
+
+VlanInterface::VlanInterface(sdbusplus::bus::bus& bus,
+                             const std::string& objPath, DHCPConf dhcpEnabled,
+                             bool nicEnabled, uint32_t vlanID,
+                             EthernetInterface& intf, Manager& parent) :
+    VlanIface(bus, objPath.c_str()),
+    DeleteIface(bus, objPath.c_str()),
+    EthernetInterface(bus, objPath, dhcpEnabled, parent, false),
+    parentInterface(intf)
+{
+    id(vlanID);
+    EthernetInterfaceIntf::nicEnabled(nicEnabled);
+    VlanIface::interfaceName(EthernetInterface::interfaceName());
+    MacAddressIntf::macAddress(parentInterface.macAddress());
+
+    emit_object_added();
+}
+
+std::string VlanInterface::macAddress(std::string)
+{
+    log<level::ERR>("Tried to set MAC address on VLAN");
+    elog<InternalFailure>();
+}
+
+void VlanInterface::writeDeviceFile()
+{
+    using namespace std::string_literals;
+    fs::path confPath = manager.getConfDir();
+    std::string fileName = EthernetInterface::interfaceName() + ".netdev"s;
+    confPath /= fileName;
+    std::fstream stream;
+    try
+    {
+        stream.open(confPath.c_str(), std::fstream::out);
+    }
+    catch (std::ios_base::failure& e)
+    {
+        log<level::ERR>("Unable to open the VLAN device file",
+                        entry("FILE=%s", confPath.c_str()),
+                        entry("ERROR=%s", e.what()));
+        elog<InternalFailure>();
+    }
+
+    stream << "[NetDev]\n";
+    stream << "Name=" << EthernetInterface::interfaceName() << "\n";
+    stream << "Kind=vlan\n";
+    stream << "[VLAN]\n";
+    stream << "Id=" << id() << "\n";
+    stream.close();
+}
+
+void VlanInterface::delete_()
+{
+    parentInterface.deleteVLANObject(EthernetInterface::interfaceName());
+}
+
+} // namespace network
+} // namespace phosphor
diff --git a/src/vlan_interface.hpp b/src/vlan_interface.hpp
new file mode 100644
index 0000000..c003056
--- /dev/null
+++ b/src/vlan_interface.hpp
@@ -0,0 +1,78 @@
+#pragma once
+
+#include "ethernet_interface.hpp"
+#include "types.hpp"
+#include "xyz/openbmc_project/Network/VLAN/server.hpp"
+
+#include <sdbusplus/bus.hpp>
+#include <sdbusplus/server/object.hpp>
+#include <string>
+#include <xyz/openbmc_project/Object/Delete/server.hpp>
+
+namespace phosphor
+{
+namespace network
+{
+
+class EthernetInterface;
+class Manager;
+
+using DeleteIface = sdbusplus::xyz::openbmc_project::Object::server::Delete;
+using VlanIface = sdbusplus::xyz::openbmc_project::Network::server::VLAN;
+
+/** @class VlanInterface
+ *  @brief OpenBMC vlan Interface implementation.
+ *  @details A concrete implementation for the vlan interface
+ */
+class VlanInterface : public VlanIface,
+                      public DeleteIface,
+                      public EthernetInterface
+{
+  public:
+    VlanInterface() = delete;
+    VlanInterface(const VlanInterface&) = delete;
+    VlanInterface& operator=(const VlanInterface&) = delete;
+    VlanInterface(VlanInterface&&) = delete;
+    VlanInterface& operator=(VlanInterface&&) = delete;
+    virtual ~VlanInterface() = default;
+
+    /** @brief Constructor to put object onto bus at a dbus path.
+     *  @param[in] bus - Bus to attach to.
+     *  @param[in] objPath - Path to attach at.
+     *  @param[in] dhcpEnabled - DHCP enable value.
+     *  @param[in] vlanID - vlan identifier.
+     *  @param[in] intf - ethernet interface object.
+     *  @param[in] manager - network manager object.
+     *
+     *  This constructor is called during loading the VLAN Interface
+     */
+    VlanInterface(sdbusplus::bus::bus& bus, const std::string& objPath,
+                  DHCPConf dhcpEnabled, bool nicEnabled, uint32_t vlanID,
+                  EthernetInterface& intf, Manager& parent);
+
+    /** @brief Delete this d-bus object.
+     */
+    void delete_() override;
+
+    /** @brief sets the MAC address.
+     *  @param[in] value - MAC address which needs to be set on the system.
+     *  @returns macAddress of the interface or throws an error.
+     */
+    std::string macAddress(std::string value) override;
+
+    /** @brief writes the device configuration.
+               systemd reads this configuration file
+               and creates the vlan interface.*/
+    void writeDeviceFile();
+
+  private:
+    /** @brief VLAN Identifier. */
+    using VlanIface::id;
+
+    EthernetInterface& parentInterface;
+
+    friend class TestVlanInterface;
+};
+
+} // namespace network
+} // namespace phosphor
diff --git a/src/watch.cpp b/src/watch.cpp
new file mode 100644
index 0000000..0efe300
--- /dev/null
+++ b/src/watch.cpp
@@ -0,0 +1,125 @@
+#include "watch.hpp"
+
+#include <errno.h>
+#include <sys/inotify.h>
+
+#include <phosphor-logging/elog-errors.hpp>
+#include <phosphor-logging/log.hpp>
+#include <xyz/openbmc_project/Common/error.hpp>
+
+namespace phosphor
+{
+namespace network
+{
+namespace inotify
+{
+
+using namespace phosphor::logging;
+using namespace sdbusplus::xyz::openbmc_project::Common::Error;
+
+Watch::Watch(phosphor::network::EventPtr& eventPtr, fs::path path,
+             UserCallBack userFunc, int flags, uint32_t mask, uint32_t events) :
+    path(path),
+    userFunc(userFunc), flags(flags), mask(mask), events(events),
+    fd(inotifyInit())
+{
+    // Check if watch file exists
+    // This is supposed to be there always
+    if (!fs::is_regular_file(path))
+    {
+        log<level::ERR>("Watch file doesn't exist",
+                        entry("FILE=%s", path.c_str()));
+        elog<InternalFailure>();
+    }
+
+    auto dirPath = path.parent_path();
+    wd = inotify_add_watch(fd(), dirPath.c_str(), mask);
+    if (wd == -1)
+    {
+        log<level::ERR>("Error from inotify_add_watch",
+                        entry("ERRNO=%d", errno));
+        elog<InternalFailure>();
+    }
+
+    // Register the fd with sd_event infrastructure and setup a
+    // callback handler to be invoked on events
+    auto rc = sd_event_add_io(eventPtr.get(), nullptr, fd(), events,
+                              Watch::processEvents, this);
+    if (rc < 0)
+    {
+        // Failed to add to event loop
+        log<level::ERR>("Error registering with sd_event_add_io",
+                        entry("RC=%d", rc));
+        elog<InternalFailure>();
+    }
+}
+
+int Watch::inotifyInit()
+{
+    auto fd = inotify_init1(flags);
+    if (fd < 0)
+    {
+        log<level::ERR>("Error from inotify_init1", entry("ERRNO=%d", errno));
+        elog<InternalFailure>();
+    }
+    return fd;
+}
+
+int Watch::processEvents(sd_event_source* /*eventSource*/, int fd,
+                         uint32_t retEvents, void* userData)
+{
+    auto watch = static_cast<Watch*>(userData);
+
+    // Not the ones we are interested in
+    if (!(retEvents & watch->events))
+    {
+        return 0;
+    }
+
+    // Buffer size to be used while reading events.
+    // per inotify(7), below number should be fine for reading
+    // at-least one event
+    constexpr auto maxBytes = sizeof(struct inotify_event) + NAME_MAX + 1;
+    uint8_t eventData[maxBytes]{};
+
+    auto bytes = read(fd, eventData, maxBytes);
+    if (bytes <= 0)
+    {
+        // Failed to read inotify event data
+        // Report error and return
+        log<level::ERR>("Error reading inotify event",
+                        entry("ERRNO=%d", errno));
+        report<InternalFailure>();
+        return 0;
+    }
+
+    auto offset = 0;
+    auto stateFile = watch->path.filename();
+    while (offset < bytes)
+    {
+        auto event = reinterpret_cast<inotify_event*>(&eventData[offset]);
+
+        // Filter the interesting ones
+        auto mask = event->mask & watch->mask;
+        if (mask)
+        {
+            if ((event->len > 0) &&
+                (strstr(event->name, stateFile.string().c_str())))
+            {
+                if (watch->userFunc)
+                {
+                    watch->userFunc(watch->path);
+                }
+                // Found the event of interest
+                break;
+            }
+        }
+        // Move past this entry
+        offset += offsetof(inotify_event, name) + event->len;
+    }
+    return 0;
+}
+
+} // namespace inotify
+} // namespace network
+} // namespace phosphor
diff --git a/src/watch.hpp b/src/watch.hpp
new file mode 100644
index 0000000..b45f53f
--- /dev/null
+++ b/src/watch.hpp
@@ -0,0 +1,109 @@
+#pragma once
+
+#include "dns_updater.hpp"
+#include "types.hpp"
+#include "util.hpp"
+
+#include <sys/inotify.h>
+#include <systemd/sd-event.h>
+
+#include <filesystem>
+#include <functional>
+#include <map>
+
+namespace phosphor
+{
+namespace network
+{
+namespace inotify
+{
+
+namespace fs = std::filesystem;
+
+// Auxiliary callback to be invoked on inotify events
+using UserCallBack = std::function<void(const std::string&)>;
+
+/** @class Watch
+ *
+ *  @brief Adds inotify watch on directory
+ *
+ *  @details Calls back user function on matching events
+ */
+class Watch
+{
+  public:
+    Watch() = delete;
+    Watch(const Watch&) = delete;
+    Watch& operator=(const Watch&) = delete;
+    Watch(Watch&&) = delete;
+    Watch& operator=(Watch&&) = delete;
+
+    /** @brief Hooks inotify watch with sd-event
+     *
+     *  @param[in] eventPtr - Reference to sd_event wrapped in unique_ptr
+     *  @param[in] path     - File path to be watched
+     *  @param[in] userFunc - User specific callback function on events
+     *  @param[in] flags    - Flags to be supplied to inotify
+     *  @param[in] mask     - Mask of events to be supplied to inotify
+     *  @param[in] events   - Events to be watched
+     */
+    Watch(phosphor::network::EventPtr& eventPtr, const fs::path path,
+          UserCallBack userFunc, int flags = IN_NONBLOCK,
+          uint32_t mask = IN_CLOSE_WRITE, uint32_t events = EPOLLIN);
+
+    /** @brief Remove inotify watch and close fd's */
+    ~Watch()
+    {
+        if ((fd() >= 0) && (wd >= 0))
+        {
+            inotify_rm_watch(fd(), wd);
+        }
+    }
+
+  private:
+    /** @brief Callback invoked when inotify event fires
+     *
+     *  @details On a matching event, calls back into user supplied
+     *           function if there is one registered
+     *
+     *  @param[in] eventSource - Event source
+     *  @param[in] fd          - Inotify fd
+     *  @param[in] retEvents   - Events that matched for fd
+     *  @param[in] userData    - Pointer to Watch object
+     *
+     *  @returns 0 on success, -1 on fail
+     */
+    static int processEvents(sd_event_source* eventSource, int fd,
+                             uint32_t retEvents, void* userData);
+
+    /** @brief Initializes an inotify instance
+     *
+     *  @return Descriptor on success, -1 on failure
+     */
+    int inotifyInit();
+
+    /** @brief File path to be watched */
+    const fs::path path;
+
+    /** @brief User callback function */
+    UserCallBack userFunc;
+
+    /** @brief Inotify flags */
+    int flags;
+
+    /** @brief Mask of events */
+    uint32_t mask;
+
+    /** @brief Events to be watched */
+    uint32_t events;
+
+    /** @brief Watch descriptor */
+    int wd = -1;
+
+    /** @brief File descriptor manager */
+    phosphor::Descriptor fd;
+};
+
+} // namespace inotify
+} // namespace network
+} // namespace phosphor