blob: acff25166308a40cdbab470746be41870d68b791 [file] [log] [blame]
#include "transporthandler.hpp"
#include "app/channel.hpp"
#include "user_channel/channel_layer.hpp"
#include <arpa/inet.h>
#include <chrono>
#include <filesystem>
#include <fstream>
#include <ipmid/api.hpp>
#include <ipmid/utils.hpp>
#include <phosphor-logging/elog-errors.hpp>
#include <phosphor-logging/log.hpp>
#include <sdbusplus/message/types.hpp>
#include <sdbusplus/timer.hpp>
#include <string>
#include <xyz/openbmc_project/Common/error.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));
struct ChannelConfig_t* getChannelConfig(int channel)
{
auto item = channelConfig.find(channel);
if (item == channelConfig.end())
{
channelConfig[channel] = std::make_unique<struct ChannelConfig_t>();
}
return channelConfig[channel].get();
}
// 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)
{
ipmi_ret_t rc = IPMI_CC_OK;
sdbusplus::bus::bus bus(ipmid_get_sd_bus_connection());
auto ethdevice = ipmi::getChannelName(channel);
// if ethdevice is an empty string they weren't expecting this channel.
if (ethdevice.empty())
{
// TODO: return error from getNetworkData()
return IPMI_CC_INVALID_FIELD_REQUEST;
}
auto ethIP = ethdevice + "/" + ipmi::network::IP_TYPE;
auto channelConf = getChannelConfig(channel);
try
{
switch (static_cast<LanParam>(lan_param))
{
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:
{
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 =
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);
}
else if (channelConf->lan_set_in_progress == SET_IN_PROGRESS)
{
inet_pton(AF_INET, channelConf->netmask.c_str(),
reinterpret_cast<void*>(data));
}
}
break;
case LanParam::GATEWAY:
{
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));
}
break;
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);
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));
}
break;
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);
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);
}
}
break;
default:
rc = IPMI_CC_PARM_OUT_OF_RANGE;
}
}
catch (InternalFailure& e)
{
commit<InternalFailure>();
rc = IPMI_CC_UNSPECIFIED_ERROR;
return rc;
}
return rc;
}
namespace cipher
{
std::vector<uint8_t> getCipherList()
{
std::vector<uint8_t> cipherList;
std::ifstream jsonFile(configFile);
if (!jsonFile.is_open())
{
log<level::ERR>("Channel Cipher suites file not found");
elog<InternalFailure>();
}
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);
for (const auto& record : data)
{
cipherList.push_back(record.value(cipher, 0));
}
return cipherList;
}
} // namespace cipher
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)
{
// Status code.
ipmi_ret_t rc = IPMI_CC_INVALID;
*data_len = 0;
return rc;
}
struct set_lan_t
{
uint8_t channel;
uint8_t parameter;
uint8_t data[8]; // Per IPMI spec, not expecting more than this size
} __attribute__((packed));
ipmi_ret_t checkAndUpdateNetwork(int channel)
{
auto channelConf = getChannelConfig(channel);
using namespace std::chrono_literals;
// time to wait before applying the network changes.
constexpr auto networkTimeout = 10000000us; // 10 sec
// Skip the timer. Expecting more update as we are in SET_IN_PROGRESS
if (channelConf->lan_set_in_progress == SET_IN_PROGRESS)
{
return IPMI_CC_OK;
}
// 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)
{
log<level::ERR>("Network timer is not instantiated");
return IPMI_CC_UNSPECIFIED_ERROR;
}
// 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)
{
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());
size_t reqLen = *data_len;
*data_len = 0;
// channel number is the lower nibble
int channel = reqptr->channel & CHANNEL_MASK;
auto ethdevice = ipmi::getChannelName(channel);
ipmi::ChannelInfo chInfo;
ipmi::getChannelInfo(channel, chInfo);
if (ethdevice.empty() ||
chInfo.mediumType !=
static_cast<uint8_t>(ipmi::EChannelMediumType::lan8032))
{
return IPMI_CC_INVALID_FIELD_REQUEST;
}
auto channelConf = getChannelConfig(channel);
switch (static_cast<LanParam>(reqptr->parameter))
{
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]);
channelConf->ipaddr.assign(ipaddr);
}
break;
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;
case LanParam::MAC:
{
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]);
auto macObjectInfo =
ipmi::getDbusObject(bus, ipmi::network::MAC_INTERFACE,
ipmi::network::ROOT, ethdevice);
ipmi::setDbusProperty(
bus, macObjectInfo.second, macObjectInfo.first,
ipmi::network::MAC_INTERFACE, "MACAddress", std::string(mac));
channelConf->macAddress = mac;
}
break;
case LanParam::SUBNET:
{
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);
}
break;
case LanParam::GATEWAY:
{
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);
}
break;
case LanParam::VLAN:
{
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;
}
break;
case LanParam::INPROGRESS:
{
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;
}
}
break;
default:
{
rc = IPMI_CC_PARM_NOT_SUPPORTED;
return rc;
}
}
rc = checkAndUpdateNetwork(channel);
return rc;
}
struct get_lan_t
{
uint8_t rev_channel;
uint8_t parameter;
uint8_t parameter_set;
uint8_t parameter_block;
} __attribute__((packed));
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)
{
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;
ipmi::ChannelInfo chInfo;
ipmi::getChannelInfo(channel, chInfo);
if (chInfo.mediumType !=
static_cast<uint8_t>(ipmi::EChannelMediumType::lan8032))
{
return IPMI_CC_INVALID_FIELD_REQUEST;
}
if (reqptr->rev_channel & 0x80) // Revision is bit 7
{
// Only current revision was requested
*data_len = sizeof(current_revision);
std::memcpy(response, &current_revision, *data_len);
return IPMI_CC_OK;
}
static std::vector<uint8_t> cipherList;
static auto listInit = false;
if (!listInit)
{
try
{
cipherList = cipher::getCipherList();
listInit = true;
}
catch (const std::exception& e)
{
return IPMI_CC_UNSPECIFIED_ERROR;
}
}
auto ethdevice = ipmi::getChannelName(channel);
if (ethdevice.empty())
{
return IPMI_CC_INVALID_FIELD_REQUEST;
}
auto channelConf = getChannelConfig(channel);
LanParam param = static_cast<LanParam>(reqptr->parameter);
switch (param)
{
case LanParam::INPROGRESS:
{
uint8_t buf[] = {current_revision,
channelConf->lan_set_in_progress};
*data_len = sizeof(buf);
std::memcpy(response, &buf, *data_len);
break;
}
case LanParam::AUTHSUPPORT:
{
uint8_t buf[] = {current_revision, 0x04};
*data_len = sizeof(buf);
std::memcpy(response, &buf, *data_len);
break;
}
case LanParam::AUTHENABLES:
{
uint8_t buf[] = {current_revision, 0x04, 0x04, 0x04, 0x04, 0x04};
*data_len = sizeof(buf);
std::memcpy(response, &buf, *data_len);
break;
}
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 (param == LanParam::MAC)
{
*data_len = sizeof(buf);
}
else
{
*data_len = ipmi::network::IPV4_ADDRESS_SIZE_BYTE + 1;
}
std::memcpy(response, &buf, *data_len);
}
else
{
rc = IPMI_CC_UNSPECIFIED_ERROR;
}
break;
}
case LanParam::VLAN:
{
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)
{
*data_len = sizeof(buf);
std::memcpy(response, &buf, *data_len);
}
break;
}
case LanParam::IPSRC:
{
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)
{
*data_len = sizeof(buff);
std::memcpy(response, &buff, *data_len);
}
break;
}
case LanParam::CIPHER_SUITE_COUNT:
{
*(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;
}
case LanParam::CIPHER_SUITE_ENTRIES:
{
*(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;
}
default:
log<level::ERR>("Unsupported parameter",
entry("PARAMETER=0x%x", reqptr->parameter));
rc = IPMI_CC_PARM_NOT_SUPPORTED;
}
return rc;
}
void applyChanges(int channel)
{
std::string ipaddress;
std::string gateway;
uint8_t prefix{};
uint32_t vlanID{};
std::string networkInterfacePath;
ipmi::DbusObjectInfo ipObject;
ipmi::DbusObjectInfo systemObject;
auto ethdevice = ipmi::getChannelName(channel);
if (ethdevice.empty())
{
log<level::ERR>("Unable to get the interface name",
entry("CHANNEL=%d", channel));
return;
}
auto ethIp = ethdevice + "/" + ipmi::network::IP_TYPE;
auto channelConf = getChannelConfig(channel);
try
{
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;
}
// if the asked ip src is DHCP then not interested in
// any given data except vlan.
if (channelConf->ipsrc != ipmi::network::IPOrigin::DHCP)
{
// 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.
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);
}
// 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)
{
channelConf->ipsrc = (enableDHCP)
? ipmi::network::IPOrigin::DHCP
: ipmi::network::IPOrigin::STATIC;
}
// 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
{
// convert mask into prefix
ipaddress = channelConf->ipaddr;
prefix = ipmi::network::toPrefix(AF_INET, channelConf->netmask);
gateway = channelConf->gateway;
}
else // asked ip src = static and configured system src = static
// or partially given data.
{
// 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;
}
}
// 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)
{
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;
}
if (channelConf->ipsrc == ipmi::network::IPOrigin::DHCP)
{
ipmi::setDbusProperty(
bus, ipmi::network::SERVICE, networkInterfacePath,
ipmi::network::ETHERNET_INTERFACE, "DHCPEnabled", true);
}
else
{
// change the mode to static
ipmi::setDbusProperty(
bus, ipmi::network::SERVICE, networkInterfacePath,
ipmi::network::ETHERNET_INTERFACE, "DHCPEnabled", false);
if (!ipaddress.empty())
{
ipmi::network::createIP(bus, ipmi::network::SERVICE,
networkInterfacePath, ipv4Protocol,
ipaddress, prefix);
}
if (!gateway.empty())
{
ipmi::setDbusProperty(bus, systemObject.second,
systemObject.first,
ipmi::network::SYSTEMCONFIG_INTERFACE,
"DefaultGateway", std::string(gateway));
}
}
}
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)
{
applyChanges(channel.first);
}
}
}
void createNetworkTimer()
{
if (!networkTimer)
{
std::function<void()> networkTimerCallback(
std::bind(&commitNetworkChanges));
networkTimer = std::make_unique<phosphor::Timer>(networkTimerCallback);
}
}
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;
}