Backend changes for Populating Nameservers(DNS & Static)
- As per the proposed design :
https://lists.ozlabs.org/pipermail/openbmc/2019-September/018399.html
Depends on :
https://gerrit.openbmc-project.xyz/#/c/openbmc/phosphor-dbus-interfaces/+/26060/
The idea of this commit is to
- Support NameServers(Read Only) property to display current
Nameservers (both DHCP provided& Static) configured on the
interface.
- Support StaticNameServers(Writable) property by which user
can set the Nameservers on an interface.
Tested By:
1.Configure a DNS via DHCP Server & Make sure NameServer property
populates accordingly.
2.With DNS from DHCP existing, set a name server using PATCH on
StaticNameServer property & make sure both properties populate
the data as per the proposal.
3.Make sure /etc/resolv.conf is populated with the right content
in case of DHCP/Static and DHCP & Static (Co-existing)
4.Make sure network configuration file is updated properly when
user sets a staic name server.
Signed-off-by: Manojkiran Eda <manojkiran.eda@gmail.com>
Change-Id: If10b9aa683d2b50e51780e91323c6d10a5ec3710
diff --git a/ethernet_interface.cpp b/ethernet_interface.cpp
index ed1e1ba..600ab83 100644
--- a/ethernet_interface.cpp
+++ b/ethernet_interface.cpp
@@ -35,7 +35,14 @@
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
{
@@ -78,7 +85,6 @@
MacAddressIntf::mACAddress(getMACAddress(intfName));
}
EthernetInterfaceIntf::nTPServers(getNTPServersFromConf());
- EthernetInterfaceIntf::nameservers(getNameServerFromConf());
#if NIC_SUPPORTS_ETHTOOL
InterfaceInfo ifInfo = EthernetInterface::getInterfaceInfo();
@@ -494,6 +500,12 @@
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) &&
@@ -502,13 +514,13 @@
log<level::ERR>("Not a valid IP address"),
entry("ADDRESS=%s", nameserverip.c_str());
elog<InvalidArgument>(
- Argument::ARGUMENT_NAME("Nameserver"),
+ Argument::ARGUMENT_NAME("StaticNameserver"),
Argument::ARGUMENT_VALUE(nameserverip.c_str()));
}
}
try
{
- EthernetInterfaceIntf::nameservers(value);
+ EthernetInterfaceIntf::staticNameServers(value);
writeConfigurationFile();
// resolved reads the DNS server configuration from the
// network file.
@@ -518,10 +530,16 @@
{
log<level::ERR>("Exception processing DNS entries");
}
- return EthernetInterfaceIntf::nameservers();
+ return EthernetInterfaceIntf::staticNameServers();
}
-ServerList EthernetInterface::getNameServerFromConf()
+void EthernetInterface::loadNameServers()
+{
+ EthernetInterfaceIntf::nameservers(getNameServerFromResolvd());
+ EthernetInterfaceIntf::staticNameServers(getstaticNameServerFromConf());
+}
+
+ServerList EthernetInterface::getstaticNameServerFromConf()
{
fs::path confPath = manager.getConfDir();
@@ -541,6 +559,58 @@
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)
+ {
+ std::vector<uint8_t> ipaddress = std::get<1>(*i);
+ std::string address;
+ for (auto byte : ipaddress)
+ {
+ address += std::to_string(byte) + ".";
+ }
+ address.pop_back();
+ servers.push_back(address);
+ }
+ return servers;
+}
+
void EthernetInterface::loadVLAN(VlanId id)
{
std::string vlanInterfaceName = interfaceName() + "." + std::to_string(id);
@@ -706,7 +776,7 @@
}
// Add the DNS entry
- for (const auto& dns : EthernetInterfaceIntf::nameservers())
+ for (const auto& dns : EthernetInterfaceIntf::staticNameServers())
{
stream << "DNS=" << dns << "\n";
}
diff --git a/ethernet_interface.hpp b/ethernet_interface.hpp
index 68668d6..4a0f030 100644
--- a/ethernet_interface.hpp
+++ b/ethernet_interface.hpp
@@ -86,6 +86,10 @@
bool 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.
@@ -180,6 +184,12 @@
*/
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.
*/
@@ -275,7 +285,8 @@
/** @brief get the name server details from the network conf
*
*/
- ServerList getNameServerFromConf();
+ virtual ServerList getNameServerFromResolvd();
+ ServerList getstaticNameServerFromConf();
/** @brief Persistent sdbusplus DBus bus connection. */
sdbusplus::bus::bus& bus;
diff --git a/network_manager.cpp b/network_manager.cpp
index 043d7a2..637092b 100644
--- a/network_manager.cpp
+++ b/network_manager.cpp
@@ -151,6 +151,7 @@
intf->createIPAddressObjects();
intf->createStaticNeighborObjects();
+ intf->loadNameServers();
this->interfaces.emplace(
std::make_pair(std::move(interface), std::move(intf)));
diff --git a/network_manager.hpp b/network_manager.hpp
index edb341f..80d017e 100644
--- a/network_manager.hpp
+++ b/network_manager.hpp
@@ -66,7 +66,7 @@
* from the system and create the ethernet interraces
* dbus object.
*/
- void createInterfaces();
+ virtual void createInterfaces();
/** @brief create child interface object and the system conf object.
*/
diff --git a/test/mock_ethernet_interface.hpp b/test/mock_ethernet_interface.hpp
new file mode 100644
index 0000000..c7c4022
--- /dev/null
+++ b/test/mock_ethernet_interface.hpp
@@ -0,0 +1,25 @@
+#pragma once
+
+#include "ethernet_interface.hpp"
+#include "mock_syscall.hpp"
+
+#include <gmock/gmock.h>
+
+namespace phosphor
+{
+namespace network
+{
+class MockEthernetInterface : public EthernetInterface
+{
+ public:
+ MockEthernetInterface(sdbusplus::bus::bus& bus, const std::string& objPath,
+ bool dhcpEnabled, Manager& parent, bool emitSignal) :
+ EthernetInterface(bus, objPath, dhcpEnabled, parent, emitSignal)
+ {
+ }
+
+ MOCK_METHOD((ServerList), getNameServerFromResolvd, (), (override));
+ friend class TestEthernetInterface;
+};
+} // namespace network
+} // namespace phosphor
diff --git a/test/mock_network_manager.hpp b/test/mock_network_manager.hpp
index df3efdf..b4a6894 100644
--- a/test/mock_network_manager.hpp
+++ b/test/mock_network_manager.hpp
@@ -1,5 +1,8 @@
+#pragma once
+
#include "config.h"
+#include "mock_ethernet_interface.hpp"
#include "network_manager.hpp"
#include <gmock/gmock.h>
@@ -18,6 +21,27 @@
{
}
+ void createInterfaces() override
+ {
+ // clear all the interfaces first
+ interfaces.clear();
+ auto interfaceStrList = getInterfaces();
+ for (auto& interface : interfaceStrList)
+ {
+ fs::path objPath = objectPath;
+ // normal ethernet interface
+ objPath /= interface;
+ auto dhcp = getDHCPValue(confDir, interface);
+ auto intf =
+ std::make_shared<phosphor::network::MockEthernetInterface>(
+ bus, objPath.string(), dhcp, *this, true);
+ intf->createIPAddressObjects();
+ intf->createStaticNeighborObjects();
+ intf->loadNameServers();
+ this->interfaces.emplace(
+ std::make_pair(std::move(interface), std::move(intf)));
+ }
+ }
MOCK_METHOD1(restartSystemdUnit, void(const std::string& service));
};
diff --git a/test/test_ethernet_interface.cpp b/test/test_ethernet_interface.cpp
index 30dee8a..d0beef7 100644
--- a/test/test_ethernet_interface.cpp
+++ b/test/test_ethernet_interface.cpp
@@ -12,6 +12,7 @@
#include <exception>
#include <fstream>
#include <sdbusplus/bus.hpp>
+#include <xyz/openbmc_project/Common/error.hpp>
#include <gtest/gtest.h>
@@ -25,7 +26,7 @@
public:
sdbusplus::bus::bus bus;
MockManager manager;
- EthernetInterface interface;
+ MockEthernetInterface interface;
std::string confDir;
TestEthernetInterface() :
bus(sdbusplus::bus::new_default()),
@@ -53,12 +54,12 @@
static constexpr ether_addr mac{0x11, 0x22, 0x33, 0x44, 0x55, 0x66};
- static EthernetInterface makeInterface(sdbusplus::bus::bus& bus,
- MockManager& manager)
+ static MockEthernetInterface makeInterface(sdbusplus::bus::bus& bus,
+ MockManager& manager)
{
mock_clear();
mock_addIF("test0", 1, mac);
- return {bus, "/xyz/openbmc_test/network/test0", false, manager};
+ return {bus, "/xyz/openbmc_test/network/test0", false, manager, true};
}
int countIPObjects()
@@ -159,11 +160,11 @@
EXPECT_EQ(expectedObjectPath, getObjectPath(ipaddress, prefix, gateway));
}
-TEST_F(TestEthernetInterface, addNameServers)
+TEST_F(TestEthernetInterface, addStaticNameServers)
{
ServerList servers = {"9.1.1.1", "9.2.2.2", "9.3.3.3"};
EXPECT_CALL(manager, restartSystemdUnit(networkdService)).Times(1);
- interface.nameservers(servers);
+ interface.staticNameServers(servers);
fs::path filePath = confDir;
filePath /= "00-bmc-test0.network";
config::Parser parser(filePath.string());
@@ -173,6 +174,21 @@
EXPECT_EQ(servers, values);
}
+TEST_F(TestEthernetInterface, addDynamicNameServers)
+{
+ using namespace sdbusplus::xyz::openbmc_project::Common::Error;
+ ServerList servers = {"9.1.1.1", "9.2.2.2", "9.3.3.3"};
+ EXPECT_THROW(interface.nameservers(servers), NotAllowed);
+}
+
+TEST_F(TestEthernetInterface, getDynamicNameServers)
+{
+ ServerList servers = {"9.1.1.1", "9.2.2.2", "9.3.3.3"};
+ EXPECT_CALL(interface, getNameServerFromResolvd())
+ .WillRepeatedly(testing::Return(servers));
+ EXPECT_EQ(interface.getNameServerFromResolvd(), servers);
+}
+
TEST_F(TestEthernetInterface, addNTPServers)
{
ServerList servers = {"10.1.1.1", "10.2.2.2", "10.3.3.3"};
diff --git a/test/test_network_manager.cpp b/test/test_network_manager.cpp
index 64ca87d..7943057 100644
--- a/test/test_network_manager.cpp
+++ b/test/test_network_manager.cpp
@@ -27,7 +27,7 @@
{
public:
sdbusplus::bus::bus bus;
- Manager manager;
+ MockManager manager;
std::string confDir;
TestNetworkManager() :
bus(sdbusplus::bus::new_default()),
@@ -63,7 +63,6 @@
using namespace sdbusplus::xyz::openbmc_project::Common::Error;
EXPECT_THROW(createInterfaces(), InternalFailure);
}
-
// getifaddrs returns single interface.
TEST_F(TestNetworkManager, WithSingleInterface)
{
@@ -96,6 +95,5 @@
EXPECT_EQ(true, manager.hasInterface("igb0"));
EXPECT_EQ(true, manager.hasInterface("igb1"));
}
-
} // namespace network
} // namespace phosphor