system_queries: Get Interface Info from netlink
This reduces all of the interface information probing down to a single
netlink request for all the label + mac information needed to build an
ethernet interface.
If we ever plumb the ethernet stats into dbus, we can get that
information with this same netlink code.
Change-Id: Ied8a73639dcd74bcfecda392905638573ab7970f
Signed-off-by: William A. Kennington III <wak@google.com>
diff --git a/test/meson.build b/test/meson.build
index 62ca28b..651f542 100644
--- a/test/meson.build
+++ b/test/meson.build
@@ -51,6 +51,7 @@
'netlink',
'network_manager',
#'rtnetlink',
+ 'system_queries',
'types',
'util',
'vlan_interface',
diff --git a/test/mock_network_manager.hpp b/test/mock_network_manager.hpp
index 69a3263..637ce69 100644
--- a/test/mock_network_manager.hpp
+++ b/test/mock_network_manager.hpp
@@ -25,20 +25,18 @@
void createInterfaces() override
{
- // clear all the interfaces first
interfaces.clear();
- auto interfaceStrList = system::getInterfaces();
- for (auto& interface : interfaceStrList)
+ for (auto& interface : system::getInterfaces())
{
- // normal ethernet interface
- config::Parser config(config::pathForIntfConf(confDir, interface));
+ config::Parser config(
+ config::pathForIntfConf(confDir, *interface.name));
auto intf = std::make_unique<MockEthernetInterface>(
- bus, *this, getInterfaceInfo(interface), objectPath, config);
+ bus, *this, interface, objectPath, config);
intf->createIPAddressObjects();
intf->createStaticNeighborObjects();
intf->loadNameServers(config);
this->interfaces.emplace(
- std::make_pair(std::move(interface), std::move(intf)));
+ std::make_pair(std::move(*interface.name), std::move(intf)));
}
}
diff --git a/test/mock_syscall.cpp b/test/mock_syscall.cpp
index e5f97be..e481afd 100644
--- a/test/mock_syscall.cpp
+++ b/test/mock_syscall.cpp
@@ -1,8 +1,8 @@
+#include "system_queries.hpp"
#include "util.hpp"
#include <arpa/inet.h>
#include <dlfcn.h>
-#include <ifaddrs.h>
#include <linux/netlink.h>
#include <linux/rtnetlink.h>
#include <net/ethernet.h>
@@ -24,46 +24,15 @@
#include <string_view>
#include <vector>
-#define MAX_IFADDRS 5
-
-int debugging = false;
-
-/* Data for mocking getifaddrs */
-struct ifaddr_storage
-{
- struct ifaddrs ifaddr;
- struct sockaddr_storage addr;
- struct sockaddr_storage mask;
- struct sockaddr_storage bcast;
-} mock_ifaddr_storage[MAX_IFADDRS];
-
-struct ifaddrs* mock_ifaddrs = nullptr;
-
-int ifaddr_count = 0;
-
-/* Stub library functions */
-void freeifaddrs(ifaddrs* /*ifp*/)
-{
- return;
-}
-
std::map<int, std::queue<std::string>> mock_rtnetlinks;
-struct MockInfo
-{
- unsigned idx;
- unsigned flags;
- std::optional<ether_addr> mac;
- std::optional<unsigned> mtu;
-};
+using phosphor::network::system::InterfaceInfo;
-std::map<std::string, MockInfo> mock_if;
+std::map<std::string, InterfaceInfo> mock_if;
std::map<int, std::string> mock_if_indextoname;
void mock_clear()
{
- mock_ifaddrs = nullptr;
- ifaddr_count = 0;
mock_rtnetlinks.clear();
mock_if.clear();
mock_if_indextoname.clear();
@@ -78,40 +47,12 @@
}
mock_if.emplace(
- name, MockInfo{.idx = idx, .flags = flags, .mac = mac, .mtu = mtu});
+ name,
+ InterfaceInfo{
+ .idx = idx, .flags = flags, .name = name, .mac = mac, .mtu = mtu});
mock_if_indextoname.emplace(idx, name);
}
-void mock_addIP(const char* name, const char* addr, const char* mask)
-{
- struct ifaddrs* ifaddr = &mock_ifaddr_storage[ifaddr_count].ifaddr;
-
- struct sockaddr_in* in =
- reinterpret_cast<sockaddr_in*>(&mock_ifaddr_storage[ifaddr_count].addr);
- struct sockaddr_in* mask_in =
- reinterpret_cast<sockaddr_in*>(&mock_ifaddr_storage[ifaddr_count].mask);
-
- in->sin_family = AF_INET;
- in->sin_port = 0;
- in->sin_addr.s_addr = inet_addr(addr);
-
- mask_in->sin_family = AF_INET;
- mask_in->sin_port = 0;
- mask_in->sin_addr.s_addr = inet_addr(mask);
-
- ifaddr->ifa_next = nullptr;
- ifaddr->ifa_name = const_cast<char*>(name);
- ifaddr->ifa_flags = 0;
- ifaddr->ifa_addr = reinterpret_cast<struct sockaddr*>(in);
- ifaddr->ifa_netmask = reinterpret_cast<struct sockaddr*>(mask_in);
- ifaddr->ifa_data = nullptr;
-
- if (ifaddr_count > 0)
- mock_ifaddr_storage[ifaddr_count - 1].ifaddr.ifa_next = ifaddr;
- ifaddr_count++;
- mock_ifaddrs = &mock_ifaddr_storage[0].ifaddr;
-}
-
void validateMsgHdr(const struct msghdr* msg)
{
if (msg->msg_namelen != sizeof(sockaddr_nl))
@@ -132,36 +73,70 @@
}
}
+void appendRTAttr(std::string& msgBuf, unsigned short type,
+ std::string_view data)
+{
+ const auto rta_begin = msgBuf.size();
+ msgBuf.append(RTA_SPACE(data.size()), '\0');
+ auto& rta = *reinterpret_cast<rtattr*>(msgBuf.data() + rta_begin);
+ rta.rta_len = RTA_LENGTH(data.size());
+ rta.rta_type = type;
+ std::copy(data.begin(), data.end(),
+ msgBuf.data() + rta_begin + RTA_LENGTH(0));
+}
+
ssize_t sendmsg_link_dump(std::queue<std::string>& msgs, std::string_view in)
{
- const ssize_t ret = in.size();
- const auto& hdrin = stdplus::raw::copyFrom<nlmsghdr>(in);
- if (hdrin.nlmsg_type != RTM_GETLINK)
+ if (const auto& hdrin = *reinterpret_cast<const nlmsghdr*>(in.data());
+ hdrin.nlmsg_type != RTM_GETLINK)
{
return 0;
}
+ std::string msgBuf;
+ msgBuf.reserve(8192);
for (const auto& [name, i] : mock_if)
{
- ifinfomsg info{};
- info.ifi_index = i.idx;
- info.ifi_flags = i.flags;
- nlmsghdr hdr{};
- hdr.nlmsg_len = NLMSG_LENGTH(sizeof(info));
+ if (msgBuf.size() > 4096)
+ {
+ msgs.emplace(std::move(msgBuf));
+ }
+ const auto nlbegin = msgBuf.size();
+ msgBuf.append(NLMSG_SPACE(sizeof(ifinfomsg)), '\0');
+ {
+ auto& info = *reinterpret_cast<ifinfomsg*>(msgBuf.data() + nlbegin +
+ NLMSG_HDRLEN);
+ info.ifi_index = i.idx;
+ info.ifi_flags = i.flags;
+ }
+ if (i.name)
+ {
+ appendRTAttr(msgBuf, IFLA_IFNAME, {name.data(), name.size() + 1});
+ }
+ if (i.mac)
+ {
+ appendRTAttr(msgBuf, IFLA_ADDRESS,
+ stdplus::raw::asView<char>(*i.mac));
+ }
+ if (i.mtu)
+ {
+ appendRTAttr(msgBuf, IFLA_MTU, stdplus::raw::asView<char>(*i.mtu));
+ }
+ auto& hdr = *reinterpret_cast<nlmsghdr*>(msgBuf.data() + nlbegin);
+ hdr.nlmsg_len = msgBuf.size() - nlbegin;
hdr.nlmsg_type = RTM_NEWLINK;
hdr.nlmsg_flags = NLM_F_MULTI;
- auto& out = msgs.emplace(hdr.nlmsg_len, '\0');
- memcpy(out.data(), &hdr, sizeof(hdr));
- memcpy(NLMSG_DATA(out.data()), &info, sizeof(info));
+ msgBuf.resize(NLMSG_ALIGN(msgBuf.size()), '\0');
}
-
- nlmsghdr hdr{};
+ const auto nlbegin = msgBuf.size();
+ msgBuf.append(NLMSG_SPACE(0), '\0');
+ auto& hdr = *reinterpret_cast<nlmsghdr*>(msgBuf.data() + nlbegin);
hdr.nlmsg_len = NLMSG_LENGTH(0);
hdr.nlmsg_type = NLMSG_DONE;
hdr.nlmsg_flags = NLM_F_MULTI;
- auto& out = msgs.emplace(hdr.nlmsg_len, '\0');
- memcpy(out.data(), &hdr, sizeof(hdr));
- return ret;
+
+ msgs.emplace(std::move(msgBuf));
+ return in.size();
}
ssize_t sendmsg_ack(std::queue<std::string>& msgs, std::string_view in)
@@ -178,23 +153,6 @@
extern "C" {
-int getifaddrs(ifaddrs** ifap)
-{
- *ifap = mock_ifaddrs;
- return 0;
-}
-
-unsigned if_nametoindex(const char* ifname)
-{
- auto it = mock_if.find(ifname);
- if (it == mock_if.end())
- {
- errno = ENXIO;
- return 0;
- }
- return it->second.idx;
-}
-
char* if_indextoname(unsigned ifindex, char* ifname)
{
auto it = mock_if_indextoname.find(ifindex);
@@ -214,24 +172,7 @@
va_end(vl);
auto req = reinterpret_cast<ifreq*>(data);
- if (request == SIOCGIFHWADDR)
- {
- auto it = mock_if.find(req->ifr_name);
- if (it == mock_if.end())
- {
- errno = ENXIO;
- return -1;
- }
- if (!it->second.mac)
- {
- errno = EOPNOTSUPP;
- return -1;
- }
- std::memcpy(req->ifr_hwaddr.sa_data, &*it->second.mac,
- sizeof(*it->second.mac));
- return 0;
- }
- else if (request == SIOCGIFFLAGS)
+ if (request == SIOCGIFFLAGS)
{
auto it = mock_if.find(req->ifr_name);
if (it == mock_if.end())
diff --git a/test/mock_syscall.hpp b/test/mock_syscall.hpp
index 8d51e0f..39c3986 100644
--- a/test/mock_syscall.hpp
+++ b/test/mock_syscall.hpp
@@ -8,16 +8,6 @@
*/
void mock_clear();
-/** @brief Adds the given interface and addr info
- * into the ifaddr list.
- * @param[in] name - Interface name.
- * @param[in] addr - IP address.
- * @param[in] mask - subnet mask.
- * @param[in] flags - Interface flags.
- */
-
-void mock_addIP(const char* name, const char* addr, const char* mask);
-
/** @brief Adds an address string to index mapping and MAC mapping
*
* @param[in] name - Interface name
diff --git a/test/test_ethernet_interface.cpp b/test/test_ethernet_interface.cpp
index ace2b1d..e65d03c 100644
--- a/test/test_ethernet_interface.cpp
+++ b/test/test_ethernet_interface.cpp
@@ -2,6 +2,7 @@
#include "ipaddress.hpp"
#include "mock_network_manager.hpp"
#include "mock_syscall.hpp"
+#include "system_queries.hpp"
#include "util.hpp"
#include <arpa/inet.h>
@@ -45,7 +46,8 @@
{
mock_clear();
mock_addIF("test0", /*idx=*/1);
- return {bus, manager, getInterfaceInfo("test0"),
+ return {bus, manager,
+ system::InterfaceInfo{.idx = 1, .flags = 0, .name = "test0"},
"/xyz/openbmc_test/network"sv, config::Parser()};
}
@@ -113,7 +115,12 @@
constexpr unsigned mtu = 150;
mock_addIF("test1", idx, IFF_RUNNING, mac, mtu);
- MockEthernetInterface intf(bus, manager, getInterfaceInfo("test1"),
+ MockEthernetInterface intf(bus, manager,
+ system::InterfaceInfo{.idx = idx,
+ .flags = IFF_RUNNING,
+ .name = "test1",
+ .mac = mac,
+ .mtu = mtu},
"/xyz/openbmc_test/network"sv, config::Parser());
EXPECT_EQ(mtu, intf.mtu());
diff --git a/test/test_network_manager.cpp b/test/test_network_manager.cpp
index a34a5e7..5298719 100644
--- a/test/test_network_manager.cpp
+++ b/test/test_network_manager.cpp
@@ -50,7 +50,6 @@
// Adds the following ip in the getifaddrs list.
mock_addIF("igb1", /*idx=*/2);
- mock_addIP("igb1", "192.0.2.3", "255.255.255.128");
// Now create the interfaces which will call the mocked getifaddrs
// which returns the above interface detail.
@@ -64,9 +63,7 @@
mock_clear();
mock_addIF("igb0", /*idx=*/1);
- mock_addIP("igb0", "192.0.2.2", "255.255.255.128");
mock_addIF("igb1", /*idx=*/2);
- mock_addIP("igb1", "192.0.2.3", "255.255.255.128");
createInterfaces();
EXPECT_THAT(manager.getInterfaces(),
diff --git a/test/test_system_queries.cpp b/test/test_system_queries.cpp
new file mode 100644
index 0000000..d615a87
--- /dev/null
+++ b/test/test_system_queries.cpp
@@ -0,0 +1,115 @@
+#include "system_queries.hpp"
+
+#include <linux/rtnetlink.h>
+#include <net/if.h>
+
+#include <stdplus/raw.hpp>
+
+#include <gtest/gtest.h>
+
+using std::literals::string_view_literals::operator""sv;
+
+namespace phosphor::network::system
+{
+namespace detail
+{
+
+TEST(ParseInterface, NotLinkType)
+{
+ nlmsghdr hdr{};
+ hdr.nlmsg_type = RTM_NEWADDR;
+
+ EXPECT_THROW(parseInterface(hdr, ""), std::runtime_error);
+}
+
+TEST(ParseInterface, SmallMsg)
+{
+ nlmsghdr hdr{};
+ hdr.nlmsg_type = RTM_NEWLINK;
+ auto data = "1"sv;
+
+ EXPECT_THROW(parseInterface(hdr, data), std::runtime_error);
+}
+
+TEST(ParseInterface, NoAttrs)
+{
+ nlmsghdr hdr{};
+ hdr.nlmsg_type = RTM_NEWLINK;
+ struct
+ {
+ ifinfomsg hdr __attribute__((aligned(NLMSG_ALIGNTO)));
+ } msg;
+ msg.hdr.ifi_index = 1;
+ msg.hdr.ifi_flags = 2;
+ auto data = stdplus::raw::asView<char>(msg);
+
+ auto info = parseInterface(hdr, data);
+ auto expected = InterfaceInfo{.idx = 1, .flags = 2};
+ EXPECT_EQ(info, expected);
+}
+
+TEST(ParseInterface, AllAttrs)
+{
+ nlmsghdr hdr{};
+ hdr.nlmsg_type = RTM_NEWLINK;
+ struct
+ {
+ ifinfomsg hdr __attribute__((aligned(NLMSG_ALIGNTO)));
+ rtattr addr_hdr __attribute__((aligned((RTA_ALIGNTO))));
+ char addr[6]
+ __attribute__((aligned((RTA_ALIGNTO)))) = {0, 1, 2, 3, 4, 5};
+ rtattr name_hdr __attribute__((aligned((RTA_ALIGNTO))));
+ char name[5] __attribute__((aligned((RTA_ALIGNTO)))) = "eth0";
+ rtattr mtu_hdr __attribute__((aligned((RTA_ALIGNTO))));
+ unsigned mtu __attribute__((aligned((RTA_ALIGNTO)))) = 50;
+ } msg;
+ msg.hdr.ifi_index = 1;
+ msg.hdr.ifi_flags = 2;
+ msg.addr_hdr.rta_type = IFLA_ADDRESS;
+ msg.addr_hdr.rta_len = RTA_LENGTH(sizeof(msg.addr));
+ msg.name_hdr.rta_type = IFLA_IFNAME;
+ msg.name_hdr.rta_len = RTA_LENGTH(sizeof(msg.name));
+ msg.mtu_hdr.rta_type = IFLA_MTU;
+ msg.mtu_hdr.rta_len = RTA_LENGTH(sizeof(msg.mtu));
+ auto data = stdplus::raw::asView<char>(msg);
+
+ auto info = parseInterface(hdr, data);
+ auto expected = InterfaceInfo{.idx = 1,
+ .flags = 2,
+ .name = "eth0",
+ .mac = ether_addr{0, 1, 2, 3, 4, 5},
+ .mtu = 50};
+ EXPECT_EQ(info, expected);
+}
+
+TEST(ValidateNewInterface, Loopback)
+{
+ InterfaceInfo info;
+ info.flags = IFF_LOOPBACK | IFF_RUNNING;
+ EXPECT_FALSE(validateNewInterface(info));
+}
+
+TEST(ValidateNewInterface, NoName)
+{
+ EXPECT_THROW(validateNewInterface(InterfaceInfo{}), std::invalid_argument);
+}
+
+TEST(ValidateNewInterface, IgnoredInterface)
+{
+ InterfaceInfo info;
+ setenv("IGNORED_INTERFACES", "ign", true);
+ info.name = "ign";
+ info.flags = IFF_RUNNING;
+ EXPECT_FALSE(validateNewInterface(info));
+}
+
+TEST(ValidateNewInterface, Valid)
+{
+ InterfaceInfo info;
+ info.name = "eth0";
+ info.flags = 0;
+ EXPECT_TRUE(validateNewInterface(info));
+}
+
+} // namespace detail
+} // namespace phosphor::network::system
diff --git a/test/test_vlan_interface.cpp b/test/test_vlan_interface.cpp
index 499ab34..c17bf97 100644
--- a/test/test_vlan_interface.cpp
+++ b/test/test_vlan_interface.cpp
@@ -2,6 +2,7 @@
#include "ipaddress.hpp"
#include "mock_network_manager.hpp"
#include "mock_syscall.hpp"
+#include "system_queries.hpp"
#include "vlan_interface.hpp"
#include <arpa/inet.h>
@@ -44,7 +45,7 @@
mock_addIF("test0", /*idx=*/1);
return {bus,
manager,
- getInterfaceInfo("test0"),
+ system::InterfaceInfo{.idx = 1, .flags = 0, .name = "test0"},
"/xyz/openbmc_test/network"sv,
config::Parser(),
/*emitSignal=*/false,