11bbe3d1eSWilliam A. Kennington III #include "ncsi_util.hpp"
21bbe3d1eSWilliam A. Kennington III
3*a42a8651SJeremy Kerr #include <errno.h>
4*a42a8651SJeremy Kerr #include <fcntl.h>
5ca9d8677SJeremy Kerr #include <linux/mctp.h>
61bbe3d1eSWilliam A. Kennington III #include <linux/ncsi.h>
71bbe3d1eSWilliam A. Kennington III #include <netlink/genl/ctrl.h>
81bbe3d1eSWilliam A. Kennington III #include <netlink/genl/genl.h>
91bbe3d1eSWilliam A. Kennington III #include <netlink/netlink.h>
10ca9d8677SJeremy Kerr #include <unistd.h>
111bbe3d1eSWilliam A. Kennington III
1289d734b9SPatrick Williams #include <phosphor-logging/lg2.hpp>
1389d734b9SPatrick Williams
14b788524aSJeremy Kerr #include <optional>
15b788524aSJeremy Kerr #include <span>
16ca9d8677SJeremy Kerr #include <system_error>
177c44a78eSJiaqing Zhao #include <vector>
181bbe3d1eSWilliam A. Kennington III
191bbe3d1eSWilliam A. Kennington III namespace phosphor
201bbe3d1eSWilliam A. Kennington III {
211bbe3d1eSWilliam A. Kennington III namespace network
221bbe3d1eSWilliam A. Kennington III {
231bbe3d1eSWilliam A. Kennington III namespace ncsi
241bbe3d1eSWilliam A. Kennington III {
251bbe3d1eSWilliam A. Kennington III
26*a42a8651SJeremy Kerr static const char* mctp_iid_path = "/run/ncsi-mctp-iids";
27*a42a8651SJeremy Kerr
NCSICommand(uint8_t opcode,uint8_t package,std::optional<uint8_t> channel,std::span<unsigned char> payload)28b788524aSJeremy Kerr NCSICommand::NCSICommand(uint8_t opcode, uint8_t package,
29b788524aSJeremy Kerr std::optional<uint8_t> channel,
30b788524aSJeremy Kerr std::span<unsigned char> payload) :
31b788524aSJeremy Kerr opcode(opcode), package(package), channel(channel)
32b788524aSJeremy Kerr {
33b788524aSJeremy Kerr this->payload.assign(payload.begin(), payload.end());
34b788524aSJeremy Kerr }
35b788524aSJeremy Kerr
getChannel()36b788524aSJeremy Kerr uint8_t NCSICommand::getChannel()
37b788524aSJeremy Kerr {
38b788524aSJeremy Kerr return channel.value_or(CHANNEL_ID_NONE);
39b788524aSJeremy Kerr }
40b788524aSJeremy Kerr
411bbe3d1eSWilliam A. Kennington III using CallBack = int (*)(struct nl_msg* msg, void* arg);
421bbe3d1eSWilliam A. Kennington III
431bbe3d1eSWilliam A. Kennington III namespace internal
441bbe3d1eSWilliam A. Kennington III {
451bbe3d1eSWilliam A. Kennington III
46fa1f5c03SEddie James struct NCSIPacketHeader
47fa1f5c03SEddie James {
48fa1f5c03SEddie James uint8_t MCID;
49fa1f5c03SEddie James uint8_t revision;
50fa1f5c03SEddie James uint8_t reserved;
51fa1f5c03SEddie James uint8_t id;
52fa1f5c03SEddie James uint8_t type;
53fa1f5c03SEddie James uint8_t channel;
54fa1f5c03SEddie James uint16_t length;
55fa1f5c03SEddie James uint32_t rsvd[2];
56fa1f5c03SEddie James };
57fa1f5c03SEddie James
58b788524aSJeremy Kerr struct NCSIResponsePayload
59b788524aSJeremy Kerr {
60b788524aSJeremy Kerr uint16_t response;
61b788524aSJeremy Kerr uint16_t reason;
62b788524aSJeremy Kerr };
63b788524aSJeremy Kerr
643f34ff6aSJeremy Kerr class NetlinkCommand
65fa1f5c03SEddie James {
66fa1f5c03SEddie James public:
673f34ff6aSJeremy Kerr NetlinkCommand() = delete;
683f34ff6aSJeremy Kerr ~NetlinkCommand() = default;
693f34ff6aSJeremy Kerr NetlinkCommand(const NetlinkCommand&) = delete;
703f34ff6aSJeremy Kerr NetlinkCommand& operator=(const NetlinkCommand&) = delete;
713f34ff6aSJeremy Kerr NetlinkCommand(NetlinkCommand&&) = default;
723f34ff6aSJeremy Kerr NetlinkCommand& operator=(NetlinkCommand&&) = default;
NetlinkCommand(int ncsiCmd,int operation=DEFAULT_VALUE,std::span<const unsigned char> p=std::span<const unsigned char> ())733f34ff6aSJeremy Kerr NetlinkCommand(
741ebea28cSJohnathan Mantey int ncsiCmd, int operation = DEFAULT_VALUE,
75fa1f5c03SEddie James std::span<const unsigned char> p = std::span<const unsigned char>()) :
76ad205028SPatrick Williams ncsi_cmd(ncsiCmd), operation(operation), payload(p)
7789d734b9SPatrick Williams {}
78fa1f5c03SEddie James
79fa1f5c03SEddie James int ncsi_cmd;
801ebea28cSJohnathan Mantey int operation;
81fa1f5c03SEddie James std::span<const unsigned char> payload;
82fa1f5c03SEddie James };
83fa1f5c03SEddie James
841bbe3d1eSWilliam A. Kennington III using nlMsgPtr = std::unique_ptr<nl_msg, decltype(&::nlmsg_free)>;
851bbe3d1eSWilliam A. Kennington III using nlSocketPtr = std::unique_ptr<nl_sock, decltype(&::nl_socket_free)>;
861bbe3d1eSWilliam A. Kennington III
877f7c085eSJeremy Kerr struct infoCallBackContext
887f7c085eSJeremy Kerr {
897f7c085eSJeremy Kerr InterfaceInfo* info;
907f7c085eSJeremy Kerr };
917f7c085eSJeremy Kerr
__anon66112f010102(struct nl_msg* msg, void* arg) 927f7c085eSJeremy Kerr CallBack infoCallBack = [](struct nl_msg* msg, void* arg) {
937f7c085eSJeremy Kerr if (arg == nullptr)
947f7c085eSJeremy Kerr {
957f7c085eSJeremy Kerr lg2::error("Internal error: invalid info callback context");
967f7c085eSJeremy Kerr return -1;
977f7c085eSJeremy Kerr }
987f7c085eSJeremy Kerr
997f7c085eSJeremy Kerr struct infoCallBackContext* info = (struct infoCallBackContext*)arg;
1001bbe3d1eSWilliam A. Kennington III using namespace phosphor::network::ncsi;
1011bbe3d1eSWilliam A. Kennington III auto nlh = nlmsg_hdr(msg);
1021bbe3d1eSWilliam A. Kennington III
1031bbe3d1eSWilliam A. Kennington III struct nlattr* tb[NCSI_ATTR_MAX + 1] = {nullptr};
1041bbe3d1eSWilliam A. Kennington III struct nla_policy ncsiPolicy[NCSI_ATTR_MAX + 1] = {
1051bbe3d1eSWilliam A. Kennington III {NLA_UNSPEC, 0, 0}, {NLA_U32, 0, 0}, {NLA_NESTED, 0, 0},
1061bbe3d1eSWilliam A. Kennington III {NLA_U32, 0, 0}, {NLA_U32, 0, 0},
1071bbe3d1eSWilliam A. Kennington III };
1081bbe3d1eSWilliam A. Kennington III
1091bbe3d1eSWilliam A. Kennington III struct nlattr* packagetb[NCSI_PKG_ATTR_MAX + 1] = {nullptr};
1101bbe3d1eSWilliam A. Kennington III struct nla_policy packagePolicy[NCSI_PKG_ATTR_MAX + 1] = {
1111bbe3d1eSWilliam A. Kennington III {NLA_UNSPEC, 0, 0}, {NLA_NESTED, 0, 0}, {NLA_U32, 0, 0},
1121bbe3d1eSWilliam A. Kennington III {NLA_FLAG, 0, 0}, {NLA_NESTED, 0, 0},
1131bbe3d1eSWilliam A. Kennington III };
1141bbe3d1eSWilliam A. Kennington III
1151bbe3d1eSWilliam A. Kennington III struct nlattr* channeltb[NCSI_CHANNEL_ATTR_MAX + 1] = {nullptr};
1161bbe3d1eSWilliam A. Kennington III struct nla_policy channelPolicy[NCSI_CHANNEL_ATTR_MAX + 1] = {
1171bbe3d1eSWilliam A. Kennington III {NLA_UNSPEC, 0, 0}, {NLA_NESTED, 0, 0}, {NLA_U32, 0, 0},
1181bbe3d1eSWilliam A. Kennington III {NLA_FLAG, 0, 0}, {NLA_NESTED, 0, 0}, {NLA_UNSPEC, 0, 0},
1191bbe3d1eSWilliam A. Kennington III };
1201bbe3d1eSWilliam A. Kennington III
1211bbe3d1eSWilliam A. Kennington III auto ret = genlmsg_parse(nlh, 0, tb, NCSI_ATTR_MAX, ncsiPolicy);
1221bbe3d1eSWilliam A. Kennington III if (!tb[NCSI_ATTR_PACKAGE_LIST])
1231bbe3d1eSWilliam A. Kennington III {
124d423bebfSJagpal Singh Gill lg2::error("No Packages");
1251bbe3d1eSWilliam A. Kennington III return -1;
1261bbe3d1eSWilliam A. Kennington III }
1271bbe3d1eSWilliam A. Kennington III
1281bbe3d1eSWilliam A. Kennington III auto attrTgt = static_cast<nlattr*>(nla_data(tb[NCSI_ATTR_PACKAGE_LIST]));
1291bbe3d1eSWilliam A. Kennington III if (!attrTgt)
1301bbe3d1eSWilliam A. Kennington III {
131d423bebfSJagpal Singh Gill lg2::error("Package list attribute is null");
1321bbe3d1eSWilliam A. Kennington III return -1;
1331bbe3d1eSWilliam A. Kennington III }
1341bbe3d1eSWilliam A. Kennington III
1351bbe3d1eSWilliam A. Kennington III auto rem = nla_len(tb[NCSI_ATTR_PACKAGE_LIST]);
1361bbe3d1eSWilliam A. Kennington III nla_for_each_nested(attrTgt, tb[NCSI_ATTR_PACKAGE_LIST], rem)
1371bbe3d1eSWilliam A. Kennington III {
1381bbe3d1eSWilliam A. Kennington III ret = nla_parse_nested(packagetb, NCSI_PKG_ATTR_MAX, attrTgt,
1391bbe3d1eSWilliam A. Kennington III packagePolicy);
1401bbe3d1eSWilliam A. Kennington III if (ret < 0)
1411bbe3d1eSWilliam A. Kennington III {
142d423bebfSJagpal Singh Gill lg2::error("Failed to parse package nested");
1431bbe3d1eSWilliam A. Kennington III return -1;
1441bbe3d1eSWilliam A. Kennington III }
1451bbe3d1eSWilliam A. Kennington III
1467f7c085eSJeremy Kerr PackageInfo pkg;
1477f7c085eSJeremy Kerr
1481bbe3d1eSWilliam A. Kennington III if (packagetb[NCSI_PKG_ATTR_ID])
1491bbe3d1eSWilliam A. Kennington III {
1501bbe3d1eSWilliam A. Kennington III auto attrID = nla_get_u32(packagetb[NCSI_PKG_ATTR_ID]);
1517f7c085eSJeremy Kerr pkg.id = attrID;
1521bbe3d1eSWilliam A. Kennington III }
1531bbe3d1eSWilliam A. Kennington III else
1541bbe3d1eSWilliam A. Kennington III {
155d423bebfSJagpal Singh Gill lg2::debug("Package with no id");
1561bbe3d1eSWilliam A. Kennington III }
1571bbe3d1eSWilliam A. Kennington III
1581bbe3d1eSWilliam A. Kennington III if (packagetb[NCSI_PKG_ATTR_FORCED])
1591bbe3d1eSWilliam A. Kennington III {
1607f7c085eSJeremy Kerr pkg.forced = true;
1611bbe3d1eSWilliam A. Kennington III }
1621bbe3d1eSWilliam A. Kennington III
1631bbe3d1eSWilliam A. Kennington III auto channelListTarget = static_cast<nlattr*>(
1641bbe3d1eSWilliam A. Kennington III nla_data(packagetb[NCSI_PKG_ATTR_CHANNEL_LIST]));
1651bbe3d1eSWilliam A. Kennington III
1661bbe3d1eSWilliam A. Kennington III auto channelrem = nla_len(packagetb[NCSI_PKG_ATTR_CHANNEL_LIST]);
1671bbe3d1eSWilliam A. Kennington III nla_for_each_nested(channelListTarget,
1681bbe3d1eSWilliam A. Kennington III packagetb[NCSI_PKG_ATTR_CHANNEL_LIST], channelrem)
1691bbe3d1eSWilliam A. Kennington III {
1701bbe3d1eSWilliam A. Kennington III ret = nla_parse_nested(channeltb, NCSI_CHANNEL_ATTR_MAX,
1711bbe3d1eSWilliam A. Kennington III channelListTarget, channelPolicy);
1721bbe3d1eSWilliam A. Kennington III if (ret < 0)
1731bbe3d1eSWilliam A. Kennington III {
174d423bebfSJagpal Singh Gill lg2::error("Failed to parse channel nested");
1757f7c085eSJeremy Kerr continue;
1761bbe3d1eSWilliam A. Kennington III }
1771bbe3d1eSWilliam A. Kennington III
1787f7c085eSJeremy Kerr ChannelInfo chan;
1797f7c085eSJeremy Kerr
1801bbe3d1eSWilliam A. Kennington III if (channeltb[NCSI_CHANNEL_ATTR_ID])
1811bbe3d1eSWilliam A. Kennington III {
1827f7c085eSJeremy Kerr chan.id = nla_get_u32(channeltb[NCSI_CHANNEL_ATTR_ID]);
1837f7c085eSJeremy Kerr chan.active = !!channeltb[NCSI_CHANNEL_ATTR_ACTIVE];
1847f7c085eSJeremy Kerr chan.forced = !!channeltb[NCSI_CHANNEL_ATTR_FORCED];
1851bbe3d1eSWilliam A. Kennington III }
1861bbe3d1eSWilliam A. Kennington III else
1871bbe3d1eSWilliam A. Kennington III {
188d423bebfSJagpal Singh Gill lg2::debug("Channel with no ID");
1897f7c085eSJeremy Kerr continue;
1901bbe3d1eSWilliam A. Kennington III }
1911bbe3d1eSWilliam A. Kennington III
1921bbe3d1eSWilliam A. Kennington III if (channeltb[NCSI_CHANNEL_ATTR_VERSION_MAJOR])
1931bbe3d1eSWilliam A. Kennington III {
1947f7c085eSJeremy Kerr chan.version_major =
1951bbe3d1eSWilliam A. Kennington III nla_get_u32(channeltb[NCSI_CHANNEL_ATTR_VERSION_MAJOR]);
1961bbe3d1eSWilliam A. Kennington III }
1971bbe3d1eSWilliam A. Kennington III if (channeltb[NCSI_CHANNEL_ATTR_VERSION_MINOR])
1981bbe3d1eSWilliam A. Kennington III {
1997f7c085eSJeremy Kerr chan.version_minor =
2001bbe3d1eSWilliam A. Kennington III nla_get_u32(channeltb[NCSI_CHANNEL_ATTR_VERSION_MINOR]);
2011bbe3d1eSWilliam A. Kennington III }
2021bbe3d1eSWilliam A. Kennington III if (channeltb[NCSI_CHANNEL_ATTR_VERSION_STR])
2031bbe3d1eSWilliam A. Kennington III {
2047f7c085eSJeremy Kerr chan.version =
2051bbe3d1eSWilliam A. Kennington III nla_get_string(channeltb[NCSI_CHANNEL_ATTR_VERSION_STR]);
2061bbe3d1eSWilliam A. Kennington III }
2071bbe3d1eSWilliam A. Kennington III if (channeltb[NCSI_CHANNEL_ATTR_LINK_STATE])
2081bbe3d1eSWilliam A. Kennington III {
2097f7c085eSJeremy Kerr chan.link_state =
2101bbe3d1eSWilliam A. Kennington III nla_get_u32(channeltb[NCSI_CHANNEL_ATTR_LINK_STATE]);
2111bbe3d1eSWilliam A. Kennington III }
2121bbe3d1eSWilliam A. Kennington III if (channeltb[NCSI_CHANNEL_ATTR_VLAN_LIST])
2131bbe3d1eSWilliam A. Kennington III {
2141bbe3d1eSWilliam A. Kennington III auto vids = channeltb[NCSI_CHANNEL_ATTR_VLAN_LIST];
2151bbe3d1eSWilliam A. Kennington III auto vid = static_cast<nlattr*>(nla_data(vids));
2161bbe3d1eSWilliam A. Kennington III auto len = nla_len(vids);
2171bbe3d1eSWilliam A. Kennington III while (nla_ok(vid, len))
2181bbe3d1eSWilliam A. Kennington III {
2191bbe3d1eSWilliam A. Kennington III auto id = nla_get_u16(vid);
2207f7c085eSJeremy Kerr chan.vlan_ids.push_back(id);
2211bbe3d1eSWilliam A. Kennington III vid = nla_next(vid, &len);
2221bbe3d1eSWilliam A. Kennington III }
2231bbe3d1eSWilliam A. Kennington III }
2247f7c085eSJeremy Kerr pkg.channels.push_back(chan);
2251bbe3d1eSWilliam A. Kennington III }
2267f7c085eSJeremy Kerr
2277f7c085eSJeremy Kerr info->info->packages.push_back(pkg);
2281bbe3d1eSWilliam A. Kennington III }
229bea6cdecSJeremy Kerr return static_cast<int>(NL_STOP);
2301bbe3d1eSWilliam A. Kennington III };
2311bbe3d1eSWilliam A. Kennington III
232147086d0SJeremy Kerr struct sendCallBackContext
233147086d0SJeremy Kerr {
234b788524aSJeremy Kerr NCSIResponse resp;
235147086d0SJeremy Kerr };
236147086d0SJeremy Kerr
__anon66112f010202(struct nl_msg* msg, void* arg) 237147086d0SJeremy Kerr CallBack sendCallBack = [](struct nl_msg* msg, void* arg) {
238fa1f5c03SEddie James using namespace phosphor::network::ncsi;
239fa1f5c03SEddie James auto nlh = nlmsg_hdr(msg);
240fa1f5c03SEddie James struct nlattr* tb[NCSI_ATTR_MAX + 1] = {nullptr};
241fa1f5c03SEddie James static struct nla_policy ncsiPolicy[NCSI_ATTR_MAX + 1] = {
242fa1f5c03SEddie James {NLA_UNSPEC, 0, 0}, {NLA_U32, 0, 0}, {NLA_NESTED, 0, 0},
243fa1f5c03SEddie James {NLA_U32, 0, 0}, {NLA_U32, 0, 0}, {NLA_BINARY, 0, 0},
244fa1f5c03SEddie James {NLA_FLAG, 0, 0}, {NLA_U32, 0, 0}, {NLA_U32, 0, 0},
245fa1f5c03SEddie James };
246fa1f5c03SEddie James
247147086d0SJeremy Kerr if (arg == nullptr)
248147086d0SJeremy Kerr {
249147086d0SJeremy Kerr lg2::error("Internal error: invalid send callback context");
250147086d0SJeremy Kerr return -1;
251147086d0SJeremy Kerr }
252147086d0SJeremy Kerr
253147086d0SJeremy Kerr struct sendCallBackContext* ctx = (struct sendCallBackContext*)arg;
254147086d0SJeremy Kerr
255fa1f5c03SEddie James auto ret = genlmsg_parse(nlh, 0, tb, NCSI_ATTR_MAX, ncsiPolicy);
256fa1f5c03SEddie James if (ret)
257fa1f5c03SEddie James {
258de8d666cSJeremy Kerr lg2::error("Failed to parse message");
259fa1f5c03SEddie James return ret;
260fa1f5c03SEddie James }
261fa1f5c03SEddie James
262442d9e5cSJian Zhang if (tb[NCSI_ATTR_DATA] == nullptr)
263442d9e5cSJian Zhang {
264d423bebfSJagpal Singh Gill lg2::error("Response: No data");
265442d9e5cSJian Zhang return -1;
266442d9e5cSJian Zhang }
267442d9e5cSJian Zhang
268b788524aSJeremy Kerr size_t data_len = nla_len(tb[NCSI_ATTR_DATA]);
269b788524aSJeremy Kerr unsigned char* data = (unsigned char*)nla_data(tb[NCSI_ATTR_DATA]);
270fa1f5c03SEddie James
271b788524aSJeremy Kerr ctx->resp.full_payload.assign(data, data + data_len);
272b788524aSJeremy Kerr
273b788524aSJeremy Kerr int rc = ctx->resp.parseFullPayload();
274b788524aSJeremy Kerr if (rc)
275b788524aSJeremy Kerr {
276b788524aSJeremy Kerr return -1;
277b788524aSJeremy Kerr }
278fa1f5c03SEddie James
279bea6cdecSJeremy Kerr return static_cast<int>(NL_STOP);
280fa1f5c03SEddie James };
281fa1f5c03SEddie James
applyCmd(NetlinkInterface & interface,const NetlinkCommand & cmd,int package=DEFAULT_VALUE,int channel=DEFAULT_VALUE,int flags=NONE,CallBack function=nullptr,void * arg=nullptr)2822d0b48daSJeremy Kerr int applyCmd(NetlinkInterface& interface, const NetlinkCommand& cmd,
2838d9af023SJeremy Kerr int package = DEFAULT_VALUE, int channel = DEFAULT_VALUE,
2847f7c085eSJeremy Kerr int flags = NONE, CallBack function = nullptr, void* arg = nullptr)
2851bbe3d1eSWilliam A. Kennington III {
2861bbe3d1eSWilliam A. Kennington III nlSocketPtr socket(nl_socket_alloc(), &::nl_socket_free);
287d49c5c65SJohnathan Mantey if (socket == nullptr)
288d49c5c65SJohnathan Mantey {
289d423bebfSJagpal Singh Gill lg2::error("Unable to allocate memory for the socket");
290d49c5c65SJohnathan Mantey return -ENOMEM;
291d49c5c65SJohnathan Mantey }
292d49c5c65SJohnathan Mantey
293bea6cdecSJeremy Kerr nl_socket_disable_auto_ack(socket.get());
294bea6cdecSJeremy Kerr
2951bbe3d1eSWilliam A. Kennington III auto ret = genl_connect(socket.get());
2961bbe3d1eSWilliam A. Kennington III if (ret < 0)
2971bbe3d1eSWilliam A. Kennington III {
298d423bebfSJagpal Singh Gill lg2::error("Failed to open the socket , RC : {RC}", "RC", ret);
2991bbe3d1eSWilliam A. Kennington III return ret;
3001bbe3d1eSWilliam A. Kennington III }
3011bbe3d1eSWilliam A. Kennington III
3021bbe3d1eSWilliam A. Kennington III auto driverID = genl_ctrl_resolve(socket.get(), "NCSI");
3031bbe3d1eSWilliam A. Kennington III if (driverID < 0)
3041bbe3d1eSWilliam A. Kennington III {
305d423bebfSJagpal Singh Gill lg2::error("Failed to resolve, RC : {RC}", "RC", ret);
3061bbe3d1eSWilliam A. Kennington III return driverID;
3071bbe3d1eSWilliam A. Kennington III }
3081bbe3d1eSWilliam A. Kennington III
3091bbe3d1eSWilliam A. Kennington III nlMsgPtr msg(nlmsg_alloc(), &::nlmsg_free);
310d49c5c65SJohnathan Mantey if (msg == nullptr)
311d49c5c65SJohnathan Mantey {
312d423bebfSJagpal Singh Gill lg2::error("Unable to allocate memory for the message");
313d49c5c65SJohnathan Mantey return -ENOMEM;
314d49c5c65SJohnathan Mantey }
3151bbe3d1eSWilliam A. Kennington III
3161ebea28cSJohnathan Mantey auto msgHdr = genlmsg_put(msg.get(), NL_AUTO_PORT, NL_AUTO_SEQ, driverID, 0,
3171ebea28cSJohnathan Mantey flags, cmd.ncsi_cmd, 0);
3181bbe3d1eSWilliam A. Kennington III if (!msgHdr)
3191bbe3d1eSWilliam A. Kennington III {
320d423bebfSJagpal Singh Gill lg2::error("Unable to add the netlink headers , COMMAND : {COMMAND}",
3211ebea28cSJohnathan Mantey "COMMAND", cmd.ncsi_cmd);
322d49c5c65SJohnathan Mantey return -ENOMEM;
3231bbe3d1eSWilliam A. Kennington III }
3241bbe3d1eSWilliam A. Kennington III
3251bbe3d1eSWilliam A. Kennington III if (package != DEFAULT_VALUE)
3261bbe3d1eSWilliam A. Kennington III {
3271bbe3d1eSWilliam A. Kennington III ret = nla_put_u32(msg.get(), ncsi_nl_attrs::NCSI_ATTR_PACKAGE_ID,
3281bbe3d1eSWilliam A. Kennington III package);
3291bbe3d1eSWilliam A. Kennington III if (ret < 0)
3301bbe3d1eSWilliam A. Kennington III {
331d423bebfSJagpal Singh Gill lg2::error("Failed to set the attribute , RC : {RC} PACKAGE "
332d423bebfSJagpal Singh Gill "{PACKAGE}",
333d423bebfSJagpal Singh Gill "RC", ret, "PACKAGE", lg2::hex, package);
3341bbe3d1eSWilliam A. Kennington III return ret;
3351bbe3d1eSWilliam A. Kennington III }
3361bbe3d1eSWilliam A. Kennington III }
3371bbe3d1eSWilliam A. Kennington III
3381bbe3d1eSWilliam A. Kennington III if (channel != DEFAULT_VALUE)
3391bbe3d1eSWilliam A. Kennington III {
3401bbe3d1eSWilliam A. Kennington III ret = nla_put_u32(msg.get(), ncsi_nl_attrs::NCSI_ATTR_CHANNEL_ID,
3411bbe3d1eSWilliam A. Kennington III channel);
3421bbe3d1eSWilliam A. Kennington III if (ret < 0)
3431bbe3d1eSWilliam A. Kennington III {
344d423bebfSJagpal Singh Gill lg2::error("Failed to set the attribute , RC : {RC} CHANNEL : "
345d423bebfSJagpal Singh Gill "{CHANNEL}",
346d423bebfSJagpal Singh Gill "RC", ret, "CHANNEL", lg2::hex, channel);
3471bbe3d1eSWilliam A. Kennington III return ret;
3481bbe3d1eSWilliam A. Kennington III }
3491bbe3d1eSWilliam A. Kennington III }
3501bbe3d1eSWilliam A. Kennington III
3518d9af023SJeremy Kerr ret = nla_put_u32(msg.get(), ncsi_nl_attrs::NCSI_ATTR_IFINDEX,
3528d9af023SJeremy Kerr interface.ifindex);
3531bbe3d1eSWilliam A. Kennington III if (ret < 0)
3541bbe3d1eSWilliam A. Kennington III {
355d423bebfSJagpal Singh Gill lg2::error("Failed to set the attribute , RC : {RC} INTERFACE : "
356d423bebfSJagpal Singh Gill "{INTERFACE}",
3578a76d89dSJeremy Kerr "RC", ret, "INTERFACE", interface);
3581bbe3d1eSWilliam A. Kennington III return ret;
3591bbe3d1eSWilliam A. Kennington III }
3601bbe3d1eSWilliam A. Kennington III
3615a456061SJohnathan Mantey if ((cmd.ncsi_cmd == ncsi_nl_commands::NCSI_CMD_SET_PACKAGE_MASK) ||
3625a456061SJohnathan Mantey (cmd.ncsi_cmd == ncsi_nl_commands::NCSI_CMD_SET_CHANNEL_MASK))
3635a456061SJohnathan Mantey {
3645a456061SJohnathan Mantey if (cmd.payload.size() != sizeof(unsigned int))
3655a456061SJohnathan Mantey {
3665a456061SJohnathan Mantey lg2::error("Package/Channel mask must be 32-bits");
3675a456061SJohnathan Mantey return -EINVAL;
3685a456061SJohnathan Mantey }
3695a456061SJohnathan Mantey int maskAttr =
3705a456061SJohnathan Mantey cmd.ncsi_cmd == ncsi_nl_commands::NCSI_CMD_SET_PACKAGE_MASK
3715a456061SJohnathan Mantey ? NCSI_ATTR_PACKAGE_MASK
3725a456061SJohnathan Mantey : NCSI_ATTR_CHANNEL_MASK;
3735a456061SJohnathan Mantey ret = nla_put_u32(
3745a456061SJohnathan Mantey msg.get(), maskAttr,
3755a456061SJohnathan Mantey *(reinterpret_cast<const unsigned int*>(cmd.payload.data())));
3765a456061SJohnathan Mantey if (ret < 0)
3775a456061SJohnathan Mantey {
3785a456061SJohnathan Mantey lg2::error("Failed to set the mask attribute, RC : {RC}", "RC",
3795a456061SJohnathan Mantey ret);
3805a456061SJohnathan Mantey return ret;
3815a456061SJohnathan Mantey }
3825a456061SJohnathan Mantey }
3835a456061SJohnathan Mantey else if (cmd.ncsi_cmd == ncsi_nl_commands::NCSI_CMD_SEND_CMD)
384fa1f5c03SEddie James {
385ad205028SPatrick Williams std::vector<unsigned char> pl(
386ad205028SPatrick Williams sizeof(NCSIPacketHeader) + cmd.payload.size());
387fa1f5c03SEddie James NCSIPacketHeader* hdr = (NCSIPacketHeader*)pl.data();
388fa1f5c03SEddie James
389fa1f5c03SEddie James std::copy(cmd.payload.begin(), cmd.payload.end(),
390fa1f5c03SEddie James pl.begin() + sizeof(NCSIPacketHeader));
391fa1f5c03SEddie James
3921ebea28cSJohnathan Mantey hdr->type = cmd.operation;
393fa1f5c03SEddie James hdr->length = htons(cmd.payload.size());
394fa1f5c03SEddie James
395fa1f5c03SEddie James ret = nla_put(msg.get(), ncsi_nl_attrs::NCSI_ATTR_DATA, pl.size(),
396fa1f5c03SEddie James pl.data());
397fa1f5c03SEddie James if (ret < 0)
398fa1f5c03SEddie James {
399d423bebfSJagpal Singh Gill lg2::error("Failed to set the data attribute, RC : {RC}", "RC",
400d423bebfSJagpal Singh Gill ret);
401fa1f5c03SEddie James return ret;
402fa1f5c03SEddie James }
403fa1f5c03SEddie James
404fa1f5c03SEddie James nl_socket_disable_seq_check(socket.get());
405fa1f5c03SEddie James }
406fa1f5c03SEddie James
4071bbe3d1eSWilliam A. Kennington III // Add a callback function to the socket
40867b159a8SJeremy Kerr enum nl_cb_kind cb_kind = function ? NL_CB_CUSTOM : NL_CB_DEFAULT;
4097f7c085eSJeremy Kerr nl_socket_modify_cb(socket.get(), NL_CB_VALID, cb_kind, function, arg);
4101bbe3d1eSWilliam A. Kennington III
4111bbe3d1eSWilliam A. Kennington III ret = nl_send_auto(socket.get(), msg.get());
4121bbe3d1eSWilliam A. Kennington III if (ret < 0)
4131bbe3d1eSWilliam A. Kennington III {
414d423bebfSJagpal Singh Gill lg2::error("Failed to send the message , RC : {RC}", "RC", ret);
4151bbe3d1eSWilliam A. Kennington III return ret;
4161bbe3d1eSWilliam A. Kennington III }
4171bbe3d1eSWilliam A. Kennington III
4181bbe3d1eSWilliam A. Kennington III ret = nl_recvmsgs_default(socket.get());
4191bbe3d1eSWilliam A. Kennington III if (ret < 0)
4201bbe3d1eSWilliam A. Kennington III {
421d423bebfSJagpal Singh Gill lg2::error("Failed to receive the message , RC : {RC}", "RC", ret);
4221bbe3d1eSWilliam A. Kennington III return ret;
4231bbe3d1eSWilliam A. Kennington III }
4241bbe3d1eSWilliam A. Kennington III
425bea6cdecSJeremy Kerr return 0;
426bea6cdecSJeremy Kerr }
427bea6cdecSJeremy Kerr
4281bbe3d1eSWilliam A. Kennington III } // namespace internal
4291bbe3d1eSWilliam A. Kennington III
to_string(Interface & interface)4308a76d89dSJeremy Kerr std::string to_string(Interface& interface)
4318a76d89dSJeremy Kerr {
4322d0b48daSJeremy Kerr return interface.toString();
4338a76d89dSJeremy Kerr }
4348a76d89dSJeremy Kerr
NetlinkInterface(int ifindex)4352d0b48daSJeremy Kerr NetlinkInterface::NetlinkInterface(int ifindex) : ifindex(ifindex) {}
4362d0b48daSJeremy Kerr
toString()4372d0b48daSJeremy Kerr std::string NetlinkInterface::toString()
4382d0b48daSJeremy Kerr {
4392d0b48daSJeremy Kerr return std::to_string(ifindex);
4402d0b48daSJeremy Kerr }
4412d0b48daSJeremy Kerr
sendCommand(NCSICommand & cmd)4422d0b48daSJeremy Kerr std::optional<NCSIResponse> NetlinkInterface::sendCommand(NCSICommand& cmd)
443fa1f5c03SEddie James {
444b788524aSJeremy Kerr lg2::debug("Send Command, CHANNEL : {CHANNEL} , PACKAGE : {PACKAGE}, "
4458a76d89dSJeremy Kerr "INTERFACE: {INTERFACE}",
446b788524aSJeremy Kerr "CHANNEL", lg2::hex, cmd.getChannel(), "PACKAGE", lg2::hex,
447b788524aSJeremy Kerr cmd.package, "INTERFACE", this);
448fa1f5c03SEddie James
449147086d0SJeremy Kerr internal::sendCallBackContext ctx{};
450147086d0SJeremy Kerr
451b788524aSJeremy Kerr internal::NetlinkCommand nl_cmd(ncsi_nl_commands::NCSI_CMD_SEND_CMD,
452b788524aSJeremy Kerr cmd.opcode, cmd.payload);
4533f34ff6aSJeremy Kerr
454b788524aSJeremy Kerr int rc = internal::applyCmd(*this, nl_cmd, cmd.package, cmd.getChannel(),
455b788524aSJeremy Kerr NONE, internal::sendCallBack, &ctx);
456147086d0SJeremy Kerr
457147086d0SJeremy Kerr if (rc < 0)
458147086d0SJeremy Kerr {
459147086d0SJeremy Kerr return {};
460147086d0SJeremy Kerr }
461147086d0SJeremy Kerr
462b788524aSJeremy Kerr return ctx.resp;
463fa1f5c03SEddie James }
464fa1f5c03SEddie James
setChannel(int package,int channel)4652d0b48daSJeremy Kerr int NetlinkInterface::setChannel(int package, int channel)
4661bbe3d1eSWilliam A. Kennington III {
4678a76d89dSJeremy Kerr lg2::debug("Set CHANNEL : {CHANNEL} , PACKAGE : {PACKAGE}, INTERFACE : "
4688a76d89dSJeremy Kerr "{INTERFACE}",
469d423bebfSJagpal Singh Gill "CHANNEL", lg2::hex, channel, "PACKAGE", lg2::hex, package,
470bc22f81cSJeremy Kerr "INTERFACE", this);
4713f34ff6aSJeremy Kerr
4723f34ff6aSJeremy Kerr internal::NetlinkCommand cmd(ncsi_nl_commands::NCSI_CMD_SET_INTERFACE);
4733f34ff6aSJeremy Kerr
4743f34ff6aSJeremy Kerr return internal::applyCmd(*this, cmd, package, channel);
4751bbe3d1eSWilliam A. Kennington III }
4761bbe3d1eSWilliam A. Kennington III
clearInterface()4772d0b48daSJeremy Kerr int NetlinkInterface::clearInterface()
4781bbe3d1eSWilliam A. Kennington III {
479bc22f81cSJeremy Kerr lg2::debug("ClearInterface , INTERFACE : {INTERFACE}", "INTERFACE", this);
4803f34ff6aSJeremy Kerr
4813f34ff6aSJeremy Kerr internal::NetlinkCommand cmd(ncsi_nl_commands::NCSI_CMD_CLEAR_INTERFACE);
4823f34ff6aSJeremy Kerr return internal::applyCmd(*this, cmd);
4831bbe3d1eSWilliam A. Kennington III }
4841bbe3d1eSWilliam A. Kennington III
getInfo(int package)4852d0b48daSJeremy Kerr std::optional<InterfaceInfo> NetlinkInterface::getInfo(int package)
4861bbe3d1eSWilliam A. Kennington III {
4877f7c085eSJeremy Kerr int rc, flags = package == DEFAULT_VALUE ? NLM_F_DUMP : NONE;
4887f7c085eSJeremy Kerr InterfaceInfo info;
4897f7c085eSJeremy Kerr
4908a76d89dSJeremy Kerr lg2::debug("Get Info , PACKAGE : {PACKAGE}, INTERFACE: {INTERFACE}",
491bc22f81cSJeremy Kerr "PACKAGE", lg2::hex, package, "INTERFACE", this);
4927f7c085eSJeremy Kerr
4937f7c085eSJeremy Kerr struct internal::infoCallBackContext ctx = {
4947f7c085eSJeremy Kerr .info = &info,
4957f7c085eSJeremy Kerr };
4967f7c085eSJeremy Kerr
4973f34ff6aSJeremy Kerr internal::NetlinkCommand cmd(ncsi_nl_commands::NCSI_CMD_PKG_INFO);
4983f34ff6aSJeremy Kerr
4993f34ff6aSJeremy Kerr rc = internal::applyCmd(*this, cmd, package, DEFAULT_VALUE, flags,
5003f34ff6aSJeremy Kerr internal::infoCallBack, &ctx);
5017f7c085eSJeremy Kerr
5027f7c085eSJeremy Kerr if (rc < 0)
5031bbe3d1eSWilliam A. Kennington III {
5047f7c085eSJeremy Kerr return {};
5051bbe3d1eSWilliam A. Kennington III }
5067f7c085eSJeremy Kerr
5077f7c085eSJeremy Kerr return info;
5081bbe3d1eSWilliam A. Kennington III }
5091bbe3d1eSWilliam A. Kennington III
setPackageMask(unsigned int mask)5102d0b48daSJeremy Kerr int NetlinkInterface::setPackageMask(unsigned int mask)
5115a456061SJohnathan Mantey {
5128a76d89dSJeremy Kerr lg2::debug("Set Package Mask , INTERFACE: {INTERFACE} MASK: {MASK}",
513bc22f81cSJeremy Kerr "INTERFACE", this, "MASK", lg2::hex, mask);
5145a456061SJohnathan Mantey auto payload = std::span<const unsigned char>(
5155a456061SJohnathan Mantey reinterpret_cast<const unsigned char*>(&mask),
5165a456061SJohnathan Mantey reinterpret_cast<const unsigned char*>(&mask) + sizeof(decltype(mask)));
5173f34ff6aSJeremy Kerr
5183f34ff6aSJeremy Kerr internal::NetlinkCommand cmd(ncsi_nl_commands::NCSI_CMD_SET_PACKAGE_MASK, 0,
5193f34ff6aSJeremy Kerr payload);
5203f34ff6aSJeremy Kerr return internal::applyCmd(*this, cmd);
5215a456061SJohnathan Mantey }
5225a456061SJohnathan Mantey
setChannelMask(int package,unsigned int mask)5232d0b48daSJeremy Kerr int NetlinkInterface::setChannelMask(int package, unsigned int mask)
5245a456061SJohnathan Mantey {
5255a456061SJohnathan Mantey lg2::debug(
5268a76d89dSJeremy Kerr "Set Channel Mask , INTERFACE: {INTERFACE}, PACKAGE : {PACKAGE} MASK: {MASK}",
527bc22f81cSJeremy Kerr "INTERFACE", this, "PACKAGE", lg2::hex, package, "MASK", lg2::hex,
5288a76d89dSJeremy Kerr mask);
5295a456061SJohnathan Mantey auto payload = std::span<const unsigned char>(
5305a456061SJohnathan Mantey reinterpret_cast<const unsigned char*>(&mask),
5315a456061SJohnathan Mantey reinterpret_cast<const unsigned char*>(&mask) + sizeof(decltype(mask)));
5323f34ff6aSJeremy Kerr
5333f34ff6aSJeremy Kerr internal::NetlinkCommand cmd(ncsi_nl_commands::NCSI_CMD_SET_CHANNEL_MASK, 0,
5343f34ff6aSJeremy Kerr payload);
5353f34ff6aSJeremy Kerr return internal::applyCmd(*this, cmd);
5365a456061SJohnathan Mantey }
5375a456061SJohnathan Mantey
parseFullPayload()538b788524aSJeremy Kerr int NCSIResponse::parseFullPayload()
539b788524aSJeremy Kerr {
540b788524aSJeremy Kerr if (this->full_payload.size() < sizeof(internal::NCSIPacketHeader) +
541b788524aSJeremy Kerr sizeof(internal::NCSIResponsePayload))
542b788524aSJeremy Kerr {
543b788524aSJeremy Kerr lg2::error("Response: Not enough data for a response message");
544b788524aSJeremy Kerr return -1;
545b788524aSJeremy Kerr }
546b788524aSJeremy Kerr
547b788524aSJeremy Kerr internal::NCSIPacketHeader* respHeader =
548b788524aSJeremy Kerr reinterpret_cast<decltype(respHeader)>(this->full_payload.data());
549b788524aSJeremy Kerr
550b788524aSJeremy Kerr unsigned int payloadLen = ntohs(respHeader->length & htons(0x0fff));
551b788524aSJeremy Kerr /* we have determined that the payload size is larger than *respHeader,
552b788524aSJeremy Kerr * so cannot underflow here */
553b788524aSJeremy Kerr if (payloadLen > this->full_payload.size() - sizeof(*respHeader))
554b788524aSJeremy Kerr {
555b788524aSJeremy Kerr lg2::error("Invalid header length {HDRLEN} (vs {LEN}) in response",
556b788524aSJeremy Kerr "HDRLEN", payloadLen, "LEN",
557b788524aSJeremy Kerr this->full_payload.size() - sizeof(*respHeader));
558b788524aSJeremy Kerr return -1;
559b788524aSJeremy Kerr }
560b788524aSJeremy Kerr
561b788524aSJeremy Kerr this->opcode = respHeader->type;
562b788524aSJeremy Kerr this->payload =
563b788524aSJeremy Kerr std::span(this->full_payload.begin() + sizeof(*respHeader), payloadLen);
564b788524aSJeremy Kerr
565b788524aSJeremy Kerr internal::NCSIResponsePayload* respPayload =
566b788524aSJeremy Kerr reinterpret_cast<decltype(respPayload)>(this->payload.data());
567b788524aSJeremy Kerr this->response = ntohs(respPayload->response);
568b788524aSJeremy Kerr this->reason = ntohs(respPayload->reason);
569b788524aSJeremy Kerr
570b788524aSJeremy Kerr return 0;
571b788524aSJeremy Kerr }
572b788524aSJeremy Kerr
573ca9d8677SJeremy Kerr static const uint8_t MCTP_TYPE_NCSI = 2;
574ca9d8677SJeremy Kerr
575ca9d8677SJeremy Kerr struct NCSIResponsePayload
576ca9d8677SJeremy Kerr {
577ca9d8677SJeremy Kerr uint16_t response;
578ca9d8677SJeremy Kerr uint16_t reason;
579ca9d8677SJeremy Kerr };
580ca9d8677SJeremy Kerr
sendCommand(NCSICommand & cmd)581ca9d8677SJeremy Kerr std::optional<NCSIResponse> MCTPInterface::sendCommand(NCSICommand& cmd)
582ca9d8677SJeremy Kerr {
583ca9d8677SJeremy Kerr static constexpr uint8_t mcid = 0; /* no need to distinguish controllers */
584ca9d8677SJeremy Kerr static constexpr size_t maxRespLen = 16384;
585ca9d8677SJeremy Kerr size_t payloadLen, padLen;
586ca9d8677SJeremy Kerr ssize_t wlen, rlen;
587ca9d8677SJeremy Kerr
588ca9d8677SJeremy Kerr payloadLen = cmd.payload.size();
589ca9d8677SJeremy Kerr
590*a42a8651SJeremy Kerr auto tmp = allocateIID();
591*a42a8651SJeremy Kerr if (!tmp.has_value())
592*a42a8651SJeremy Kerr {
593*a42a8651SJeremy Kerr return {};
594*a42a8651SJeremy Kerr }
595*a42a8651SJeremy Kerr uint8_t iid = *tmp;
596*a42a8651SJeremy Kerr
597ca9d8677SJeremy Kerr internal::NCSIPacketHeader cmdHeader{};
598ca9d8677SJeremy Kerr cmdHeader.MCID = mcid;
599ca9d8677SJeremy Kerr cmdHeader.revision = 1;
600ca9d8677SJeremy Kerr cmdHeader.id = iid;
601ca9d8677SJeremy Kerr cmdHeader.type = cmd.opcode;
602ca9d8677SJeremy Kerr cmdHeader.channel = (uint8_t)(cmd.package << 5 | cmd.getChannel());
603ca9d8677SJeremy Kerr cmdHeader.length = htons(payloadLen);
604ca9d8677SJeremy Kerr
605ca9d8677SJeremy Kerr struct iovec iov[3];
606ca9d8677SJeremy Kerr iov[0].iov_base = &cmdHeader;
607ca9d8677SJeremy Kerr iov[0].iov_len = sizeof(cmdHeader);
608ca9d8677SJeremy Kerr iov[1].iov_base = cmd.payload.data();
609ca9d8677SJeremy Kerr iov[1].iov_len = payloadLen;
610ca9d8677SJeremy Kerr
611ca9d8677SJeremy Kerr /* the checksum must appear on a 4-byte boundary */
612ca9d8677SJeremy Kerr padLen = 4 - (payloadLen & 0x3);
613ca9d8677SJeremy Kerr if (padLen == 4)
614ca9d8677SJeremy Kerr {
615ca9d8677SJeremy Kerr padLen = 0;
616ca9d8677SJeremy Kerr }
617ca9d8677SJeremy Kerr uint8_t crc32buf[8] = {};
618ca9d8677SJeremy Kerr /* todo: set csum; zeros currently indicate no checksum present */
619ca9d8677SJeremy Kerr uint32_t crc32 = 0;
620ca9d8677SJeremy Kerr
621ca9d8677SJeremy Kerr memcpy(crc32buf + padLen, &crc32, sizeof(crc32));
622ca9d8677SJeremy Kerr padLen += sizeof(crc32);
623ca9d8677SJeremy Kerr
624ca9d8677SJeremy Kerr iov[2].iov_base = crc32buf;
625ca9d8677SJeremy Kerr iov[2].iov_len = padLen;
626ca9d8677SJeremy Kerr
627ca9d8677SJeremy Kerr struct sockaddr_mctp addr = {};
628ca9d8677SJeremy Kerr addr.smctp_family = AF_MCTP;
629ca9d8677SJeremy Kerr addr.smctp_network = net;
630ca9d8677SJeremy Kerr addr.smctp_addr.s_addr = eid;
631ca9d8677SJeremy Kerr addr.smctp_tag = MCTP_TAG_OWNER;
632ca9d8677SJeremy Kerr addr.smctp_type = MCTP_TYPE_NCSI;
633ca9d8677SJeremy Kerr
634ca9d8677SJeremy Kerr struct msghdr msg = {};
635ca9d8677SJeremy Kerr msg.msg_name = &addr;
636ca9d8677SJeremy Kerr msg.msg_namelen = sizeof(addr);
637ca9d8677SJeremy Kerr msg.msg_iov = iov;
638ca9d8677SJeremy Kerr msg.msg_iovlen = 3;
639ca9d8677SJeremy Kerr
640ca9d8677SJeremy Kerr wlen = sendmsg(sd, &msg, 0);
641ca9d8677SJeremy Kerr if (wlen < 0)
642ca9d8677SJeremy Kerr {
643ca9d8677SJeremy Kerr lg2::error("Failed to send MCTP message, ERRNO: {ERRNO}", "ERRNO",
644ca9d8677SJeremy Kerr -wlen);
645ca9d8677SJeremy Kerr return {};
646ca9d8677SJeremy Kerr }
647ca9d8677SJeremy Kerr else if ((size_t)wlen != sizeof(cmdHeader) + payloadLen + padLen)
648ca9d8677SJeremy Kerr {
649ca9d8677SJeremy Kerr lg2::error("Short write sending MCTP message, LEN: {LEN}", "LEN", wlen);
650ca9d8677SJeremy Kerr return {};
651ca9d8677SJeremy Kerr }
652ca9d8677SJeremy Kerr
653ca9d8677SJeremy Kerr internal::NCSIPacketHeader* respHeader;
654ca9d8677SJeremy Kerr NCSIResponsePayload* respPayload;
655ca9d8677SJeremy Kerr NCSIResponse resp{};
656ca9d8677SJeremy Kerr
657ca9d8677SJeremy Kerr resp.full_payload.resize(maxRespLen);
658ca9d8677SJeremy Kerr iov[0].iov_len = resp.full_payload.size();
659ca9d8677SJeremy Kerr iov[0].iov_base = resp.full_payload.data();
660ca9d8677SJeremy Kerr
661ca9d8677SJeremy Kerr msg.msg_name = &addr;
662ca9d8677SJeremy Kerr msg.msg_namelen = sizeof(addr);
663ca9d8677SJeremy Kerr msg.msg_iov = iov;
664ca9d8677SJeremy Kerr msg.msg_iovlen = 1;
665ca9d8677SJeremy Kerr
666ca9d8677SJeremy Kerr /* we have set SO_RCVTIMEO, so this won't block forever... */
667ca9d8677SJeremy Kerr rlen = recvmsg(sd, &msg, MSG_TRUNC);
668ca9d8677SJeremy Kerr if (rlen < 0)
669ca9d8677SJeremy Kerr {
670ca9d8677SJeremy Kerr lg2::error("Failed to read MCTP response, ERRNO: {ERRNO}", "ERRNO",
671ca9d8677SJeremy Kerr -rlen);
672ca9d8677SJeremy Kerr return {};
673ca9d8677SJeremy Kerr }
674ca9d8677SJeremy Kerr else if ((size_t)rlen < sizeof(*respHeader) + sizeof(*respPayload))
675ca9d8677SJeremy Kerr {
676ca9d8677SJeremy Kerr lg2::error("Short read receiving MCTP message, LEN: {LEN}", "LEN",
677ca9d8677SJeremy Kerr rlen);
678ca9d8677SJeremy Kerr return {};
679ca9d8677SJeremy Kerr }
680ca9d8677SJeremy Kerr else if ((size_t)rlen > maxRespLen)
681ca9d8677SJeremy Kerr {
682ca9d8677SJeremy Kerr lg2::error("MCTP response is too large, LEN: {LEN}", "LEN", rlen);
683ca9d8677SJeremy Kerr return {};
684ca9d8677SJeremy Kerr }
685ca9d8677SJeremy Kerr
686ca9d8677SJeremy Kerr resp.full_payload.resize(rlen);
687ca9d8677SJeremy Kerr
688ca9d8677SJeremy Kerr respHeader =
689ca9d8677SJeremy Kerr reinterpret_cast<decltype(respHeader)>(resp.full_payload.data());
690ca9d8677SJeremy Kerr
691ca9d8677SJeremy Kerr /* header validation */
692ca9d8677SJeremy Kerr if (respHeader->MCID != mcid)
693ca9d8677SJeremy Kerr {
694ca9d8677SJeremy Kerr lg2::error("Invalid MCID {MCID} in response", "MCID", lg2::hex,
695ca9d8677SJeremy Kerr respHeader->MCID);
696ca9d8677SJeremy Kerr return {};
697ca9d8677SJeremy Kerr }
698ca9d8677SJeremy Kerr
699ca9d8677SJeremy Kerr if (respHeader->id != iid)
700ca9d8677SJeremy Kerr {
701ca9d8677SJeremy Kerr lg2::error("Invalid IID {IID} in response", "IID", lg2::hex,
702ca9d8677SJeremy Kerr respHeader->id);
703ca9d8677SJeremy Kerr return {};
704ca9d8677SJeremy Kerr }
705ca9d8677SJeremy Kerr
706ca9d8677SJeremy Kerr if (respHeader->type != (cmd.opcode | 0x80))
707ca9d8677SJeremy Kerr {
708ca9d8677SJeremy Kerr lg2::error("Invalid opcode {OPCODE} in response", "OPCODE", lg2::hex,
709ca9d8677SJeremy Kerr respHeader->type);
710ca9d8677SJeremy Kerr return {};
711ca9d8677SJeremy Kerr }
712ca9d8677SJeremy Kerr
713ca9d8677SJeremy Kerr int rc = resp.parseFullPayload();
714ca9d8677SJeremy Kerr if (rc)
715ca9d8677SJeremy Kerr {
716ca9d8677SJeremy Kerr return {};
717ca9d8677SJeremy Kerr }
718ca9d8677SJeremy Kerr
719ca9d8677SJeremy Kerr return resp;
720ca9d8677SJeremy Kerr }
721ca9d8677SJeremy Kerr
toString()722ca9d8677SJeremy Kerr std::string MCTPInterface::toString()
723ca9d8677SJeremy Kerr {
724ca9d8677SJeremy Kerr return std::to_string(net) + "," + std::to_string(eid);
725ca9d8677SJeremy Kerr }
726ca9d8677SJeremy Kerr
MCTPInterface(int net,uint8_t eid)727ca9d8677SJeremy Kerr MCTPInterface::MCTPInterface(int net, uint8_t eid) : net(net), eid(eid)
728ca9d8677SJeremy Kerr {
729ca9d8677SJeremy Kerr static const struct timeval receiveTimeout = {
730ca9d8677SJeremy Kerr .tv_sec = 1,
731ca9d8677SJeremy Kerr .tv_usec = 0,
732ca9d8677SJeremy Kerr };
733ca9d8677SJeremy Kerr
734ca9d8677SJeremy Kerr int _sd = socket(AF_MCTP, SOCK_DGRAM, 0);
735ca9d8677SJeremy Kerr if (_sd < 0)
736ca9d8677SJeremy Kerr {
737ca9d8677SJeremy Kerr throw std::system_error(errno, std::system_category(),
738ca9d8677SJeremy Kerr "Can't create MCTP socket");
739ca9d8677SJeremy Kerr }
740ca9d8677SJeremy Kerr
741ca9d8677SJeremy Kerr int rc = setsockopt(_sd, SOL_SOCKET, SO_RCVTIMEO, &receiveTimeout,
742ca9d8677SJeremy Kerr sizeof(receiveTimeout));
743ca9d8677SJeremy Kerr if (rc != 0)
744ca9d8677SJeremy Kerr {
745ca9d8677SJeremy Kerr throw std::system_error(errno, std::system_category(),
746ca9d8677SJeremy Kerr "Can't set socket receive timemout");
747ca9d8677SJeremy Kerr }
748ca9d8677SJeremy Kerr
749ca9d8677SJeremy Kerr sd = _sd;
750ca9d8677SJeremy Kerr }
751ca9d8677SJeremy Kerr
~MCTPInterface()752ca9d8677SJeremy Kerr MCTPInterface::~MCTPInterface()
753ca9d8677SJeremy Kerr {
754ca9d8677SJeremy Kerr close(sd);
755ca9d8677SJeremy Kerr }
756ca9d8677SJeremy Kerr
757*a42a8651SJeremy Kerr /* Small fd wrapper to provide RAII semantics, closing the IID file descriptor
758*a42a8651SJeremy Kerr * when we go out of scope.
759*a42a8651SJeremy Kerr */
760*a42a8651SJeremy Kerr struct IidFd
761*a42a8651SJeremy Kerr {
762*a42a8651SJeremy Kerr int fd;
IidFdphosphor::network::ncsi::IidFd763*a42a8651SJeremy Kerr IidFd(int _fd) : fd(_fd) {};
~IidFdphosphor::network::ncsi::IidFd764*a42a8651SJeremy Kerr ~IidFd()
765*a42a8651SJeremy Kerr {
766*a42a8651SJeremy Kerr close(fd);
767*a42a8651SJeremy Kerr };
768*a42a8651SJeremy Kerr };
769*a42a8651SJeremy Kerr
allocateIID()770*a42a8651SJeremy Kerr std::optional<uint8_t> MCTPInterface::allocateIID()
771*a42a8651SJeremy Kerr {
772*a42a8651SJeremy Kerr int fd = open(mctp_iid_path, O_RDWR | O_CREAT, 0600);
773*a42a8651SJeremy Kerr if (fd < 0)
774*a42a8651SJeremy Kerr {
775*a42a8651SJeremy Kerr lg2::warning("Error opening IID database {FILE}: {ERROR}", "FILE",
776*a42a8651SJeremy Kerr mctp_iid_path, "ERROR", strerror(errno));
777*a42a8651SJeremy Kerr return {};
778*a42a8651SJeremy Kerr }
779*a42a8651SJeremy Kerr
780*a42a8651SJeremy Kerr IidFd iidFd(fd);
781*a42a8651SJeremy Kerr
782*a42a8651SJeremy Kerr /* lock while we read/modity/write; the lock will be short-lived, so
783*a42a8651SJeremy Kerr * we keep it simple and lock the entire file range
784*a42a8651SJeremy Kerr */
785*a42a8651SJeremy Kerr struct flock flock = {
786*a42a8651SJeremy Kerr .l_type = F_WRLCK,
787*a42a8651SJeremy Kerr .l_whence = SEEK_SET,
788*a42a8651SJeremy Kerr .l_start = 0,
789*a42a8651SJeremy Kerr .l_len = 0,
790*a42a8651SJeremy Kerr .l_pid = 0,
791*a42a8651SJeremy Kerr };
792*a42a8651SJeremy Kerr
793*a42a8651SJeremy Kerr int rc = fcntl(iidFd.fd, F_OFD_SETLKW, &flock);
794*a42a8651SJeremy Kerr if (rc)
795*a42a8651SJeremy Kerr {
796*a42a8651SJeremy Kerr lg2::warning("Error locking IID database {FILE}: {ERROR}", "FILE",
797*a42a8651SJeremy Kerr mctp_iid_path, "ERROR", strerror(errno));
798*a42a8651SJeremy Kerr return {};
799*a42a8651SJeremy Kerr }
800*a42a8651SJeremy Kerr
801*a42a8651SJeremy Kerr /* An EOF (rc == 0) would indicate that we don't yet have an entry for that
802*a42a8651SJeremy Kerr * eid, which we handle as iid = 0.
803*a42a8651SJeremy Kerr */
804*a42a8651SJeremy Kerr uint8_t iid = 0;
805*a42a8651SJeremy Kerr rc = pread(iidFd.fd, &iid, sizeof(iid), eid);
806*a42a8651SJeremy Kerr if (rc < 0)
807*a42a8651SJeremy Kerr {
808*a42a8651SJeremy Kerr lg2::warning("Error reading IID database {FILE}: {ERROR}", "FILE",
809*a42a8651SJeremy Kerr mctp_iid_path, "ERROR", strerror(errno));
810*a42a8651SJeremy Kerr return {};
811*a42a8651SJeremy Kerr }
812*a42a8651SJeremy Kerr
813*a42a8651SJeremy Kerr /* DSP0222 defines valid IIDs in the range [1, 0xff], so manually wrap */
814*a42a8651SJeremy Kerr if (iid == 0xff)
815*a42a8651SJeremy Kerr {
816*a42a8651SJeremy Kerr iid = 1;
817*a42a8651SJeremy Kerr }
818*a42a8651SJeremy Kerr else
819*a42a8651SJeremy Kerr {
820*a42a8651SJeremy Kerr iid++;
821*a42a8651SJeremy Kerr }
822*a42a8651SJeremy Kerr
823*a42a8651SJeremy Kerr rc = pwrite(iidFd.fd, &iid, sizeof(iid), eid);
824*a42a8651SJeremy Kerr if (rc != sizeof(iid))
825*a42a8651SJeremy Kerr {
826*a42a8651SJeremy Kerr lg2::warning("Error writing IID database {FILE}: {ERROR}", "FILE",
827*a42a8651SJeremy Kerr mctp_iid_path, "ERROR", strerror(errno));
828*a42a8651SJeremy Kerr return {};
829*a42a8651SJeremy Kerr }
830*a42a8651SJeremy Kerr
831*a42a8651SJeremy Kerr return iid;
832*a42a8651SJeremy Kerr }
833*a42a8651SJeremy Kerr
8341bbe3d1eSWilliam A. Kennington III } // namespace ncsi
8351bbe3d1eSWilliam A. Kennington III } // namespace network
8361bbe3d1eSWilliam A. Kennington III } // namespace phosphor
837