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/src/dhcp_configuration.cpp b/src/dhcp_configuration.cpp
index 33b2cbd..1822c35 100644
--- a/src/dhcp_configuration.cpp
+++ b/src/dhcp_configuration.cpp
@@ -27,11 +27,11 @@
{
config::Parser conf;
{
- auto interfaceStrList = system::getInterfaces();
- if (!interfaceStrList.empty())
+ auto interfaces = system::getInterfaces();
+ if (!interfaces.empty())
{
conf.setFile(config::pathForIntfConf(manager.getConfDir(),
- *interfaceStrList.begin()));
+ *interfaces[0].name));
}
}
diff --git a/src/ethernet_interface.cpp b/src/ethernet_interface.cpp
index 09fd9dd..021c126 100644
--- a/src/ethernet_interface.cpp
+++ b/src/ethernet_interface.cpp
@@ -12,6 +12,7 @@
#include <linux/if_addr.h>
#include <linux/neighbour.h>
#include <linux/rtnetlink.h>
+#include <net/if.h>
#include <algorithm>
#include <filesystem>
@@ -64,15 +65,6 @@
return fallback;
}
-InterfaceInfo getInterfaceInfo(stdplus::zstring_view ifname)
-{
- return InterfaceInfo{.running = system::intfIsRunning(ifname),
- .index = system::intfIndex(ifname),
- .name = std::string(ifname),
- .mac = system::getMAC(ifname),
- .mtu = system::getMTU(ifname)};
-}
-
static std::string makeObjPath(std::string_view root, std::string_view intf)
{
auto ret = fmt::format(FMT_COMPILE("{}/{}"), root, intf);
@@ -81,18 +73,18 @@
}
EthernetInterface::EthernetInterface(sdbusplus::bus_t& bus, Manager& manager,
- const InterfaceInfo& info,
+ const system::InterfaceInfo& info,
std::string_view objRoot,
const config::Parser& config,
bool emitSignal,
std::optional<bool> enabled) :
- EthernetInterface(bus, manager, info, makeObjPath(objRoot, info.name),
+ EthernetInterface(bus, manager, info, makeObjPath(objRoot, *info.name),
config, emitSignal, enabled)
{
}
EthernetInterface::EthernetInterface(sdbusplus::bus_t& bus, Manager& manager,
- const InterfaceInfo& info,
+ const system::InterfaceInfo& info,
std::string&& objPath,
const config::Parser& config,
bool emitSignal,
@@ -100,9 +92,9 @@
Ifaces(bus, objPath.c_str(),
emitSignal ? Ifaces::action::defer_emit
: Ifaces::action::emit_no_signals),
- bus(bus), manager(manager), objPath(std::move(objPath)), ifIdx(info.index)
+ bus(bus), manager(manager), objPath(std::move(objPath)), ifIdx(info.idx)
{
- interfaceName(info.name);
+ interfaceName(*info.name);
auto dhcpVal = getDHCPValue(config);
EthernetInterfaceIntf::dhcp4(dhcpVal.v4);
EthernetInterfaceIntf::dhcp6(dhcpVal.v6);
@@ -115,7 +107,7 @@
for (const auto& gateway : gatewayList)
{
- if (gateway.first == info.name)
+ if (gateway.first == *info.name)
{
defaultGateway = gateway.second;
break;
@@ -124,7 +116,7 @@
for (const auto& gateway6 : gateway6List)
{
- if (gateway6.first == info.name)
+ if (gateway6.first == *info.name)
{
defaultGateway6 = gateway6.second;
break;
@@ -138,8 +130,8 @@
if (ifIdx > 0)
{
- auto ethInfo = ignoreError("GetEthInfo", info.name, {}, [&] {
- return system::getEthInfo(info.name);
+ auto ethInfo = ignoreError("GetEthInfo", *info.name, {}, [&] {
+ return system::getEthInfo(*info.name);
});
EthernetInterfaceIntf::autoNeg(ethInfo.autoneg);
EthernetInterfaceIntf::speed(ethInfo.speed);
@@ -154,9 +146,9 @@
}
}
-void EthernetInterface::updateInfo(const InterfaceInfo& info)
+void EthernetInterface::updateInfo(const system::InterfaceInfo& info)
{
- EthernetInterfaceIntf::linkUp(info.running);
+ EthernetInterfaceIntf::linkUp(info.flags & IFF_RUNNING);
if (info.mac)
{
MacAddressIntf::macAddress(std::to_string(*info.mac));
@@ -775,14 +767,13 @@
return fmt::format(FMT_COMPILE("{}.{}"), interfaceName(), id);
}
-void EthernetInterface::loadVLAN(std::string_view objRoot, uint16_t id)
+void EthernetInterface::loadVLAN(std::string_view objRoot, uint16_t id,
+ system::InterfaceInfo&& info)
{
- auto vlanInterfaceName = vlanIntfName(id);
config::Parser config(
- config::pathForIntfConf(manager.getConfDir(), vlanInterfaceName));
+ config::pathForIntfConf(manager.getConfDir(), *info.name));
auto vlanIntf = std::make_unique<phosphor::network::VlanInterface>(
- bus, manager, getInterfaceInfo(vlanInterfaceName), objRoot, config, id,
- *this);
+ bus, manager, info, objRoot, config, id, *this);
// Fetch the ip address from the system
// and create the dbus object.
@@ -791,8 +782,7 @@
vlanIntf->loadNameServers(config);
vlanIntf->loadNTPServers(config);
- this->vlanInterfaces.emplace(std::move(vlanInterfaceName),
- std::move(vlanIntf));
+ this->vlanInterfaces.emplace(std::move(*info.name), std::move(vlanIntf));
}
ObjectPath EthernetInterface::createVLAN(uint16_t id)
@@ -808,8 +798,19 @@
}
auto objRoot = std::string_view(objPath).substr(0, objPath.rfind('/'));
- auto info = getInterfaceInfo(interfaceName());
- info.name = vlanInterfaceName;
+ auto macStr = MacAddressIntf::macAddress();
+ std::optional<ether_addr> mac;
+ if (!macStr.empty())
+ {
+ mac.emplace(mac_address::fromString(macStr));
+ }
+ auto info = system::InterfaceInfo{
+ .idx = 0, // TODO: Query the correct value after creation
+ .flags = 0,
+ .name = vlanInterfaceName,
+ .mac = std::move(mac),
+ .mtu = mtu(),
+ };
// Pass the parents nicEnabled property, so that the child
// VLAN interface can inherit.
@@ -821,7 +822,8 @@
// write the device file for the vlan interface.
vlanIntf->writeDeviceFile();
- this->vlanInterfaces.emplace(vlanInterfaceName, std::move(vlanIntf));
+ this->vlanInterfaces.emplace(std::move(vlanInterfaceName),
+ std::move(vlanIntf));
writeConfigurationFile();
manager.reloadConfigs();
diff --git a/src/ethernet_interface.hpp b/src/ethernet_interface.hpp
index 3480a57..4db338e 100644
--- a/src/ethernet_interface.hpp
+++ b/src/ethernet_interface.hpp
@@ -5,8 +5,6 @@
#include "xyz/openbmc_project/Network/IP/Create/server.hpp"
#include "xyz/openbmc_project/Network/Neighbor/CreateStatic/server.hpp"
-#include <netinet/ether.h>
-
#include <optional>
#include <sdbusplus/bus.hpp>
#include <sdbusplus/server/object.hpp>
@@ -49,23 +47,11 @@
{
class Parser;
}
-
-/** @class InterfaceInfo
- * @brief Information about interfaces from the kernel
- */
-struct InterfaceInfo
+namespace system
{
- bool running;
- unsigned index;
- std::string name;
- std::optional<ether_addr> mac;
- std::optional<unsigned> mtu;
+struct InterfaceInfo;
};
-/** @brief Returns an InterfaceInfo for the given string name
- */
-InterfaceInfo getInterfaceInfo(stdplus::zstring_view ifname);
-
/** @class EthernetInterface
* @brief OpenBMC Ethernet Interface implementation.
* @details A concrete implementation for the
@@ -92,12 +78,13 @@
* @param[in] enabled - Override the lookup of nicEnabled
*/
EthernetInterface(sdbusplus::bus_t& bus, Manager& manager,
- const InterfaceInfo& info, std::string_view objRoot,
- const config::Parser& config, bool emitSignal = true,
+ const system::InterfaceInfo& info,
+ std::string_view objRoot, const config::Parser& config,
+ bool emitSignal = true,
std::optional<bool> enabled = std::nullopt);
/** @brief Updates the interface information based on new InterfaceInfo */
- void updateInfo(const InterfaceInfo& info);
+ void updateInfo(const system::InterfaceInfo& info);
/** @brief Function used to load the ntpservers
*/
@@ -221,7 +208,8 @@
* and creates the ip address dbus objects.
* @param[in] vlanID- VLAN identifier.
*/
- void loadVLAN(std::string_view objRoot, uint16_t vlanID);
+ void loadVLAN(std::string_view objRoot, uint16_t vlanID,
+ system::InterfaceInfo&& info);
/** @brief write the network conf file with the in-memory objects.
*/
@@ -308,7 +296,7 @@
private:
EthernetInterface(sdbusplus::bus_t& bus, Manager& manager,
- const InterfaceInfo& info, std::string&& objPath,
+ const system::InterfaceInfo& info, std::string&& objPath,
const config::Parser& config, bool emitSignal,
std::optional<bool> enabled);
diff --git a/src/network_manager.cpp b/src/network_manager.cpp
index 9a9067e..6763b4b 100644
--- a/src/network_manager.cpp
+++ b/src/network_manager.cpp
@@ -48,25 +48,26 @@
auto isCreated = false;
try
{
- auto interfaceStrList = system::getInterfaces();
- for (const auto& interface : interfaceStrList)
+ for (const auto& interface : system::getInterfaces())
{
// 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)
+ if (interface.name->find(".") != std::string::npos)
{
continue;
}
- fs::path filePath = config::pathForIntfConf(confDir, interface);
+ fs::path filePath =
+ config::pathForIntfConf(confDir, *interface.name);
// create the interface specific network file
// if not existing.
if (!fs::is_regular_file(filePath))
{
- bmc::writeDHCPDefault(filePath, interface);
- log<level::INFO>("Created the default network file.",
- entry("INTERFACE=%s", interface.c_str()));
+ bmc::writeDHCPDefault(filePath, *interface.name);
+ log<level::INFO>(
+ "Created the default network file.",
+ entry("INTERFACE=%s", interface.name->c_str()));
isCreated = true;
}
}
@@ -98,19 +99,17 @@
{
// clear all the interfaces first
interfaces.clear();
- auto interfaceStrList = system::getInterfaces();
- for (auto& interface : interfaceStrList)
+ for (auto& interface : system::getInterfaces())
{
- 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)
+ if (const auto index = interface.name->find(".");
+ index != std::string::npos)
{
// it is vlan interface
- auto sv = std::string_view(interface);
+ auto sv = std::string_view(*interface.name);
auto interfaceName = sv.substr(0, index);
auto vlanStr = sv.substr(index + 1);
uint16_t vlanId;
@@ -125,25 +124,23 @@
if (it == interfaces.end())
{
auto msg = fmt::format("Missing interface({}) for VLAN({}): {}",
- interfaceName, vlanId, interface);
+ interfaceName, vlanId, *interface.name);
log<level::ERR>(msg.c_str());
continue;
}
- it->second->loadVLAN(objectPath, vlanId);
+ it->second->loadVLAN(objectPath, vlanId, std::move(interface));
continue;
}
- // normal ethernet interface
- config::Parser config(config::pathForIntfConf(confDir, interface));
+ config::Parser config(
+ config::pathForIntfConf(confDir, *interface.name));
auto intf = std::make_unique<phosphor::network::EthernetInterface>(
- bus, *this, getInterfaceInfo(interface), objectPath, config);
-
+ bus, *this, interface, objectPath, config);
intf->createIPAddressObjects();
intf->createStaticNeighborObjects();
intf->loadNameServers(config);
intf->loadNTPServers(config);
-
- this->interfaces.emplace(std::move(interface), std::move(intf));
+ this->interfaces.emplace(std::move(*interface.name), std::move(intf));
}
}
diff --git a/src/system_queries.cpp b/src/system_queries.cpp
index 0aa245e..aa8e46b 100644
--- a/src/system_queries.cpp
+++ b/src/system_queries.cpp
@@ -1,9 +1,9 @@
#include "system_queries.hpp"
+#include "netlink.hpp"
#include "util.hpp"
#include <fmt/format.h>
-#include <ifaddrs.h>
#include <linux/ethtool.h>
#include <linux/sockios.h>
#include <net/if.h>
@@ -104,25 +104,6 @@
return executeIFReq(ifname, SIOCGIFFLAGS).ifr_flags & IFF_RUNNING;
}
-unsigned intfIndex(stdplus::const_zstring ifname)
-{
- unsigned idx = if_nametoindex(ifname.c_str());
- if (idx == 0)
- {
- auto msg = fmt::format("if_nametoindex({})", ifname);
- throw std::system_error(errno, std::generic_category(), msg);
- }
- return idx;
-}
-
-std::optional<ether_addr> getMAC(stdplus::zstring_view ifname)
-{
- return optionalIFReq(
- ifname, SIOCGIFHWADDR, "IFHWADDR", [](const ifreq& ifr) {
- return stdplus::raw::refFrom<ether_addr>(ifr.ifr_hwaddr.sa_data);
- });
-}
-
std::optional<unsigned> getMTU(stdplus::zstring_view ifname)
{
return optionalIFReq(ifname, SIOCGIFMTU, "GMTU",
@@ -144,21 +125,70 @@
getIFSock().ioctl(SIOCSIFFLAGS, &ifr);
}
-string_uset getInterfaces()
+InterfaceInfo detail::parseInterface(const nlmsghdr& hdr, std::string_view msg)
{
- string_uset ret;
- struct ifaddrs* root;
- CHECK_ERRNO(getifaddrs(&root), "getifaddrs");
- const auto& ignored = internal::getIgnoredInterfaces();
- for (auto it = root; it != nullptr; it = it->ifa_next)
+ if (hdr.nlmsg_type != RTM_NEWLINK)
{
- if (!(it->ifa_flags & IFF_LOOPBACK) &&
- ignored.find(it->ifa_name) == ignored.end())
+ throw std::runtime_error("Not an interface msg");
+ }
+ auto ifinfo = stdplus::raw::extract<ifinfomsg>(msg);
+ InterfaceInfo ret;
+ ret.flags = ifinfo.ifi_flags;
+ ret.idx = ifinfo.ifi_index;
+ while (!msg.empty())
+ {
+ auto [hdr, data] = netlink::extractRtAttr(msg);
+ if (hdr.rta_type == IFLA_IFNAME)
{
- ret.emplace(it->ifa_name);
+ ret.name.emplace(data.begin(), data.end() - 1);
+ }
+ else if (hdr.rta_type == IFLA_ADDRESS)
+ {
+ if (data.size() != sizeof(ether_addr))
+ {
+ // Some interfaces have IP addresses for their LLADDR
+ continue;
+ }
+ ret.mac.emplace(stdplus::raw::copyFrom<ether_addr>(data));
+ }
+ else if (hdr.rta_type == IFLA_MTU)
+ {
+ ret.mtu.emplace(stdplus::raw::copyFrom<unsigned>(data));
}
}
- freeifaddrs(root);
+ return ret;
+}
+
+bool detail::validateNewInterface(const InterfaceInfo& info)
+{
+ if (info.flags & IFF_LOOPBACK)
+ {
+ return false;
+ }
+ if (!info.name)
+ {
+ throw std::invalid_argument("Interface Dump missing name");
+ }
+ const auto& ignored = internal::getIgnoredInterfaces();
+ if (ignored.find(*info.name) != ignored.end())
+ {
+ return false;
+ }
+ return true;
+}
+
+std::vector<InterfaceInfo> getInterfaces()
+{
+ std::vector<InterfaceInfo> ret;
+ auto cb = [&](const nlmsghdr& hdr, std::string_view msg) {
+ auto info = detail::parseInterface(hdr, msg);
+ if (detail::validateNewInterface(info))
+ {
+ ret.emplace_back(std::move(info));
+ }
+ };
+ ifinfomsg msg{};
+ netlink::performRequest(NETLINK_ROUTE, RTM_GETLINK, NLM_F_DUMP, msg, cb);
return ret;
}
diff --git a/src/system_queries.hpp b/src/system_queries.hpp
index 3182cd6..3bbb021 100644
--- a/src/system_queries.hpp
+++ b/src/system_queries.hpp
@@ -1,17 +1,20 @@
#pragma once
#include "types.hpp"
-#include <netinet/ether.h>
+#include <net/ethernet.h>
#include <cstdint>
#include <optional>
#include <stdplus/zstring.hpp>
#include <stdplus/zstring_view.hpp>
+#include <string>
#include <string_view>
+#include <vector>
+
+struct nlmsghdr;
namespace phosphor::network::system
{
-
struct EthInfo
{
bool autoneg;
@@ -21,19 +24,39 @@
bool intfIsRunning(std::string_view ifname);
-unsigned intfIndex(stdplus::const_zstring ifname);
-
-std::optional<ether_addr> getMAC(stdplus::zstring_view ifname);
-
std::optional<unsigned> getMTU(stdplus::zstring_view ifname);
void setMTU(std::string_view ifname, unsigned mtu);
void setNICUp(std::string_view ifname, bool up);
+/** @class InterfaceInfo
+ * @brief Information about interfaces from the kernel
+ */
+struct InterfaceInfo
+{
+ unsigned idx;
+ unsigned flags;
+ std::optional<std::string> name = std::nullopt;
+ std::optional<ether_addr> mac = std::nullopt;
+ std::optional<unsigned> mtu = std::nullopt;
+
+ constexpr bool operator==(const InterfaceInfo& rhs) const noexcept
+ {
+ return idx == rhs.idx && flags == rhs.flags && name == rhs.name &&
+ mac == rhs.mac && mtu == rhs.mtu;
+ }
+};
+
+namespace detail
+{
+InterfaceInfo parseInterface(const nlmsghdr& hdr, std::string_view msg);
+bool validateNewInterface(const InterfaceInfo& info);
+} // namespace detail
+
/** @brief Get all the interfaces from the system.
* @returns list of interface names.
*/
-string_uset getInterfaces();
+std::vector<InterfaceInfo> getInterfaces();
} // namespace phosphor::network::system
diff --git a/src/util.cpp b/src/util.cpp
index 111c090..e65c17d 100644
--- a/src/util.cpp
+++ b/src/util.cpp
@@ -10,7 +10,6 @@
#include <fmt/format.h>
#include <sys/wait.h>
-#include <algorithm>
#include <cctype>
#include <charconv>
#include <fstream>
diff --git a/src/util.hpp b/src/util.hpp
index 46353a4..19eb108 100644
--- a/src/util.hpp
+++ b/src/util.hpp
@@ -1,7 +1,7 @@
#pragma once
#include "types.hpp"
-#include <netinet/ether.h>
+#include <net/ethernet.h>
#include <netinet/in.h>
#include <unistd.h>
diff --git a/src/vlan_interface.cpp b/src/vlan_interface.cpp
index 0d7879c..cb086c7 100644
--- a/src/vlan_interface.cpp
+++ b/src/vlan_interface.cpp
@@ -3,6 +3,7 @@
#include "config_parser.hpp"
#include "ethernet_interface.hpp"
#include "network_manager.hpp"
+#include "system_queries.hpp"
#include <phosphor-logging/elog-errors.hpp>
#include <phosphor-logging/log.hpp>
@@ -18,7 +19,7 @@
using namespace sdbusplus::xyz::openbmc_project::Common::Error;
VlanInterface::VlanInterface(sdbusplus::bus_t& bus, Manager& manager,
- const InterfaceInfo& info,
+ const system::InterfaceInfo& info,
std::string_view objRoot,
const config::Parser& config, uint16_t vlanID,
EthernetInterface& parent, bool emitSignal,
diff --git a/src/vlan_interface.hpp b/src/vlan_interface.hpp
index 9d27e30..7cdb61d 100644
--- a/src/vlan_interface.hpp
+++ b/src/vlan_interface.hpp
@@ -16,6 +16,10 @@
class EthernetInterface;
class Manager;
+namespace system
+{
+struct InterfaceInfo;
+}
using DeleteIface = sdbusplus::xyz::openbmc_project::Object::server::Delete;
using VlanIface = sdbusplus::xyz::openbmc_project::Network::server::VLAN;
@@ -51,7 +55,7 @@
* This constructor is called during loading the VLAN Interface
*/
VlanInterface(sdbusplus::bus_t& bus, Manager& manager,
- const InterfaceInfo& info, std::string_view objRoot,
+ const system::InterfaceInfo& info, std::string_view objRoot,
const config::Parser& config, uint16_t vlanID,
EthernetInterface& parent, bool emitSignal = true,
std::optional<bool> enabled = std::nullopt);