summaryrefslogtreecommitdiffstats
path: root/transporthandler.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'transporthandler.cpp')
-rw-r--r--transporthandler.cpp2516
1 files changed, 1731 insertions, 785 deletions
diff --git a/transporthandler.cpp b/transporthandler.cpp
index 59d933a..a3b3c35 100644
--- a/transporthandler.cpp
+++ b/transporthandler.cpp
@@ -1,1002 +1,1948 @@
-#include "transporthandler.hpp"
-
#include "app/channel.hpp"
-#include "user_channel/channel_layer.hpp"
#include <arpa/inet.h>
+#include <netinet/ether.h>
-#include <chrono>
-#include <filesystem>
+#include <array>
+#include <bitset>
+#include <cinttypes>
+#include <cstdint>
+#include <cstring>
#include <fstream>
+#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>
+#include <xyz/openbmc_project/Network/Neighbor/server.hpp>
+
+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;
+using sdbusplus::xyz::openbmc_project::Network::server::Neighbor;
-#define SYSTEMD_NETWORKD_DBUS 1
+namespace cipher
+{
-#ifdef SYSTEMD_NETWORKD_DBUS
-#include <mapper.h>
-#include <systemd/sd-bus.h>
-#endif
+std::vector<uint8_t> getCipherList()
+{
+ std::vector<uint8_t> cipherList;
-// timer for network changes
-std::unique_ptr<phosphor::Timer> networkTimer = nullptr;
+ std::ifstream jsonFile(cipher::configFile);
+ if (!jsonFile.is_open())
+ {
+ log<level::ERR>("Channel Cipher suites file not found");
+ elog<InternalFailure>();
+ }
-const int SIZE_MAC = 18; // xx:xx:xx:xx:xx:xx
-constexpr auto ipv4Protocol = "xyz.openbmc_project.Network.IP.Protocol.IPv4";
+ auto data = Json::parse(jsonFile, nullptr, false);
+ if (data.is_discarded())
+ {
+ log<level::ERR>("Parsing channel cipher suites JSON failed");
+ elog<InternalFailure>();
+ }
+
+ // Byte 1 is reserved
+ cipherList.push_back(0x00);
-std::map<int, std::unique_ptr<struct ChannelConfig_t>> channelConfig;
+ for (const auto& record : data)
+ {
+ cipherList.push_back(record.value(cipher, 0));
+ }
-using namespace phosphor::logging;
-using namespace sdbusplus::xyz::openbmc_project::Common::Error;
+ return cipherList;
+}
+} // namespace cipher
-namespace fs = std::filesystem;
-namespace variant_ns = sdbusplus::message::variant_ns;
+namespace ipmi
+{
+namespace transport
+{
-void register_netfn_transport_functions() __attribute__((constructor));
+// 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;
+
+// Arbitrary v6 Address Limits to prevent too much output in ipmitool
+constexpr uint8_t MAX_IPV6_STATIC_ADDRESSES = 15;
+constexpr uint8_t MAX_IPV6_DYNAMIC_ADDRESSES = 15;
+
+// 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_NEIGHBOR = "xyz.openbmc_project.Network.Neighbor";
+constexpr auto INTF_NEIGHBOR_CREATE_STATIC =
+ "xyz.openbmc_project.Network.Neighbor.CreateStatic";
+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
+{
+};
-struct ChannelConfig_t* getChannelConfig(int channel)
+/** @brief Parameter specialization for IPv4 */
+template <>
+struct AddrFamily<AF_INET>
{
- auto item = channelConfig.find(channel);
- if (item == channelConfig.end())
- {
- channelConfig[channel] = std::make_unique<struct ChannelConfig_t>();
- }
+ 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 Parameter specialization for IPv6 */
+template <>
+struct AddrFamily<AF_INET6>
+{
+ using addr = in6_addr;
+ static constexpr auto protocol = IP::Protocol::IPv6;
+ static constexpr size_t maxStrLen = INET6_ADDRSTRLEN;
+ static constexpr uint8_t defaultPrefix = 128;
+ static constexpr char propertyGateway[] = "DefaultGateway6";
+};
+
+/** @brief Valid address origins for IPv4 */
+const std::unordered_set<IP::AddressOrigin> originsV4 = {
+ IP::AddressOrigin::Static,
+ IP::AddressOrigin::DHCP,
+};
+
+/** @brief Valid address origins for IPv6 */
+const std::unordered_set<IP::AddressOrigin> originsV6Static = {
+ IP::AddressOrigin::Static};
+const std::unordered_set<IP::AddressOrigin> originsV6Dynamic = {
+ IP::AddressOrigin::DHCP,
+ IP::AddressOrigin::SLAAC,
+};
+
+/** @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 Interface Neighbor configuration parameters */
+template <int family>
+struct IfNeigh
+{
+ std::string path;
+ typename AddrFamily<family>::addr ip;
+ ether_addr mac;
+};
- return channelConfig[channel].get();
+/** @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,
+ Gateway1MAC = 13,
+ VLANId = 20,
+ CiphersuiteSupport = 22,
+ CiphersuiteEntries = 23,
+ IPFamilySupport = 50,
+ IPFamilyEnables = 51,
+ IPv6Status = 55,
+ IPv6StaticAddresses = 56,
+ IPv6DynamicAddresses = 59,
+ IPv6RouterControl = 64,
+ IPv6StaticRouter1IP = 65,
+ IPv6StaticRouter1MAC = 66,
+ IPv6StaticRouter1PrefixLength = 67,
+ IPv6StaticRouter1PrefixValue = 68,
+};
+
+static constexpr uint8_t oemCmdStart = 192;
+static constexpr uint8_t oemCmdEnd = 255;
+
+/** @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 IPMI Family Suport Bits */
+namespace IPFamilySupportFlag
+{
+constexpr uint8_t IPv6Only = 0;
+constexpr uint8_t DualStack = 1;
+constexpr uint8_t IPv6Alerts = 2;
+} // namespace IPFamilySupportFlag
+
+/** @brief IPMI IPFamily Enables Flag */
+enum class IPFamilyEnables : uint8_t
+{
+ IPv4Only = 0,
+ IPv6Only = 1,
+ DualStack = 2,
+};
+
+/** @brief IPMI IPv6 Dyanmic Status Bits */
+namespace IPv6StatusFlag
+{
+constexpr uint8_t DHCP = 0;
+constexpr uint8_t SLAAC = 1;
+}; // namespace IPv6StatusFlag
+
+/** @brief IPMI IPv6 Source */
+enum class IPv6Source : uint8_t
+{
+ Static = 0,
+ SLAAC = 1,
+ DHCP = 2,
+};
+
+/** @brief IPMI IPv6 Address Status */
+enum class IPv6AddressStatus : uint8_t
+{
+ Active = 0,
+ Disabled = 1,
+};
+
+namespace IPv6RouterControlFlag
+{
+constexpr uint8_t Static = 0;
+constexpr uint8_t Dynamic = 1;
+}; // namespace IPv6RouterControlFlag
+
+/** @brief A trivial helper used to determine if two PODs are equal
+ *
+ * @params[in] a - The first object to compare
+ * @params[in] b - The second object to compare
+ * @return True if the objects are the same bytewise
+ */
+template <typename T>
+bool equal(const T& a, const T& b)
+{
+ static_assert(std::is_trivially_copyable_v<T>);
+ return std::memcmp(&a, &b, sizeof(T)) == 0;
}
-// 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 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)
{
- ipmi_ret_t rc = IPMI_CC_OK;
- sdbusplus::bus::bus bus(ipmid_get_sd_bus_connection());
+ static_assert(std::is_trivially_copyable_v<T>);
+ static_assert(N == sizeof(T));
+ std::memcpy(&t, bytes.data(), bytes.size());
+}
+
+/** @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)
+{
+ 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)
+ {
+ continue;
+ }
+ for (const auto& [service, intfs] : impls)
{
- case LanParam::IP:
+ bool vlan = false;
+ bool ethernet = false;
+ for (const auto& intf : intfs)
{
- std::string ipaddress;
- 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);
-
- ipaddress =
- variant_ns::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.
- }
+ vlan = true;
}
- else if (channelConf->lan_set_in_progress == SET_IN_PROGRESS)
+ else if (intf == INTF_ETHERNET)
{
- ipaddress = channelConf->ipaddr;
+ ethernet = true;
}
-
- inet_pton(AF_INET, ipaddress.c_str(),
- reinterpret_cast<void*>(data));
}
- break;
-
- case LanParam::IPSRC:
+ if (params.service.empty() && (vlan || ethernet))
+ {
+ params.service = service;
+ }
+ if (params.ifPath.empty() && !vlan && ethernet)
+ {
+ params.ifPath = path;
+ }
+ if (params.logicalPath.empty() && vlan)
{
- std::string networkInterfacePath;
+ params.logicalPath = path;
+ }
+ }
+ }
- 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);
+ // We must have a path for the underlying interface
+ if (params.ifPath.empty())
+ {
+ return std::nullopt;
+ }
+ // We don't have a VLAN so the logical path is the same
+ if (params.logicalPath.empty())
+ {
+ params.logicalPath = params.ifPath;
+ }
- networkInterfacePath = networkInterfaceObject.first;
- }
+ params.id = channel;
+ params.ifname = std::move(ifname);
+ return std::move(params);
+}
- auto variant = ipmi::getDbusProperty(
- bus, ipmi::network::SERVICE, networkInterfacePath,
- ipmi::network::ETHERNET_INTERFACE, "DHCPEnabled");
+/** @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)
+{
+ auto params = maybeGetChannelParams(bus, channel);
+ if (!params)
+ {
+ log<level::ERR>("Failed to get channel params",
+ entry("CHANNEL=%" PRIu8, channel));
+ elog<InternalFailure>();
+ }
+ return std::move(*params);
+}
- auto dhcpEnabled = variant_ns::get<bool>(variant);
- // As per IPMI spec 2=>DHCP, 1=STATIC
- auto ipsrc = dhcpEnabled ? ipmi::network::IPOrigin::DHCP
- : ipmi::network::IPOrigin::STATIC;
+/** @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)
+ {
+ return logWithChannel<level>(*params, std::forward<Args>(args)...);
+ }
+ return log<level>(std::forward<Args>(args)...);
+}
- 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;
+/** @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)...);
+}
- case LanParam::SUBNET:
- {
- unsigned long mask{};
- 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);
-
- auto prefix = variant_ns::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);
- }
- else if (channelConf->lan_set_in_progress == SET_IN_PROGRESS)
- {
- inet_pton(AF_INET, channelConf->netmask.c_str(),
- reinterpret_cast<void*>(data));
- }
- }
- break;
+/** @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"));
+}
- case LanParam::GATEWAY:
- {
- std::string gateway;
+/** @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);
+}
- if (channelConf->lan_set_in_progress == SET_COMPLETE)
- {
- try
- {
- auto systemObject = ipmi::getDbusObject(
- bus, ipmi::network::SYSTEMCONFIG_INTERFACE,
- ipmi::network::ROOT);
+/** @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)
+{
+ const ether_addr* ret = ether_aton(mac);
+ if (ret == nullptr)
+ {
+ log<level::ERR>("Invalid MAC Address", entry("MAC=%s", mac));
+ elog<InternalFailure>();
+ }
+ return *ret;
+}
- auto systemProperties = ipmi::getAllDbusProperties(
- bus, systemObject.second, systemObject.first,
- ipmi::network::SYSTEMCONFIG_INTERFACE);
+/** @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)
+{
+ auto macStr = std::get<std::string>(getDbusProperty(
+ bus, params.service, params.ifPath, INTF_MAC, "MACAddress"));
+ return stringToMAC(macStr.c_str());
+}
- gateway = variant_ns::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;
- }
+/** @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)
+{
+ std::string macStr = ether_ntoa(&mac);
+ setDbusProperty(bus, params.service, params.ifPath, INTF_MAC, "MACAddress",
+ macStr);
+}
- inet_pton(AF_INET, gateway.c_str(),
- reinterpret_cast<void*>(data));
- }
- break;
+/** @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 ret;
+ }
+ return std::nullopt;
+}
- case LanParam::MAC:
- {
- std::string macAddress;
- if (channelConf->lan_set_in_progress == SET_COMPLETE)
- {
- auto macObjectInfo =
- ipmi::getDbusObject(bus, ipmi::network::MAC_INTERFACE,
- ipmi::network::ROOT, ethdevice);
+/** @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>("Failed to convert IP Address",
+ entry("FAMILY=%d", family),
+ entry("ADDRESS=%s", address));
+ elog<InternalFailure>();
+ }
+ return *ret;
+}
- auto variant = ipmi::getDbusProperty(
- bus, macObjectInfo.second, macObjectInfo.first,
- ipmi::network::MAC_INTERFACE, "MACAddress");
+/** @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)
+{
+ std::string ret(AddrFamily<family>::maxStrLen, '\0');
+ inet_ntop(family, &address, ret.data(), ret.size());
+ ret.resize(strlen(ret.c_str()));
+ return ret;
+}
- macAddress = variant_ns::get<std::string>(variant);
- }
- else if (channelConf->lan_set_in_progress == SET_IN_PROGRESS)
- {
- macAddress = channelConf->macAddress;
- }
+/** @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());
+}
- sscanf(macAddress.c_str(), ipmi::network::MAC_ADDRESS_FORMAT,
- (data), (data + 1), (data + 2), (data + 3), (data + 4),
- (data + 5));
- }
- break;
+/** @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, ""))
+ {
+ }
- case LanParam::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);
+ class iterator : public ObjectTree::const_iterator
+ {
+ public:
+ using value_type = PropertiesCache::value_type;
- vlanID = static_cast<uint16_t>(
- ipmi::network::getVLAN(ipObjectInfo.first));
+ iterator(ObjectTree::const_iterator it, ObjectLookupCache& container) :
+ ObjectTree::const_iterator(it), container(container),
+ ret(container.cache.end())
+ {
+ }
+ value_type& operator*()
+ {
+ ret = container.get(ObjectTree::const_iterator::operator*().first);
+ return *ret;
+ }
+ value_type* operator->()
+ {
+ return &operator*();
+ }
- vlanID = htole16(vlanID);
+ private:
+ ObjectLookupCache& container;
+ PropertiesCache::iterator ret;
+ };
- 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
- }
+ iterator begin() noexcept
+ {
+ return iterator(objs.begin(), *this);
+ }
- 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);
- }
- }
- break;
+ iterator end() noexcept
+ {
+ return iterator(objs.end(), *this);
+ }
- default:
- rc = IPMI_CC_PARM_OUT_OF_RANGE;
+ 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())
+ {
+ return it;
}
+ auto properties = getAllDbusProperties(bus, params.service, path, intf);
+ return cache.insert({path, std::move(properties)}).first;
}
- catch (InternalFailure& e)
+};
+
+/** @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)
{
- commit<InternalFailure>();
- rc = IPMI_CC_UNSPECIFIED_ERROR;
- return rc;
+ const auto& addrStr = std::get<std::string>(properties.at("Address"));
+ auto addr = maybeStringToAddr<family>(addrStr.c_str());
+ if (!addr)
+ {
+ continue;
+ }
+
+ IP::AddressOrigin origin = IP::convertAddressOriginFromString(
+ std::get<std::string>(properties.at("Origin")));
+ if (origins.find(origin) == origins.end())
+ {
+ continue;
+ }
+
+ if (idx > 0)
+ {
+ idx--;
+ continue;
+ }
+
+ 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 rc;
+
+ return std::nullopt;
}
-namespace cipher
+/** @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);
+}
-std::vector<uint8_t> getCipherList()
+/** @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)
{
- std::vector<uint8_t> cipherList;
-
- std::ifstream jsonFile(configFile);
- if (!jsonFile.is_open())
+ if (path.empty())
{
- log<level::ERR>("Channel Cipher suites file not found");
- elog<InternalFailure>();
+ return;
}
-
- auto data = Json::parse(jsonFile, nullptr, false);
- if (data.is_discarded())
+ try
{
- log<level::ERR>("Parsing channel cipher suites JSON failed");
- elog<InternalFailure>();
+ auto req = bus.new_method_call(service.c_str(), path.c_str(),
+ ipmi::DELETE_INTERFACE, "Delete");
+ bus.call_noreply(req);
}
-
- // Byte 1 is reserved
- cipherList.push_back(0x00);
-
- for (const auto& record : data)
+ catch (const sdbusplus::exception::SdBusError& e)
{
- cipherList.push_back(record.value(cipher, 0));
+ if (strcmp(e.name(), "org.freedesktop.DBus.Error.UnknownObject") != 0)
+ {
+ // We want to rethrow real errors
+ throw;
+ }
}
+}
- return cipherList;
+/** @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)
+{
+ 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);
}
-} // namespace cipher
+/** @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)
+{
+ return getIfAddr<AF_INET>(bus, params, 0, originsV4);
+}
-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 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)
{
- // Status code.
- ipmi_ret_t rc = IPMI_CC_INVALID;
- *data_len = 0;
- return rc;
+ auto ifaddr = getIfAddr4(bus, params);
+ if (!ifaddr && !address)
+ {
+ 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));
}
-struct set_lan_t
+template <int family>
+std::optional<IfNeigh<family>>
+ findStaticNeighbor(sdbusplus::bus::bus& bus, const ChannelParams& params,
+ const typename AddrFamily<family>::addr& ip,
+ ObjectLookupCache& neighbors)
{
- uint8_t channel;
- uint8_t parameter;
- uint8_t data[8]; // Per IPMI spec, not expecting more than this size
-} __attribute__((packed));
+ const auto state =
+ sdbusplus::xyz::openbmc_project::Network::server::convertForMessage(
+ Neighbor::State::Permanent);
+ for (const auto& [path, neighbor] : neighbors)
+ {
+ const auto& ipStr = std::get<std::string>(neighbor.at("IPAddress"));
+ auto neighIP = maybeStringToAddr<family>(ipStr.c_str());
+ if (!neighIP)
+ {
+ continue;
+ }
+ if (!equal(*neighIP, ip))
+ {
+ continue;
+ }
+ if (state != std::get<std::string>(neighbor.at("State")))
+ {
+ continue;
+ }
+
+ IfNeigh<family> ret;
+ ret.path = path;
+ ret.ip = ip;
+ const auto& macStr = std::get<std::string>(neighbor.at("MACAddress"));
+ ret.mac = stringToMAC(macStr.c_str());
+ return std::move(ret);
+ }
+
+ return std::nullopt;
+}
-ipmi_ret_t checkAndUpdateNetwork(int channel)
+template <int family>
+void createNeighbor(sdbusplus::bus::bus& bus, const ChannelParams& params,
+ const typename AddrFamily<family>::addr& address,
+ 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
+ auto newreq =
+ bus.new_method_call(params.service.c_str(), params.logicalPath.c_str(),
+ INTF_NEIGHBOR_CREATE_STATIC, "Neighbor");
+ std::string macStr = ether_ntoa(&mac);
+ newreq.append(addrToString<family>(address), macStr);
+ bus.call_noreply(newreq);
+}
- // Skip the timer. Expecting more update as we are in SET_IN_PROGRESS
- if (channelConf->lan_set_in_progress == SET_IN_PROGRESS)
+/** @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)
+{
+ // Save the old gateway MAC address if it exists so we can recreate it
+ auto gateway = getGatewayProperty<family>(bus, params);
+ std::optional<IfNeigh<family>> neighbor;
+ if (gateway)
{
- return IPMI_CC_OK;
+ ObjectLookupCache neighbors(bus, params, INTF_NEIGHBOR);
+ neighbor = findStaticNeighbor<family>(bus, params, *gateway, neighbors);
}
- // 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)
+ setDbusProperty(bus, params.service, PATH_SYSTEMCONFIG, INTF_SYSTEMCONFIG,
+ AddrFamily<family>::propertyGateway,
+ addrToString<family>(address));
+
+ // Restore the gateway MAC if we had one
+ if (neighbor)
{
- log<level::ERR>("Network timer is not instantiated");
- return IPMI_CC_UNSPECIFIED_ERROR;
+ deleteObjectIfExists(bus, params.service, neighbor->path);
+ createNeighbor<family>(bus, params, address, neighbor->mac);
}
- // start the timer.
- networkTimer->start(networkTimeout);
- return IPMI_CC_OK;
}
-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)
+template <int family>
+std::optional<IfNeigh<family>> findGatewayNeighbor(sdbusplus::bus::bus& bus,
+ const ChannelParams& params,
+ ObjectLookupCache& neighbors)
{
- ipmi_ret_t rc = IPMI_CC_OK;
- *data_len = 0;
+ auto gateway = getGatewayProperty<family>(bus, params);
+ if (!gateway)
+ {
+ return std::nullopt;
+ }
- char ipaddr[INET_ADDRSTRLEN];
- char netmask[INET_ADDRSTRLEN];
- char gateway[INET_ADDRSTRLEN];
+ return findStaticNeighbor<family>(bus, params, *gateway, neighbors);
+}
- auto reqptr = reinterpret_cast<const set_lan_t*>(request);
- sdbusplus::bus::bus bus(ipmid_get_sd_bus_connection());
+template <int family>
+std::optional<IfNeigh<family>> getGatewayNeighbor(sdbusplus::bus::bus& bus,
+ const ChannelParams& params)
+{
+ ObjectLookupCache neighbors(bus, params, INTF_NEIGHBOR);
+ return findGatewayNeighbor<family>(bus, params, neighbors);
+}
- // channel number is the lower nibble
- int channel = reqptr->channel & CHANNEL_MASK;
- auto ethdevice = ipmi::getChannelName(channel);
- if (ethdevice.empty())
+template <int family>
+void reconfigureGatewayMAC(sdbusplus::bus::bus& bus,
+ const ChannelParams& params, const ether_addr& mac)
+{
+ auto gateway = getGatewayProperty<family>(bus, params);
+ if (!gateway)
{
- return IPMI_CC_INVALID_FIELD_REQUEST;
+ log<level::ERR>("Tried to set Gateway MAC without Gateway");
+ elog<InternalFailure>();
}
- auto channelConf = getChannelConfig(channel);
- switch (static_cast<LanParam>(reqptr->parameter))
+ ObjectLookupCache neighbors(bus, params, INTF_NEIGHBOR);
+ auto neighbor =
+ findStaticNeighbor<family>(bus, params, *gateway, neighbors);
+ if (neighbor)
{
- 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]);
+ deleteObjectIfExists(bus, params.service, neighbor->path);
+ }
- channelConf->ipaddr.assign(ipaddr);
- }
- break;
+ createNeighbor<family>(bus, params, *gateway, mac);
+}
- case LanParam::IPSRC:
- {
- uint8_t ipsrc{};
- std::memcpy(&ipsrc, reqptr->data, ipmi::network::IPSRC_SIZE_BYTE);
- channelConf->ipsrc = static_cast<ipmi::network::IPOrigin>(ipsrc);
- }
- break;
+/** @brief Deconfigures the IPv6 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] idx - The address index to operate on
+ */
+void deconfigureIfAddr6(sdbusplus::bus::bus& bus, const ChannelParams& params,
+ uint8_t idx)
+{
+ auto ifaddr = getIfAddr<AF_INET6>(bus, params, idx, originsV6Static);
+ if (ifaddr)
+ {
+ deleteObjectIfExists(bus, params.service, ifaddr->path);
+ }
+}
- case LanParam::MAC:
+/** @brief Reconfigures the IPv6 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] idx - The address index to operate on
+ * @param[in] address - The new address
+ * @param[in] prefix - The new address prefix
+ */
+void reconfigureIfAddr6(sdbusplus::bus::bus& bus, const ChannelParams& params,
+ uint8_t idx, const in6_addr& address, uint8_t prefix)
+{
+ deconfigureIfAddr6(bus, params, idx);
+ createIfAddr<AF_INET6>(bus, params, address, prefix);
+}
+
+/** @brief Converts the AddressOrigin into an IPv6Source
+ *
+ * @param[in] origin - The DBus Address Origin to convert
+ * @return The IPv6Source version of the origin
+ */
+IPv6Source originToSourceType(IP::AddressOrigin origin)
+{
+ switch (origin)
+ {
+ case IP::AddressOrigin::Static:
+ return IPv6Source::Static;
+ case IP::AddressOrigin::DHCP:
+ return IPv6Source::DHCP;
+ case IP::AddressOrigin::SLAAC:
+ return IPv6Source::SLAAC;
+ default:
{
- char mac[SIZE_MAC];
+ auto originStr = sdbusplus::xyz::openbmc_project::Network::server::
+ convertForMessage(origin);
+ log<level::ERR>(
+ "Invalid IP::AddressOrigin conversion to IPv6Source",
+ entry("ORIGIN=%s", originStr.c_str()));
+ elog<InternalFailure>();
+ }
+ }
+}
- 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]);
+/** @brief Packs the IPMI message response with IPv6 address data
+ *
+ * @param[out] ret - The IPMI response payload to be packed
+ * @param[in] channel - The channel id corresponding to an ethernet interface
+ * @param[in] set - The set selector for determining address index
+ * @param[in] origins - Set of valid origins for address filtering
+ */
+void getLanIPv6Address(message::Payload& ret, uint8_t channel, uint8_t set,
+ const std::unordered_set<IP::AddressOrigin>& origins)
+{
+ auto source = IPv6Source::Static;
+ bool enabled = false;
+ in6_addr addr{};
+ uint8_t prefix = AddrFamily<AF_INET6>::defaultPrefix;
+ auto status = IPv6AddressStatus::Disabled;
+
+ auto ifaddr = channelCall<getIfAddr<AF_INET6>>(channel, set, origins);
+ if (ifaddr)
+ {
+ source = originToSourceType(ifaddr->origin);
+ enabled = true;
+ addr = ifaddr->address;
+ prefix = ifaddr->prefix;
+ status = IPv6AddressStatus::Active;
+ }
- auto macObjectInfo =
- ipmi::getDbusObject(bus, ipmi::network::MAC_INTERFACE,
- ipmi::network::ROOT, ethdevice);
+ ret.pack(set);
+ ret.pack(static_cast<uint4_t>(source), uint3_t{}, enabled);
+ ret.pack(std::string_view(reinterpret_cast<char*>(&addr), sizeof(addr)));
+ ret.pack(prefix);
+ ret.pack(static_cast<uint8_t>(status));
+}
- ipmi::setDbusProperty(
- bus, macObjectInfo.second, macObjectInfo.first,
- ipmi::network::MAC_INTERFACE, "MACAddress", std::string(mac));
+/** @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)
+ {
+ return 0;
+ }
- channelConf->macAddress = mac;
- }
- break;
+ 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;
+}
- case LanParam::SUBNET:
+/** @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)
+ {
+ if (path.find(params.ifname) == path.npos)
{
- 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);
+ continue;
}
- break;
-
- case LanParam::GATEWAY:
+ for (const auto& [service, intfs] : impls)
{
- 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);
+ deleteObjectIfExists(bus, service, path);
}
- break;
-
- case LanParam::VLAN:
+ // Update params to reflect the deletion of vlan
+ if (path == params.logicalPath)
{
- 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);
- channelConf->vlanID = vlan;
+ params.logicalPath = params.ifPath;
}
- break;
+ }
- case LanParam::INPROGRESS:
- {
- if (reqptr->data[0] == SET_COMPLETE)
- {
- channelConf->lan_set_in_progress = SET_COMPLETE;
+ // Clear out any settings on the lower physical interface
+ setDHCPProperty(bus, params, false);
+}
- 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;
- }
- }
- break;
+/** @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;
+ }
- default:
+ 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);
+ std::vector<IfAddr<AF_INET6>> ifaddrs6;
+ for (uint8_t i = 0; i < MAX_IPV6_STATIC_ADDRESSES; ++i)
+ {
+ auto ifaddr6 =
+ findIfAddr<AF_INET6>(bus, params, i, originsV6Static, ips);
+ if (!ifaddr6)
{
- rc = IPMI_CC_PARM_NOT_SUPPORTED;
- return rc;
+ break;
}
+ ifaddrs6.push_back(std::move(*ifaddr6));
}
- rc = checkAndUpdateNetwork(channel);
+ auto dhcp = getDHCPProperty(bus, params);
+ ObjectLookupCache neighbors(bus, params, INTF_NEIGHBOR);
+ auto neighbor4 = findGatewayNeighbor<AF_INET>(bus, params, neighbors);
+ auto neighbor6 = findGatewayNeighbor<AF_INET6>(bus, params, neighbors);
+
+ deconfigureChannel(bus, params);
+ createVLAN(bus, params, vlan);
- return rc;
+ // Re-establish the saved settings
+ setDHCPProperty(bus, params, dhcp);
+ if (ifaddr4)
+ {
+ createIfAddr<AF_INET>(bus, params, ifaddr4->address, ifaddr4->prefix);
+ }
+ for (const auto& ifaddr6 : ifaddrs6)
+ {
+ createIfAddr<AF_INET6>(bus, params, ifaddr6.address, ifaddr6.prefix);
+ }
+ if (neighbor4)
+ {
+ createNeighbor<AF_INET>(bus, params, neighbor4->ip, neighbor4->mac);
+ }
+ if (neighbor6)
+ {
+ createNeighbor<AF_INET6>(bus, params, neighbor6->ip, neighbor6->mac);
+ }
}
-struct get_lan_t
+/** @brief Turns a prefix into a netmask
+ *
+ * @param[in] prefix - The prefix length
+ * @return The netmask
+ */
+in_addr prefixToNetmask(uint8_t prefix)
{
- uint8_t rev_channel;
- uint8_t parameter;
- uint8_t parameter_set;
- uint8_t parameter_block;
-} __attribute__((packed));
+ if (prefix > 32)
+ {
+ 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))};
+}
-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 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)
{
- ipmi_ret_t rc = IPMI_CC_OK;
- *data_len = 0;
- const uint8_t current_revision = 0x11; // Current rev per IPMI Spec 2.0
-
- get_lan_t* reqptr = (get_lan_t*)request;
- // channel number is the lower nibble
- int channel = reqptr->rev_channel & CHANNEL_MASK;
+ 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 static_cast<bool>(x)
+ ? AddrFamily<AF_INET>::defaultPrefix - __builtin_ctz(x)
+ : 0;
+}
- if (reqptr->rev_channel & 0x80) // Revision is bit 7
+// 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())
{
- // Only current revision was requested
- *data_len = sizeof(current_revision);
- std::memcpy(response, &current_revision, *data_len);
- return IPMI_CC_OK;
+ return it->second;
}
+ return setStatus[channel] = SetStatus::Complete;
+}
- static std::vector<uint8_t> cipherList;
- static auto listInit = false;
+/**
+ * Define placeholder command handlers for the OEM Extension bytes for the Set
+ * LAN Configuration Parameters and Get LAN Configuration Parameters
+ * commands. Using "weak" linking allows the placeholder setLanOem/getLanOem
+ * functions below to be overridden.
+ * To create handlers for your own proprietary command set:
+ * Create/modify a phosphor-ipmi-host Bitbake append file within your Yocto
+ * recipe
+ * Create C++ file(s) that define IPMI handler functions matching the
+ * function names below (i.e. setLanOem). The default name for the
+ * transport IPMI commands is transporthandler_oem.cpp.
+ * Add:
+ * EXTRA_OECONF_append = " --enable-transport-oem=yes"
+ * Create a do_compile_prepend()/do_install_append method in your
+ * bbappend file to copy the file to the build directory.
+ * Add:
+ * PROJECT_SRC_DIR := "${THISDIR}/${PN}"
+ * # Copy the "strong" functions into the working directory, overriding the
+ * # placeholder functions.
+ * do_compile_prepend(){
+ * cp -f ${PROJECT_SRC_DIR}/transporthandler_oem.cpp ${S}
+ * }
+ *
+ * # Clean up after complilation has completed
+ * do_install_append(){
+ * rm -f ${S}/transporthandler_oem.cpp
+ * }
+ *
+ */
+
+/**
+ * Define the placeholder OEM commands as having weak linkage. Create
+ * setLanOem, and getLanOem functions in the transporthandler_oem.cpp
+ * file. The functions defined there must not have the "weak" attribute
+ * applied to them.
+ */
+RspType<> setLanOem(uint8_t channel, uint8_t parameter, message::Payload& req)
+ __attribute__((weak));
+RspType<message::Payload> getLanOem(uint8_t channel, uint8_t parameter,
+ uint8_t set, uint8_t block)
+ __attribute__((weak));
+
+RspType<> setLanOem(uint8_t channel, uint8_t parameter, message::Payload& req)
+{
+ req.trailingOk = true;
+ return response(ccParamNotSupported);
+}
- if (!listInit)
+RspType<message::Payload> getLanOem(uint8_t channel, uint8_t parameter,
+ uint8_t set, uint8_t block)
+{
+ return response(ccParamNotSupported);
+}
+/**
+ * @brief is MAC address valid.
+ *
+ * This function checks whether the MAC address is valid or not.
+ *
+ * @param[in] mac - MAC address.
+ * @return true if MAC address is valid else retun false.
+ **/
+bool isValidMACAddress(const ether_addr& mac)
+{
+ // check if mac address is empty
+ if (equal(mac, ether_addr{}))
{
- try
- {
- cipherList = cipher::getCipherList();
- listInit = true;
- }
- catch (const std::exception& e)
- {
- return IPMI_CC_UNSPECIFIED_ERROR;
- }
+ return false;
+ }
+ // we accept only unicast MAC addresses and same thing has been checked in
+ // phosphor-network layer. If the least significant bit of the first octet
+ // is set to 1, it is multicast MAC else it is unicast MAC address.
+ if (mac.ether_addr_octet[0] & 1)
+ {
+ return false;
}
+ return true;
+}
- auto ethdevice = ipmi::getChannelName(channel);
- if (ethdevice.empty())
+RspType<> setLan(uint4_t channelBits, uint4_t, uint8_t parameter,
+ message::Payload& req)
+{
+ auto channel = static_cast<uint8_t>(channelBits);
+ if (!doesDeviceExist(channel))
{
- return IPMI_CC_INVALID_FIELD_REQUEST;
+ req.trailingOk = true;
+ return responseInvalidFieldRequest();
}
- auto channelConf = getChannelConfig(channel);
- LanParam param = static_cast<LanParam>(reqptr->parameter);
- switch (param)
+ switch (static_cast<LanParam>(parameter))
{
- case LanParam::INPROGRESS:
+ 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();
+ }
+ if (rsvd)
+ {
+ return responseInvalidFieldRequest();
+ }
+ 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(ccParamReadOnly);
}
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)
+ if (channelCall<getDHCPProperty>(channel))
+ {
+ return responseCommandNotAvailable();
+ }
+ in_addr ip;
+ std::array<uint8_t, sizeof(ip)> bytes;
+ if (req.unpack(bytes) != 0 || !req.fullyUnpacked())
{
- if (param == LanParam::MAC)
+ 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())
+ {
+ return responseReqDataLenInvalid();
+ }
+ if (rsvd)
+ {
+ return responseInvalidFieldRequest();
+ }
+ 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);
+
+ if (!isValidMACAddress(mac))
+ {
+ return responseInvalidFieldRequest();
+ }
+ channelCall<setMACProperty>(channel, mac);
+ return responseSuccess();
+ }
+ case LanParam::SubnetMask:
+ {
+ if (channelCall<getDHCPProperty>(channel))
+ {
+ return responseCommandNotAvailable();
+ }
+ in_addr netmask;
+ std::array<uint8_t, sizeof(netmask)> bytes;
+ if (req.unpack(bytes) != 0 || !req.fullyUnpacked())
+ {
+ return responseReqDataLenInvalid();
+ }
+ copyInto(netmask, bytes);
+ channelCall<reconfigureIfAddr4>(channel, std::nullopt,
+ netmaskToPrefix(netmask));
+ return responseSuccess();
+ }
+ case LanParam::Gateway1:
+ {
+ if (channelCall<getDHCPProperty>(channel))
+ {
+ return responseCommandNotAvailable();
+ }
+ in_addr gateway;
+ std::array<uint8_t, sizeof(gateway)> bytes;
+ if (req.unpack(bytes) != 0 || !req.fullyUnpacked())
+ {
+ return responseReqDataLenInvalid();
+ }
+ copyInto(gateway, bytes);
+ channelCall<setGatewayProperty<AF_INET>>(channel, gateway);
+ return responseSuccess();
+ }
+ case LanParam::Gateway1MAC:
+ {
+ ether_addr gatewayMAC;
+ std::array<uint8_t, sizeof(gatewayMAC)> bytes;
+ if (req.unpack(bytes) != 0 || !req.fullyUnpacked())
+ {
+ return responseReqDataLenInvalid();
+ }
+ copyInto(gatewayMAC, bytes);
+ channelCall<reconfigureGatewayMAC<AF_INET>>(channel, gatewayMAC);
+ return responseSuccess();
}
- case LanParam::VLAN:
+ case LanParam::VLANId:
{
- uint8_t buf[ipmi::network::VLAN_SIZE_BYTE + 1] = {};
+ uint12_t vlanData = 0;
+ uint3_t reserved = 0;
+ bool vlanEnable = 0;
- *data_len = sizeof(current_revision);
- std::memcpy(buf, &current_revision, *data_len);
- if (getNetworkData(reqptr->parameter, &buf[1], channel) ==
- IPMI_CC_OK)
+ if (req.unpack(vlanData) || req.unpack(reserved) ||
+ req.unpack(vlanEnable) || !req.fullyUnpacked())
{
- *data_len = sizeof(buf);
- std::memcpy(response, &buf, *data_len);
+ return responseReqDataLenInvalid();
}
- break;
+
+ if (reserved)
+ {
+ return responseInvalidFieldRequest();
+ }
+
+ uint16_t vlan = static_cast<uint16_t>(vlanData);
+
+ if (!vlanEnable)
+ {
+ lastDisabledVlan[channel] = vlan;
+ vlan = 0;
+ }
+ channelCall<reconfigureVLAN>(channel, vlan);
+
+ return responseSuccess();
}
- case LanParam::IPSRC:
+ case LanParam::CiphersuiteSupport:
+ case LanParam::CiphersuiteEntries:
+ case LanParam::IPFamilySupport:
{
- 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)
+ req.trailingOk = true;
+ return response(ccParamReadOnly);
+ }
+ case LanParam::IPFamilyEnables:
+ {
+ uint8_t enables;
+ if (req.unpack(enables) != 0 || !req.fullyUnpacked())
{
- *data_len = sizeof(buff);
- std::memcpy(response, &buff, *data_len);
+ return responseReqDataLenInvalid();
}
- break;
+ switch (static_cast<IPFamilyEnables>(enables))
+ {
+ case IPFamilyEnables::DualStack:
+ return responseSuccess();
+ case IPFamilyEnables::IPv4Only:
+ case IPFamilyEnables::IPv6Only:
+ return response(ccParamNotSupported);
+ }
+ return response(ccParamNotSupported);
}
- case LanParam::CIPHER_SUITE_COUNT:
+ case LanParam::IPv6Status:
{
- *(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;
+ req.trailingOk = true;
+ return response(ccParamReadOnly);
}
- case LanParam::CIPHER_SUITE_ENTRIES:
+ case LanParam::IPv6StaticAddresses:
{
- *(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;
+ uint8_t set;
+ uint7_t rsvd;
+ bool enabled;
+ in6_addr ip;
+ std::array<uint8_t, sizeof(ip)> ipbytes;
+ uint8_t prefix;
+ uint8_t status;
+ if (req.unpack(set, rsvd, enabled, ipbytes, prefix, status) != 0 ||
+ !req.fullyUnpacked())
+ {
+ return responseReqDataLenInvalid();
+ }
+ if (rsvd)
+ {
+ return responseInvalidFieldRequest();
+ }
+ copyInto(ip, ipbytes);
+ if (enabled)
+ {
+ channelCall<reconfigureIfAddr6>(channel, set, ip, prefix);
+ }
+ else
+ {
+ channelCall<deconfigureIfAddr6>(channel, set);
+ }
+ return responseSuccess();
+ }
+ case LanParam::IPv6DynamicAddresses:
+ {
+ req.trailingOk = true;
+ return response(ccParamReadOnly);
+ }
+ case LanParam::IPv6RouterControl:
+ {
+ std::bitset<8> control;
+ if (req.unpack(control) != 0 || !req.fullyUnpacked())
+ {
+ return responseReqDataLenInvalid();
+ }
+ std::bitset<8> expected;
+ if (channelCall<getDHCPProperty>(channel))
+ {
+ expected[IPv6RouterControlFlag::Dynamic] = 1;
+ }
+ else
+ {
+ expected[IPv6RouterControlFlag::Static] = 1;
+ }
+ if (expected != control)
+ {
+ return responseInvalidFieldRequest();
+ }
+ return responseSuccess();
+ }
+ case LanParam::IPv6StaticRouter1IP:
+ {
+ in6_addr gateway;
+ std::array<uint8_t, sizeof(gateway)> bytes;
+ if (req.unpack(bytes) != 0 || !req.fullyUnpacked())
+ {
+ return responseReqDataLenInvalid();
+ }
+ copyInto(gateway, bytes);
+ channelCall<setGatewayProperty<AF_INET6>>(channel, gateway);
+ return responseSuccess();
+ }
+ case LanParam::IPv6StaticRouter1MAC:
+ {
+ ether_addr mac;
+ std::array<uint8_t, sizeof(mac)> bytes;
+ if (req.unpack(bytes) != 0 || !req.fullyUnpacked())
+ {
+ return responseReqDataLenInvalid();
+ }
+ copyInto(mac, bytes);
+ channelCall<reconfigureGatewayMAC<AF_INET6>>(channel, mac);
+ return responseSuccess();
+ }
+ case LanParam::IPv6StaticRouter1PrefixLength:
+ {
+ uint8_t prefix;
+ if (req.unpack(prefix) != 0 || !req.fullyUnpacked())
+ {
+ return responseReqDataLenInvalid();
+ }
+ if (prefix != 0)
+ {
+ return responseInvalidFieldRequest();
+ }
+ return responseSuccess();
+ }
+ case LanParam::IPv6StaticRouter1PrefixValue:
+ {
+ std::array<uint8_t, sizeof(in6_addr)> bytes;
+ if (req.unpack(bytes) != 0 || !req.fullyUnpacked())
+ {
+ return responseReqDataLenInvalid();
+ }
+ // Accept any prefix value since our prefix length has to be 0
+ return responseSuccess();
}
- default:
- log<level::ERR>("Unsupported parameter",
- entry("PARAMETER=0x%x", reqptr->parameter));
- rc = IPMI_CC_PARM_NOT_SUPPORTED;
}
- return rc;
+ if ((parameter >= oemCmdStart) && (parameter <= oemCmdEnd))
+ {
+ return setLanOem(channel, parameter, req);
+ }
+
+ 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());
+ return responseInvalidFieldRequest();
+ }
- 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)
+ static std::vector<uint8_t> cipherList;
+ static bool listInit = false;
+ if (!listInit)
+ {
+ try
{
- // 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;
+ cipherList = cipher::getCipherList();
+ listInit = true;
}
-
- // if the asked ip src is DHCP then not interested in
- // any given data except vlan.
- if (channelConf->ipsrc != ipmi::network::IPOrigin::DHCP)
+ catch (const std::exception& e)
{
- // 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.
+ switch (static_cast<LanParam>(parameter))
+ {
+ case LanParam::SetStatus:
+ {
+ 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;
+ status = setStatus.at(channel);
}
- catch (InternalFailure& e)
+ catch (const std::out_of_range&)
{
- // 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::Complete;
}
-
- // get the configured mode on the system.
- auto enableDHCP = variant_ns::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)
+ 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)
{
- channelConf->ipsrc = (enableDHCP)
- ? ipmi::network::IPOrigin::DHCP
- : ipmi::network::IPOrigin::STATIC;
+ addr = ifaddr->address;
}
-
- // 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(dataRef(addr));
+ return responseSuccess(std::move(ret));
+ }
+ case LanParam::IPSrc:
+ {
+ auto src = IPSrc::Static;
+ if (channelCall<getDHCPProperty>(channel))
{
- // convert mask into prefix
- ipaddress = channelConf->ipaddr;
- prefix = ipmi::network::toPrefix(AF_INET, channelConf->netmask);
- gateway = channelConf->gateway;
+ src = IPSrc::DHCP;
}
- else // asked ip src = static and configured system src = static
- // or partially given data.
+ ret.pack(static_cast<uint4_t>(src), uint4_t{});
+ return responseSuccess(std::move(ret));
+ }
+ case LanParam::MAC:
+ {
+ ether_addr mac = channelCall<getMACProperty>(channel);
+ ret.pack(dataRef(mac));
+ return responseSuccess(std::move(ret));
+ }
+ case LanParam::SubnetMask:
+ {
+ auto ifaddr = channelCall<getIfAddr4>(channel);
+ uint8_t prefix = AddrFamily<AF_INET>::defaultPrefix;
+ if (ifaddr)
{
- // 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()
- ? variant_ns::get<std::string>(
- properties["Address"])
- : channelConf->ipaddr;
-
- prefix = channelConf->netmask.empty()
- ? variant_ns::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()
- ? variant_ns::get<std::string>(
- systemProperties["DefaultGateway"])
- : channelConf->gateway;
+ prefix = ifaddr->prefix;
}
+ in_addr netmask = prefixToNetmask(prefix);
+ ret.pack(dataRef(netmask));
+ 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::Gateway1:
{
- 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;
+ auto gateway =
+ channelCall<getGatewayProperty<AF_INET>>(channel).value_or(
+ in_addr{});
+ ret.pack(dataRef(gateway));
+ return responseSuccess(std::move(ret));
}
-
- if (channelConf->ipsrc == ipmi::network::IPOrigin::DHCP)
+ case LanParam::Gateway1MAC:
{
- ipmi::setDbusProperty(
- bus, ipmi::network::SERVICE, networkInterfacePath,
- ipmi::network::ETHERNET_INTERFACE, "DHCPEnabled", true);
+ ether_addr mac{};
+ auto neighbor = channelCall<getGatewayNeighbor<AF_INET>>(channel);
+ if (neighbor)
+ {
+ mac = neighbor->mac;
+ }
+ ret.pack(dataRef(mac));
+ return responseSuccess(std::move(ret));
}
- else
+ case LanParam::VLANId:
{
- // change the mode to static
- ipmi::setDbusProperty(
- bus, ipmi::network::SERVICE, networkInterfacePath,
- ipmi::network::ETHERNET_INTERFACE, "DHCPEnabled", false);
-
- if (!ipaddress.empty())
+ 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>();
- }
-
- channelConf->clear();
-}
-
-void commitNetworkChanges()
-{
- for (const auto& channel : channelConfig)
- {
- if (channel.second->flush)
+ case LanParam::CiphersuiteSupport:
+ {
+ if (!listInit)
+ {
+ return responseUnspecifiedError();
+ }
+ ret.pack(static_cast<uint8_t>(cipherList.size() - 1));
+ return responseSuccess(std::move(ret));
+ }
+ case LanParam::CiphersuiteEntries:
+ {
+ if (!listInit)
+ {
+ return responseUnspecifiedError();
+ }
+ ret.pack(cipherList);
+ return responseSuccess(std::move(ret));
+ }
+ case LanParam::IPFamilySupport:
+ {
+ std::bitset<8> support;
+ support[IPFamilySupportFlag::IPv6Only] = 0;
+ support[IPFamilySupportFlag::DualStack] = 1;
+ support[IPFamilySupportFlag::IPv6Alerts] = 1;
+ ret.pack(support);
+ return responseSuccess(std::move(ret));
+ }
+ case LanParam::IPFamilyEnables:
+ {
+ ret.pack(static_cast<uint8_t>(IPFamilyEnables::DualStack));
+ return responseSuccess(std::move(ret));
+ }
+ case LanParam::IPv6Status:
{
- applyChanges(channel.first);
+ ret.pack(MAX_IPV6_STATIC_ADDRESSES);
+ ret.pack(MAX_IPV6_DYNAMIC_ADDRESSES);
+ std::bitset<8> support;
+ support[IPv6StatusFlag::DHCP] = 1;
+ support[IPv6StatusFlag::SLAAC] = 1;
+ ret.pack(support);
+ return responseSuccess(std::move(ret));
+ }
+ case LanParam::IPv6StaticAddresses:
+ {
+ if (set >= MAX_IPV6_STATIC_ADDRESSES)
+ {
+ return responseParmOutOfRange();
+ }
+ getLanIPv6Address(ret, channel, set, originsV6Static);
+ return responseSuccess(std::move(ret));
+ }
+ case LanParam::IPv6DynamicAddresses:
+ {
+ if (set >= MAX_IPV6_DYNAMIC_ADDRESSES)
+ {
+ return responseParmOutOfRange();
+ }
+ getLanIPv6Address(ret, channel, set, originsV6Dynamic);
+ return responseSuccess(std::move(ret));
+ }
+ case LanParam::IPv6RouterControl:
+ {
+ std::bitset<8> control;
+ if (channelCall<getDHCPProperty>(channel))
+ {
+ control[IPv6RouterControlFlag::Dynamic] = 1;
+ }
+ else
+ {
+ control[IPv6RouterControlFlag::Static] = 1;
+ }
+ ret.pack(control);
+ return responseSuccess(std::move(ret));
+ }
+ case LanParam::IPv6StaticRouter1IP:
+ {
+ in6_addr gateway{};
+ if (!channelCall<getDHCPProperty>(channel))
+ {
+ gateway =
+ channelCall<getGatewayProperty<AF_INET6>>(channel).value_or(
+ in6_addr{});
+ }
+ ret.pack(dataRef(gateway));
+ return responseSuccess(std::move(ret));
+ }
+ case LanParam::IPv6StaticRouter1MAC:
+ {
+ ether_addr mac{};
+ auto neighbor = channelCall<getGatewayNeighbor<AF_INET6>>(channel);
+ if (neighbor)
+ {
+ mac = neighbor->mac;
+ }
+ ret.pack(dataRef(mac));
+ return responseSuccess(std::move(ret));
+ }
+ case LanParam::IPv6StaticRouter1PrefixLength:
+ {
+ ret.pack(UINT8_C(0));
+ return responseSuccess(std::move(ret));
+ }
+ case LanParam::IPv6StaticRouter1PrefixValue:
+ {
+ in6_addr prefix{};
+ ret.pack(dataRef(prefix));
+ return responseSuccess(std::move(ret));
}
}
-}
-void createNetworkTimer()
-{
- if (!networkTimer)
+ if ((parameter >= oemCmdStart) && (parameter <= oemCmdEnd))
{
- std::function<void()> networkTimerCallback(
- std::bind(&commitNetworkChanges));
-
- networkTimer = std::make_unique<phosphor::Timer>(networkTimerCallback);
+ return getLanOem(channel, parameter, set, block);
}
-}
-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);
+ return response(ccParamNotSupported);
+}
- // <Set LAN Configuration Parameters>
- ipmi_register_callback(NETFUN_TRANSPORT, IPMI_CMD_SET_LAN, NULL,
- ipmi_transport_set_lan, PRIVILEGE_ADMIN);
+} // namespace transport
+} // namespace ipmi
- // <Get LAN Configuration Parameters>
- ipmi_register_callback(NETFUN_TRANSPORT, IPMI_CMD_GET_LAN, NULL,
- ipmi_transport_get_lan, PRIVILEGE_OPERATOR);
+void register_netfn_transport_functions() __attribute__((constructor));
- return;
+void register_netfn_transport_functions()
+{
+ 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::Operator, ipmi::transport::getLan);
}
OpenPOWER on IntegriCloud