blob: 4def9fe498be551017eb10eb0e7be8bf8b03096e [file] [log] [blame]
#include "ncsi_util.hpp"
#include <errno.h>
#include <fcntl.h>
#include <linux/mctp.h>
#include <linux/ncsi.h>
#include <netlink/genl/ctrl.h>
#include <netlink/genl/genl.h>
#include <netlink/netlink.h>
#include <unistd.h>
#include <phosphor-logging/lg2.hpp>
#include <optional>
#include <span>
#include <system_error>
#include <vector>
namespace phosphor
{
namespace network
{
namespace ncsi
{
static const char* mctp_iid_path = "/run/ncsi-mctp-iids";
NCSICommand::NCSICommand(uint8_t opcode, uint8_t package,
std::optional<uint8_t> channel,
std::span<unsigned char> payload) :
opcode(opcode), package(package), channel(channel)
{
this->payload.assign(payload.begin(), payload.end());
}
uint8_t NCSICommand::getChannel()
{
return channel.value_or(CHANNEL_ID_NONE);
}
using CallBack = int (*)(struct nl_msg* msg, void* arg);
namespace internal
{
struct NCSIPacketHeader
{
uint8_t MCID;
uint8_t revision;
uint8_t reserved;
uint8_t id;
uint8_t type;
uint8_t channel;
uint16_t length;
uint32_t rsvd[2];
};
struct NCSIResponsePayload
{
uint16_t response;
uint16_t reason;
};
class NetlinkCommand
{
public:
NetlinkCommand() = delete;
~NetlinkCommand() = default;
NetlinkCommand(const NetlinkCommand&) = delete;
NetlinkCommand& operator=(const NetlinkCommand&) = delete;
NetlinkCommand(NetlinkCommand&&) = default;
NetlinkCommand& operator=(NetlinkCommand&&) = default;
NetlinkCommand(
int ncsiCmd, int operation = DEFAULT_VALUE,
std::span<const unsigned char> p = std::span<const unsigned char>()) :
ncsi_cmd(ncsiCmd), operation(operation), payload(p)
{}
int ncsi_cmd;
int operation;
std::span<const unsigned char> payload;
};
using nlMsgPtr = std::unique_ptr<nl_msg, decltype(&::nlmsg_free)>;
using nlSocketPtr = std::unique_ptr<nl_sock, decltype(&::nl_socket_free)>;
struct infoCallBackContext
{
InterfaceInfo* info;
};
CallBack infoCallBack = [](struct nl_msg* msg, void* arg) {
if (arg == nullptr)
{
lg2::error("Internal error: invalid info callback context");
return -1;
}
struct infoCallBackContext* info = (struct infoCallBackContext*)arg;
using namespace phosphor::network::ncsi;
auto nlh = nlmsg_hdr(msg);
struct nlattr* tb[NCSI_ATTR_MAX + 1] = {nullptr};
struct nla_policy ncsiPolicy[NCSI_ATTR_MAX + 1] = {
{NLA_UNSPEC, 0, 0}, {NLA_U32, 0, 0}, {NLA_NESTED, 0, 0},
{NLA_U32, 0, 0}, {NLA_U32, 0, 0},
};
struct nlattr* packagetb[NCSI_PKG_ATTR_MAX + 1] = {nullptr};
struct nla_policy packagePolicy[NCSI_PKG_ATTR_MAX + 1] = {
{NLA_UNSPEC, 0, 0}, {NLA_NESTED, 0, 0}, {NLA_U32, 0, 0},
{NLA_FLAG, 0, 0}, {NLA_NESTED, 0, 0},
};
struct nlattr* channeltb[NCSI_CHANNEL_ATTR_MAX + 1] = {nullptr};
struct nla_policy channelPolicy[NCSI_CHANNEL_ATTR_MAX + 1] = {
{NLA_UNSPEC, 0, 0}, {NLA_NESTED, 0, 0}, {NLA_U32, 0, 0},
{NLA_FLAG, 0, 0}, {NLA_NESTED, 0, 0}, {NLA_UNSPEC, 0, 0},
};
auto ret = genlmsg_parse(nlh, 0, tb, NCSI_ATTR_MAX, ncsiPolicy);
if (!tb[NCSI_ATTR_PACKAGE_LIST])
{
lg2::error("No Packages");
return -1;
}
auto attrTgt = static_cast<nlattr*>(nla_data(tb[NCSI_ATTR_PACKAGE_LIST]));
if (!attrTgt)
{
lg2::error("Package list attribute is null");
return -1;
}
auto rem = nla_len(tb[NCSI_ATTR_PACKAGE_LIST]);
nla_for_each_nested(attrTgt, tb[NCSI_ATTR_PACKAGE_LIST], rem)
{
ret = nla_parse_nested(packagetb, NCSI_PKG_ATTR_MAX, attrTgt,
packagePolicy);
if (ret < 0)
{
lg2::error("Failed to parse package nested");
return -1;
}
PackageInfo pkg;
if (packagetb[NCSI_PKG_ATTR_ID])
{
auto attrID = nla_get_u32(packagetb[NCSI_PKG_ATTR_ID]);
pkg.id = attrID;
}
else
{
lg2::debug("Package with no id");
}
if (packagetb[NCSI_PKG_ATTR_FORCED])
{
pkg.forced = true;
}
auto channelListTarget = static_cast<nlattr*>(
nla_data(packagetb[NCSI_PKG_ATTR_CHANNEL_LIST]));
auto channelrem = nla_len(packagetb[NCSI_PKG_ATTR_CHANNEL_LIST]);
nla_for_each_nested(channelListTarget,
packagetb[NCSI_PKG_ATTR_CHANNEL_LIST], channelrem)
{
ret = nla_parse_nested(channeltb, NCSI_CHANNEL_ATTR_MAX,
channelListTarget, channelPolicy);
if (ret < 0)
{
lg2::error("Failed to parse channel nested");
continue;
}
ChannelInfo chan;
if (channeltb[NCSI_CHANNEL_ATTR_ID])
{
chan.id = nla_get_u32(channeltb[NCSI_CHANNEL_ATTR_ID]);
chan.active = !!channeltb[NCSI_CHANNEL_ATTR_ACTIVE];
chan.forced = !!channeltb[NCSI_CHANNEL_ATTR_FORCED];
}
else
{
lg2::debug("Channel with no ID");
continue;
}
if (channeltb[NCSI_CHANNEL_ATTR_VERSION_MAJOR])
{
chan.version_major =
nla_get_u32(channeltb[NCSI_CHANNEL_ATTR_VERSION_MAJOR]);
}
if (channeltb[NCSI_CHANNEL_ATTR_VERSION_MINOR])
{
chan.version_minor =
nla_get_u32(channeltb[NCSI_CHANNEL_ATTR_VERSION_MINOR]);
}
if (channeltb[NCSI_CHANNEL_ATTR_VERSION_STR])
{
chan.version =
nla_get_string(channeltb[NCSI_CHANNEL_ATTR_VERSION_STR]);
}
if (channeltb[NCSI_CHANNEL_ATTR_LINK_STATE])
{
chan.link_state =
nla_get_u32(channeltb[NCSI_CHANNEL_ATTR_LINK_STATE]);
}
if (channeltb[NCSI_CHANNEL_ATTR_VLAN_LIST])
{
auto vids = channeltb[NCSI_CHANNEL_ATTR_VLAN_LIST];
auto vid = static_cast<nlattr*>(nla_data(vids));
auto len = nla_len(vids);
while (nla_ok(vid, len))
{
auto id = nla_get_u16(vid);
chan.vlan_ids.push_back(id);
vid = nla_next(vid, &len);
}
}
pkg.channels.push_back(chan);
}
info->info->packages.push_back(pkg);
}
return static_cast<int>(NL_STOP);
};
struct sendCallBackContext
{
NCSIResponse resp;
};
CallBack sendCallBack = [](struct nl_msg* msg, void* arg) {
using namespace phosphor::network::ncsi;
auto nlh = nlmsg_hdr(msg);
struct nlattr* tb[NCSI_ATTR_MAX + 1] = {nullptr};
static struct nla_policy ncsiPolicy[NCSI_ATTR_MAX + 1] = {
{NLA_UNSPEC, 0, 0}, {NLA_U32, 0, 0}, {NLA_NESTED, 0, 0},
{NLA_U32, 0, 0}, {NLA_U32, 0, 0}, {NLA_BINARY, 0, 0},
{NLA_FLAG, 0, 0}, {NLA_U32, 0, 0}, {NLA_U32, 0, 0},
};
if (arg == nullptr)
{
lg2::error("Internal error: invalid send callback context");
return -1;
}
struct sendCallBackContext* ctx = (struct sendCallBackContext*)arg;
auto ret = genlmsg_parse(nlh, 0, tb, NCSI_ATTR_MAX, ncsiPolicy);
if (ret)
{
lg2::error("Failed to parse message");
return ret;
}
if (tb[NCSI_ATTR_DATA] == nullptr)
{
lg2::error("Response: No data");
return -1;
}
size_t data_len = nla_len(tb[NCSI_ATTR_DATA]);
unsigned char* data = (unsigned char*)nla_data(tb[NCSI_ATTR_DATA]);
ctx->resp.full_payload.assign(data, data + data_len);
int rc = ctx->resp.parseFullPayload();
if (rc)
{
return -1;
}
return static_cast<int>(NL_STOP);
};
int applyCmd(NetlinkInterface& interface, const NetlinkCommand& cmd,
int package = DEFAULT_VALUE, int channel = DEFAULT_VALUE,
int flags = NONE, CallBack function = nullptr, void* arg = nullptr)
{
nlSocketPtr socket(nl_socket_alloc(), &::nl_socket_free);
if (socket == nullptr)
{
lg2::error("Unable to allocate memory for the socket");
return -ENOMEM;
}
nl_socket_disable_auto_ack(socket.get());
auto ret = genl_connect(socket.get());
if (ret < 0)
{
lg2::error("Failed to open the socket , RC : {RC}", "RC", ret);
return ret;
}
auto driverID = genl_ctrl_resolve(socket.get(), "NCSI");
if (driverID < 0)
{
lg2::error("Failed to resolve, RC : {RC}", "RC", ret);
return driverID;
}
nlMsgPtr msg(nlmsg_alloc(), &::nlmsg_free);
if (msg == nullptr)
{
lg2::error("Unable to allocate memory for the message");
return -ENOMEM;
}
auto msgHdr = genlmsg_put(msg.get(), NL_AUTO_PORT, NL_AUTO_SEQ, driverID, 0,
flags, cmd.ncsi_cmd, 0);
if (!msgHdr)
{
lg2::error("Unable to add the netlink headers , COMMAND : {COMMAND}",
"COMMAND", cmd.ncsi_cmd);
return -ENOMEM;
}
if (package != DEFAULT_VALUE)
{
ret = nla_put_u32(msg.get(), ncsi_nl_attrs::NCSI_ATTR_PACKAGE_ID,
package);
if (ret < 0)
{
lg2::error("Failed to set the attribute , RC : {RC} PACKAGE "
"{PACKAGE}",
"RC", ret, "PACKAGE", lg2::hex, package);
return ret;
}
}
if (channel != DEFAULT_VALUE)
{
ret = nla_put_u32(msg.get(), ncsi_nl_attrs::NCSI_ATTR_CHANNEL_ID,
channel);
if (ret < 0)
{
lg2::error("Failed to set the attribute , RC : {RC} CHANNEL : "
"{CHANNEL}",
"RC", ret, "CHANNEL", lg2::hex, channel);
return ret;
}
}
ret = nla_put_u32(msg.get(), ncsi_nl_attrs::NCSI_ATTR_IFINDEX,
interface.ifindex);
if (ret < 0)
{
lg2::error("Failed to set the attribute , RC : {RC} INTERFACE : "
"{INTERFACE}",
"RC", ret, "INTERFACE", interface);
return ret;
}
if ((cmd.ncsi_cmd == ncsi_nl_commands::NCSI_CMD_SET_PACKAGE_MASK) ||
(cmd.ncsi_cmd == ncsi_nl_commands::NCSI_CMD_SET_CHANNEL_MASK))
{
if (cmd.payload.size() != sizeof(unsigned int))
{
lg2::error("Package/Channel mask must be 32-bits");
return -EINVAL;
}
int maskAttr =
cmd.ncsi_cmd == ncsi_nl_commands::NCSI_CMD_SET_PACKAGE_MASK
? NCSI_ATTR_PACKAGE_MASK
: NCSI_ATTR_CHANNEL_MASK;
ret = nla_put_u32(
msg.get(), maskAttr,
*(reinterpret_cast<const unsigned int*>(cmd.payload.data())));
if (ret < 0)
{
lg2::error("Failed to set the mask attribute, RC : {RC}", "RC",
ret);
return ret;
}
}
else if (cmd.ncsi_cmd == ncsi_nl_commands::NCSI_CMD_SEND_CMD)
{
std::vector<unsigned char> pl(
sizeof(NCSIPacketHeader) + cmd.payload.size());
NCSIPacketHeader* hdr = (NCSIPacketHeader*)pl.data();
std::copy(cmd.payload.begin(), cmd.payload.end(),
pl.begin() + sizeof(NCSIPacketHeader));
hdr->type = cmd.operation;
hdr->length = htons(cmd.payload.size());
ret = nla_put(msg.get(), ncsi_nl_attrs::NCSI_ATTR_DATA, pl.size(),
pl.data());
if (ret < 0)
{
lg2::error("Failed to set the data attribute, RC : {RC}", "RC",
ret);
return ret;
}
nl_socket_disable_seq_check(socket.get());
}
// Add a callback function to the socket
enum nl_cb_kind cb_kind = function ? NL_CB_CUSTOM : NL_CB_DEFAULT;
nl_socket_modify_cb(socket.get(), NL_CB_VALID, cb_kind, function, arg);
ret = nl_send_auto(socket.get(), msg.get());
if (ret < 0)
{
lg2::error("Failed to send the message , RC : {RC}", "RC", ret);
return ret;
}
ret = nl_recvmsgs_default(socket.get());
if (ret < 0)
{
lg2::error("Failed to receive the message , RC : {RC}", "RC", ret);
return ret;
}
return 0;
}
} // namespace internal
std::string to_string(Interface& interface)
{
return interface.toString();
}
NetlinkInterface::NetlinkInterface(int ifindex) : ifindex(ifindex) {}
std::string NetlinkInterface::toString()
{
return std::to_string(ifindex);
}
std::optional<NCSIResponse> NetlinkInterface::sendCommand(NCSICommand& cmd)
{
lg2::debug("Send Command, CHANNEL : {CHANNEL} , PACKAGE : {PACKAGE}, "
"INTERFACE: {INTERFACE}",
"CHANNEL", lg2::hex, cmd.getChannel(), "PACKAGE", lg2::hex,
cmd.package, "INTERFACE", this);
internal::sendCallBackContext ctx{};
internal::NetlinkCommand nl_cmd(ncsi_nl_commands::NCSI_CMD_SEND_CMD,
cmd.opcode, cmd.payload);
int rc = internal::applyCmd(*this, nl_cmd, cmd.package, cmd.getChannel(),
NONE, internal::sendCallBack, &ctx);
if (rc < 0)
{
return {};
}
return ctx.resp;
}
int NetlinkInterface::setChannel(int package, int channel)
{
lg2::debug("Set CHANNEL : {CHANNEL} , PACKAGE : {PACKAGE}, INTERFACE : "
"{INTERFACE}",
"CHANNEL", lg2::hex, channel, "PACKAGE", lg2::hex, package,
"INTERFACE", this);
internal::NetlinkCommand cmd(ncsi_nl_commands::NCSI_CMD_SET_INTERFACE);
return internal::applyCmd(*this, cmd, package, channel);
}
int NetlinkInterface::clearInterface()
{
lg2::debug("ClearInterface , INTERFACE : {INTERFACE}", "INTERFACE", this);
internal::NetlinkCommand cmd(ncsi_nl_commands::NCSI_CMD_CLEAR_INTERFACE);
return internal::applyCmd(*this, cmd);
}
std::optional<InterfaceInfo> NetlinkInterface::getInfo(int package)
{
int rc, flags = package == DEFAULT_VALUE ? NLM_F_DUMP : NONE;
InterfaceInfo info;
lg2::debug("Get Info , PACKAGE : {PACKAGE}, INTERFACE: {INTERFACE}",
"PACKAGE", lg2::hex, package, "INTERFACE", this);
struct internal::infoCallBackContext ctx = {
.info = &info,
};
internal::NetlinkCommand cmd(ncsi_nl_commands::NCSI_CMD_PKG_INFO);
rc = internal::applyCmd(*this, cmd, package, DEFAULT_VALUE, flags,
internal::infoCallBack, &ctx);
if (rc < 0)
{
return {};
}
return info;
}
int NetlinkInterface::setPackageMask(unsigned int mask)
{
lg2::debug("Set Package Mask , INTERFACE: {INTERFACE} MASK: {MASK}",
"INTERFACE", this, "MASK", lg2::hex, mask);
auto payload = std::span<const unsigned char>(
reinterpret_cast<const unsigned char*>(&mask),
reinterpret_cast<const unsigned char*>(&mask) + sizeof(decltype(mask)));
internal::NetlinkCommand cmd(ncsi_nl_commands::NCSI_CMD_SET_PACKAGE_MASK, 0,
payload);
return internal::applyCmd(*this, cmd);
}
int NetlinkInterface::setChannelMask(int package, unsigned int mask)
{
lg2::debug(
"Set Channel Mask , INTERFACE: {INTERFACE}, PACKAGE : {PACKAGE} MASK: {MASK}",
"INTERFACE", this, "PACKAGE", lg2::hex, package, "MASK", lg2::hex,
mask);
auto payload = std::span<const unsigned char>(
reinterpret_cast<const unsigned char*>(&mask),
reinterpret_cast<const unsigned char*>(&mask) + sizeof(decltype(mask)));
internal::NetlinkCommand cmd(ncsi_nl_commands::NCSI_CMD_SET_CHANNEL_MASK, 0,
payload);
return internal::applyCmd(*this, cmd);
}
int NCSIResponse::parseFullPayload()
{
if (this->full_payload.size() < sizeof(internal::NCSIPacketHeader) +
sizeof(internal::NCSIResponsePayload))
{
lg2::error("Response: Not enough data for a response message");
return -1;
}
internal::NCSIPacketHeader* respHeader =
reinterpret_cast<decltype(respHeader)>(this->full_payload.data());
unsigned int payloadLen = ntohs(respHeader->length & htons(0x0fff));
/* we have determined that the payload size is larger than *respHeader,
* so cannot underflow here */
if (payloadLen > this->full_payload.size() - sizeof(*respHeader))
{
lg2::error("Invalid header length {HDRLEN} (vs {LEN}) in response",
"HDRLEN", payloadLen, "LEN",
this->full_payload.size() - sizeof(*respHeader));
return -1;
}
this->opcode = respHeader->type;
this->payload =
std::span(this->full_payload.begin() + sizeof(*respHeader), payloadLen);
internal::NCSIResponsePayload* respPayload =
reinterpret_cast<decltype(respPayload)>(this->payload.data());
this->response = ntohs(respPayload->response);
this->reason = ntohs(respPayload->reason);
return 0;
}
static const uint8_t MCTP_TYPE_NCSI = 2;
struct NCSIResponsePayload
{
uint16_t response;
uint16_t reason;
};
std::optional<NCSIResponse> MCTPInterface::sendCommand(NCSICommand& cmd)
{
static constexpr uint8_t mcid = 0; /* no need to distinguish controllers */
static constexpr size_t maxRespLen = 16384;
size_t payloadLen, padLen;
ssize_t wlen, rlen;
payloadLen = cmd.payload.size();
auto tmp = allocateIID();
if (!tmp.has_value())
{
return {};
}
uint8_t iid = *tmp;
internal::NCSIPacketHeader cmdHeader{};
cmdHeader.MCID = mcid;
cmdHeader.revision = 1;
cmdHeader.id = iid;
cmdHeader.type = cmd.opcode;
cmdHeader.channel = (uint8_t)(cmd.package << 5 | cmd.getChannel());
cmdHeader.length = htons(payloadLen);
struct iovec iov[3];
iov[0].iov_base = &cmdHeader;
iov[0].iov_len = sizeof(cmdHeader);
iov[1].iov_base = cmd.payload.data();
iov[1].iov_len = payloadLen;
/* the checksum must appear on a 4-byte boundary */
padLen = 4 - (payloadLen & 0x3);
if (padLen == 4)
{
padLen = 0;
}
uint8_t crc32buf[8] = {};
/* todo: set csum; zeros currently indicate no checksum present */
uint32_t crc32 = 0;
memcpy(crc32buf + padLen, &crc32, sizeof(crc32));
padLen += sizeof(crc32);
iov[2].iov_base = crc32buf;
iov[2].iov_len = padLen;
struct sockaddr_mctp addr = {};
addr.smctp_family = AF_MCTP;
addr.smctp_network = net;
addr.smctp_addr.s_addr = eid;
addr.smctp_tag = MCTP_TAG_OWNER;
addr.smctp_type = MCTP_TYPE_NCSI;
struct msghdr msg = {};
msg.msg_name = &addr;
msg.msg_namelen = sizeof(addr);
msg.msg_iov = iov;
msg.msg_iovlen = 3;
wlen = sendmsg(sd, &msg, 0);
if (wlen < 0)
{
lg2::error("Failed to send MCTP message, ERRNO: {ERRNO}", "ERRNO",
-wlen);
return {};
}
else if ((size_t)wlen != sizeof(cmdHeader) + payloadLen + padLen)
{
lg2::error("Short write sending MCTP message, LEN: {LEN}", "LEN", wlen);
return {};
}
internal::NCSIPacketHeader* respHeader;
NCSIResponsePayload* respPayload;
NCSIResponse resp{};
resp.full_payload.resize(maxRespLen);
iov[0].iov_len = resp.full_payload.size();
iov[0].iov_base = resp.full_payload.data();
msg.msg_name = &addr;
msg.msg_namelen = sizeof(addr);
msg.msg_iov = iov;
msg.msg_iovlen = 1;
/* we have set SO_RCVTIMEO, so this won't block forever... */
rlen = recvmsg(sd, &msg, MSG_TRUNC);
if (rlen < 0)
{
lg2::error("Failed to read MCTP response, ERRNO: {ERRNO}", "ERRNO",
-rlen);
return {};
}
else if ((size_t)rlen < sizeof(*respHeader) + sizeof(*respPayload))
{
lg2::error("Short read receiving MCTP message, LEN: {LEN}", "LEN",
rlen);
return {};
}
else if ((size_t)rlen > maxRespLen)
{
lg2::error("MCTP response is too large, LEN: {LEN}", "LEN", rlen);
return {};
}
resp.full_payload.resize(rlen);
respHeader =
reinterpret_cast<decltype(respHeader)>(resp.full_payload.data());
/* header validation */
if (respHeader->MCID != mcid)
{
lg2::error("Invalid MCID {MCID} in response", "MCID", lg2::hex,
respHeader->MCID);
return {};
}
if (respHeader->id != iid)
{
lg2::error("Invalid IID {IID} in response", "IID", lg2::hex,
respHeader->id);
return {};
}
if (respHeader->type != (cmd.opcode | 0x80))
{
lg2::error("Invalid opcode {OPCODE} in response", "OPCODE", lg2::hex,
respHeader->type);
return {};
}
int rc = resp.parseFullPayload();
if (rc)
{
return {};
}
return resp;
}
std::string MCTPInterface::toString()
{
return std::to_string(net) + "," + std::to_string(eid);
}
MCTPInterface::MCTPInterface(int net, uint8_t eid) : net(net), eid(eid)
{
static const struct timeval receiveTimeout = {
.tv_sec = 1,
.tv_usec = 0,
};
int _sd = socket(AF_MCTP, SOCK_DGRAM, 0);
if (_sd < 0)
{
throw std::system_error(errno, std::system_category(),
"Can't create MCTP socket");
}
int rc = setsockopt(_sd, SOL_SOCKET, SO_RCVTIMEO, &receiveTimeout,
sizeof(receiveTimeout));
if (rc != 0)
{
throw std::system_error(errno, std::system_category(),
"Can't set socket receive timemout");
}
sd = _sd;
}
MCTPInterface::~MCTPInterface()
{
close(sd);
}
/* Small fd wrapper to provide RAII semantics, closing the IID file descriptor
* when we go out of scope.
*/
struct IidFd
{
int fd;
IidFd(int _fd) : fd(_fd) {};
~IidFd()
{
close(fd);
};
};
std::optional<uint8_t> MCTPInterface::allocateIID()
{
int fd = open(mctp_iid_path, O_RDWR | O_CREAT, 0600);
if (fd < 0)
{
lg2::warning("Error opening IID database {FILE}: {ERROR}", "FILE",
mctp_iid_path, "ERROR", strerror(errno));
return {};
}
IidFd iidFd(fd);
/* lock while we read/modity/write; the lock will be short-lived, so
* we keep it simple and lock the entire file range
*/
struct flock flock = {
.l_type = F_WRLCK,
.l_whence = SEEK_SET,
.l_start = 0,
.l_len = 0,
.l_pid = 0,
};
int rc = fcntl(iidFd.fd, F_OFD_SETLKW, &flock);
if (rc)
{
lg2::warning("Error locking IID database {FILE}: {ERROR}", "FILE",
mctp_iid_path, "ERROR", strerror(errno));
return {};
}
/* An EOF (rc == 0) would indicate that we don't yet have an entry for that
* eid, which we handle as iid = 0.
*/
uint8_t iid = 0;
rc = pread(iidFd.fd, &iid, sizeof(iid), eid);
if (rc < 0)
{
lg2::warning("Error reading IID database {FILE}: {ERROR}", "FILE",
mctp_iid_path, "ERROR", strerror(errno));
return {};
}
/* DSP0222 defines valid IIDs in the range [1, 0xff], so manually wrap */
if (iid == 0xff)
{
iid = 1;
}
else
{
iid++;
}
rc = pwrite(iidFd.fd, &iid, sizeof(iid), eid);
if (rc != sizeof(iid))
{
lg2::warning("Error writing IID database {FILE}: {ERROR}", "FILE",
mctp_iid_path, "ERROR", strerror(errno));
return {};
}
return iid;
}
} // namespace ncsi
} // namespace network
} // namespace phosphor