summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorWilliam A. Kennington III <wak@google.com>2019-04-06 18:19:38 -0700
committerVernon Mauery <vernon.mauery@linux.intel.com>2019-09-27 17:18:29 +0000
commitc514d874e4ce3ed1f747cfcb4bab5990393c55e8 (patch)
tree3f53b6aee184df63fed0e12be530fb0ab1e7184d
parente7023926675030a5976dffda0825445ca0b5ef84 (diff)
downloadphosphor-host-ipmid-c514d874e4ce3ed1f747cfcb4bab5990393c55e8.tar.gz
phosphor-host-ipmid-c514d874e4ce3ed1f747cfcb4bab5990393c55e8.zip
transporthandler: Rewrite + New Handler
This rewrites the old transport handler to use the new ipmi handler registration functions. It attempts to clean up the old code, by refactoring any business logic out of the code that parses the IPMI messages. This makes the code paths easier to understand and allows for better code re-use. This also gets rid of the concept of the settings change timer. Clients expect to see their settings take effect as soon as they are set, regardless of the "Set In Progress" flag. This means we no longer need a cache for our network settings that are about to be set by the daemon, and a client can hold the BMC in "Set In Progress" while it verifies settings like other BMC implementations. Change-Id: I5406a674f087600afdfc2c0b3adeacde10986abc Signed-off-by: William A. Kennington III <wak@google.com>
-rw-r--r--app/channel.cpp1
-rw-r--r--apphandler.cpp1
-rw-r--r--include/ipmid/types.hpp21
-rw-r--r--include/ipmid/utils.hpp70
-rw-r--r--libipmid/utils.cpp148
-rw-r--r--transporthandler.cpp1826
-rw-r--r--transporthandler.hpp141
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, &current_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, &current_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, &current_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, &current_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;
OpenPOWER on IntegriCloud