xref: /openbmc/phosphor-networkd/src/ncsi_util.cpp (revision a42a8651da4462615e5d22a9c048583f9bde6fdd)
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