summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-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