ncsid: Set MAC address using ioctl calls
Set the MAC address directly via ioctl calls, removing the
dependency on phosphor-networkd. This simplifies the process
and reduces overhead as phosphor-networkd is not extensively
utilized in this context.
Change-Id: If3465f52834ba38c551d5152ef354cbf455f6a90
Signed-off-by: Mo Elbadry <elbadrym@google.com>
diff --git a/subprojects/ncsid/src/net_config.cpp b/subprojects/ncsid/src/net_config.cpp
index e8c00d2..65e50a7 100644
--- a/subprojects/ncsid/src/net_config.cpp
+++ b/subprojects/ncsid/src/net_config.cpp
@@ -14,6 +14,9 @@
#include "net_config.h"
+#include <linux/if.h>
+#include <net/if_arp.h>
+#include <sys/ioctl.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>
@@ -28,6 +31,7 @@
#include <cstring>
#include <filesystem>
#include <format>
+#include <thread>
#include <utility>
#include <variant>
@@ -49,15 +53,6 @@
constexpr auto NETWORK_SERVICE = "xyz.openbmc_project.Network";
constexpr auto PROP_INTERFACE = "org.freedesktop.DBus.Properties";
-int parse_mac(const std::string& mac_addr, mac_addr_t* mac)
-{
- int ret = sscanf(mac_addr.c_str(), MAC_FORMAT, mac->octet, mac->octet + 1,
- mac->octet + 2, mac->octet + 3, mac->octet + 4,
- mac->octet + 5);
-
- return ret < 6 ? -1 : 0;
-}
-
std::string format_mac(const mac_addr_t& mac)
{
// 2 chars for every byte + 5 colons + Null byte
@@ -76,18 +71,6 @@
bus(sdbusplus::bus::new_default())
{}
-sdbusplus::message_t
- PhosphorConfig::new_networkd_call(sdbusplus::bus_t* dbus, bool get) const
-{
- auto networkd_call =
- dbus->new_method_call(NETWORK_SERVICE, iface_path_.c_str(),
- PROP_INTERFACE, get ? "Get" : "Set");
-
- networkd_call.append(MAC_INTERFACE, "MACAddress");
-
- return networkd_call;
-}
-
int PhosphorConfig::get_mac_addr(mac_addr_t* mac)
{
if (mac == nullptr)
@@ -101,41 +84,41 @@
{
*mac = shared_host_mac_.value();
}
- else // Cache miss: read MAC over DBus, and store in cache.
+ else // Cache miss: read from interface, cache it for future requests.
{
- std::string mac_string;
+ struct ifreq ifr = {};
try
{
- auto networkd_call = new_networkd_call(&bus, true);
- auto reply = bus.call(networkd_call);
- std::variant<std::string> result;
- reply.read(result);
- mac_string = std::get<std::string>(result);
+ auto fd = stdplus::fd::socket(stdplus::fd::SocketDomain::INet6,
+ stdplus::fd::SocketType::Datagram,
+ stdplus::fd::SocketProto::IP);
+ call_nic(fd, ifr, SIOCGIFHWADDR);
}
- catch (const sdbusplus::exception::SdBusError& ex)
+ catch (const std::exception& ex)
{
- stdplus::println(stderr, "Failed to get MACAddress: {}", ex.what());
+ stdplus::println(
+ stderr,
+ "Failed to get MAC Addr for Interface {} writing file: {}",
+ iface_name_, ex.what());
return -1;
}
-
- if (parse_mac(mac_string, mac) < 0)
- {
- stdplus::println(stderr, "Failed to parse MAC Address `{}`",
- mac_string);
- return -1;
- }
-
+ std::copy_n(ifr.ifr_addr.sa_data, sizeof(*mac), mac->octet);
shared_host_mac_ = *mac;
}
-
return 0;
}
+void PhosphorConfig::call_nic(auto fd, struct ifreq& ifr, int op)
+{
+ std::copy_n(iface_name_.c_str(), iface_name_.size() + 1, ifr.ifr_name);
+ fd.ioctl(op, &ifr);
+}
+
int PhosphorConfig::set_mac_addr(const mac_addr_t& mac)
{
- auto networkd_call = new_networkd_call(&bus, false);
std::variant<std::string> mac_value(format_mac(mac));
- networkd_call.append(mac_value);
+ struct ifreq ifr = {};
+ short flags_copy;
try
{
@@ -158,18 +141,62 @@
std::get<std::string>(mac_value), ex.what());
return -1;
}
-
try
{
- auto reply = bus.call(networkd_call);
+ auto fd = stdplus::fd::socket(stdplus::fd::SocketDomain::INet6,
+ stdplus::fd::SocketType::Datagram,
+ stdplus::fd::SocketProto::IP);
+ // Try setting MAC Address directly without bringing interface down
+ try
+ {
+ std::copy_n(mac.octet, 6, ifr.ifr_hwaddr.sa_data);
+ call_nic(fd, ifr, SIOCSIFHWADDR);
+ }
+ catch (const std::exception& e)
+ {
+ // Regardless of error attempt to set MAC Address again after
+ // bringing interface down
+ stdplus::println(
+ stderr,
+ "Could not set MAC Address directly, retrying after bringing interface down, error = {}",
+ e.what());
+ try
+ {
+ // Read interface flags configuration and store (once interface
+ // is brought down, existing state is lost)
+ call_nic(fd, ifr, SIOCGIFFLAGS);
+ flags_copy = ifr.ifr_flags;
+ // set interface down
+ ifr.ifr_flags &= ~IFF_UP;
+ call_nic(fd, ifr, SIOCSIFFLAGS);
+ // Wait for 1 milliseconds - sometimes interface is still
+ // going down
+ std::this_thread::sleep_for(std::chrono::milliseconds(1));
+ // set MAC Address
+ ifr.ifr_hwaddr.sa_family = ARPHRD_ETHER;
+ std::copy_n(mac.octet, 6, ifr.ifr_hwaddr.sa_data);
+ call_nic(fd, ifr, SIOCSIFHWADDR);
+ // set interface up with the flags state prior to bringing
+ // it down
+ ifr.ifr_flags = flags_copy | IFF_UP;
+ call_nic(fd, ifr, SIOCSIFFLAGS);
+ }
+ catch (const std::exception& e)
+ {
+ stdplus::println(
+ stderr, "Failed to set MAC Address {} writing file: {}",
+ std::get<std::string>(mac_value), e.what());
+ return -1;
+ }
+ }
}
- catch (const sdbusplus::exception::SdBusError& ex)
+ catch (const std::exception& e)
{
- stdplus::println(stderr, "Failed to set MAC Addr `{}`: {}",
- std::get<std::string>(mac_value), ex.what());
+ stdplus::println(stderr, "Error creating socket: {}", e.what());
return -1;
}
-
+ stdplus::println(stderr, "Success setting Mac address for {}: {}",
+ iface_name_, std::get<std::string>(mac_value));
shared_host_mac_ = std::experimental::nullopt;
return 0;
}
diff --git a/subprojects/ncsid/src/net_config.h b/subprojects/ncsid/src/net_config.h
index 7aeb18b..02c9aff 100644
--- a/subprojects/ncsid/src/net_config.h
+++ b/subprojects/ncsid/src/net_config.h
@@ -53,22 +53,22 @@
virtual int set_nic_hostless(bool is_nic_hostless) = 0;
};
-// Calls phosphord-networkd
+// Calls Socket Ioctl to obtain information about NIC (previously
+// phosphor-networkd)
class PhosphorConfig : public ConfigBase
{
public:
explicit PhosphorConfig(const std::string& iface_name);
- // Reads the MAC address from phosphor-networkd interface or internal
+ // Reads the MAC address from socket interface or internal
// cache, and store in the mac pointer.
// Returns -1 if failed, 0 if succeeded.
int get_mac_addr(mac_addr_t* mac) override;
- // Sets the MAC address over phosphor-networkd, and update internal
+ // Sets the MAC address over socket, and update internal
// cache.
// Returns -1 if failed, 0 if succeeded.
int set_mac_addr(const mac_addr_t& mac) override;
-
virtual int set_nic_hostless(bool is_nic_hostless) override;
private:
@@ -81,6 +81,10 @@
// Stores the currently configured nic state, if previously set
std::optional<bool> was_nic_hostless_;
+ // Function helper allows get_mac_addr and set_mac_addr to do
+ // ioctl calls to get and set different states of NIC.
+ void call_nic(auto fd, struct ifreq& ifr, int op);
+
// The MAC address obtained from NIC.
// ncsid will commit this MAC address over DBus to phosphor-networkd
// and expect it to be persisted. If actual host MAC address changes or