Add LLDP configuration support

This commit implements EmitLLDP D-bus property to support configuration
of enable/disable LLDP of each ethernet interface.

Tested by:
Set EmitLLDP D-bus property on
xyz.openbmc_project.Network.EthernetInterface

Change-Id: I4ebedff9d3f914219f2f84c861fdee126584a94b
Signed-off-by: Ravi Teja <raviteja28031990@gmail.com>
diff --git a/src/ethernet_interface.cpp b/src/ethernet_interface.cpp
index 46967f5..9cef02f 100644
--- a/src/ethernet_interface.cpp
+++ b/src/ethernet_interface.cpp
@@ -102,7 +102,11 @@
     EthernetInterfaceIntf::dhcp6(dhcpVal.v6, true);
     EthernetInterfaceIntf::ipv6AcceptRA(getIPv6AcceptRA(config), true);
     EthernetInterfaceIntf::nicEnabled(enabled, true);
-
+    auto lldpVal = parseLLDPConf();
+    if (!lldpVal.empty())
+    {
+        EthernetInterfaceIntf::emitLLDP(lldpVal[interfaceName()], true);
+    }
     EthernetInterfaceIntf::ntpServers(
         config.map.getValueStrings("Network", "NTP"), true);
 
@@ -1094,6 +1098,16 @@
     eth.get().manager.get().reloadConfigs();
 }
 
+bool EthernetInterface::emitLLDP(bool value)
+{
+    if (emitLLDP() != EthernetInterfaceIntf::emitLLDP(value))
+    {
+        manager.get().writeLLDPDConfigurationFile();
+        manager.get().reloadLLDPService();
+    }
+    return value;
+}
+
 void EthernetInterface::reloadConfigs()
 {
     manager.get().reloadConfigs();
diff --git a/src/ethernet_interface.hpp b/src/ethernet_interface.hpp
index 3140528..858779b 100644
--- a/src/ethernet_interface.hpp
+++ b/src/ethernet_interface.hpp
@@ -227,6 +227,11 @@
      */
     void reloadConfigs();
 
+    /** @brief set conf file for LLDP
+     *  @param[in] value - lldp value of the interface.
+     */
+    bool emitLLDP(bool value) override;
+
     using EthernetInterfaceIntf::interfaceName;
     using EthernetInterfaceIntf::linkUp;
     using EthernetInterfaceIntf::mtu;
@@ -235,6 +240,7 @@
 
     using EthernetInterfaceIntf::defaultGateway;
     using EthernetInterfaceIntf::defaultGateway6;
+    using EthernetInterfaceIntf::emitLLDP;
 
   protected:
     /** @brief get the NTP server list from the timsyncd dbus obj
diff --git a/src/network_manager.cpp b/src/network_manager.cpp
index fb3a801..75c759b 100644
--- a/src/network_manager.cpp
+++ b/src/network_manager.cpp
@@ -22,6 +22,7 @@
 
 #include <filesystem>
 #include <format>
+#include <fstream>
 
 namespace phosphor
 {
@@ -33,6 +34,12 @@
 using Argument = xyz::openbmc_project::Common::InvalidArgument;
 using std::literals::string_view_literals::operator""sv;
 
+constexpr auto systemdBusname = "org.freedesktop.systemd1";
+constexpr auto systemdObjPath = "/org/freedesktop/systemd1";
+constexpr auto systemdInterface = "org.freedesktop.systemd1.Manager";
+constexpr auto lldpFilePath = "/etc/lldpd.conf";
+constexpr auto lldpService = "lldpd.service";
+
 static constexpr const char enabledMatch[] =
     "type='signal',sender='org.freedesktop.network1',path_namespace='/org/"
     "freedesktop/network1/"
@@ -515,5 +522,45 @@
     }
 }
 
+void Manager::writeLLDPDConfigurationFile()
+{
+    std::ofstream lldpdConfig(lldpFilePath);
+
+    lldpdConfig << "configure system description BMC" << std::endl;
+    lldpdConfig << "configure system ip management pattern eth*" << std::endl;
+    for (const auto& intf : interfaces)
+    {
+        bool emitlldp = intf.second->emitLLDP();
+        if (emitlldp)
+        {
+            lldpdConfig << "configure ports " << intf.second->interfaceName()
+                        << " lldp status tx-only" << std::endl;
+        }
+        else
+        {
+            lldpdConfig << "configure ports " << intf.second->interfaceName()
+                        << " lldp status disabled" << std::endl;
+        }
+    }
+
+    lldpdConfig.close();
+}
+
+void Manager::reloadLLDPService()
+{
+    try
+    {
+        auto method = bus.get().new_method_call(
+            systemdBusname, systemdObjPath, systemdInterface, "RestartUnit");
+        method.append(lldpService, "replace");
+        bus.get().call_noreply(method);
+    }
+    catch (const sdbusplus::exception_t& ex)
+    {
+        lg2::error("Failed to restart service {SERVICE}: {ERR}", "SERVICE",
+                   lldpService, "ERR", ex);
+    }
+}
+
 } // namespace network
 } // namespace phosphor
diff --git a/src/network_manager.hpp b/src/network_manager.hpp
index b92c528..3bc8ad2 100644
--- a/src/network_manager.hpp
+++ b/src/network_manager.hpp
@@ -55,6 +55,10 @@
      */
     void writeToConfigurationFile();
 
+    /** @brief write the lldp conf file
+     */
+    void writeLLDPDConfigurationFile();
+
     /** @brief Adds a single interface to the interface map */
     void addInterface(const InterfaceInfo& info);
     void removeInterface(const InterfaceInfo& info);
@@ -102,6 +106,10 @@
         reload.get().schedule();
     }
 
