diff options
author | Ratan Gupta <ratagupt@in.ibm.com> | 2017-08-18 17:10:07 +0530 |
---|---|---|
committer | Ratan Gupta <ratagupt@in.ibm.com> | 2017-09-09 00:44:57 +0530 |
commit | bd303b1097695f6e6ddadef96d64476830fbdc19 (patch) | |
tree | 930362073665d9a0d1c881f07eb5d4e7c4ba580b | |
parent | 4f67dac2061f5fafbae83aba9d44029b994275a7 (diff) | |
download | phosphor-networkd-bd303b1097695f6e6ddadef96d64476830fbdc19.tar.gz phosphor-networkd-bd303b1097695f6e6ddadef96d64476830fbdc19.zip |
Implement Set function for MAC address
Change-Id: I16992dda259246a66512792f06cbbb874e56a15d
Signed-off-by: Ratan Gupta <ratagupt@in.ibm.com>
-rw-r--r-- | ethernet_interface.cpp | 78 | ||||
-rw-r--r-- | ethernet_interface.hpp | 9 | ||||
-rw-r--r-- | util.cpp | 143 | ||||
-rw-r--r-- | util.hpp | 74 | ||||
-rw-r--r-- | vlan_interface.cpp | 2 | ||||
-rw-r--r-- | vlan_interface.hpp | 6 |
6 files changed, 302 insertions, 10 deletions
diff --git a/ethernet_interface.cpp b/ethernet_interface.cpp index c24a3e8..39c12cf 100644 --- a/ethernet_interface.cpp +++ b/ethernet_interface.cpp @@ -33,10 +33,6 @@ namespace network using namespace phosphor::logging; using namespace sdbusplus::xyz::openbmc_project::Common::Error; -constexpr auto MAC_ADDRESS_FORMAT = "%hhx:%hhx:%hhx:%hhx:%hhx:%hhx"; -constexpr size_t SIZE_MAC = 18; -constexpr size_t SIZE_BUFF = 512; - EthernetInterface::EthernetInterface(sdbusplus::bus::bus& bus, const std::string& objPath, bool dhcpEnabled, @@ -51,7 +47,7 @@ EthernetInterface::EthernetInterface(sdbusplus::bus::bus& bus, std::replace(intfName.begin(), intfName.end(), '_', '.'); interfaceName(intfName); EthernetInterfaceIntf::dHCPEnabled(dhcpEnabled); - mACAddress(getMACAddress(intfName)); + MacAddressIntf::mACAddress(getMACAddress(intfName)); // Emit deferred signal. if (emitSignal) @@ -116,7 +112,7 @@ void EthernetInterface::iP(IP::Protocol protType, if (dHCPEnabled()) { log<level::INFO>("DHCP enabled on the interface"), - entry("INTERFACE=%s",interfaceName()); + entry("INTERFACE=%s",interfaceName().c_str()); return; } @@ -200,7 +196,7 @@ std::string EthernetInterface::getMACAddress( const std::string& interfaceName) const { struct ifreq ifr{}; - char macAddress[SIZE_MAC] {}; + char macAddress[mac_address::size] {}; int sock = socket(AF_INET, SOCK_DGRAM, IPPROTO_IP); if (sock < 0) @@ -218,7 +214,7 @@ std::string EthernetInterface::getMACAddress( return macAddress; } - snprintf(macAddress, SIZE_MAC, MAC_ADDRESS_FORMAT, + snprintf(macAddress, mac_address::size, mac_address::format, ifr.ifr_hwaddr.sa_data[0], ifr.ifr_hwaddr.sa_data[1], ifr.ifr_hwaddr.sa_data[2], ifr.ifr_hwaddr.sa_data[3], ifr.ifr_hwaddr.sa_data[4], ifr.ifr_hwaddr.sa_data[5]); @@ -510,5 +506,71 @@ void EthernetInterface::writeDHCPSection(std::fstream& stream) } } +std::string EthernetInterface::mACAddress(std::string value) +{ + if (!mac_address::validate(value)) + { + log<level::DEBUG>("MACAddress is not valid.", + entry("MAC=%s", value.c_str())); + return MacAddressIntf::mACAddress(); + } + + // check whether MAC is broadcast mac. + auto intMac = mac_address::internal::convertToInt(value); + + if (!(intMac ^ mac_address::broadcastMac)) + { + log<level::DEBUG>("MACAddress is a broadcast mac.", + entry("MAC=%s", value.c_str())); + return MacAddressIntf::mACAddress(); + } + + // Allow the mac to be set if one of the condition is true. + // 1) Incoming Mac is of local admin type. + // or + // 2) Incoming mac is same as eeprom Mac. + + if (!(intMac & mac_address::localAdminMask)) + { + try + { + auto inventoryMac = mac_address::getfromInventory(bus); + auto intInventoryMac = mac_address::internal::convertToInt(inventoryMac); + + if (intInventoryMac != intMac) + { + log<level::DEBUG>("Given MAC address is neither a local Admin \ + type nor is same as in inventory"); + return MacAddressIntf::mACAddress(); + } + } + catch(InternalFailure& e) + { + log<level::ERR>("Exception occured during getting of MAC \ + address from Inventory"); + return MacAddressIntf::mACAddress(); + } + } + auto interface = interfaceName(); + execute("/sbin/fw_setenv", "fw_setenv", "ethaddr", value.c_str()); + //TODO: would replace below three calls + // with restarting of systemd-netwokd + // through https://github.com/systemd/systemd/issues/6696 + execute("/sbin/ip", "ip", "link", "set", "dev", interface.c_str(), "down"); + execute("/sbin/ip", "ip", "link", "set", "dev", interface.c_str(), "address", + value.c_str()); + + execute("/sbin/ip", "ip", "link", "set", "dev", interface.c_str(), "up"); + + auto mac = MacAddressIntf::mACAddress(std::move(value)); + //update all the vlan interfaces + for(const auto& intf: vlanInterfaces) + { + intf.second->updateMacAddress(); + } + return mac; + +} + }//namespace network }//namespace phosphor diff --git a/ethernet_interface.hpp b/ethernet_interface.hpp index 5283da4..3a746d2 100644 --- a/ethernet_interface.hpp +++ b/ethernet_interface.hpp @@ -27,6 +27,8 @@ using IP = sdbusplus::xyz::openbmc_project::Network::server::IP; using EthernetInterfaceIntf = sdbusplus::xyz::openbmc_project::Network::server::EthernetInterface; +using MacAddressIntf = + sdbusplus::xyz::openbmc_project::Network::server::MACAddress; namespace fs = std::experimental::filesystem; @@ -113,6 +115,12 @@ class EthernetInterface : public Ifaces /** Set value of DHCPEnabled */ bool dHCPEnabled(bool value) override; + /** @brief sets the MAC address. + * @param[in] value - MAC address which needs to be set on the system. + * @returns macAddress of the interface. + */ + std::string mACAddress(std::string value) override; + /** @brief create Vlan interface. * @param[in] id- VLAN identifier. */ @@ -130,6 +138,7 @@ class EthernetInterface : public Ifaces using EthernetInterfaceIntf::dHCPEnabled; using EthernetInterfaceIntf::interfaceName; + using MacAddressIntf::mACAddress; protected: @@ -364,11 +364,152 @@ bool getDHCPValue(const std::string& confDir, const std::string& intf) } catch (InternalFailure& e) { - log<level::INFO>("Exception occured during getting of DHCP value"); + log<level::INFO>("Exception occured during getting of DHCP value"); } return dhcp; } +namespace internal +{ + +void executeCommandinChildProcess(const char* path, char** args) +{ + using namespace std::string_literals; + pid_t pid = fork(); + int status {}; + + if (pid == 0) + { + execv(path, args); + auto error = errno; + // create the command from var args. + std::string command = path + " "s; + + for(int i = 0; args[i]; i++) + { + command += args[i] + " "s; + } + + log<level::ERR>("Couldn't exceute the command", + entry("ERRNO=%d", error), + entry("CMD=%s", command.c_str())); + elog<InternalFailure>(); + } + else if (pid < 0) + { + auto error = errno; + log<level::ERR>("Error occurred during fork", + entry("ERRNO=%d", error)); + elog<InternalFailure>(); + } + else if (pid > 0) + { + while (waitpid(pid, &status, 0) == -1) + { + if (errno != EINTR) + { //Error other than EINTR + status = -1; + break; + } + } + + if(status < 0) + { + std::string command = path + " "s; + for(int i = 0; args[i]; i++) + { + command += args[i] + " "s; + } + + log<level::ERR>("Unable to execute the command", + entry("CMD=%s", command.c_str(), + entry("STATUS=%d", status))); + elog<InternalFailure>(); + } + } + +} +} //namespace internal + +namespace mac_address +{ + +constexpr auto mapperBus = "xyz.openbmc_project.ObjectMapper"; +constexpr auto mapperObj = "/xyz/openbmc_project/object_mapper"; +constexpr auto mapperIntf = "xyz.openbmc_project.ObjectMapper"; +constexpr auto propIntf = "org.freedesktop.DBus.Properties"; +constexpr auto methodGet = "Get"; + +using DbusObjectPath = std::string; +using DbusService = std::string; +using DbusInterface = std::string; +using ObjectTree = std::map<DbusObjectPath, + std::map<DbusService, std::vector<DbusInterface>>>; + +constexpr auto invBus = "xyz.openbmc_project.Inventory.Manager"; +constexpr auto invNetworkIntf = + "xyz.openbmc_project.Inventory.Item.NetworkInterface"; +constexpr auto invRoot = "/xyz/openbmc_project/inventory"; + +std::string getfromInventory(sdbusplus::bus::bus& bus) +{ + std::vector<DbusInterface> interfaces; + interfaces.emplace_back(invNetworkIntf); + + auto depth = 0; + + auto mapperCall = bus.new_method_call(mapperBus, + mapperObj, + mapperIntf, + "GetSubTree"); + + mapperCall.append(invRoot, depth, interfaces); + + auto mapperReply = bus.call(mapperCall); + if (mapperReply.is_method_error()) + { + log<level::ERR>("Error in mapper call"); + elog<InternalFailure>(); + } + + ObjectTree objectTree; + mapperReply.read(objectTree); + + if (objectTree.empty()) + { + log<level::ERR>("No Object has implemented the interface", + entry("INTERFACE=%s", invNetworkIntf)); + elog<InternalFailure>(); + } + + // It is expected that only one object have impelmented this interface. + + auto objPath = objectTree.begin()->first; + auto service = objectTree.begin()->second.begin()->first; + + sdbusplus::message::variant<std::string> value; + + auto method = bus.new_method_call( + service.c_str(), + objPath.c_str(), + propIntf, + methodGet); + + method.append(invNetworkIntf, "MACAddress"); + + auto reply = bus.call(method); + if (reply.is_method_error()) + { + log<level::ERR>("Failed to get MACAddress", + entry("PATH=%s", objPath.c_str()), + entry("INTERFACE=%s", invNetworkIntf)); + elog<InternalFailure>(); + } + + reply.read(value); + return value.get<std::string>(); +} +}//namespace mac_address }//namespace network }//namespace phosphor @@ -5,11 +5,60 @@ #include "config.h" #include "types.hpp" #include <sdbusplus/bus.hpp> +#include <regex> namespace phosphor { namespace network { +namespace mac_address +{ + +constexpr auto regex = "^([0-9A-Fa-f]{2}[:-]){5}([0-9A-Fa-f]{2})$"; +constexpr auto localAdminMask = 0x020000000000; +constexpr auto broadcastMac = 0xFFFFFFFFFFFF; + +constexpr auto format = "%hhx:%hhx:%hhx:%hhx:%hhx:%hhx"; +constexpr size_t size = 18; + +/** @brief validate the mac address + * @param[in] value - MAC address. + * @returns true if validate otherwise false. + */ +inline bool validate(const std::string& value) +{ + std::regex regexToCheck(regex); + return std::regex_search(value, regexToCheck); +} + +/** @brief gets the MAC address from the Inventory. + * @param[in] bus - DBUS Bus Object. + */ +std::string getfromInventory(sdbusplus::bus::bus& bus); + +namespace internal +{ +/** @brief Converts the given mac address into unsigned 64 bit integer + * @param[in] value - MAC address. + * @returns converted unsigned 64 bit number. + */ +inline uint64_t convertToInt(const std::string& value) +{ + unsigned char mac[6]; + + sscanf(value.c_str(), "%hhx:%hhx:%hhx:%hhx:%hhx:%hhx", + mac + 0, mac + 1, mac + 2, mac + 3, mac + 4, mac + 5); + return + static_cast<uint64_t>(mac[0]) << 40 | + static_cast<uint64_t>(mac[1]) << 32 | + static_cast<uint64_t>(mac[2]) << 24 | + static_cast<uint64_t>(mac[3]) << 16 | + static_cast<uint64_t>(mac[4]) << 8 | + static_cast<uint64_t>(mac[5]); +} + +}//namespace internal +}//namespace mac_address /* @brief converts the given subnet into prefix notation. * @param[in] addressFamily - IP address family(AF_INET/AF_INET6). @@ -74,6 +123,31 @@ void deleteInterface(const std::string& intf); */ bool getDHCPValue(const std::string& confDir, const std::string& intf); +namespace internal +{ + +/* @brief runs the given command in child process. + * @param[in] path - path of the binary file which needs to be execeuted. + * @param[in] args - arguments of the command. + */ +void executeCommandinChildProcess(const char* path, char** args); + +} // namespace internal + +/* @brief runs the given command in child process. + * @param[in] path -path of the binary file which needs to be execeuted. + * @param[in] tArgs - arguments of the command. + */ +template<typename... ArgTypes> +void execute(const char* path, ArgTypes&&... tArgs) +{ + using expandType = char*[]; + + expandType args = { const_cast<char*>(tArgs)..., nullptr }; + + internal::executeCommandinChildProcess(path, args); +} + } //namespace network class Descriptor diff --git a/vlan_interface.cpp b/vlan_interface.cpp index 8684a73..8f7c5ce 100644 --- a/vlan_interface.cpp +++ b/vlan_interface.cpp @@ -33,7 +33,7 @@ VlanInterface::VlanInterface(sdbusplus::bus::bus& bus, { id(vlanID); VlanIface::interfaceName(EthernetInterface::interfaceName()); - mACAddress(parentInterface.mACAddress()); + MacAddressIntf::mACAddress(parentInterface.mACAddress()); emit_object_added(); } diff --git a/vlan_interface.hpp b/vlan_interface.hpp index d9ea078..3f273b7 100644 --- a/vlan_interface.hpp +++ b/vlan_interface.hpp @@ -63,6 +63,12 @@ class VlanInterface : public VlanIface, and creates the vlan interface.*/ void writeDeviceFile(); + /** @brief copy the mac address from the parent interface.*/ + void updateMacAddress() + { + MacAddressIntf::mACAddress(parentInterface.mACAddress()); + } + private: /** @brief VLAN Identifier. */ |