diff options
-rw-r--r-- | app/channel.cpp | 1 | ||||
-rw-r--r-- | apphandler.cpp | 1 | ||||
-rw-r--r-- | include/ipmid/types.hpp | 21 | ||||
-rw-r--r-- | include/ipmid/utils.hpp | 70 | ||||
-rw-r--r-- | libipmid/utils.cpp | 148 | ||||
-rw-r--r-- | transporthandler.cpp | 1826 | ||||
-rw-r--r-- | transporthandler.hpp | 141 |
7 files changed, 993 insertions, 1215 deletions
diff --git a/app/channel.cpp b/app/channel.cpp index 7006c92..1fa5b52 100644 --- a/app/channel.cpp +++ b/app/channel.cpp @@ -1,6 +1,5 @@ #include "channel.hpp" -#include "transporthandler.hpp" #include "user_channel/channel_layer.hpp" #include <arpa/inet.h> diff --git a/apphandler.cpp b/apphandler.cpp index 0c8eb92..e06a999 100644 --- a/apphandler.cpp +++ b/apphandler.cpp @@ -31,7 +31,6 @@ #include <sdbusplus/message/types.hpp> #include <string> #include <sys_info_param.hpp> -#include <transporthandler.hpp> #include <tuple> #include <vector> #include <xyz/openbmc_project/Common/error.hpp> diff --git a/include/ipmid/types.hpp b/include/ipmid/types.hpp index fcc635c..e2f80c0 100644 --- a/include/ipmid/types.hpp +++ b/include/ipmid/types.hpp @@ -215,12 +215,7 @@ using EntityInfoMap = std::map<Id, EntityInfo>; namespace network { -using ChannelEthMap = std::map<int, std::string>; - constexpr auto MAC_ADDRESS_FORMAT = "%02hhx:%02hhx:%02hhx:%02hhx:%02hhx:%02hhx"; -constexpr auto IP_ADDRESS_FORMAT = "%u.%u.%u.%u"; -constexpr auto PREFIX_FORMAT = "%hhd"; -constexpr auto ADDR_TYPE_FORMAT = "%hhx"; constexpr auto IPV4_ADDRESS_SIZE_BYTE = 4; constexpr auto IPV6_ADDRESS_SIZE_BYTE = 16; @@ -228,20 +223,6 @@ constexpr auto IPV6_ADDRESS_SIZE_BYTE = 16; constexpr auto DEFAULT_MAC_ADDRESS = "00:00:00:00:00:00"; constexpr auto DEFAULT_ADDRESS = "0.0.0.0"; -constexpr auto MAC_ADDRESS_SIZE_BYTE = 6; -constexpr auto VLAN_SIZE_BYTE = 2; -constexpr auto IPSRC_SIZE_BYTE = 1; -constexpr auto BITS_32 = 32; -constexpr auto MASK_32_BIT = 0xFFFFFFFF; -constexpr auto VLAN_ID_MASK = 0x00000FFF; -constexpr auto VLAN_ENABLE_MASK = 0x8000; - -enum class IPOrigin : uint8_t -{ - UNSPECIFIED = 0, - STATIC = 1, - DHCP = 2, -}; - } // namespace network + } // namespace ipmi diff --git a/include/ipmid/utils.hpp b/include/ipmid/utils.hpp index 45c9c1a..3515eb6 100644 --- a/include/ipmid/utils.hpp +++ b/include/ipmid/utils.hpp @@ -113,20 +113,6 @@ DbusObjectInfo getDbusObject(sdbusplus::bus::bus& bus, const std::string& subtreePath = ROOT, const std::string& match = {}); -/** @brief Get the ipObject of first dbus IP object of Non-LinkLocalIPAddress - * type from the given subtree, if not available gets IP object of - * LinkLocalIPAddress type. - * @param[in] bus - DBUS Bus Object. - * @param[in] interface - Dbus interface. - * @param[in] subtreePath - subtree from where the search should start. - * @param[in] match - identifier for object. - * @return On success returns the object having objectpath and servicename. - */ -DbusObjectInfo getIPObject(sdbusplus::bus::bus& bus, - const std::string& interface, - const std::string& subtreePath, - const std::string& match); - /** @brief Gets the value associated with the given object * and the interface. * @param[in] bus - DBUS Bus Object. @@ -251,62 +237,6 @@ void callDbusMethod(sdbusplus::bus::bus& bus, const std::string& service, } // namespace method_no_args -namespace network -{ - -constexpr auto ROOT = "/xyz/openbmc_project/network"; -constexpr auto SERVICE = "xyz.openbmc_project.Network"; -constexpr auto IP_TYPE = "ipv4"; -constexpr auto IPV4_PREFIX = "169.254"; -constexpr auto IPV6_PREFIX = "fe80"; -constexpr auto IP_INTERFACE = "xyz.openbmc_project.Network.IP"; -constexpr auto MAC_INTERFACE = "xyz.openbmc_project.Network.MACAddress"; -constexpr auto SYSTEMCONFIG_INTERFACE = - "xyz.openbmc_project.Network.SystemConfiguration"; -constexpr auto ETHERNET_INTERFACE = - "xyz.openbmc_project.Network.EthernetInterface"; -constexpr auto IP_CREATE_INTERFACE = "xyz.openbmc_project.Network.IP.Create"; -constexpr auto VLAN_CREATE_INTERFACE = - "xyz.openbmc_project.Network.VLAN.Create"; -constexpr auto VLAN_INTERFACE = "xyz.openbmc_project.Network.VLAN"; - -/* @brief converts the given subnet into prefix notation. - * @param[in] addressFamily - IP address family(AF_INET/AF_INET6). - * @param[in] mask - Subnet Mask. - * @returns prefix. - */ -uint8_t toPrefix(int addressFamily, const std::string& subnetMask); - -/** @brief Sets the ip on the system. - * @param[in] bus - DBUS Bus Object. - * @param[in] service - Dbus service name. - * @param[in] objPath - Dbus object path. - * @param[in] protocolType - Protocol type - * @param[in] ipaddress - IPaddress. - * @param[in] prefix - Prefix length. - */ -void createIP(sdbusplus::bus::bus& bus, const std::string& service, - const std::string& objPath, const std::string& protocolType, - const std::string& ipaddress, uint8_t prefix); - -/** @brief Creates the VLAN on the given interface. - * @param[in] bus - DBUS Bus Object. - * @param[in] service - Dbus service name. - * @param[in] objPath - Dbus object path. - * @param[in] interface - EthernetInterface. - * @param[in] vlanID - Vlan ID. - */ -void createVLAN(sdbusplus::bus::bus& bus, const std::string& service, - const std::string& objPath, const std::string& interface, - uint32_t vlanID); - -/** @brief Gets the vlan id from the given object path. - * @param[in] path - Dbus object path. - */ -uint32_t getVLAN(const std::string& path); - -} // namespace network - /** @brief Perform the low-level i2c bus write-read. * @param[in] i2cBus - i2c bus device node name, such as /dev/i2c-2. * @param[in] slaveAddr - i2c device slave address. diff --git a/libipmid/utils.cpp b/libipmid/utils.cpp index cc4b763..ed493d6 100644 --- a/libipmid/utils.cpp +++ b/libipmid/utils.cpp @@ -98,43 +98,6 @@ DbusObjectInfo getDbusObject(sdbusplus::bus::bus& bus, return make_pair(found->first, std::move(found->second.begin()->first)); } -DbusObjectInfo getIPObject(sdbusplus::bus::bus& bus, - const std::string& interface, - const std::string& serviceRoot, - const std::string& match) -{ - auto objectTree = getAllDbusObjects(bus, serviceRoot, interface, match); - - if (objectTree.empty()) - { - log<level::ERR>("No Object has implemented the IP interface", - entry("INTERFACE=%s", interface.c_str())); - elog<InternalFailure>(); - } - - DbusObjectInfo objectInfo; - - for (auto& object : objectTree) - { - auto variant = ipmi::getDbusProperty( - bus, object.second.begin()->first, object.first, - ipmi::network::IP_INTERFACE, "Address"); - - objectInfo = std::make_pair(object.first, object.second.begin()->first); - - // if LinkLocalIP found look for Non-LinkLocalIP - if (ipmi::network::isLinkLocalIP(std::get<std::string>(variant))) - { - continue; - } - else - { - break; - } - } - return objectInfo; -} - Value getDbusProperty(sdbusplus::bus::bus& bus, const std::string& service, const std::string& objPath, const std::string& interface, const std::string& property, @@ -437,117 +400,6 @@ void callDbusMethod(sdbusplus::bus::bus& bus, const std::string& service, } } // namespace method_no_args - -namespace network -{ - -bool isLinkLocalIP(const std::string& address) -{ - return address.find(IPV4_PREFIX) == 0 || address.find(IPV6_PREFIX) == 0; -} - -void createIP(sdbusplus::bus::bus& bus, const std::string& service, - const std::string& objPath, const std::string& protocolType, - const std::string& ipaddress, uint8_t prefix) -{ - std::string gateway = ""; - - auto busMethod = bus.new_method_call(service.c_str(), objPath.c_str(), - IP_CREATE_INTERFACE, "IP"); - - busMethod.append(protocolType, ipaddress, prefix, gateway); - - auto reply = bus.call(busMethod); - - if (reply.is_method_error()) - { - log<level::ERR>("Failed to execute method", entry("METHOD=%s", "IP"), - entry("PATH=%s", objPath.c_str())); - elog<InternalFailure>(); - } -} - -void createVLAN(sdbusplus::bus::bus& bus, const std::string& service, - const std::string& objPath, const std::string& interfaceName, - uint32_t vlanID) -{ - auto busMethod = bus.new_method_call(service.c_str(), objPath.c_str(), - VLAN_CREATE_INTERFACE, "VLAN"); - - busMethod.append(interfaceName, vlanID); - - auto reply = bus.call(busMethod); - - if (reply.is_method_error()) - { - log<level::ERR>("Failed to execute method", entry("METHOD=%s", "VLAN"), - entry("PATH=%s", objPath.c_str())); - elog<InternalFailure>(); - } -} - -uint8_t toPrefix(int addressFamily, const std::string& subnetMask) -{ - if (addressFamily == AF_INET6) - { - return 0; - } - - uint32_t buff{}; - - auto rc = inet_pton(addressFamily, subnetMask.c_str(), &buff); - if (rc <= 0) - { - log<level::ERR>("inet_pton failed:", - entry("SUBNETMASK=%s", subnetMask.c_str())); - return 0; - } - - buff = be32toh(buff); - // total no of bits - total no of leading zero == total no of ones - if (((sizeof(buff) * 8) - (__builtin_ctz(buff))) == - __builtin_popcount(buff)) - { - return __builtin_popcount(buff); - } - else - { - log<level::ERR>("Invalid Mask", - entry("SUBNETMASK=%s", subnetMask.c_str())); - return 0; - } -} - -uint32_t getVLAN(const std::string& path) -{ - // Path would be look like - // /xyz/openbmc_project/network/eth0_443/ipv4 - - uint32_t vlanID = 0; - try - { - auto intfObjectPath = path.substr(0, path.find(IP_TYPE) - 1); - - auto intfName = intfObjectPath.substr(intfObjectPath.rfind("/") + 1); - - auto index = intfName.find("_"); - if (index != std::string::npos) - { - auto str = intfName.substr(index + 1); - vlanID = std::stoul(str); - } - } - catch (std::exception& e) - { - log<level::ERR>("Exception occurred during getVLAN", - entry("PATH=%s", path.c_str()), - entry("EXCEPTION=%s", e.what())); - } - return vlanID; -} - -} // namespace network - ipmi::Cc i2cWriteRead(std::string i2cBus, const uint8_t slaveAddr, std::vector<uint8_t> writeData, std::vector<uint8_t>& readBuf) diff --git a/transporthandler.cpp b/transporthandler.cpp index acff251..e88eb63 100644 --- a/transporthandler.cpp +++ b/transporthandler.cpp @@ -1,1023 +1,1181 @@ -#include "transporthandler.hpp" - -#include "app/channel.hpp" -#include "user_channel/channel_layer.hpp" - #include <arpa/inet.h> - -#include <chrono> -#include <filesystem> -#include <fstream> +#include <netinet/ether.h> + +#include <array> +#include <bitset> +#include <cinttypes> +#include <cstdint> +#include <cstring> +#include <functional> #include <ipmid/api.hpp> +#include <ipmid/message.hpp> +#include <ipmid/message/types.hpp> +#include <ipmid/types.hpp> #include <ipmid/utils.hpp> +#include <optional> #include <phosphor-logging/elog-errors.hpp> +#include <phosphor-logging/elog.hpp> #include <phosphor-logging/log.hpp> -#include <sdbusplus/message/types.hpp> -#include <sdbusplus/timer.hpp> +#include <sdbusplus/bus.hpp> +#include <sdbusplus/exception.hpp> #include <string> +#include <string_view> +#include <type_traits> +#include <unordered_map> +#include <unordered_set> +#include <user_channel/channel_layer.hpp> +#include <utility> +#include <vector> #include <xyz/openbmc_project/Common/error.hpp> +#include <xyz/openbmc_project/Network/IP/server.hpp> -#define SYSTEMD_NETWORKD_DBUS 1 - -#ifdef SYSTEMD_NETWORKD_DBUS -#include <mapper.h> -#include <systemd/sd-bus.h> -#endif - -// timer for network changes -std::unique_ptr<phosphor::Timer> networkTimer = nullptr; - -const int SIZE_MAC = 18; // xx:xx:xx:xx:xx:xx -constexpr auto ipv4Protocol = "xyz.openbmc_project.Network.IP.Protocol.IPv4"; - -std::map<int, std::unique_ptr<struct ChannelConfig_t>> channelConfig; - -using namespace phosphor::logging; -using namespace sdbusplus::xyz::openbmc_project::Common::Error; - -namespace fs = std::filesystem; - -void register_netfn_transport_functions() __attribute__((constructor)); +namespace ipmi +{ +namespace transport +{ -struct ChannelConfig_t* getChannelConfig(int channel) +using phosphor::logging::commit; +using phosphor::logging::elog; +using phosphor::logging::entry; +using phosphor::logging::level; +using phosphor::logging::log; +using sdbusplus::xyz::openbmc_project::Common::Error::InternalFailure; +using sdbusplus::xyz::openbmc_project::Network::server::IP; + +// LAN Handler specific response codes +constexpr Cc ccParamNotSupported = 0x80; +constexpr Cc ccParamSetLocked = 0x81; +constexpr Cc ccParamReadOnly = 0x82; + +// VLANs are a 12-bit value +constexpr uint16_t VLAN_VALUE_MASK = 0x0fff; +constexpr uint16_t VLAN_ENABLE_FLAG = 0x8000; + +// D-Bus Network Daemon definitions +constexpr auto PATH_ROOT = "/xyz/openbmc_project/network"; +constexpr auto PATH_SYSTEMCONFIG = "/xyz/openbmc_project/network/config"; + +constexpr auto INTF_SYSTEMCONFIG = + "xyz.openbmc_project.Network.SystemConfiguration"; +constexpr auto INTF_ETHERNET = "xyz.openbmc_project.Network.EthernetInterface"; +constexpr auto INTF_IP = "xyz.openbmc_project.Network.IP"; +constexpr auto INTF_IP_CREATE = "xyz.openbmc_project.Network.IP.Create"; +constexpr auto INTF_MAC = "xyz.openbmc_project.Network.MACAddress"; +constexpr auto INTF_VLAN = "xyz.openbmc_project.Network.VLAN"; +constexpr auto INTF_VLAN_CREATE = "xyz.openbmc_project.Network.VLAN.Create"; + +/** @brief Generic paramters for different address families */ +template <int family> +struct AddrFamily { - auto item = channelConfig.find(channel); - if (item == channelConfig.end()) - { - channelConfig[channel] = std::make_unique<struct ChannelConfig_t>(); - } +}; - return channelConfig[channel].get(); +/** @brief Parameter specialization for IPv4 */ +template <> +struct AddrFamily<AF_INET> +{ + using addr = in_addr; + static constexpr auto protocol = IP::Protocol::IPv4; + static constexpr size_t maxStrLen = INET6_ADDRSTRLEN; + static constexpr uint8_t defaultPrefix = 32; + static constexpr char propertyGateway[] = "DefaultGateway"; +}; + +/** @brief Valid address origins for IPv4 */ +const std::unordered_set<IP::AddressOrigin> originsV4 = { + IP::AddressOrigin::Static, + IP::AddressOrigin::DHCP, +}; + +/** @brief Interface IP Address configuration parameters */ +template <int family> +struct IfAddr +{ + std::string path; + typename AddrFamily<family>::addr address; + IP::AddressOrigin origin; + uint8_t prefix; +}; + +/** @brief IPMI LAN Parameters */ +enum class LanParam : uint8_t +{ + SetStatus = 0, + AuthSupport = 1, + AuthEnables = 2, + IP = 3, + IPSrc = 4, + MAC = 5, + SubnetMask = 6, + Gateway1 = 12, + VLANId = 20, + CiphersuiteSupport = 22, + CiphersuiteEntries = 23, +}; + +/** @brief IPMI IP Origin Types */ +enum class IPSrc : uint8_t +{ + Unspecified = 0, + Static = 1, + DHCP = 2, + BIOS = 3, + BMC = 4, +}; + +/** @brief IPMI Set Status */ +enum class SetStatus : uint8_t +{ + Complete = 0, + InProgress = 1, + Commit = 2, +}; + +/** @brief Copies bytes from an array into a trivially copyable container + * + * @params[out] t - The container receiving the data + * @params[in] bytes - The data to copy + */ +template <size_t N, typename T> +void copyInto(T& t, const std::array<uint8_t, N>& bytes) +{ + static_assert(std::is_trivially_copyable_v<T>); + static_assert(N == sizeof(T)); + std::memcpy(&t, bytes.data(), bytes.size()); } -// Helper Function to get IP Address/NetMask/Gateway/MAC Address from Network -// Manager or Cache based on Set-In-Progress State -ipmi_ret_t getNetworkData(uint8_t lan_param, uint8_t* data, int channel) +/** @brief Gets a generic view of the bytes in the input container + * + * @params[in] t - The data to reference + * @return A string_view referencing the bytes in the container + */ +template <typename T> +std::string_view dataRef(const T& t) { - ipmi_ret_t rc = IPMI_CC_OK; - sdbusplus::bus::bus bus(ipmid_get_sd_bus_connection()); + static_assert(std::is_trivially_copyable_v<T>); + return {reinterpret_cast<const char*>(&t), sizeof(T)}; +} - auto ethdevice = ipmi::getChannelName(channel); - // if ethdevice is an empty string they weren't expecting this channel. - if (ethdevice.empty()) +/** @brief The dbus parameters for the interface corresponding to a channel + * This helps reduce the number of mapper lookups we need for each + * query and simplifies finding the VLAN interface if needed. + */ +struct ChannelParams +{ + /** @brief The channel ID */ + int id; + /** @brief channel name for the interface */ + std::string ifname; + /** @brief Name of the service on the bus */ + std::string service; + /** @brief Lower level adapter path that is guaranteed to not be a VLAN */ + std::string ifPath; + /** @brief Logical adapter path used for address assignment */ + std::string logicalPath; +}; + +/** @brief Determines the ethernet interface name corresponding to a channel + * Tries to map a VLAN object first so that the address information + * is accurate. Otherwise it gets the standard ethernet interface. + * + * @param[in] bus - The bus object used for lookups + * @param[in] channel - The channel id corresponding to an ethernet interface + * @return Ethernet interface service and object path if it exists + */ +std::optional<ChannelParams> maybeGetChannelParams(sdbusplus::bus::bus& bus, + uint8_t channel) +{ + auto ifname = getChannelName(channel); + if (ifname.empty()) { - // TODO: return error from getNetworkData() - return IPMI_CC_INVALID_FIELD_REQUEST; + return std::nullopt; } - auto ethIP = ethdevice + "/" + ipmi::network::IP_TYPE; - auto channelConf = getChannelConfig(channel); - try + // Enumerate all VLAN + ETHERNET interfaces + auto req = bus.new_method_call(MAPPER_BUS_NAME, MAPPER_OBJ, MAPPER_INTF, + "GetSubTree"); + req.append(PATH_ROOT, 0, + std::vector<std::string>{INTF_VLAN, INTF_ETHERNET}); + auto reply = bus.call(req); + ObjectTree objs; + reply.read(objs); + + ChannelParams params; + for (const auto& [path, impls] : objs) { - switch (static_cast<LanParam>(lan_param)) + if (path.find(ifname) == path.npos) { - case LanParam::IP: - { - std::string ipaddress; - if (channelConf->lan_set_in_progress == SET_COMPLETE) - { - try - { - auto ipObjectInfo = - ipmi::getIPObject(bus, ipmi::network::IP_INTERFACE, - ipmi::network::ROOT, ethIP); - - auto properties = ipmi::getAllDbusProperties( - bus, ipObjectInfo.second, ipObjectInfo.first, - ipmi::network::IP_INTERFACE); - - ipaddress = - std::get<std::string>(properties["Address"]); - } - // ignore the exception, as it is a valid condition that - // the system is not configured with any IP. - catch (InternalFailure& e) - { - // nothing to do. - } - } - else if (channelConf->lan_set_in_progress == SET_IN_PROGRESS) - { - ipaddress = channelConf->ipaddr; - } - - inet_pton(AF_INET, ipaddress.c_str(), - reinterpret_cast<void*>(data)); - } - break; - - case LanParam::IPSRC: - { - std::string networkInterfacePath; - - if (channelConf->lan_set_in_progress == SET_COMPLETE) - { - try - { - ipmi::ObjectTree ancestorMap; - // if the system is having ip object,then - // get the IP object. - auto ipObject = ipmi::getDbusObject( - bus, ipmi::network::IP_INTERFACE, - ipmi::network::ROOT, ethIP); - - // Get the parent interface of the IP object. - try - { - ipmi::InterfaceList interfaces; - interfaces.emplace_back( - ipmi::network::ETHERNET_INTERFACE); - - ancestorMap = ipmi::getAllAncestors( - bus, ipObject.first, std::move(interfaces)); - } - catch (InternalFailure& e) - { - // if unable to get the parent interface - // then commit the error and return. - log<level::ERR>( - "Unable to get the parent interface", - entry("PATH=%s", ipObject.first.c_str()), - entry("INTERFACE=%s", - ipmi::network::ETHERNET_INTERFACE)); - break; - } - // for an ip object there would be single parent - // interface. - networkInterfacePath = ancestorMap.begin()->first; - } - catch (InternalFailure& e) - { - // if there is no ip configured on the system,then - // get the network interface object. - auto networkInterfaceObject = ipmi::getDbusObject( - bus, ipmi::network::ETHERNET_INTERFACE, - ipmi::network::ROOT, ethdevice); - - networkInterfacePath = networkInterfaceObject.first; - } - - auto variant = ipmi::getDbusProperty( - bus, ipmi::network::SERVICE, networkInterfacePath, - ipmi::network::ETHERNET_INTERFACE, "DHCPEnabled"); - - auto dhcpEnabled = std::get<bool>(variant); - // As per IPMI spec 2=>DHCP, 1=STATIC - auto ipsrc = dhcpEnabled ? ipmi::network::IPOrigin::DHCP - : ipmi::network::IPOrigin::STATIC; - - std::memcpy(data, &ipsrc, ipmi::network::IPSRC_SIZE_BYTE); - } - else if (channelConf->lan_set_in_progress == SET_IN_PROGRESS) - { - std::memcpy(data, &(channelConf->ipsrc), - ipmi::network::IPSRC_SIZE_BYTE); - } - } - break; - - case LanParam::SUBNET: + continue; + } + for (const auto& [service, intfs] : impls) + { + bool vlan = false; + bool ethernet = false; + for (const auto& intf : intfs) { - unsigned long mask{}; - if (channelConf->lan_set_in_progress == SET_COMPLETE) + if (intf == INTF_VLAN) { - try - { - auto ipObjectInfo = - ipmi::getIPObject(bus, ipmi::network::IP_INTERFACE, - ipmi::network::ROOT, ethIP); - - auto properties = ipmi::getAllDbusProperties( - bus, ipObjectInfo.second, ipObjectInfo.first, - ipmi::network::IP_INTERFACE); - - auto prefix = - std::get<uint8_t>(properties["PrefixLength"]); - mask = ipmi::network::MASK_32_BIT; - mask = htonl(mask << (ipmi::network::BITS_32 - prefix)); - } - // ignore the exception, as it is a valid condition that - // the system is not configured with any IP. - catch (InternalFailure& e) - { - // nothing to do - } - std::memcpy(data, &mask, - ipmi::network::IPV4_ADDRESS_SIZE_BYTE); + vlan = true; } - else if (channelConf->lan_set_in_progress == SET_IN_PROGRESS) + else if (intf == INTF_ETHERNET) { - inet_pton(AF_INET, channelConf->netmask.c_str(), - reinterpret_cast<void*>(data)); + ethernet = true; } } - break; - - case LanParam::GATEWAY: + if (params.service.empty() && (vlan || ethernet)) { - std::string gateway; - - if (channelConf->lan_set_in_progress == SET_COMPLETE) - { - try - { - auto systemObject = ipmi::getDbusObject( - bus, ipmi::network::SYSTEMCONFIG_INTERFACE, - ipmi::network::ROOT); - - auto systemProperties = ipmi::getAllDbusProperties( - bus, systemObject.second, systemObject.first, - ipmi::network::SYSTEMCONFIG_INTERFACE); - - gateway = std::get<std::string>( - systemProperties["DefaultGateway"]); - } - // ignore the exception, as it is a valid condition that - // the system is not configured with any IP. - catch (InternalFailure& e) - { - // nothing to do - } - } - else if (channelConf->lan_set_in_progress == SET_IN_PROGRESS) - { - gateway = channelConf->gateway; - } - - inet_pton(AF_INET, gateway.c_str(), - reinterpret_cast<void*>(data)); + params.service = service; } - break; - - case LanParam::MAC: + if (params.ifPath.empty() && !vlan && ethernet) { - std::string macAddress; - if (channelConf->lan_set_in_progress == SET_COMPLETE) - { - auto macObjectInfo = - ipmi::getDbusObject(bus, ipmi::network::MAC_INTERFACE, - ipmi::network::ROOT, ethdevice); - - auto variant = ipmi::getDbusProperty( - bus, macObjectInfo.second, macObjectInfo.first, - ipmi::network::MAC_INTERFACE, "MACAddress"); - - macAddress = std::get<std::string>(variant); - } - else if (channelConf->lan_set_in_progress == SET_IN_PROGRESS) - { - macAddress = channelConf->macAddress; - } - - sscanf(macAddress.c_str(), ipmi::network::MAC_ADDRESS_FORMAT, - (data), (data + 1), (data + 2), (data + 3), (data + 4), - (data + 5)); + params.ifPath = path; } - break; - - case LanParam::VLAN: + if (params.logicalPath.empty() && vlan) { - uint16_t vlanID{}; - if (channelConf->lan_set_in_progress == SET_COMPLETE) - { - try - { - auto ipObjectInfo = ipmi::getIPObject( - bus, ipmi::network::IP_INTERFACE, - ipmi::network::ROOT, ipmi::network::IP_TYPE); - - vlanID = static_cast<uint16_t>( - ipmi::network::getVLAN(ipObjectInfo.first)); - - vlanID = htole16(vlanID); - - if (vlanID) - { - // Enable the 16th bit - vlanID |= htole16(ipmi::network::VLAN_ENABLE_MASK); - } - } - // ignore the exception, as it is a valid condition that - // the system is not configured with any IP. - catch (InternalFailure& e) - { - // nothing to do - } - - std::memcpy(data, &vlanID, ipmi::network::VLAN_SIZE_BYTE); - } - else if (channelConf->lan_set_in_progress == SET_IN_PROGRESS) - { - std::memcpy(data, &(channelConf->vlanID), - ipmi::network::VLAN_SIZE_BYTE); - } + params.logicalPath = path; } - break; - - default: - rc = IPMI_CC_PARM_OUT_OF_RANGE; } } - catch (InternalFailure& e) + + // We must have a path for the underlying interface + if (params.ifPath.empty()) { - commit<InternalFailure>(); - rc = IPMI_CC_UNSPECIFIED_ERROR; - return rc; + return std::nullopt; + } + // We don't have a VLAN so the logical path is the same + if (params.logicalPath.empty()) + { + params.logicalPath = params.ifPath; } - return rc; -} -namespace cipher -{ + params.id = channel; + params.ifname = std::move(ifname); + return std::move(params); +} -std::vector<uint8_t> getCipherList() +/** @brief A trivial helper around maybeGetChannelParams() that throws an + * exception when it is unable to acquire parameters for the channel. + * + * @param[in] bus - The bus object used for lookups + * @param[in] channel - The channel id corresponding to an ethernet interface + * @return Ethernet interface service and object path + */ +ChannelParams getChannelParams(sdbusplus::bus::bus& bus, uint8_t channel) { - std::vector<uint8_t> cipherList; - - std::ifstream jsonFile(configFile); - if (!jsonFile.is_open()) + auto params = maybeGetChannelParams(bus, channel); + if (!params) { - log<level::ERR>("Channel Cipher suites file not found"); + log<level::ERR>("Failed to get channel params", + entry("CHANNEL=%" PRIu8, channel)); elog<InternalFailure>(); } + return std::move(*params); +} - auto data = Json::parse(jsonFile, nullptr, false); - if (data.is_discarded()) +/** @brief Wraps the phosphor logging method to insert some additional metadata + * + * @param[in] params - The parameters for the channel + * ... + */ +template <auto level, typename... Args> +auto logWithChannel(const ChannelParams& params, Args&&... args) +{ + return log<level>(std::forward<Args>(args)..., + entry("CHANNEL=%d", params.id), + entry("IFNAME=%s", params.ifname.c_str())); +} +template <auto level, typename... Args> +auto logWithChannel(const std::optional<ChannelParams>& params, Args&&... args) +{ + if (params) { - log<level::ERR>("Parsing channel cipher suites JSON failed"); - elog<InternalFailure>(); + return logWithChannel<level>(*params, std::forward<Args>(args)...); } + return log<level>(std::forward<Args>(args)...); +} - // Byte 1 is reserved - cipherList.push_back(0x00); - - for (const auto& record : data) - { - cipherList.push_back(record.value(cipher, 0)); - } +/** @brief Trivializes using parameter getter functions by providing a bus + * and channel parameters automatically. + * + * @param[in] channel - The channel id corresponding to an ethernet interface + * ... + */ +template <auto func, typename... Args> +auto channelCall(uint8_t channel, Args&&... args) +{ + sdbusplus::bus::bus bus(ipmid_get_sd_bus_connection()); + auto params = getChannelParams(bus, channel); + return std::invoke(func, bus, params, std::forward<Args>(args)...); +} - return cipherList; +/** @brief Determines if the ethernet interface is using DHCP + * + * @param[in] bus - The bus object used for lookups + * @param[in] params - The parameters for the channel + * @return True if DHCP is enabled, false otherwise + */ +bool getDHCPProperty(sdbusplus::bus::bus& bus, const ChannelParams& params) +{ + return std::get<bool>(getDbusProperty( + bus, params.service, params.logicalPath, INTF_ETHERNET, "DHCPEnabled")); } -} // namespace cipher +/** @brief Sets the system value for DHCP on the given interface + * + * @param[in] bus - The bus object used for lookups + * @param[in] params - The parameters for the channel + * @param[in] on - Whether or not to enable DHCP + */ +void setDHCPProperty(sdbusplus::bus::bus& bus, const ChannelParams& params, + bool on) +{ + setDbusProperty(bus, params.service, params.logicalPath, INTF_ETHERNET, + "DHCPEnabled", on); +} -ipmi_ret_t ipmi_transport_wildcard(ipmi_netfn_t netfn, ipmi_cmd_t cmd, - ipmi_request_t request, - ipmi_response_t response, - ipmi_data_len_t data_len, - ipmi_context_t context) +/** @brief Converts a human readable MAC string into MAC bytes + * + * @param[in] mac - The MAC string + * @return MAC in bytes + */ +ether_addr stringToMAC(const char* mac) { - // Status code. - ipmi_ret_t rc = IPMI_CC_INVALID; - *data_len = 0; - return rc; + const ether_addr* ret = ether_aton(mac); + if (ret == nullptr) + { + log<level::ERR>("Invalid MAC Address", entry("MAC=%s", mac)); + elog<InternalFailure>(); + } + return *ret; } -struct set_lan_t +/** @brief Determines the MAC of the ethernet interface + * + * @param[in] bus - The bus object used for lookups + * @param[in] params - The parameters for the channel + * @return The configured mac address + */ +ether_addr getMACProperty(sdbusplus::bus::bus& bus, const ChannelParams& params) { - uint8_t channel; - uint8_t parameter; - uint8_t data[8]; // Per IPMI spec, not expecting more than this size -} __attribute__((packed)); + auto macStr = std::get<std::string>(getDbusProperty( + bus, params.service, params.ifPath, INTF_MAC, "MACAddress")); + return stringToMAC(macStr.c_str()); +} -ipmi_ret_t checkAndUpdateNetwork(int channel) +/** @brief Sets the system value for MAC address on the given interface + * + * @param[in] bus - The bus object used for lookups + * @param[in] params - The parameters for the channel + * @param[in] mac - MAC address to apply + */ +void setMACProperty(sdbusplus::bus::bus& bus, const ChannelParams& params, + const ether_addr& mac) { - auto channelConf = getChannelConfig(channel); - using namespace std::chrono_literals; - // time to wait before applying the network changes. - constexpr auto networkTimeout = 10000000us; // 10 sec + std::string macStr = ether_ntoa(&mac); + setDbusProperty(bus, params.service, params.ifPath, INTF_MAC, "MACAddress", + macStr); +} - // Skip the timer. Expecting more update as we are in SET_IN_PROGRESS - if (channelConf->lan_set_in_progress == SET_IN_PROGRESS) +/** @brief Turns an IP address string into the network byte order form + * NOTE: This version strictly validates family matches + * + * @param[in] address - The string form of the address + * @return A network byte order address or none if conversion failed + */ +template <int family> +std::optional<typename AddrFamily<family>::addr> + maybeStringToAddr(const char* address) +{ + typename AddrFamily<family>::addr ret; + if (inet_pton(family, address, &ret) == 1) { - return IPMI_CC_OK; + return ret; } + return std::nullopt; +} - // Start the timer, if it is direct single param update without - // SET_IN_PROGRESS or many params updated through SET_IN_PROGRESS to - // SET_COMPLETE Note: Even for update with SET_IN_PROGRESS, don't apply the - // changes immediately, as ipmitool sends each param individually - // through SET_IN_PROGRESS to SET_COMPLETE. - channelConf->flush = true; - if (!networkTimer) +/** @brief Turns an IP address string into the network byte order form + * NOTE: This version strictly validates family matches + * + * @param[in] address - The string form of the address + * @return A network byte order address + */ +template <int family> +typename AddrFamily<family>::addr stringToAddr(const char* address) +{ + auto ret = maybeStringToAddr<family>(address); + if (!ret) { - log<level::ERR>("Network timer is not instantiated"); - return IPMI_CC_UNSPECIFIED_ERROR; + log<level::ERR>("Failed to convert IP Address", + entry("FAMILY=%d", family), + entry("ADDRESS=%s", address)); + elog<InternalFailure>(); } - // start the timer. - networkTimer->start(networkTimeout); - return IPMI_CC_OK; + return *ret; } -ipmi_ret_t ipmi_transport_set_lan(ipmi_netfn_t netfn, ipmi_cmd_t cmd, - ipmi_request_t request, - ipmi_response_t response, - ipmi_data_len_t data_len, - ipmi_context_t context) +/** @brief Turns an IP address in network byte order into a string + * + * @param[in] address - The string form of the address + * @return A network byte order address + */ +template <int family> +std::string addrToString(const typename AddrFamily<family>::addr& address) { - ipmi_ret_t rc = IPMI_CC_OK; - - char ipaddr[INET_ADDRSTRLEN]; - char netmask[INET_ADDRSTRLEN]; - char gateway[INET_ADDRSTRLEN]; - - auto reqptr = reinterpret_cast<const set_lan_t*>(request); - sdbusplus::bus::bus bus(ipmid_get_sd_bus_connection()); + std::string ret(AddrFamily<family>::maxStrLen, '\0'); + inet_ntop(family, &address, ret.data(), ret.size()); + ret.resize(strlen(ret.c_str())); + return ret; +} - size_t reqLen = *data_len; - *data_len = 0; +/** @brief Retrieves the current gateway for the address family on the system + * NOTE: The gateway is currently system wide and not per channel + * + * @param[in] bus - The bus object used for lookups + * @param[in] params - The parameters for the channel + * @return An address representing the gateway address if it exists + */ +template <int family> +std::optional<typename AddrFamily<family>::addr> + getGatewayProperty(sdbusplus::bus::bus& bus, const ChannelParams& params) +{ + auto gatewayStr = std::get<std::string>(getDbusProperty( + bus, params.service, PATH_SYSTEMCONFIG, INTF_SYSTEMCONFIG, + AddrFamily<family>::propertyGateway)); + if (gatewayStr.empty()) + { + return std::nullopt; + } + return stringToAddr<family>(gatewayStr.c_str()); +} - // channel number is the lower nibble - int channel = reqptr->channel & CHANNEL_MASK; - auto ethdevice = ipmi::getChannelName(channel); - ipmi::ChannelInfo chInfo; - ipmi::getChannelInfo(channel, chInfo); +/** @brief Sets the system wide value for the default gateway + * + * @param[in] bus - The bus object used for lookups + * @param[in] params - The parameters for the channel + * @param[in] gateway - Gateway address to apply + */ +template <int family> +void setGatewayProperty(sdbusplus::bus::bus& bus, const ChannelParams& params, + const typename AddrFamily<family>::addr& address) +{ + setDbusProperty(bus, params.service, PATH_SYSTEMCONFIG, INTF_SYSTEMCONFIG, + AddrFamily<family>::propertyGateway, + addrToString<family>(address)); +} - if (ethdevice.empty() || - chInfo.mediumType != - static_cast<uint8_t>(ipmi::EChannelMediumType::lan8032)) +/** @brief A lazy lookup mechanism for iterating over object properties stored + * in DBus. This will only perform the object lookup when needed, and + * retains a cache of previous lookups to speed up future iterations. + */ +class ObjectLookupCache +{ + public: + using PropertiesCache = std::unordered_map<std::string, PropertyMap>; + + /** @brief Creates a new ObjectLookupCache for the interface on the bus + * NOTE: The inputs to this object must outlive the object since + * they are only referenced by it. + * + * @param[in] bus - The bus object used for lookups + * @param[in] params - The parameters for the channel + * @param[in] intf - The interface we are looking up + */ + ObjectLookupCache(sdbusplus::bus::bus& bus, const ChannelParams& params, + const char* intf) : + bus(bus), + params(params), intf(intf), + objs(getAllDbusObjects(bus, params.logicalPath, intf, "")) { - return IPMI_CC_INVALID_FIELD_REQUEST; } - auto channelConf = getChannelConfig(channel); - switch (static_cast<LanParam>(reqptr->parameter)) + class iterator : public ObjectTree::const_iterator { - case LanParam::IP: - { - std::snprintf(ipaddr, INET_ADDRSTRLEN, - ipmi::network::IP_ADDRESS_FORMAT, reqptr->data[0], - reqptr->data[1], reqptr->data[2], reqptr->data[3]); + public: + using value_type = PropertiesCache::value_type; - channelConf->ipaddr.assign(ipaddr); + iterator(ObjectTree::const_iterator it, ObjectLookupCache& container) : + ObjectTree::const_iterator(it), container(container), + ret(container.cache.end()) + { } - break; - - case LanParam::IPSRC: + value_type& operator*() { - uint8_t ipsrc{}; - std::memcpy(&ipsrc, reqptr->data, ipmi::network::IPSRC_SIZE_BYTE); - channelConf->ipsrc = static_cast<ipmi::network::IPOrigin>(ipsrc); + ret = container.get(ObjectTree::const_iterator::operator*().first); + return *ret; } - break; - - case LanParam::MAC: + value_type* operator->() { - char mac[SIZE_MAC]; - - std::snprintf(mac, SIZE_MAC, ipmi::network::MAC_ADDRESS_FORMAT, - reqptr->data[0], reqptr->data[1], reqptr->data[2], - reqptr->data[3], reqptr->data[4], reqptr->data[5]); + return &operator*(); + } - auto macObjectInfo = - ipmi::getDbusObject(bus, ipmi::network::MAC_INTERFACE, - ipmi::network::ROOT, ethdevice); + private: + ObjectLookupCache& container; + PropertiesCache::iterator ret; + }; - ipmi::setDbusProperty( - bus, macObjectInfo.second, macObjectInfo.first, - ipmi::network::MAC_INTERFACE, "MACAddress", std::string(mac)); + iterator begin() noexcept + { + return iterator(objs.begin(), *this); + } - channelConf->macAddress = mac; - } - break; + iterator end() noexcept + { + return iterator(objs.end(), *this); + } - case LanParam::SUBNET: + private: + sdbusplus::bus::bus& bus; + const ChannelParams& params; + const char* const intf; + const ObjectTree objs; + PropertiesCache cache; + + /** @brief Gets a cached copy of the object properties if possible + * Otherwise performs a query on DBus to look them up + * + * @param[in] path - The object path to lookup + * @return An iterator for the specified object path + properties + */ + PropertiesCache::iterator get(const std::string& path) + { + auto it = cache.find(path); + if (it != cache.end()) { - std::snprintf(netmask, INET_ADDRSTRLEN, - ipmi::network::IP_ADDRESS_FORMAT, reqptr->data[0], - reqptr->data[1], reqptr->data[2], reqptr->data[3]); - channelConf->netmask.assign(netmask); + return it; } - break; - - case LanParam::GATEWAY: + auto properties = getAllDbusProperties(bus, params.service, path, intf); + return cache.insert({path, std::move(properties)}).first; + } +}; + +/** @brief Searches the ip object lookup cache for an address matching + * the input parameters. NOTE: The index lacks stability across address + * changes since the network daemon has no notion of stable indicies. + * + * @param[in] bus - The bus object used for lookups + * @param[in] params - The parameters for the channel + * @param[in] idx - The index of the desired address on the interface + * @param[in] origins - The allowed origins for the address objects + * @param[in] ips - The object lookup cache holding all of the address info + * @return The address and prefix if it was found + */ +template <int family> +std::optional<IfAddr<family>> + findIfAddr(sdbusplus::bus::bus& bus, const ChannelParams& params, + uint8_t idx, + const std::unordered_set<IP::AddressOrigin>& origins, + ObjectLookupCache& ips) +{ + for (const auto& [path, properties] : ips) + { + const auto& addrStr = std::get<std::string>(properties.at("Address")); + auto addr = maybeStringToAddr<family>(addrStr.c_str()); + if (!addr) { - std::snprintf(gateway, INET_ADDRSTRLEN, - ipmi::network::IP_ADDRESS_FORMAT, reqptr->data[0], - reqptr->data[1], reqptr->data[2], reqptr->data[3]); - channelConf->gateway.assign(gateway); + continue; } - break; - case LanParam::VLAN: + IP::AddressOrigin origin = IP::convertAddressOriginFromString( + std::get<std::string>(properties.at("Origin"))); + if (origins.find(origin) == origins.end()) { - if (reqLen != lanParamVLANSize) - { - return IPMI_CC_REQ_DATA_LEN_INVALID; - } - - uint16_t vlan{}; - std::memcpy(&vlan, reqptr->data, ipmi::network::VLAN_SIZE_BYTE); - // We are not storing the enable bit - // We assume that ipmitool always send enable - // bit as 1. - vlan = le16toh(vlan); - if (vlan == 0 || vlan > maxValidVLANIDValue) - { - return IPMI_CC_INVALID_FIELD_REQUEST; - } - channelConf->vlanID = vlan; + continue; } - break; - case LanParam::INPROGRESS: + if (idx > 0) { - if (reqptr->data[0] == SET_COMPLETE) - { - channelConf->lan_set_in_progress = SET_COMPLETE; - - log<level::INFO>( - "Network data from Cache", - entry("PREFIX=%s", channelConf->netmask.c_str()), - entry("ADDRESS=%s", channelConf->ipaddr.c_str()), - entry("GATEWAY=%s", channelConf->gateway.c_str()), - entry("VLAN=%d", channelConf->vlanID)); - } - else if (reqptr->data[0] == SET_IN_PROGRESS) // Set In Progress - { - channelConf->lan_set_in_progress = SET_IN_PROGRESS; - } + idx--; + continue; } - break; - default: + IfAddr<family> ifaddr; + ifaddr.path = path; + ifaddr.address = *addr; + ifaddr.prefix = std::get<uint8_t>(properties.at("PrefixLength")); + ifaddr.origin = origin; + return std::move(ifaddr); + } + + return std::nullopt; +} + +/** @brief Trivial helper around findIfAddr that simplifies calls + * for one off lookups. Don't use this if you intend to do multiple + * lookups at a time. + * + * @param[in] bus - The bus object used for lookups + * @param[in] params - The parameters for the channel + * @param[in] idx - The index of the desired address on the interface + * @param[in] origins - The allowed origins for the address objects + * @return The address and prefix if it was found + */ +template <int family> +auto getIfAddr(sdbusplus::bus::bus& bus, const ChannelParams& params, + uint8_t idx, + const std::unordered_set<IP::AddressOrigin>& origins) +{ + ObjectLookupCache ips(bus, params, INTF_IP); + return findIfAddr<family>(bus, params, idx, origins, ips); +} + +/** @brief Deletes the dbus object. Ignores empty objects or objects that are + * missing from the bus. + * + * @param[in] bus - The bus object used for lookups + * @param[in] service - The name of the service + * @param[in] path - The path of the object to delete + */ +void deleteObjectIfExists(sdbusplus::bus::bus& bus, const std::string& service, + const std::string& path) +{ + if (path.empty()) + { + return; + } + try + { + auto req = bus.new_method_call(service.c_str(), path.c_str(), + ipmi::DELETE_INTERFACE, "Delete"); + bus.call_noreply(req); + } + catch (const sdbusplus::exception::SdBusError& e) + { + if (strcmp(e.name(), "org.freedesktop.DBus.Error.UnknownObject") != 0) { - rc = IPMI_CC_PARM_NOT_SUPPORTED; - return rc; + // We want to rethrow real errors + throw; } } - rc = checkAndUpdateNetwork(channel); - - return rc; } -struct get_lan_t +/** @brief Sets the address info configured for the interface + * If a previous address path exists then it will be removed + * before the new address is added. + * + * @param[in] bus - The bus object used for lookups + * @param[in] params - The parameters for the channel + * @param[in] address - The address of the new IP + * @param[in] prefix - The prefix of the new IP + */ +template <int family> +void createIfAddr(sdbusplus::bus::bus& bus, const ChannelParams& params, + const typename AddrFamily<family>::addr& address, + uint8_t prefix) { - uint8_t rev_channel; - uint8_t parameter; - uint8_t parameter_set; - uint8_t parameter_block; -} __attribute__((packed)); + auto newreq = + bus.new_method_call(params.service.c_str(), params.logicalPath.c_str(), + INTF_IP_CREATE, "IP"); + std::string protocol = + sdbusplus::xyz::openbmc_project::Network::server::convertForMessage( + AddrFamily<family>::protocol); + newreq.append(protocol, addrToString<family>(address), prefix, ""); + bus.call_noreply(newreq); +} -ipmi_ret_t ipmi_transport_get_lan(ipmi_netfn_t netfn, ipmi_cmd_t cmd, - ipmi_request_t request, - ipmi_response_t response, - ipmi_data_len_t data_len, - ipmi_context_t context) +/** @brief Trivial helper for getting the IPv4 address from getIfAddrs() + * + * @param[in] bus - The bus object used for lookups + * @param[in] params - The parameters for the channel + * @return The address and prefix if found + */ +auto getIfAddr4(sdbusplus::bus::bus& bus, const ChannelParams& params) { - ipmi_ret_t rc = IPMI_CC_OK; - *data_len = 0; - const uint8_t current_revision = 0x11; // Current rev per IPMI Spec 2.0 + return getIfAddr<AF_INET>(bus, params, 0, originsV4); +} - get_lan_t* reqptr = (get_lan_t*)request; - // channel number is the lower nibble - int channel = reqptr->rev_channel & CHANNEL_MASK; - ipmi::ChannelInfo chInfo; - ipmi::getChannelInfo(channel, chInfo); - if (chInfo.mediumType != - static_cast<uint8_t>(ipmi::EChannelMediumType::lan8032)) +/** @brief Reconfigures the IPv4 address info configured for the interface + * + * @param[in] bus - The bus object used for lookups + * @param[in] params - The parameters for the channel + * @param[in] address - The new address if specified + * @param[in] prefix - The new address prefix if specified + */ +void reconfigureIfAddr4(sdbusplus::bus::bus& bus, const ChannelParams& params, + const std::optional<in_addr>& address, + std::optional<uint8_t> prefix) +{ + auto ifaddr = getIfAddr4(bus, params); + if (!ifaddr && !address) { - return IPMI_CC_INVALID_FIELD_REQUEST; + log<level::ERR>("Missing address for IPv4 assignment"); + elog<InternalFailure>(); } + uint8_t fallbackPrefix = AddrFamily<AF_INET>::defaultPrefix; + if (ifaddr) + { + fallbackPrefix = ifaddr->prefix; + deleteObjectIfExists(bus, params.service, ifaddr->path); + } + createIfAddr<AF_INET>(bus, params, address.value_or(ifaddr->address), + prefix.value_or(fallbackPrefix)); +} - if (reqptr->rev_channel & 0x80) // Revision is bit 7 +/** @brief Gets the vlan ID configured on the interface + * + * @param[in] bus - The bus object used for lookups + * @param[in] params - The parameters for the channel + * @return VLAN id or the standard 0 for no VLAN + */ +uint16_t getVLANProperty(sdbusplus::bus::bus& bus, const ChannelParams& params) +{ + // VLAN devices will always have a separate logical object + if (params.ifPath == params.logicalPath) { - // Only current revision was requested - *data_len = sizeof(current_revision); - std::memcpy(response, ¤t_revision, *data_len); - return IPMI_CC_OK; + return 0; } - static std::vector<uint8_t> cipherList; - static auto listInit = false; + auto vlan = std::get<uint32_t>(getDbusProperty( + bus, params.service, params.logicalPath, INTF_VLAN, "Id")); + if ((vlan & VLAN_VALUE_MASK) != vlan) + { + logWithChannel<level::ERR>(params, "networkd returned an invalid vlan", + entry("VLAN=%" PRIu32, vlan)); + elog<InternalFailure>(); + } + return vlan; +} - if (!listInit) +/** @brief Deletes all of the possible configuration parameters for a channel + * + * @param[in] bus - The bus object used for lookups + * @param[in] params - The parameters for the channel + */ +void deconfigureChannel(sdbusplus::bus::bus& bus, ChannelParams& params) +{ + // Delete all objects associated with the interface + auto objreq = bus.new_method_call(MAPPER_BUS_NAME, MAPPER_OBJ, MAPPER_INTF, + "GetSubTree"); + objreq.append(PATH_ROOT, 0, std::vector<std::string>{DELETE_INTERFACE}); + auto objreply = bus.call(objreq); + ObjectTree objs; + objreply.read(objs); + for (const auto& [path, impls] : objs) { - try + if (path.find(params.ifname) == path.npos) { - cipherList = cipher::getCipherList(); - listInit = true; + continue; } - catch (const std::exception& e) + for (const auto& [service, intfs] : impls) { - return IPMI_CC_UNSPECIFIED_ERROR; + deleteObjectIfExists(bus, service, path); + } + // Update params to reflect the deletion of vlan + if (path == params.logicalPath) + { + params.logicalPath = params.ifPath; } } - auto ethdevice = ipmi::getChannelName(channel); - if (ethdevice.empty()) + // Clear out any settings on the lower physical interface + setDHCPProperty(bus, params, false); +} + +/** @brief Creates a new VLAN on the specified interface + * + * @param[in] bus - The bus object used for lookups + * @param[in] params - The parameters for the channel + * @param[in] vlan - The id of the new vlan + */ +void createVLAN(sdbusplus::bus::bus& bus, ChannelParams& params, uint16_t vlan) +{ + if (vlan == 0) { - return IPMI_CC_INVALID_FIELD_REQUEST; + return; } - auto channelConf = getChannelConfig(channel); - LanParam param = static_cast<LanParam>(reqptr->parameter); - switch (param) + auto req = bus.new_method_call(params.service.c_str(), PATH_ROOT, + INTF_VLAN_CREATE, "VLAN"); + req.append(params.ifname, static_cast<uint32_t>(vlan)); + auto reply = bus.call(req); + sdbusplus::message::object_path newPath; + reply.read(newPath); + params.logicalPath = std::move(newPath); +} + +/** @brief Performs the necessary reconfiguration to change the VLAN + * + * @param[in] bus - The bus object used for lookups + * @param[in] params - The parameters for the channel + * @param[in] vlan - The new vlan id to use + */ +void reconfigureVLAN(sdbusplus::bus::bus& bus, ChannelParams& params, + uint16_t vlan) +{ + // Unfortunatetly we don't have built-in functions to migrate our interface + // customizations to new VLAN interfaces, or have some kind of decoupling. + // We therefore must retain all of our old information, setup the new VLAN + // configuration, then restore the old info. + + // Save info from the old logical interface + ObjectLookupCache ips(bus, params, INTF_IP); + auto ifaddr4 = findIfAddr<AF_INET>(bus, params, 0, originsV4, ips); + auto dhcp = getDHCPProperty(bus, params); + + deconfigureChannel(bus, params); + createVLAN(bus, params, vlan); + + // Re-establish the saved settings + setDHCPProperty(bus, params, dhcp); + if (ifaddr4) + { + createIfAddr<AF_INET>(bus, params, ifaddr4->address, ifaddr4->prefix); + } +} + +/** @brief Turns a prefix into a netmask + * + * @param[in] prefix - The prefix length + * @return The netmask + */ +in_addr prefixToNetmask(uint8_t prefix) +{ + if (prefix > 32) { - case LanParam::INPROGRESS: + log<level::ERR>("Invalid prefix", entry("PREFIX=%" PRIu8, prefix)); + elog<InternalFailure>(); + } + if (prefix == 0) + { + // Avoids 32-bit lshift by 32 UB + return {}; + } + return {htobe32(~UINT32_C(0) << (32 - prefix))}; +} + +/** @brief Turns a a netmask into a prefix length + * + * @param[in] netmask - The netmask in byte form + * @return The prefix length + */ +uint8_t netmaskToPrefix(in_addr netmask) +{ + uint32_t x = be32toh(netmask.s_addr); + if ((~x & (~x + 1)) != 0) + { + char maskStr[INET_ADDRSTRLEN]; + inet_ntop(AF_INET, &netmask, maskStr, sizeof(maskStr)); + log<level::ERR>("Invalid netmask", entry("NETMASK=%s", maskStr)); + elog<InternalFailure>(); + } + return 32 - __builtin_ctz(x); +} + +// We need to store this value so it can be returned to the client +// It is volatile so safe to store in daemon memory. +static std::unordered_map<uint8_t, SetStatus> setStatus; + +// Until we have good support for fixed versions of IPMI tool +// we need to return the VLAN id for disabled VLANs. The value is only +// used for verification that a disable operation succeeded and will only +// be sent if our system indicates that vlans are disabled. +static std::unordered_map<uint8_t, uint16_t> lastDisabledVlan; + +/** @brief Gets the set status for the channel if it exists + * Otherise populates and returns the default value. + * + * @param[in] channel - The channel id corresponding to an ethernet interface + * @return A reference to the SetStatus for the channel + */ +SetStatus& getSetStatus(uint8_t channel) +{ + auto it = setStatus.find(channel); + if (it != setStatus.end()) + { + return it->second; + } + return setStatus[channel] = SetStatus::Complete; +} + +RspType<> setLan(uint4_t channelBits, uint4_t, uint8_t parameter, + message::Payload& req) +{ + auto channel = static_cast<uint8_t>(channelBits); + if (!doesDeviceExist(channel)) + { + req.trailingOk = true; + return responseInvalidFieldRequest(); + } + + switch (static_cast<LanParam>(parameter)) + { + case LanParam::SetStatus: { - uint8_t buf[] = {current_revision, - channelConf->lan_set_in_progress}; - *data_len = sizeof(buf); - std::memcpy(response, &buf, *data_len); - break; + uint2_t flag; + uint6_t rsvd; + if (req.unpack(flag, rsvd) != 0 || !req.fullyUnpacked()) + { + return responseReqDataLenInvalid(); + } + auto status = static_cast<SetStatus>(static_cast<uint8_t>(flag)); + switch (status) + { + case SetStatus::Complete: + { + getSetStatus(channel) = status; + return responseSuccess(); + } + case SetStatus::InProgress: + { + auto& storedStatus = getSetStatus(channel); + if (storedStatus == SetStatus::InProgress) + { + return response(ccParamSetLocked); + } + storedStatus = status; + return responseSuccess(); + } + case SetStatus::Commit: + if (getSetStatus(channel) != SetStatus::InProgress) + { + return responseInvalidFieldRequest(); + } + return responseSuccess(); + } + return response(ccParamNotSupported); } - case LanParam::AUTHSUPPORT: + case LanParam::AuthSupport: { - uint8_t buf[] = {current_revision, 0x04}; - *data_len = sizeof(buf); - std::memcpy(response, &buf, *data_len); - break; + req.trailingOk = true; + return response(ccParamReadOnly); } - case LanParam::AUTHENABLES: + case LanParam::AuthEnables: { - uint8_t buf[] = {current_revision, 0x04, 0x04, 0x04, 0x04, 0x04}; - *data_len = sizeof(buf); - std::memcpy(response, &buf, *data_len); - break; + req.trailingOk = true; + return response(ccParamNotSupported); } case LanParam::IP: - case LanParam::SUBNET: - case LanParam::GATEWAY: - case LanParam::MAC: { - uint8_t buf[ipmi::network::MAC_ADDRESS_SIZE_BYTE + 1] = {}; - - *data_len = sizeof(current_revision); - std::memcpy(buf, ¤t_revision, *data_len); - - if (getNetworkData(reqptr->parameter, &buf[1], channel) == - IPMI_CC_OK) + in_addr ip; + std::array<uint8_t, sizeof(ip)> bytes; + if (req.unpack(bytes) != 0 || !req.fullyUnpacked()) + { + return responseReqDataLenInvalid(); + } + copyInto(ip, bytes); + channelCall<reconfigureIfAddr4>(channel, ip, std::nullopt); + return responseSuccess(); + } + case LanParam::IPSrc: + { + uint4_t flag; + uint4_t rsvd; + if (req.unpack(flag, rsvd) != 0 || !req.fullyUnpacked()) { - if (param == LanParam::MAC) + return responseReqDataLenInvalid(); + } + switch (static_cast<IPSrc>(static_cast<uint8_t>(flag))) + { + case IPSrc::DHCP: { - *data_len = sizeof(buf); + channelCall<setDHCPProperty>(channel, true); + return responseSuccess(); } - else + case IPSrc::Unspecified: + case IPSrc::Static: + case IPSrc::BIOS: + case IPSrc::BMC: { - *data_len = ipmi::network::IPV4_ADDRESS_SIZE_BYTE + 1; + channelCall<setDHCPProperty>(channel, false); + return responseSuccess(); } - std::memcpy(response, &buf, *data_len); } - else + return response(ccParamNotSupported); + } + case LanParam::MAC: + { + ether_addr mac; + std::array<uint8_t, sizeof(mac)> bytes; + if (req.unpack(bytes) != 0 || !req.fullyUnpacked()) { - rc = IPMI_CC_UNSPECIFIED_ERROR; + return responseReqDataLenInvalid(); } - break; + copyInto(mac, bytes); + channelCall<setMACProperty>(channel, mac); + return responseSuccess(); } - case LanParam::VLAN: + case LanParam::SubnetMask: { - uint8_t buf[ipmi::network::VLAN_SIZE_BYTE + 1] = {}; - - *data_len = sizeof(current_revision); - std::memcpy(buf, ¤t_revision, *data_len); - if (getNetworkData(reqptr->parameter, &buf[1], channel) == - IPMI_CC_OK) + in_addr netmask; + std::array<uint8_t, sizeof(netmask)> bytes; + if (req.unpack(bytes) != 0 || !req.fullyUnpacked()) { - *data_len = sizeof(buf); - std::memcpy(response, &buf, *data_len); + return responseReqDataLenInvalid(); } - break; + copyInto(netmask, bytes); + channelCall<reconfigureIfAddr4>(channel, std::nullopt, + netmaskToPrefix(netmask)); + return responseSuccess(); } - case LanParam::IPSRC: + case LanParam::Gateway1: { - uint8_t buff[ipmi::network::IPSRC_SIZE_BYTE + 1] = {}; - *data_len = sizeof(current_revision); - std::memcpy(buff, ¤t_revision, *data_len); - if (getNetworkData(reqptr->parameter, &buff[1], channel) == - IPMI_CC_OK) + in_addr gateway; + std::array<uint8_t, sizeof(gateway)> bytes; + if (req.unpack(bytes) != 0 || !req.fullyUnpacked()) { - *data_len = sizeof(buff); - std::memcpy(response, &buff, *data_len); + return responseReqDataLenInvalid(); } - break; + copyInto(gateway, bytes); + channelCall<setGatewayProperty<AF_INET>>(channel, gateway); + return responseSuccess(); } - case LanParam::CIPHER_SUITE_COUNT: + case LanParam::VLANId: { - *(static_cast<uint8_t*>(response)) = current_revision; - // Byte 1 is reserved byte and does not indicate a cipher suite ID, - // so no of cipher suite entry count is one less than the size of - // the vector - auto count = static_cast<uint8_t>(cipherList.size() - 1); - *(static_cast<uint8_t*>(response) + 1) = count; - *data_len = sizeof(current_revision) + sizeof(count); - break; + uint16_t vlanData; + if (req.unpack(vlanData) != 0 || !req.fullyUnpacked()) + { + return responseReqDataLenInvalid(); + } + if ((vlanData & VLAN_ENABLE_FLAG) == 0) + { + lastDisabledVlan[channel] = vlanData & VLAN_VALUE_MASK; + vlanData = 0; + } + channelCall<reconfigureVLAN>(channel, vlanData & VLAN_VALUE_MASK); + return responseSuccess(); } - case LanParam::CIPHER_SUITE_ENTRIES: + case LanParam::CiphersuiteSupport: + case LanParam::CiphersuiteEntries: { - *(static_cast<uint8_t*>(response)) = current_revision; - // Byte 1 is reserved - std::copy_n(cipherList.data(), cipherList.size(), - static_cast<uint8_t*>(response) + 1); - *data_len = sizeof(current_revision) + - static_cast<uint8_t>(cipherList.size()); - break; + req.trailingOk = true; + return response(ccParamReadOnly); } - default: - log<level::ERR>("Unsupported parameter", - entry("PARAMETER=0x%x", reqptr->parameter)); - rc = IPMI_CC_PARM_NOT_SUPPORTED; } - return rc; + req.trailingOk = true; + return response(ccParamNotSupported); } -void applyChanges(int channel) +RspType<message::Payload> getLan(uint4_t channelBits, uint3_t, bool revOnly, + uint8_t parameter, uint8_t set, uint8_t block) { - std::string ipaddress; - std::string gateway; - uint8_t prefix{}; - uint32_t vlanID{}; - std::string networkInterfacePath; - ipmi::DbusObjectInfo ipObject; - ipmi::DbusObjectInfo systemObject; + message::Payload ret; + constexpr uint8_t current_revision = 0x11; + ret.pack(current_revision); - auto ethdevice = ipmi::getChannelName(channel); - if (ethdevice.empty()) + if (revOnly) { - log<level::ERR>("Unable to get the interface name", - entry("CHANNEL=%d", channel)); - return; + return responseSuccess(std::move(ret)); } - auto ethIp = ethdevice + "/" + ipmi::network::IP_TYPE; - auto channelConf = getChannelConfig(channel); - try + auto channel = static_cast<uint8_t>(channelBits); + if (!doesDeviceExist(channel)) { - sdbusplus::bus::bus bus(ipmid_get_sd_bus_connection()); - - log<level::INFO>("Network data from Cache", - entry("PREFIX=%s", channelConf->netmask.c_str()), - entry("ADDRESS=%s", channelConf->ipaddr.c_str()), - entry("GATEWAY=%s", channelConf->gateway.c_str()), - entry("VLAN=%d", channelConf->vlanID), - entry("IPSRC=%d", channelConf->ipsrc)); - if (channelConf->vlanID != ipmi::network::VLAN_ID_MASK) - { - // get the first twelve bits which is vlan id - // not interested in rest of the bits. - channelConf->vlanID = le32toh(channelConf->vlanID); - vlanID = channelConf->vlanID & ipmi::network::VLAN_ID_MASK; - } + return responseInvalidFieldRequest(); + } - // if the asked ip src is DHCP then not interested in - // any given data except vlan. - if (channelConf->ipsrc != ipmi::network::IPOrigin::DHCP) + switch (static_cast<LanParam>(parameter)) + { + case LanParam::SetStatus: { - // always get the system object - systemObject = - ipmi::getDbusObject(bus, ipmi::network::SYSTEMCONFIG_INTERFACE, - ipmi::network::ROOT); - - // the below code is to determine the mode of the interface - // as the handling is same, if the system is configured with - // DHCP or user has given all the data. + SetStatus status; try { - ipmi::ObjectTree ancestorMap; - - ipmi::InterfaceList interfaces{ - ipmi::network::ETHERNET_INTERFACE}; - - // if the system is having ip object,then - // get the IP object. - ipObject = ipmi::getIPObject(bus, ipmi::network::IP_INTERFACE, - ipmi::network::ROOT, ethIp); - - // Get the parent interface of the IP object. - try - { - ancestorMap = ipmi::getAllAncestors(bus, ipObject.first, - std::move(interfaces)); - } - catch (InternalFailure& e) - { - // if unable to get the parent interface - // then commit the error and return. - log<level::ERR>("Unable to get the parent interface", - entry("PATH=%s", ipObject.first.c_str()), - entry("INTERFACE=%s", - ipmi::network::ETHERNET_INTERFACE)); - commit<InternalFailure>(); - channelConf->clear(); - return; - } - - networkInterfacePath = ancestorMap.begin()->first; - } - catch (InternalFailure& e) - { - // TODO Currently IPMI supports single interface,need to handle - // Multiple interface through - // https://github.com/openbmc/openbmc/issues/2138 - - // if there is no ip configured on the system,then - // get the network interface object. - auto networkInterfaceObject = - ipmi::getDbusObject(bus, ipmi::network::ETHERNET_INTERFACE, - ipmi::network::ROOT, ethdevice); - - networkInterfacePath = std::move(networkInterfaceObject.first); + status = setStatus.at(channel); } - - // get the configured mode on the system. - auto enableDHCP = std::get<bool>(ipmi::getDbusProperty( - bus, ipmi::network::SERVICE, networkInterfacePath, - ipmi::network::ETHERNET_INTERFACE, "DHCPEnabled")); - - // if ip address source is not given then get the ip source mode - // from the system so that it can be applied later. - if (channelConf->ipsrc == ipmi::network::IPOrigin::UNSPECIFIED) + catch (const std::out_of_range&) { - channelConf->ipsrc = (enableDHCP) - ? ipmi::network::IPOrigin::DHCP - : ipmi::network::IPOrigin::STATIC; + status = SetStatus::Complete; } - - // check whether user has given all the data - // or the configured system interface is dhcp enabled, - // in both of the cases get the values from the cache. - if ((!channelConf->ipaddr.empty() && - !channelConf->netmask.empty() && - !channelConf->gateway.empty()) || - (enableDHCP)) // configured system interface mode = DHCP + ret.pack(static_cast<uint2_t>(status), uint6_t{}); + return responseSuccess(std::move(ret)); + } + case LanParam::AuthSupport: + { + std::bitset<6> support; + ret.pack(support, uint2_t{}); + return responseSuccess(std::move(ret)); + } + case LanParam::AuthEnables: + { + std::bitset<6> enables; + ret.pack(enables, uint2_t{}); // Callback + ret.pack(enables, uint2_t{}); // User + ret.pack(enables, uint2_t{}); // Operator + ret.pack(enables, uint2_t{}); // Admin + ret.pack(enables, uint2_t{}); // OEM + return responseSuccess(std::move(ret)); + } + case LanParam::IP: + { + auto ifaddr = channelCall<getIfAddr4>(channel); + in_addr addr{}; + if (ifaddr) { - // convert mask into prefix - ipaddress = channelConf->ipaddr; - prefix = ipmi::network::toPrefix(AF_INET, channelConf->netmask); - gateway = channelConf->gateway; + addr = ifaddr->address; } - else // asked ip src = static and configured system src = static - // or partially given data. + ret.pack(dataRef(addr)); + return responseSuccess(std::move(ret)); + } + case LanParam::IPSrc: + { + auto src = IPSrc::Static; + if (channelCall<getDHCPProperty>(channel)) { - // We have partial filled cache so get the remaining - // info from the system. - - // Get the network data from the system as user has - // not given all the data then use the data fetched from the - // system but it is implementation dependent,IPMI spec doesn't - // force it. - - // if system is not having any ip object don't throw error, - try - { - auto properties = ipmi::getAllDbusProperties( - bus, ipObject.second, ipObject.first, - ipmi::network::IP_INTERFACE); - - ipaddress = - channelConf->ipaddr.empty() - ? std::get<std::string>(properties["Address"]) - : channelConf->ipaddr; - - prefix = channelConf->netmask.empty() - ? std::get<uint8_t>(properties["PrefixLength"]) - : ipmi::network::toPrefix( - AF_INET, channelConf->netmask); - } - catch (InternalFailure& e) - { - log<level::INFO>( - "Failed to get IP object which matches", - entry("INTERFACE=%s", ipmi::network::IP_INTERFACE), - entry("MATCH=%s", ethIp.c_str())); - } - - auto systemProperties = ipmi::getAllDbusProperties( - bus, systemObject.second, systemObject.first, - ipmi::network::SYSTEMCONFIG_INTERFACE); - - gateway = channelConf->gateway.empty() - ? std::get<std::string>( - systemProperties["DefaultGateway"]) - : channelConf->gateway; + src = IPSrc::DHCP; } + ret.pack(static_cast<uint4_t>(src), uint4_t{}); + return responseSuccess(std::move(ret)); } - - // Currently network manager doesn't support purging of all the - // ip addresses and the vlan interfaces from the parent interface, - // TODO once the support is there, will make the change here. - // https://github.com/openbmc/openbmc/issues/2141. - - // TODO Currently IPMI supports single interface,need to handle - // Multiple interface through - // https://github.com/openbmc/openbmc/issues/2138 - - // instead of deleting all the vlan interfaces and - // all the ipv4 address,we will call reset method. - // delete all the vlan interfaces - - ipmi::deleteAllDbusObjects(bus, ipmi::network::ROOT, - ipmi::network::VLAN_INTERFACE); - - // set the interface mode to static - auto networkInterfaceObject = - ipmi::getDbusObject(bus, ipmi::network::ETHERNET_INTERFACE, - ipmi::network::ROOT, ethdevice); - - // setting the physical interface mode to static. - ipmi::setDbusProperty( - bus, ipmi::network::SERVICE, networkInterfaceObject.first, - ipmi::network::ETHERNET_INTERFACE, "DHCPEnabled", false); - - networkInterfacePath = networkInterfaceObject.first; - - // delete all the ipv4 addresses - ipmi::deleteAllDbusObjects(bus, ipmi::network::ROOT, - ipmi::network::IP_INTERFACE, ethIp); - - if (vlanID) + case LanParam::MAC: { - ipmi::network::createVLAN(bus, ipmi::network::SERVICE, - ipmi::network::ROOT, ethdevice, vlanID); - - auto networkInterfaceObject = ipmi::getDbusObject( - bus, ipmi::network::VLAN_INTERFACE, ipmi::network::ROOT); - - networkInterfacePath = networkInterfaceObject.first; + ether_addr mac = channelCall<getMACProperty>(channel); + ret.pack(dataRef(mac)); + return responseSuccess(std::move(ret)); } - - if (channelConf->ipsrc == ipmi::network::IPOrigin::DHCP) + case LanParam::SubnetMask: { - ipmi::setDbusProperty( - bus, ipmi::network::SERVICE, networkInterfacePath, - ipmi::network::ETHERNET_INTERFACE, "DHCPEnabled", true); + auto ifaddr = channelCall<getIfAddr4>(channel); + uint8_t prefix = AddrFamily<AF_INET>::defaultPrefix; + if (ifaddr) + { + prefix = ifaddr->prefix; + } + in_addr netmask = prefixToNetmask(prefix); + ret.pack(dataRef(netmask)); + return responseSuccess(std::move(ret)); } - else + case LanParam::Gateway1: { - // change the mode to static - ipmi::setDbusProperty( - bus, ipmi::network::SERVICE, networkInterfacePath, - ipmi::network::ETHERNET_INTERFACE, "DHCPEnabled", false); - - if (!ipaddress.empty()) + auto gateway = + channelCall<getGatewayProperty<AF_INET>>(channel).value_or( + in_addr{}); + ret.pack(dataRef(gateway)); + return responseSuccess(std::move(ret)); + } + case LanParam::VLANId: + { + uint16_t vlan = channelCall<getVLANProperty>(channel); + if (vlan != 0) { - ipmi::network::createIP(bus, ipmi::network::SERVICE, - networkInterfacePath, ipv4Protocol, - ipaddress, prefix); + vlan |= VLAN_ENABLE_FLAG; } - - if (!gateway.empty()) + else { - ipmi::setDbusProperty(bus, systemObject.second, - systemObject.first, - ipmi::network::SYSTEMCONFIG_INTERFACE, - "DefaultGateway", std::string(gateway)); + vlan = lastDisabledVlan[channel]; } + ret.pack(vlan); + return responseSuccess(std::move(ret)); } - } - catch (sdbusplus::exception::exception& e) - { - log<level::ERR>( - "Failed to set network data", entry("PREFIX=%d", prefix), - entry("ADDRESS=%s", ipaddress.c_str()), - entry("GATEWAY=%s", gateway.c_str()), entry("VLANID=%d", vlanID), - entry("IPSRC=%d", channelConf->ipsrc)); - - commit<InternalFailure>(); + case LanParam::CiphersuiteSupport: + case LanParam::CiphersuiteEntries: + return response(ccParamNotSupported); } - channelConf->clear(); + return response(ccParamNotSupported); } -void commitNetworkChanges() -{ - for (const auto& channel : channelConfig) - { - if (channel.second->flush) - { - applyChanges(channel.first); - } - } -} +} // namespace transport +} // namespace ipmi -void createNetworkTimer() -{ - if (!networkTimer) - { - std::function<void()> networkTimerCallback( - std::bind(&commitNetworkChanges)); - - networkTimer = std::make_unique<phosphor::Timer>(networkTimerCallback); - } -} +void register_netfn_transport_functions() __attribute__((constructor)); void register_netfn_transport_functions() { - // As this timer is only for transport handler - // so creating it here. - createNetworkTimer(); - // <Wildcard Command> - ipmi_register_callback(NETFUN_TRANSPORT, IPMI_CMD_WILDCARD, NULL, - ipmi_transport_wildcard, PRIVILEGE_USER); - - // <Set LAN Configuration Parameters> - ipmi_register_callback(NETFUN_TRANSPORT, IPMI_CMD_SET_LAN, NULL, - ipmi_transport_set_lan, PRIVILEGE_ADMIN); - - // <Get LAN Configuration Parameters> - ipmi_register_callback(NETFUN_TRANSPORT, IPMI_CMD_GET_LAN, NULL, - ipmi_transport_get_lan, PRIVILEGE_OPERATOR); - - return; + ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnTransport, + ipmi::transport::cmdSetLanConfigParameters, + ipmi::Privilege::Admin, ipmi::transport::setLan); + ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnTransport, + ipmi::transport::cmdGetLanConfigParameters, + ipmi::Privilege::Admin, ipmi::transport::getLan); } diff --git a/transporthandler.hpp b/transporthandler.hpp deleted file mode 100644 index 5896082..0000000 --- a/transporthandler.hpp +++ /dev/null @@ -1,141 +0,0 @@ -#pragma once - -#include <ipmid/types.hpp> -#include <string> -// IPMI commands for Transport net functions. -enum ipmi_netfn_storage_cmds -{ - // Get capability bits - IPMI_CMD_SET_LAN = 0x01, - IPMI_CMD_GET_LAN = 0x02, -}; - -// Command specific completion codes -enum ipmi_transport_return_codes -{ - IPMI_CC_PARM_NOT_SUPPORTED = 0x80, -}; - -// Parameters -enum class LanParam : uint8_t -{ - INPROGRESS = 0, - AUTHSUPPORT = 1, // Read-only - AUTHENABLES = 2, - IP = 3, - IPSRC = 4, - MAC = 5, - SUBNET = 6, - IPHEADER_PARAMS = 7, - RMCP_PORT = 8, - RMCP_SECONDARY_PORT = 9, - BMC_GENERATED_ARP_CTRL = 10, - GRATUITOUS_ARP_INTERVAL = 11, - GATEWAY = 12, - GATEWAY_MAC = 13, - GATEWAY_BACKUP = 14, - GATEWAY_BACKUP_MAC = 15, - COMMUNITY_STRING = 16, - LAN_ALERT_DESTINATION_COUNT = 17, // Read-only - LAN_ALERT_DESTINATION_TYPE = 18, // Type per destination - LAN_ALERT_DESTINATIONS = 19, - VLAN = 20, - VLAN_PRIORITY = 21, - CIPHER_SUITE_COUNT = 22, // Read-only - CIPHER_SUITE_ENTRIES = 23, // Read-only - CIPHER_SUITE_PRIVILEGE_LEVELS = 24, - DESTINATION_ADDR_VLAN_TAGS = 25, - BAD_PASSWORD_THRESHOLD = 26, - IPV6_AND_IPV4_SUPPORTED = 50, // Read-only - IPV6_AND_IPV4_ENABLES = 51, - IPV6_HEADER_STATIC_TRAFFIC_CLASS = 52, - IPV6_HEADER_STATIC_HOP_LIMIT = 53, - IPV6_HEADER_FLOW_LABEL = 54, - IPV6_STATUS = 55, // Read-only - IPV6_STATIC_ADDRESSES = 56, - IPV6_DHCPV6_STATIC_DUID_STORAGE_LENGTH = 57, // Read-only - IPV6_DHCPV6_STATIC_DUIDS = 58, - IPV6_DYNAMIC_ADDRESSES = 59, // Read-only - IPV6_DHCPV6_DYNAMIC_DUID_STOR_LEN = 60, // Read-only - IPV6_DHCPV6_DYNAMIC_DUIDS = 61, - IPV6_DHCPV6_TIMING_CONF_SUPPORT = 62, // Read-only - IPV6_DHCPV6_TIMING_CONFIGURATION = 63, - IPV6_ROUTER_ADDRESS_CONF_CTRL = 64, - IPV6_STATIC_ROUTER_1_IP_ADDR = 65, - IPV6_STATIC_ROUTER_1_MAC_ADDR = 66, - IPV6_STATIC_ROUTER_1_PREFIX_LEN = 67, - IPV6_STATIC_ROUTER_1_PREFIX_VAL = 68, - IPV6_STATIC_ROUTER_2_IP_ADDR = 69, - IPV6_STATIC_ROUTER_2_MAC_ADDR = 70, - IPV6_STATIC_ROUTER_2_PREFIX_LEN = 71, - IPV6_STATIC_ROUTER_2_PREFIX_VAL = 72, - DYNAMIC_ROUTER_INFO_SET_COUNT = 73, // Read-only - IPV6_DYNAMIC_ROUTER_INFO_IP_ADDR = 74, // Read-only - IPV6_DYNAMIC_ROUTER_INFO_MAC = 75, // Read-only - IPV6_DYNAMIC_ROUTER_INFO_PREFIX_LEN = 76, // Read-only - IPV6_DYNAMIC_ROUTER_INFO_PREFIX_VAL = 77, // Read-only - IPV6_DYNAMIC_ROUTER_RECV_HOP_LIMIT = 78, - IPV6_NEIGHBOR_TIMING_CONF_SUPPORT = 79, // Read-only - IPV6_NEIGHBOR_TIMING_CONFIGURATION = 80, -}; - -// Data length of parameters -constexpr size_t lanParamVLANSize = 4; -constexpr uint8_t SET_COMPLETE = 0; -constexpr uint8_t SET_IN_PROGRESS = 1; -constexpr uint8_t SET_COMMIT_WRITE = 2; // Optional -constexpr uint8_t SET_IN_PROGRESS_RESERVED = 3; // Reserved - -const int CHANNEL_MASK = 0x0f; -const int NUM_CHANNELS = 0x0f; - -struct ChannelConfig_t -{ - std::string ipaddr; - ipmi::network::IPOrigin ipsrc = ipmi::network::IPOrigin::UNSPECIFIED; - std::string netmask; - std::string gateway; - std::string macAddress; - // IPMI stores the vlan info in 16 bits,32 bits is to aligned - // with phosphor-dbus interfaces. - // vlan id is in 12 bits and the 16th bit is for enable mask. - uint32_t vlanID = ipmi::network::VLAN_ID_MASK; - uint8_t lan_set_in_progress = SET_COMPLETE; - bool flush = false; - - void clear() - { - ipaddr.clear(); - netmask.clear(); - gateway.clear(); - macAddress.clear(); - vlanID = ipmi::network::VLAN_ID_MASK; - ipsrc = ipmi::network::IPOrigin::UNSPECIFIED; - lan_set_in_progress = SET_COMPLETE; - flush = false; - } -}; - -// Given a channel, get the corresponding configuration, -// or allocate it first. -// -// @param[in] channel the channel -// @return the ChannelConfig_t pointer. -struct ChannelConfig_t* getChannelConfig(int channel); - -/** @brief Iterate over all the channelconfig and if - * user has given the data for a channel then - * apply the network changes for that channel. - */ -void commitNetworkChanges(); - -/* @brief Apply the network changes which is there in the - * network cache for a given channel which gets filled - * through setLan command. If some of the network - * parameter was not given by the setLan then this function - * gets the value of that parameter which is already - * configured on the system. - * @param[in] channel: channel number. - */ -void applyChanges(int channel); -constexpr uint16_t maxValidVLANIDValue = 4095; |