+    /** Reload LLDP configuration
+     */
+    void reloadLLDPService();
+
     /** @brief Persistent map of EthernetInterface dbus objects and their names
      */
     stdplus::string_umap<std::unique_ptr<EthernetInterface>> interfaces;
diff --git a/src/util.cpp b/src/util.cpp
index edee79a..ac4d1b4 100644
--- a/src/util.cpp
+++ b/src/util.cpp
@@ -15,6 +15,7 @@
 #include <xyz/openbmc_project/Common/error.hpp>
 
 #include <cctype>
+#include <fstream>
 #include <string>
 #include <string_view>
 
@@ -26,6 +27,7 @@
 using std::literals::string_view_literals::operator""sv;
 using namespace phosphor::logging;
 using namespace sdbusplus::xyz::openbmc_project::Common::Error;
+static constexpr std::string_view lldpdConfigFilePath = "/etc/lldpd.conf";
 
 namespace internal
 {
@@ -226,5 +228,42 @@
         .value_or(true);
 }
 
+std::map<std::string, bool> parseLLDPConf()
+{
+    std::ifstream lldpdConfig(lldpdConfigFilePath.data());
+    std::map<std::string, bool> portStatus;
+
+    if (!lldpdConfig.is_open())
+    {
+        return portStatus;
+    }
+
+    std::string line;
+    while (std::getline(lldpdConfig, line))
+    {
+        std::string configurePortsStr = "configure ports ";
+        std::string lldpStatusStr = "lldp status ";
+        size_t portStart = line.find(configurePortsStr);
+        if (portStart != std::string::npos)
+        {
+            portStart += configurePortsStr.size();
+            size_t portEnd = line.find(' ', portStart);
+            if (portEnd == std::string::npos)
+            {
+                portEnd = line.length();
+            }
+            std::string portName = line.substr(portStart, portEnd - portStart);
+            size_t pos = line.find(lldpStatusStr);
+            if (pos != std::string::npos)
+            {
+                std::string statusStr = line.substr(pos + lldpStatusStr.size());
+                portStatus[portName] = (statusStr == "disabled") ? false : true;
+            }
+        }
+    }
+    lldpdConfig.close();
+    return portStatus;
+}
+
 } // namespace network
 } // namespace phosphor
diff --git a/src/util.hpp b/src/util.hpp
index ae34ad6..1c71eab 100644
--- a/src/util.hpp
+++ b/src/util.hpp
@@ -4,6 +4,7 @@
 #include <stdplus/raw.hpp>
 #include <stdplus/zstring_view.hpp>
 
+#include <map>
 #include <optional>
 #include <string>
 #include <string_view>
@@ -72,6 +73,10 @@
 bool getDHCPProp(const config::Parser& config, DHCPType dhcpType,
                  std::string_view key);
 
+/** @brief Read LLDP configuration from lldpd conf file
+ */
+std::map<std::string, bool> parseLLDPConf();
+
 namespace internal
 {