rtnetlink: Add public interface parser
This will be used by the rtnetlink server to parse new links.
Change-Id: Ibc0abc12ca58b78ffc22ac6f75822853d4b7bdc8
Signed-off-by: William A. Kennington III <wak@google.com>
diff --git a/src/rtnetlink.cpp b/src/rtnetlink.cpp
index febc305..fd68671 100644
--- a/src/rtnetlink.cpp
+++ b/src/rtnetlink.cpp
@@ -8,6 +8,83 @@
namespace phosphor::network::netlink
{
+using std::literals::string_view_literals::operator""sv;
+
+static void parseVlanInfo(InterfaceInfo& info, std::string_view msg)
+{
+ if (msg.data() == nullptr)
+ {
+ throw std::runtime_error("Missing VLAN data");
+ }
+ while (!msg.empty())
+ {
+ auto [hdr, data] = netlink::extractRtAttr(msg);
+ switch (hdr.rta_type)
+ {
+ case IFLA_VLAN_ID:
+ info.vlan_id.emplace(stdplus::raw::copyFrom<uint16_t>(data));
+ break;
+ }
+ }
+}
+
+static void parseLinkInfo(InterfaceInfo& info, std::string_view msg)
+{
+ std::string_view submsg;
+ while (!msg.empty())
+ {
+ auto [hdr, data] = netlink::extractRtAttr(msg);
+ switch (hdr.rta_type)
+ {
+ case IFLA_INFO_KIND:
+ data.remove_suffix(1);
+ info.kind.emplace(data);
+ break;
+ case IFLA_INFO_DATA:
+ submsg = data;
+ break;
+ }
+ }
+ if (info.kind == "vlan"sv)
+ {
+ parseVlanInfo(info, submsg);
+ }
+}
+
+InterfaceInfo intfFromRtm(std::string_view msg)
+{
+ const auto& ifinfo = netlink::extractRtData<ifinfomsg>(msg);
+ InterfaceInfo ret;
+ ret.flags = ifinfo.ifi_flags;
+ ret.idx = ifinfo.ifi_index;
+ while (!msg.empty())
+ {
+ auto [hdr, data] = netlink::extractRtAttr(msg);
+ switch (hdr.rta_type)
+ {
+ case IFLA_IFNAME:
+ ret.name.emplace(data.begin(), data.end() - 1);
+ break;
+ case IFLA_ADDRESS:
+ if (data.size() == sizeof(ether_addr))
+ {
+ ret.mac.emplace(stdplus::raw::copyFrom<ether_addr>(data));
+ }
+ break;
+ case IFLA_MTU:
+ ret.mtu.emplace(stdplus::raw::copyFrom<unsigned>(data));
+ break;
+ case IFLA_LINK:
+ ret.parent_idx.emplace(stdplus::raw::copyFrom<unsigned>(data));
+ break;
+ case IFLA_LINKINFO:
+ parseLinkInfo(ret, data);
+ break;
+ }
+ }
+ return ret;
+}
+
template <typename Addr>
static std::optional<std::tuple<unsigned, InAddrAny>>
parse(std::string_view msg)
diff --git a/src/rtnetlink.hpp b/src/rtnetlink.hpp
index 2862143..5436e9d 100644
--- a/src/rtnetlink.hpp
+++ b/src/rtnetlink.hpp
@@ -8,6 +8,8 @@
namespace phosphor::network::netlink
{
+InterfaceInfo intfFromRtm(std::string_view msg);
+
std::optional<std::tuple<unsigned, InAddrAny>>
gatewayFromRtm(std::string_view msg);
diff --git a/src/system_queries.cpp b/src/system_queries.cpp
index 19c37ba..894fc09 100644
--- a/src/system_queries.cpp
+++ b/src/system_queries.cpp
@@ -125,87 +125,6 @@
getIFSock().ioctl(SIOCSIFFLAGS, &ifr);
}
-static void parseVlanInfo(InterfaceInfo& info, std::string_view msg)
-{
- if (msg.data() == nullptr)
- {
- throw std::runtime_error("Missing VLAN data");
- }
- while (!msg.empty())
- {
- auto [hdr, data] = netlink::extractRtAttr(msg);
- switch (hdr.rta_type)
- {
- case IFLA_VLAN_ID:
- info.vlan_id.emplace(stdplus::raw::copyFrom<uint16_t>(data));
- break;
- }
- }
-}
-
-static void parseLinkInfo(InterfaceInfo& info, std::string_view msg)
-{
- std::string_view submsg;
- while (!msg.empty())
- {
- auto [hdr, data] = netlink::extractRtAttr(msg);
- switch (hdr.rta_type)
- {
- case IFLA_INFO_KIND:
- data.remove_suffix(1);
- info.kind.emplace(data);
- break;
- case IFLA_INFO_DATA:
- submsg = data;
- break;
- }
- }
- if (info.kind == "vlan"sv)
- {
- parseVlanInfo(info, submsg);
- }
-}
-
-InterfaceInfo detail::parseInterface(const nlmsghdr& hdr, std::string_view msg)
-{
- if (hdr.nlmsg_type != RTM_NEWLINK)
- {
- throw std::runtime_error("Not an interface msg");
- }
- const auto& ifinfo = netlink::extractRtData<ifinfomsg>(msg);
- InterfaceInfo ret;
- ret.flags = ifinfo.ifi_flags;
- ret.idx = ifinfo.ifi_index;
- while (!msg.empty())
- {
- auto [hdr, data] = netlink::extractRtAttr(msg);
- switch (hdr.rta_type)
- {
- case IFLA_IFNAME:
- ret.name.emplace(data.begin(), data.end() - 1);
- break;
- case IFLA_ADDRESS:
- if (data.size() != sizeof(ether_addr))
- {
- // Some interfaces have IP addresses for their LLADDR
- break;
- }
- ret.mac.emplace(stdplus::raw::copyFrom<ether_addr>(data));
- break;
- case IFLA_MTU:
- ret.mtu.emplace(stdplus::raw::copyFrom<unsigned>(data));
- break;
- case IFLA_LINK:
- ret.parent_idx.emplace(stdplus::raw::copyFrom<unsigned>(data));
- break;
- case IFLA_LINKINFO:
- parseLinkInfo(ret, data);
- break;
- }
- }
- return ret;
-}
-
bool detail::validateNewAddr(const AddressInfo& info,
const AddressFilter& filter) noexcept
{
@@ -229,10 +148,10 @@
std::vector<InterfaceInfo> getInterfaces()
{
std::vector<InterfaceInfo> ret;
- auto cb = [&](const nlmsghdr& hdr, std::string_view msg) {
+ auto cb = [&](const nlmsghdr&, std::string_view msg) {
try
{
- ret.emplace_back(detail::parseInterface(hdr, msg));
+ ret.emplace_back(netlink::intfFromRtm(msg));
}
catch (const std::exception& e)
{
diff --git a/src/system_queries.hpp b/src/system_queries.hpp
index 9ec478d..7785c39 100644
--- a/src/system_queries.hpp
+++ b/src/system_queries.hpp
@@ -42,7 +42,6 @@
namespace detail
{
-InterfaceInfo parseInterface(const nlmsghdr& hdr, std::string_view msg);
bool validateNewAddr(const AddressInfo& info,
const AddressFilter& filter) noexcept;
bool validateNewNeigh(const NeighborInfo& info,
diff --git a/test/test_rtnetlink.cpp b/test/test_rtnetlink.cpp
index 7b60072..73a0f00 100644
--- a/test/test_rtnetlink.cpp
+++ b/test/test_rtnetlink.cpp
@@ -10,6 +10,54 @@
namespace phosphor::network::netlink
{
+TEST(IntfFromRtm, SmallMsg)
+{
+ EXPECT_THROW(intfFromRtm("1"), std::runtime_error);
+}
+
+TEST(IntfFromRtm, NoAttrs)
+{
+ struct
+ {
+ ifinfomsg hdr __attribute__((aligned(NLMSG_ALIGNTO)));
+ } msg;
+ msg.hdr.ifi_index = 1;
+ msg.hdr.ifi_flags = 2;
+ EXPECT_EQ(intfFromRtm(stdplus::raw::asView<char>(msg)),
+ (InterfaceInfo{.idx = 1, .flags = 2}));
+}
+
+TEST(IntfFromRtm, AllAttrs)
+{
+ 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 info = intfFromRtm(stdplus::raw::asView<char>(msg));
+ 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(AddrFromRtm, MissingAddr)
{
struct
diff --git a/test/test_system_queries.cpp b/test/test_system_queries.cpp
index 5042cc6..e88c64b 100644
--- a/test/test_system_queries.cpp
+++ b/test/test_system_queries.cpp
@@ -1,87 +1,12 @@
#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(ValidateNewAddr, Filtering)
{
AddressInfo info = {};