blob: 49c37aba0dc3277aee711ba4af2f410c8be3729f [file] [log] [blame]
#include "phosphor-ipmi-host/utils.hpp"
#include <arpa/inet.h>
#include <dirent.h>
#include <net/if.h>
#include <cstring>
#include <iostream>
#include <ipmid/api.h>
#include <phosphor-logging/elog-errors.hpp>
#include <phosphor-logging/log.hpp>
#include <xyz/openbmc_project/Common/error.hpp>
namespace ipmi
{
using namespace phosphor::logging;
using namespace sdbusplus::xyz::openbmc_project::Common::Error;
// Parameters
enum class LanParam : uint8_t
{
INPROGRESS = 0,
AUTHSUPPORT = 1,
AUTHENABLES = 2,
IP = 3,
IPSRC = 4,
MAC = 5,
SUBNET = 6,
GATEWAY = 12,
VLAN = 20,
CIPHER_SUITE_COUNT = 22,
CIPHER_SUITE_ENTRIES = 23,
IPV6 = 59,
};
namespace network
{
/** @brief checks if the given ip is Link Local Ip or not.
* @param[in] ipaddress - IPAddress.
*/
bool isLinkLocalIP(const std::string &address)
{
return address.find(IPV4_PREFIX) == 0 || address.find(IPV6_PREFIX) == 0;
}
} // namespace network
// TODO There may be cases where an interface is implemented by multiple
// objects,to handle such cases we are interested on that object
// which are on interested busname.
// Currently mapper doesn't give the readable busname(gives busid) so we can't
// use busname to find the object,will do later once the support is there.
DbusObjectInfo getDbusObject(sdbusplus::bus::bus &bus,
const std::string &interface,
const std::string &serviceRoot,
const std::string &match)
{
std::vector<DbusInterface> interfaces;
interfaces.emplace_back(interface);
auto depth = 0;
auto mapperCall = bus.new_method_call(MAPPER_BUS_NAME, MAPPER_OBJ,
MAPPER_INTF, "GetSubTree");
mapperCall.append(serviceRoot, depth, interfaces);
ObjectTree objectTree;
try
{
auto mapperReply = bus.call(mapperCall);
mapperReply.read(objectTree);
}
catch (sdbusplus::exception_t &)
{
log<level::ERR>("Error in mapper call");
elog<InternalFailure>();
}
if (objectTree.empty())
{
log<level::ERR>("No Object has implemented the interface",
entry("INTERFACE=%s", interface.c_str()));
elog<InternalFailure>();
}
DbusObjectInfo objectInfo;
// if match is empty then return the first object
if (match == "")
{
objectInfo = std::make_pair(
objectTree.begin()->first,
std::move(objectTree.begin()->second.begin()->first));
return objectInfo;
}
// else search the match string in the object path
auto objectFound = false;
for (auto &object : objectTree)
{
if (object.first.find(match) != std::string::npos)
{
objectFound = true;
objectInfo = make_pair(object.first,
std::move(object.second.begin()->first));
break;
}
}
if (!objectFound)
{
log<level::ERR>("Failed to find object which matches",
entry("MATCH=%s", match.c_str()));
elog<InternalFailure>();
}
return objectInfo;
}
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(
sdbusplus::message::variant_ns::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)
{
Value value;
auto method = bus.new_method_call(service.c_str(), objPath.c_str(),
PROP_INTF, METHOD_GET);
method.append(interface, property);
try
{
auto reply = bus.call(method);
reply.read(value);
}
catch (sdbusplus::exception_t &)
{
log<level::ERR>("Failed to get property",
entry("PROPERTY=%s", property.c_str()),
entry("PATH=%s", objPath.c_str()),
entry("INTERFACE=%s", interface.c_str()));
elog<InternalFailure>();
}
return value;
}
PropertyMap getAllDbusProperties(sdbusplus::bus::bus &bus,
const std::string &service,
const std::string &objPath,
const std::string &interface)
{
PropertyMap properties;
auto method = bus.new_method_call(service.c_str(), objPath.c_str(),
PROP_INTF, METHOD_GET_ALL);
method.append(interface);
try
{
auto reply = bus.call(method);
reply.read(properties);
}
catch (sdbusplus::exception_t &)
{
log<level::ERR>("Failed to get all properties",
entry("PATH=%s", objPath.c_str()),
entry("INTERFACE=%s", interface.c_str()));
elog<InternalFailure>();
}
return properties;
}
ObjectValueTree getManagedObjects(sdbusplus::bus::bus &bus,
const std::string &service,
const std::string &objPath)
{
ipmi::ObjectValueTree interfaces;
auto method = bus.new_method_call(service.c_str(), objPath.c_str(),
"org.freedesktop.DBus.ObjectManager",
"GetManagedObjects");
try
{
auto reply = bus.call(method);
reply.read(interfaces);
}
catch (sdbusplus::exception_t &)
{
log<level::ERR>("Failed to get managed objects",
entry("PATH=%s", objPath.c_str()));
elog<InternalFailure>();
}
return interfaces;
}
void setDbusProperty(sdbusplus::bus::bus &bus, const std::string &service,
const std::string &objPath, const std::string &interface,
const std::string &property, const Value &value)
{
auto method = bus.new_method_call(service.c_str(), objPath.c_str(),
PROP_INTF, METHOD_SET);
method.append(interface, property, value);
try
{
bus.call(method);
}
catch (sdbusplus::exception_t &)
{
log<level::ERR>("Failed to set property",
entry("PROPERTY=%s", property.c_str()),
entry("PATH=%s", objPath.c_str()),
entry("INTERFACE=%s", interface.c_str()));
elog<InternalFailure>();
}
}
ServiceCache::ServiceCache(const std::string &intf, const std::string &path) :
intf(intf), path(path), cachedService(std::nullopt),
cachedBusName(std::nullopt)
{
}
ServiceCache::ServiceCache(std::string &&intf, std::string &&path) :
intf(std::move(intf)), path(std::move(path)), cachedService(std::nullopt),
cachedBusName(std::nullopt)
{
}
const std::string &ServiceCache::getService(sdbusplus::bus::bus &bus)
{
if (!isValid(bus))
{
cachedBusName = bus.get_unique_name();
cachedService = ::ipmi::getService(bus, intf, path);
}
return cachedService.value();
}
void ServiceCache::invalidate()
{
cachedBusName = std::nullopt;
cachedService = std::nullopt;
}
sdbusplus::message::message
ServiceCache::newMethodCall(sdbusplus::bus::bus &bus, const char *intf,
const char *method)
{
return bus.new_method_call(getService(bus).c_str(), path.c_str(), intf,
method);
}
bool ServiceCache::isValid(sdbusplus::bus::bus &bus) const
{
return cachedService && cachedBusName == bus.get_unique_name();
}
std::string getService(sdbusplus::bus::bus &bus, const std::string &intf,
const std::string &path)
{
auto mapperCall =
bus.new_method_call("xyz.openbmc_project.ObjectMapper",
"/xyz/openbmc_project/object_mapper",
"xyz.openbmc_project.ObjectMapper", "GetObject");
mapperCall.append(path);
mapperCall.append(std::vector<std::string>({intf}));
std::map<std::string, std::vector<std::string>> mapperResponse;
try
{
auto mapperResponseMsg = bus.call(mapperCall);
mapperResponseMsg.read(mapperResponse);
}
catch (sdbusplus::exception_t &)
{
throw std::runtime_error("ERROR in mapper call");
}
if (mapperResponse.begin() == mapperResponse.end())
{
throw std::runtime_error("ERROR in reading the mapper response");
}
return mapperResponse.begin()->first;
}
ipmi::ObjectTree getAllDbusObjects(sdbusplus::bus::bus &bus,
const std::string &serviceRoot,
const std::string &interface,
const std::string &match)
{
std::vector<std::string> interfaces;
interfaces.emplace_back(interface);
auto depth = 0;
auto mapperCall = bus.new_method_call(MAPPER_BUS_NAME, MAPPER_OBJ,
MAPPER_INTF, "GetSubTree");
mapperCall.append(serviceRoot, depth, interfaces);
ObjectTree objectTree;
try
{
auto mapperReply = bus.call(mapperCall);
mapperReply.read(objectTree);
}
catch (sdbusplus::exception_t &)
{
log<level::ERR>("Error in mapper call",
entry("SERVICEROOT=%s", serviceRoot.c_str()),
entry("INTERFACE=%s", interface.c_str()));
elog<InternalFailure>();
}
for (auto it = objectTree.begin(); it != objectTree.end();)
{
if (it->first.find(match) == std::string::npos)
{
it = objectTree.erase(it);
}
else
{
++it;
}
}
return objectTree;
}
void deleteAllDbusObjects(sdbusplus::bus::bus &bus,
const std::string &serviceRoot,
const std::string &interface,
const std::string &match)
{
try
{
auto objectTree = getAllDbusObjects(bus, serviceRoot, interface, match);
for (auto &object : objectTree)
{
method_no_args::callDbusMethod(bus, object.second.begin()->first,
object.first, DELETE_INTERFACE,
"Delete");
}
}
catch (sdbusplus::exception::exception &e)
{
log<level::INFO>("sdbusplus exception - Unable to delete the objects",
entry("ERROR=%s", e.what()),
entry("INTERFACE=%s", interface.c_str()),
entry("SERVICE=%s", serviceRoot.c_str()));
}
}
namespace variant_ns = sdbusplus::message::variant_ns;
ipmi_ret_t getNetworkData(uint8_t lan_param, char *data)
{
ipmi_ret_t rc = IPMI_CC_OK;
sdbusplus::bus::bus bus(ipmid_get_sd_bus_connection());
const std::string ethdevice = "eth0";
switch (static_cast<LanParam>(lan_param))
{
case LanParam::IP:
{
auto ethIP = ethdevice + "/" + ipmi::network::IP_TYPE;
std::string ipaddress;
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"]);
std::strcpy(data, ipaddress.c_str());
}
break;
case LanParam::IPV6:
{
auto ethIP = ethdevice + "/ipv6";
std::string ipaddress;
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"]);
std::strcpy(data, ipaddress.c_str());
}
break;
case LanParam::MAC:
{
std::string macAddress;
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 = variant_ns::get<std::string>(variant);
sscanf(macAddress.c_str(), ipmi::network::MAC_ADDRESS_FORMAT,
(data), (data + 1), (data + 2), (data + 3), (data + 4),
(data + 5));
std::strcpy(data, macAddress.c_str());
}
break;
default:
rc = IPMI_CC_PARM_OUT_OF_RANGE;
}
return rc;
}
namespace method_no_args
{
void callDbusMethod(sdbusplus::bus::bus &bus, const std::string &service,
const std::string &objPath, const std::string &interface,
const std::string &method)
{
auto busMethod = bus.new_method_call(service.c_str(), objPath.c_str(),
interface.c_str(), method.c_str());
try
{
bus.call(busMethod);
}
catch (sdbusplus::exception_t &)
{
log<level::ERR>("Failed to execute method",
entry("METHOD=%s", method.c_str()),
entry("PATH=%s", objPath.c_str()),
entry("INTERFACE=%s", interface.c_str()));
elog<InternalFailure>();
}
}
} // namespace method_no_args
} // namespace ipmi