blob: 4def9fe498be551017eb10eb0e7be8bf8b03096e [file] [log] [blame]
Patrick Venture189d44e2018-07-09 12:30:59 -07001#include "ncsi_util.hpp"
2
Jeremy Kerra42a8652024-12-05 11:15:35 +08003#include <errno.h>
4#include <fcntl.h>
Jeremy Kerrca9d8672024-09-16 14:22:02 +08005#include <linux/mctp.h>
Ratan Guptabbe45792018-03-23 00:22:55 +05306#include <linux/ncsi.h>
Ratan Guptabbe45792018-03-23 00:22:55 +05307#include <netlink/genl/ctrl.h>
Gunnar Mills57d9c502018-09-14 14:42:34 -05008#include <netlink/genl/genl.h>
9#include <netlink/netlink.h>
Jeremy Kerrca9d8672024-09-16 14:22:02 +080010#include <unistd.h>
Ratan Guptabbe45792018-03-23 00:22:55 +053011
Patrick Williams89d734b2023-05-10 07:50:25 -050012#include <phosphor-logging/lg2.hpp>
13
Jeremy Kerrb7885242024-09-16 12:43:36 +080014#include <optional>
15#include <span>
Jeremy Kerrca9d8672024-09-16 14:22:02 +080016#include <system_error>
Jiaqing Zhao7c44a782022-04-10 15:30:04 +080017#include <vector>
Ratan Guptabbe45792018-03-23 00:22:55 +053018
19namespace phosphor
20{
21namespace network
22{
23namespace ncsi
24{
25
Jeremy Kerra42a8652024-12-05 11:15:35 +080026static const char* mctp_iid_path = "/run/ncsi-mctp-iids";
27
Jeremy Kerrb7885242024-09-16 12:43:36 +080028NCSICommand::NCSICommand(uint8_t opcode, uint8_t package,
29 std::optional<uint8_t> channel,
30 std::span<unsigned char> payload) :
31 opcode(opcode), package(package), channel(channel)
32{
33 this->payload.assign(payload.begin(), payload.end());
34}
35
36uint8_t NCSICommand::getChannel()
37{
38 return channel.value_or(CHANNEL_ID_NONE);
39}
40
Gunnar Mills57d9c502018-09-14 14:42:34 -050041using CallBack = int (*)(struct nl_msg* msg, void* arg);
Ratan Guptaaac603e2018-03-23 00:25:54 +053042
43namespace internal
44{
Ratan Guptabbe45792018-03-23 00:22:55 +053045
Eddie Jamesfa1f5c02020-09-17 15:12:46 -050046struct NCSIPacketHeader
47{
48 uint8_t MCID;
49 uint8_t revision;
50 uint8_t reserved;
51 uint8_t id;
52 uint8_t type;
53 uint8_t channel;
54 uint16_t length;
55 uint32_t rsvd[2];
56};
57
Jeremy Kerrb7885242024-09-16 12:43:36 +080058struct NCSIResponsePayload
59{
60 uint16_t response;
61 uint16_t reason;
62};
63
Jeremy Kerr3f34ff62024-09-12 13:00:27 +080064class NetlinkCommand
Eddie Jamesfa1f5c02020-09-17 15:12:46 -050065{
66 public:
Jeremy Kerr3f34ff62024-09-12 13:00:27 +080067 NetlinkCommand() = delete;
68 ~NetlinkCommand() = default;
69 NetlinkCommand(const NetlinkCommand&) = delete;
70 NetlinkCommand& operator=(const NetlinkCommand&) = delete;
71 NetlinkCommand(NetlinkCommand&&) = default;
72 NetlinkCommand& operator=(NetlinkCommand&&) = default;
73 NetlinkCommand(
Johnathan Mantey1ebea282024-02-15 10:26:06 -080074 int ncsiCmd, int operation = DEFAULT_VALUE,
Eddie Jamesfa1f5c02020-09-17 15:12:46 -050075 std::span<const unsigned char> p = std::span<const unsigned char>()) :
Patrick Williamsad205022024-08-16 15:20:07 -040076 ncsi_cmd(ncsiCmd), operation(operation), payload(p)
Patrick Williams89d734b2023-05-10 07:50:25 -050077 {}
Eddie Jamesfa1f5c02020-09-17 15:12:46 -050078
Eddie Jamesfa1f5c02020-09-17 15:12:46 -050079 int ncsi_cmd;
Johnathan Mantey1ebea282024-02-15 10:26:06 -080080 int operation;
Eddie Jamesfa1f5c02020-09-17 15:12:46 -050081 std::span<const unsigned char> payload;
82};
83
Ratan Guptabbe45792018-03-23 00:22:55 +053084using nlMsgPtr = std::unique_ptr<nl_msg, decltype(&::nlmsg_free)>;
85using nlSocketPtr = std::unique_ptr<nl_sock, decltype(&::nl_socket_free)>;
86
Jeremy Kerr7f7c0852024-08-08 11:32:55 +080087struct infoCallBackContext
88{
89 InterfaceInfo* info;
90};
91
92CallBack infoCallBack = [](struct nl_msg* msg, void* arg) {
93 if (arg == nullptr)
94 {
95 lg2::error("Internal error: invalid info callback context");
96 return -1;
97 }
98
99 struct infoCallBackContext* info = (struct infoCallBackContext*)arg;
Ratan Guptaaac603e2018-03-23 00:25:54 +0530100 using namespace phosphor::network::ncsi;
101 auto nlh = nlmsg_hdr(msg);
102
Gunnar Mills57d9c502018-09-14 14:42:34 -0500103 struct nlattr* tb[NCSI_ATTR_MAX + 1] = {nullptr};
104 struct nla_policy ncsiPolicy[NCSI_ATTR_MAX + 1] = {
William A. Kennington III05368f12021-05-13 18:40:47 -0700105 {NLA_UNSPEC, 0, 0}, {NLA_U32, 0, 0}, {NLA_NESTED, 0, 0},
106 {NLA_U32, 0, 0}, {NLA_U32, 0, 0},
Ratan Guptaaac603e2018-03-23 00:25:54 +0530107 };
108
Gunnar Mills57d9c502018-09-14 14:42:34 -0500109 struct nlattr* packagetb[NCSI_PKG_ATTR_MAX + 1] = {nullptr};
110 struct nla_policy packagePolicy[NCSI_PKG_ATTR_MAX + 1] = {
William A. Kennington III05368f12021-05-13 18:40:47 -0700111 {NLA_UNSPEC, 0, 0}, {NLA_NESTED, 0, 0}, {NLA_U32, 0, 0},
112 {NLA_FLAG, 0, 0}, {NLA_NESTED, 0, 0},
Ratan Guptaaac603e2018-03-23 00:25:54 +0530113 };
114
Gunnar Mills57d9c502018-09-14 14:42:34 -0500115 struct nlattr* channeltb[NCSI_CHANNEL_ATTR_MAX + 1] = {nullptr};
116 struct nla_policy channelPolicy[NCSI_CHANNEL_ATTR_MAX + 1] = {
William A. Kennington III05368f12021-05-13 18:40:47 -0700117 {NLA_UNSPEC, 0, 0}, {NLA_NESTED, 0, 0}, {NLA_U32, 0, 0},
118 {NLA_FLAG, 0, 0}, {NLA_NESTED, 0, 0}, {NLA_UNSPEC, 0, 0},
Ratan Guptaaac603e2018-03-23 00:25:54 +0530119 };
120
121 auto ret = genlmsg_parse(nlh, 0, tb, NCSI_ATTR_MAX, ncsiPolicy);
122 if (!tb[NCSI_ATTR_PACKAGE_LIST])
123 {
Jagpal Singh Gilld423beb2023-04-18 11:28:03 -0700124 lg2::error("No Packages");
Ratan Guptaaac603e2018-03-23 00:25:54 +0530125 return -1;
126 }
127
128 auto attrTgt = static_cast<nlattr*>(nla_data(tb[NCSI_ATTR_PACKAGE_LIST]));
129 if (!attrTgt)
130 {
Jagpal Singh Gilld423beb2023-04-18 11:28:03 -0700131 lg2::error("Package list attribute is null");
Ratan Guptaaac603e2018-03-23 00:25:54 +0530132 return -1;
133 }
134
135 auto rem = nla_len(tb[NCSI_ATTR_PACKAGE_LIST]);
136 nla_for_each_nested(attrTgt, tb[NCSI_ATTR_PACKAGE_LIST], rem)
137 {
138 ret = nla_parse_nested(packagetb, NCSI_PKG_ATTR_MAX, attrTgt,
139 packagePolicy);
140 if (ret < 0)
141 {
Jagpal Singh Gilld423beb2023-04-18 11:28:03 -0700142 lg2::error("Failed to parse package nested");
Ratan Guptaaac603e2018-03-23 00:25:54 +0530143 return -1;
144 }
145
Jeremy Kerr7f7c0852024-08-08 11:32:55 +0800146 PackageInfo pkg;
147
Ratan Guptaaac603e2018-03-23 00:25:54 +0530148 if (packagetb[NCSI_PKG_ATTR_ID])
149 {
150 auto attrID = nla_get_u32(packagetb[NCSI_PKG_ATTR_ID]);
Jeremy Kerr7f7c0852024-08-08 11:32:55 +0800151 pkg.id = attrID;
Ratan Guptaaac603e2018-03-23 00:25:54 +0530152 }
153 else
154 {
Jagpal Singh Gilld423beb2023-04-18 11:28:03 -0700155 lg2::debug("Package with no id");
Ratan Guptaaac603e2018-03-23 00:25:54 +0530156 }
157
158 if (packagetb[NCSI_PKG_ATTR_FORCED])
159 {
Jeremy Kerr7f7c0852024-08-08 11:32:55 +0800160 pkg.forced = true;
Ratan Guptaaac603e2018-03-23 00:25:54 +0530161 }
162
163 auto channelListTarget = static_cast<nlattr*>(
Gunnar Mills57d9c502018-09-14 14:42:34 -0500164 nla_data(packagetb[NCSI_PKG_ATTR_CHANNEL_LIST]));
Ratan Guptaaac603e2018-03-23 00:25:54 +0530165
166 auto channelrem = nla_len(packagetb[NCSI_PKG_ATTR_CHANNEL_LIST]);
167 nla_for_each_nested(channelListTarget,
168 packagetb[NCSI_PKG_ATTR_CHANNEL_LIST], channelrem)
169 {
170 ret = nla_parse_nested(channeltb, NCSI_CHANNEL_ATTR_MAX,
171 channelListTarget, channelPolicy);
172 if (ret < 0)
173 {
Jagpal Singh Gilld423beb2023-04-18 11:28:03 -0700174 lg2::error("Failed to parse channel nested");
Jeremy Kerr7f7c0852024-08-08 11:32:55 +0800175 continue;
Ratan Guptaaac603e2018-03-23 00:25:54 +0530176 }
177
Jeremy Kerr7f7c0852024-08-08 11:32:55 +0800178 ChannelInfo chan;
179
Ratan Guptaaac603e2018-03-23 00:25:54 +0530180 if (channeltb[NCSI_CHANNEL_ATTR_ID])
181 {
Jeremy Kerr7f7c0852024-08-08 11:32:55 +0800182 chan.id = nla_get_u32(channeltb[NCSI_CHANNEL_ATTR_ID]);
183 chan.active = !!channeltb[NCSI_CHANNEL_ATTR_ACTIVE];
184 chan.forced = !!channeltb[NCSI_CHANNEL_ATTR_FORCED];
Ratan Guptaaac603e2018-03-23 00:25:54 +0530185 }
186 else
187 {
Jagpal Singh Gilld423beb2023-04-18 11:28:03 -0700188 lg2::debug("Channel with no ID");
Jeremy Kerr7f7c0852024-08-08 11:32:55 +0800189 continue;
Ratan Guptaaac603e2018-03-23 00:25:54 +0530190 }
Ratan Guptaed5d7ff2018-03-23 00:27:52 +0530191
Ratan Guptaaac603e2018-03-23 00:25:54 +0530192 if (channeltb[NCSI_CHANNEL_ATTR_VERSION_MAJOR])
193 {
Jeremy Kerr7f7c0852024-08-08 11:32:55 +0800194 chan.version_major =
Gunnar Mills57d9c502018-09-14 14:42:34 -0500195 nla_get_u32(channeltb[NCSI_CHANNEL_ATTR_VERSION_MAJOR]);
Ratan Guptaaac603e2018-03-23 00:25:54 +0530196 }
197 if (channeltb[NCSI_CHANNEL_ATTR_VERSION_MINOR])
198 {
Jeremy Kerr7f7c0852024-08-08 11:32:55 +0800199 chan.version_minor =
Gunnar Mills57d9c502018-09-14 14:42:34 -0500200 nla_get_u32(channeltb[NCSI_CHANNEL_ATTR_VERSION_MINOR]);
Ratan Guptaaac603e2018-03-23 00:25:54 +0530201 }
202 if (channeltb[NCSI_CHANNEL_ATTR_VERSION_STR])
203 {
Jeremy Kerr7f7c0852024-08-08 11:32:55 +0800204 chan.version =
Gunnar Mills57d9c502018-09-14 14:42:34 -0500205 nla_get_string(channeltb[NCSI_CHANNEL_ATTR_VERSION_STR]);
Ratan Guptaaac603e2018-03-23 00:25:54 +0530206 }
207 if (channeltb[NCSI_CHANNEL_ATTR_LINK_STATE])
208 {
Jeremy Kerr7f7c0852024-08-08 11:32:55 +0800209 chan.link_state =
Gunnar Mills57d9c502018-09-14 14:42:34 -0500210 nla_get_u32(channeltb[NCSI_CHANNEL_ATTR_LINK_STATE]);
Ratan Guptaaac603e2018-03-23 00:25:54 +0530211 }
212 if (channeltb[NCSI_CHANNEL_ATTR_VLAN_LIST])
213 {
Ratan Guptaaac603e2018-03-23 00:25:54 +0530214 auto vids = channeltb[NCSI_CHANNEL_ATTR_VLAN_LIST];
215 auto vid = static_cast<nlattr*>(nla_data(vids));
216 auto len = nla_len(vids);
217 while (nla_ok(vid, len))
218 {
219 auto id = nla_get_u16(vid);
Jeremy Kerr7f7c0852024-08-08 11:32:55 +0800220 chan.vlan_ids.push_back(id);
Ratan Guptaaac603e2018-03-23 00:25:54 +0530221 vid = nla_next(vid, &len);
222 }
223 }
Jeremy Kerr7f7c0852024-08-08 11:32:55 +0800224 pkg.channels.push_back(chan);
Ratan Guptaaac603e2018-03-23 00:25:54 +0530225 }
Jeremy Kerr7f7c0852024-08-08 11:32:55 +0800226
227 info->info->packages.push_back(pkg);
Ratan Guptaaac603e2018-03-23 00:25:54 +0530228 }
Jeremy Kerrbea6cde2024-08-01 15:15:42 +0800229 return static_cast<int>(NL_STOP);
Ratan Guptaaac603e2018-03-23 00:25:54 +0530230};
231
Jeremy Kerr147086d2024-08-27 13:46:38 +0800232struct sendCallBackContext
233{
Jeremy Kerrb7885242024-09-16 12:43:36 +0800234 NCSIResponse resp;
Jeremy Kerr147086d2024-08-27 13:46:38 +0800235};
236
237CallBack sendCallBack = [](struct nl_msg* msg, void* arg) {
Eddie Jamesfa1f5c02020-09-17 15:12:46 -0500238 using namespace phosphor::network::ncsi;
239 auto nlh = nlmsg_hdr(msg);
240 struct nlattr* tb[NCSI_ATTR_MAX + 1] = {nullptr};
241 static struct nla_policy ncsiPolicy[NCSI_ATTR_MAX + 1] = {
242 {NLA_UNSPEC, 0, 0}, {NLA_U32, 0, 0}, {NLA_NESTED, 0, 0},
243 {NLA_U32, 0, 0}, {NLA_U32, 0, 0}, {NLA_BINARY, 0, 0},
244 {NLA_FLAG, 0, 0}, {NLA_U32, 0, 0}, {NLA_U32, 0, 0},
245 };
246
Jeremy Kerr147086d2024-08-27 13:46:38 +0800247 if (arg == nullptr)
248 {
249 lg2::error("Internal error: invalid send callback context");
250 return -1;
251 }
252
253 struct sendCallBackContext* ctx = (struct sendCallBackContext*)arg;
254
Eddie Jamesfa1f5c02020-09-17 15:12:46 -0500255 auto ret = genlmsg_parse(nlh, 0, tb, NCSI_ATTR_MAX, ncsiPolicy);
256 if (ret)
257 {
Jeremy Kerrde8d6662024-08-01 11:33:52 +0800258 lg2::error("Failed to parse message");
Eddie Jamesfa1f5c02020-09-17 15:12:46 -0500259 return ret;
260 }
261
Jian Zhang442d9e52022-10-20 22:11:14 +0800262 if (tb[NCSI_ATTR_DATA] == nullptr)
263 {
Jagpal Singh Gilld423beb2023-04-18 11:28:03 -0700264 lg2::error("Response: No data");
Jian Zhang442d9e52022-10-20 22:11:14 +0800265 return -1;
266 }
267
Jeremy Kerrb7885242024-09-16 12:43:36 +0800268 size_t data_len = nla_len(tb[NCSI_ATTR_DATA]);
269 unsigned char* data = (unsigned char*)nla_data(tb[NCSI_ATTR_DATA]);
Eddie Jamesfa1f5c02020-09-17 15:12:46 -0500270
Jeremy Kerrb7885242024-09-16 12:43:36 +0800271 ctx->resp.full_payload.assign(data, data + data_len);
272
273 int rc = ctx->resp.parseFullPayload();
274 if (rc)
275 {
276 return -1;
277 }
Eddie Jamesfa1f5c02020-09-17 15:12:46 -0500278
Jeremy Kerrbea6cde2024-08-01 15:15:42 +0800279 return static_cast<int>(NL_STOP);
Eddie Jamesfa1f5c02020-09-17 15:12:46 -0500280};
281
Jeremy Kerr2d0b48d2024-09-16 13:03:26 +0800282int applyCmd(NetlinkInterface& interface, const NetlinkCommand& cmd,
Jeremy Kerr8d9af022024-07-26 16:47:16 +0800283 int package = DEFAULT_VALUE, int channel = DEFAULT_VALUE,
Jeremy Kerr7f7c0852024-08-08 11:32:55 +0800284 int flags = NONE, CallBack function = nullptr, void* arg = nullptr)
Ratan Guptabbe45792018-03-23 00:22:55 +0530285{
Gunnar Mills57d9c502018-09-14 14:42:34 -0500286 nlSocketPtr socket(nl_socket_alloc(), &::nl_socket_free);
Johnathan Manteyd49c5c62021-06-23 09:14:42 -0700287 if (socket == nullptr)
288 {
Jagpal Singh Gilld423beb2023-04-18 11:28:03 -0700289 lg2::error("Unable to allocate memory for the socket");
Johnathan Manteyd49c5c62021-06-23 09:14:42 -0700290 return -ENOMEM;
291 }
292
Jeremy Kerrbea6cde2024-08-01 15:15:42 +0800293 nl_socket_disable_auto_ack(socket.get());
294
Ratan Guptabbe45792018-03-23 00:22:55 +0530295 auto ret = genl_connect(socket.get());
296 if (ret < 0)
297 {
Jagpal Singh Gilld423beb2023-04-18 11:28:03 -0700298 lg2::error("Failed to open the socket , RC : {RC}", "RC", ret);
Ratan Guptabbe45792018-03-23 00:22:55 +0530299 return ret;
300 }
301
302 auto driverID = genl_ctrl_resolve(socket.get(), "NCSI");
303 if (driverID < 0)
304 {
Jagpal Singh Gilld423beb2023-04-18 11:28:03 -0700305 lg2::error("Failed to resolve, RC : {RC}", "RC", ret);
Ratan Guptabbe45792018-03-23 00:22:55 +0530306 return driverID;
307 }
308
309 nlMsgPtr msg(nlmsg_alloc(), &::nlmsg_free);
Johnathan Manteyd49c5c62021-06-23 09:14:42 -0700310 if (msg == nullptr)
311 {
Jagpal Singh Gilld423beb2023-04-18 11:28:03 -0700312 lg2::error("Unable to allocate memory for the message");
Johnathan Manteyd49c5c62021-06-23 09:14:42 -0700313 return -ENOMEM;
314 }
Ratan Guptabbe45792018-03-23 00:22:55 +0530315
Johnathan Mantey1ebea282024-02-15 10:26:06 -0800316 auto msgHdr = genlmsg_put(msg.get(), NL_AUTO_PORT, NL_AUTO_SEQ, driverID, 0,
317 flags, cmd.ncsi_cmd, 0);
Ratan Guptabbe45792018-03-23 00:22:55 +0530318 if (!msgHdr)
319 {
Jagpal Singh Gilld423beb2023-04-18 11:28:03 -0700320 lg2::error("Unable to add the netlink headers , COMMAND : {COMMAND}",
Johnathan Mantey1ebea282024-02-15 10:26:06 -0800321 "COMMAND", cmd.ncsi_cmd);
Johnathan Manteyd49c5c62021-06-23 09:14:42 -0700322 return -ENOMEM;
Ratan Guptabbe45792018-03-23 00:22:55 +0530323 }
324
325 if (package != DEFAULT_VALUE)
326 {
327 ret = nla_put_u32(msg.get(), ncsi_nl_attrs::NCSI_ATTR_PACKAGE_ID,
328 package);
329 if (ret < 0)
330 {
Jagpal Singh Gilld423beb2023-04-18 11:28:03 -0700331 lg2::error("Failed to set the attribute , RC : {RC} PACKAGE "
332 "{PACKAGE}",
333 "RC", ret, "PACKAGE", lg2::hex, package);
Ratan Guptabbe45792018-03-23 00:22:55 +0530334 return ret;
335 }
336 }
337
338 if (channel != DEFAULT_VALUE)
339 {
340 ret = nla_put_u32(msg.get(), ncsi_nl_attrs::NCSI_ATTR_CHANNEL_ID,
341 channel);
342 if (ret < 0)
343 {
Jagpal Singh Gilld423beb2023-04-18 11:28:03 -0700344 lg2::error("Failed to set the attribute , RC : {RC} CHANNEL : "
345 "{CHANNEL}",
346 "RC", ret, "CHANNEL", lg2::hex, channel);
Ratan Guptabbe45792018-03-23 00:22:55 +0530347 return ret;
348 }
349 }
350
Jeremy Kerr8d9af022024-07-26 16:47:16 +0800351 ret = nla_put_u32(msg.get(), ncsi_nl_attrs::NCSI_ATTR_IFINDEX,
352 interface.ifindex);
Ratan Guptabbe45792018-03-23 00:22:55 +0530353 if (ret < 0)
354 {
Jagpal Singh Gilld423beb2023-04-18 11:28:03 -0700355 lg2::error("Failed to set the attribute , RC : {RC} INTERFACE : "
356 "{INTERFACE}",
Jeremy Kerr8a76d892024-07-26 17:19:57 +0800357 "RC", ret, "INTERFACE", interface);
Ratan Guptabbe45792018-03-23 00:22:55 +0530358 return ret;
359 }
360
Johnathan Mantey5a456062024-02-15 08:45:08 -0800361 if ((cmd.ncsi_cmd == ncsi_nl_commands::NCSI_CMD_SET_PACKAGE_MASK) ||
362 (cmd.ncsi_cmd == ncsi_nl_commands::NCSI_CMD_SET_CHANNEL_MASK))
363 {
364 if (cmd.payload.size() != sizeof(unsigned int))
365 {
366 lg2::error("Package/Channel mask must be 32-bits");
367 return -EINVAL;
368 }
369 int maskAttr =
370 cmd.ncsi_cmd == ncsi_nl_commands::NCSI_CMD_SET_PACKAGE_MASK
371 ? NCSI_ATTR_PACKAGE_MASK
372 : NCSI_ATTR_CHANNEL_MASK;
373 ret = nla_put_u32(
374 msg.get(), maskAttr,
375 *(reinterpret_cast<const unsigned int*>(cmd.payload.data())));
376 if (ret < 0)
377 {
378 lg2::error("Failed to set the mask attribute, RC : {RC}", "RC",
379 ret);
380 return ret;
381 }
382 }
383 else if (cmd.ncsi_cmd == ncsi_nl_commands::NCSI_CMD_SEND_CMD)
Eddie Jamesfa1f5c02020-09-17 15:12:46 -0500384 {
Patrick Williamsad205022024-08-16 15:20:07 -0400385 std::vector<unsigned char> pl(
386 sizeof(NCSIPacketHeader) + cmd.payload.size());
Eddie Jamesfa1f5c02020-09-17 15:12:46 -0500387 NCSIPacketHeader* hdr = (NCSIPacketHeader*)pl.data();
388
389 std::copy(cmd.payload.begin(), cmd.payload.end(),
390 pl.begin() + sizeof(NCSIPacketHeader));
391
Johnathan Mantey1ebea282024-02-15 10:26:06 -0800392 hdr->type = cmd.operation;
Eddie Jamesfa1f5c02020-09-17 15:12:46 -0500393 hdr->length = htons(cmd.payload.size());
394
395 ret = nla_put(msg.get(), ncsi_nl_attrs::NCSI_ATTR_DATA, pl.size(),
396 pl.data());
397 if (ret < 0)
398 {
Jagpal Singh Gilld423beb2023-04-18 11:28:03 -0700399 lg2::error("Failed to set the data attribute, RC : {RC}", "RC",
400 ret);
Eddie Jamesfa1f5c02020-09-17 15:12:46 -0500401 return ret;
402 }
403
404 nl_socket_disable_seq_check(socket.get());
405 }
406
Jeremy Kerr67b159a2024-08-01 15:23:57 +0800407 // Add a callback function to the socket
408 enum nl_cb_kind cb_kind = function ? NL_CB_CUSTOM : NL_CB_DEFAULT;
Jeremy Kerr7f7c0852024-08-08 11:32:55 +0800409 nl_socket_modify_cb(socket.get(), NL_CB_VALID, cb_kind, function, arg);
Ratan Guptabbe45792018-03-23 00:22:55 +0530410
411 ret = nl_send_auto(socket.get(), msg.get());
412 if (ret < 0)
413 {
Jagpal Singh Gilld423beb2023-04-18 11:28:03 -0700414 lg2::error("Failed to send the message , RC : {RC}", "RC", ret);
Ratan Guptabbe45792018-03-23 00:22:55 +0530415 return ret;
416 }
417
Jeremy Kerrbea6cde2024-08-01 15:15:42 +0800418 ret = nl_recvmsgs_default(socket.get());
419 if (ret < 0)
Ratan Guptabbe45792018-03-23 00:22:55 +0530420 {
Jeremy Kerrbea6cde2024-08-01 15:15:42 +0800421 lg2::error("Failed to receive the message , RC : {RC}", "RC", ret);
422 return ret;
423 }
Eddie Jamesfa1f5c02020-09-17 15:12:46 -0500424
Jeremy Kerrbea6cde2024-08-01 15:15:42 +0800425 return 0;
Ratan Guptabbe45792018-03-23 00:22:55 +0530426}
427
Gunnar Mills57d9c502018-09-14 14:42:34 -0500428} // namespace internal
Ratan Guptabbe45792018-03-23 00:22:55 +0530429
Jeremy Kerr8a76d892024-07-26 17:19:57 +0800430std::string to_string(Interface& interface)
431{
Jeremy Kerr2d0b48d2024-09-16 13:03:26 +0800432 return interface.toString();
Jeremy Kerr8a76d892024-07-26 17:19:57 +0800433}
434
Jeremy Kerr2d0b48d2024-09-16 13:03:26 +0800435NetlinkInterface::NetlinkInterface(int ifindex) : ifindex(ifindex) {}
436
437std::string NetlinkInterface::toString()
438{
439 return std::to_string(ifindex);
440}
441
442std::optional<NCSIResponse> NetlinkInterface::sendCommand(NCSICommand& cmd)
Eddie Jamesfa1f5c02020-09-17 15:12:46 -0500443{
Jeremy Kerrb7885242024-09-16 12:43:36 +0800444 lg2::debug("Send Command, CHANNEL : {CHANNEL} , PACKAGE : {PACKAGE}, "
Jeremy Kerr8a76d892024-07-26 17:19:57 +0800445 "INTERFACE: {INTERFACE}",
Jeremy Kerrb7885242024-09-16 12:43:36 +0800446 "CHANNEL", lg2::hex, cmd.getChannel(), "PACKAGE", lg2::hex,
447 cmd.package, "INTERFACE", this);
Eddie Jamesfa1f5c02020-09-17 15:12:46 -0500448
Jeremy Kerr147086d2024-08-27 13:46:38 +0800449 internal::sendCallBackContext ctx{};
450
Jeremy Kerrb7885242024-09-16 12:43:36 +0800451 internal::NetlinkCommand nl_cmd(ncsi_nl_commands::NCSI_CMD_SEND_CMD,
452 cmd.opcode, cmd.payload);
Jeremy Kerr3f34ff62024-09-12 13:00:27 +0800453
Jeremy Kerrb7885242024-09-16 12:43:36 +0800454 int rc = internal::applyCmd(*this, nl_cmd, cmd.package, cmd.getChannel(),
455 NONE, internal::sendCallBack, &ctx);
Jeremy Kerr147086d2024-08-27 13:46:38 +0800456
457 if (rc < 0)
458 {
459 return {};
460 }
461
Jeremy Kerrb7885242024-09-16 12:43:36 +0800462 return ctx.resp;
Eddie Jamesfa1f5c02020-09-17 15:12:46 -0500463}
464
Jeremy Kerr2d0b48d2024-09-16 13:03:26 +0800465int NetlinkInterface::setChannel(int package, int channel)
Ratan Guptabbe45792018-03-23 00:22:55 +0530466{
Jeremy Kerr8a76d892024-07-26 17:19:57 +0800467 lg2::debug("Set CHANNEL : {CHANNEL} , PACKAGE : {PACKAGE}, INTERFACE : "
468 "{INTERFACE}",
469 "CHANNEL", lg2::hex, channel, "PACKAGE", lg2::hex, package,
Jeremy Kerrbc22f812024-07-29 17:43:35 +0800470 "INTERFACE", this);
Jeremy Kerr3f34ff62024-09-12 13:00:27 +0800471
472 internal::NetlinkCommand cmd(ncsi_nl_commands::NCSI_CMD_SET_INTERFACE);
473
474 return internal::applyCmd(*this, cmd, package, channel);
Ratan Guptabbe45792018-03-23 00:22:55 +0530475}
476
Jeremy Kerr2d0b48d2024-09-16 13:03:26 +0800477int NetlinkInterface::clearInterface()
Ratan Guptabbe45792018-03-23 00:22:55 +0530478{
Jeremy Kerrbc22f812024-07-29 17:43:35 +0800479 lg2::debug("ClearInterface , INTERFACE : {INTERFACE}", "INTERFACE", this);
Jeremy Kerr3f34ff62024-09-12 13:00:27 +0800480
481 internal::NetlinkCommand cmd(ncsi_nl_commands::NCSI_CMD_CLEAR_INTERFACE);
482 return internal::applyCmd(*this, cmd);
Ratan Guptabbe45792018-03-23 00:22:55 +0530483}
484
Jeremy Kerr2d0b48d2024-09-16 13:03:26 +0800485std::optional<InterfaceInfo> NetlinkInterface::getInfo(int package)
Ratan Guptaaac603e2018-03-23 00:25:54 +0530486{
Jeremy Kerr7f7c0852024-08-08 11:32:55 +0800487 int rc, flags = package == DEFAULT_VALUE ? NLM_F_DUMP : NONE;
488 InterfaceInfo info;
489
Jeremy Kerr8a76d892024-07-26 17:19:57 +0800490 lg2::debug("Get Info , PACKAGE : {PACKAGE}, INTERFACE: {INTERFACE}",
Jeremy Kerrbc22f812024-07-29 17:43:35 +0800491 "PACKAGE", lg2::hex, package, "INTERFACE", this);
Jeremy Kerr7f7c0852024-08-08 11:32:55 +0800492
493 struct internal::infoCallBackContext ctx = {
494 .info = &info,
495 };
496
Jeremy Kerr3f34ff62024-09-12 13:00:27 +0800497 internal::NetlinkCommand cmd(ncsi_nl_commands::NCSI_CMD_PKG_INFO);
498
499 rc = internal::applyCmd(*this, cmd, package, DEFAULT_VALUE, flags,
500 internal::infoCallBack, &ctx);
Jeremy Kerr7f7c0852024-08-08 11:32:55 +0800501
502 if (rc < 0)
Ratan Guptaaac603e2018-03-23 00:25:54 +0530503 {
Jeremy Kerr7f7c0852024-08-08 11:32:55 +0800504 return {};
Ratan Guptaaac603e2018-03-23 00:25:54 +0530505 }
Jeremy Kerr7f7c0852024-08-08 11:32:55 +0800506
507 return info;
Ratan Guptaaac603e2018-03-23 00:25:54 +0530508}
509
Jeremy Kerr2d0b48d2024-09-16 13:03:26 +0800510int NetlinkInterface::setPackageMask(unsigned int mask)
Johnathan Mantey5a456062024-02-15 08:45:08 -0800511{
Jeremy Kerr8a76d892024-07-26 17:19:57 +0800512 lg2::debug("Set Package Mask , INTERFACE: {INTERFACE} MASK: {MASK}",
Jeremy Kerrbc22f812024-07-29 17:43:35 +0800513 "INTERFACE", this, "MASK", lg2::hex, mask);
Johnathan Mantey5a456062024-02-15 08:45:08 -0800514 auto payload = std::span<const unsigned char>(
515 reinterpret_cast<const unsigned char*>(&mask),
516 reinterpret_cast<const unsigned char*>(&mask) + sizeof(decltype(mask)));
Jeremy Kerr3f34ff62024-09-12 13:00:27 +0800517
518 internal::NetlinkCommand cmd(ncsi_nl_commands::NCSI_CMD_SET_PACKAGE_MASK, 0,
519 payload);
520 return internal::applyCmd(*this, cmd);
Johnathan Mantey5a456062024-02-15 08:45:08 -0800521}
522
Jeremy Kerr2d0b48d2024-09-16 13:03:26 +0800523int NetlinkInterface::setChannelMask(int package, unsigned int mask)
Johnathan Mantey5a456062024-02-15 08:45:08 -0800524{
525 lg2::debug(
Jeremy Kerr8a76d892024-07-26 17:19:57 +0800526 "Set Channel Mask , INTERFACE: {INTERFACE}, PACKAGE : {PACKAGE} MASK: {MASK}",
Jeremy Kerrbc22f812024-07-29 17:43:35 +0800527 "INTERFACE", this, "PACKAGE", lg2::hex, package, "MASK", lg2::hex,
Jeremy Kerr8a76d892024-07-26 17:19:57 +0800528 mask);
Johnathan Mantey5a456062024-02-15 08:45:08 -0800529 auto payload = std::span<const unsigned char>(
530 reinterpret_cast<const unsigned char*>(&mask),
531 reinterpret_cast<const unsigned char*>(&mask) + sizeof(decltype(mask)));
Jeremy Kerr3f34ff62024-09-12 13:00:27 +0800532
533 internal::NetlinkCommand cmd(ncsi_nl_commands::NCSI_CMD_SET_CHANNEL_MASK, 0,
534 payload);
535 return internal::applyCmd(*this, cmd);
Johnathan Mantey5a456062024-02-15 08:45:08 -0800536}
537
Jeremy Kerrb7885242024-09-16 12:43:36 +0800538int NCSIResponse::parseFullPayload()
539{
540 if (this->full_payload.size() < sizeof(internal::NCSIPacketHeader) +
541 sizeof(internal::NCSIResponsePayload))
542 {
543 lg2::error("Response: Not enough data for a response message");
544 return -1;
545 }
546
547 internal::NCSIPacketHeader* respHeader =
548 reinterpret_cast<decltype(respHeader)>(this->full_payload.data());
549
550 unsigned int payloadLen = ntohs(respHeader->length & htons(0x0fff));
551 /* we have determined that the payload size is larger than *respHeader,
552 * so cannot underflow here */
553 if (payloadLen > this->full_payload.size() - sizeof(*respHeader))
554 {
555 lg2::error("Invalid header length {HDRLEN} (vs {LEN}) in response",
556 "HDRLEN", payloadLen, "LEN",
557 this->full_payload.size() - sizeof(*respHeader));
558 return -1;
559 }
560
561 this->opcode = respHeader->type;
562 this->payload =
563 std::span(this->full_payload.begin() + sizeof(*respHeader), payloadLen);
564
565 internal::NCSIResponsePayload* respPayload =
566 reinterpret_cast<decltype(respPayload)>(this->payload.data());
567 this->response = ntohs(respPayload->response);
568 this->reason = ntohs(respPayload->reason);
569
570 return 0;
571}
572
Jeremy Kerrca9d8672024-09-16 14:22:02 +0800573static const uint8_t MCTP_TYPE_NCSI = 2;
574
575struct NCSIResponsePayload
576{
577 uint16_t response;
578 uint16_t reason;
579};
580
581std::optional<NCSIResponse> MCTPInterface::sendCommand(NCSICommand& cmd)
582{
Jeremy Kerrca9d8672024-09-16 14:22:02 +0800583 static constexpr uint8_t mcid = 0; /* no need to distinguish controllers */
584 static constexpr size_t maxRespLen = 16384;
585 size_t payloadLen, padLen;
586 ssize_t wlen, rlen;
587
588 payloadLen = cmd.payload.size();
589
Jeremy Kerra42a8652024-12-05 11:15:35 +0800590 auto tmp = allocateIID();
591 if (!tmp.has_value())
592 {
593 return {};
594 }
595 uint8_t iid = *tmp;
596
Jeremy Kerrca9d8672024-09-16 14:22:02 +0800597 internal::NCSIPacketHeader cmdHeader{};
598 cmdHeader.MCID = mcid;
599 cmdHeader.revision = 1;
600 cmdHeader.id = iid;
601 cmdHeader.type = cmd.opcode;
602 cmdHeader.channel = (uint8_t)(cmd.package << 5 | cmd.getChannel());
603 cmdHeader.length = htons(payloadLen);
604
605 struct iovec iov[3];
606 iov[0].iov_base = &cmdHeader;
607 iov[0].iov_len = sizeof(cmdHeader);
608 iov[1].iov_base = cmd.payload.data();
609 iov[1].iov_len = payloadLen;
610
611 /* the checksum must appear on a 4-byte boundary */
612 padLen = 4 - (payloadLen & 0x3);
613 if (padLen == 4)
614 {
615 padLen = 0;
616 }
617 uint8_t crc32buf[8] = {};
618 /* todo: set csum; zeros currently indicate no checksum present */
619 uint32_t crc32 = 0;
620
621 memcpy(crc32buf + padLen, &crc32, sizeof(crc32));
622 padLen += sizeof(crc32);
623
624 iov[2].iov_base = crc32buf;
625 iov[2].iov_len = padLen;
626
627 struct sockaddr_mctp addr = {};
628 addr.smctp_family = AF_MCTP;
629 addr.smctp_network = net;
630 addr.smctp_addr.s_addr = eid;
631 addr.smctp_tag = MCTP_TAG_OWNER;
632 addr.smctp_type = MCTP_TYPE_NCSI;
633
634 struct msghdr msg = {};
635 msg.msg_name = &addr;
636 msg.msg_namelen = sizeof(addr);
637 msg.msg_iov = iov;
638 msg.msg_iovlen = 3;
639
640 wlen = sendmsg(sd, &msg, 0);
641 if (wlen < 0)
642 {
643 lg2::error("Failed to send MCTP message, ERRNO: {ERRNO}", "ERRNO",
644 -wlen);
645 return {};
646 }
647 else if ((size_t)wlen != sizeof(cmdHeader) + payloadLen + padLen)
648 {
649 lg2::error("Short write sending MCTP message, LEN: {LEN}", "LEN", wlen);
650 return {};
651 }
652
653 internal::NCSIPacketHeader* respHeader;
654 NCSIResponsePayload* respPayload;
655 NCSIResponse resp{};
656
657 resp.full_payload.resize(maxRespLen);
658 iov[0].iov_len = resp.full_payload.size();
659 iov[0].iov_base = resp.full_payload.data();
660
661 msg.msg_name = &addr;
662 msg.msg_namelen = sizeof(addr);
663 msg.msg_iov = iov;
664 msg.msg_iovlen = 1;
665
666 /* we have set SO_RCVTIMEO, so this won't block forever... */
667 rlen = recvmsg(sd, &msg, MSG_TRUNC);
668 if (rlen < 0)
669 {
670 lg2::error("Failed to read MCTP response, ERRNO: {ERRNO}", "ERRNO",
671 -rlen);
672 return {};
673 }
674 else if ((size_t)rlen < sizeof(*respHeader) + sizeof(*respPayload))
675 {
676 lg2::error("Short read receiving MCTP message, LEN: {LEN}", "LEN",
677 rlen);
678 return {};
679 }
680 else if ((size_t)rlen > maxRespLen)
681 {
682 lg2::error("MCTP response is too large, LEN: {LEN}", "LEN", rlen);
683 return {};
684 }
685
686 resp.full_payload.resize(rlen);
687
688 respHeader =
689 reinterpret_cast<decltype(respHeader)>(resp.full_payload.data());
690
691 /* header validation */
692 if (respHeader->MCID != mcid)
693 {
694 lg2::error("Invalid MCID {MCID} in response", "MCID", lg2::hex,
695 respHeader->MCID);
696 return {};
697 }
698
699 if (respHeader->id != iid)
700 {
701 lg2::error("Invalid IID {IID} in response", "IID", lg2::hex,
702 respHeader->id);
703 return {};
704 }
705
706 if (respHeader->type != (cmd.opcode | 0x80))
707 {
708 lg2::error("Invalid opcode {OPCODE} in response", "OPCODE", lg2::hex,
709 respHeader->type);
710 return {};
711 }
712
713 int rc = resp.parseFullPayload();
714 if (rc)
715 {
716 return {};
717 }
718
719 return resp;
720}
721
722std::string MCTPInterface::toString()
723{
724 return std::to_string(net) + "," + std::to_string(eid);
725}
726
727MCTPInterface::MCTPInterface(int net, uint8_t eid) : net(net), eid(eid)
728{
729 static const struct timeval receiveTimeout = {
730 .tv_sec = 1,
731 .tv_usec = 0,
732 };
733
734 int _sd = socket(AF_MCTP, SOCK_DGRAM, 0);
735 if (_sd < 0)
736 {
737 throw std::system_error(errno, std::system_category(),
738 "Can't create MCTP socket");
739 }
740
741 int rc = setsockopt(_sd, SOL_SOCKET, SO_RCVTIMEO, &receiveTimeout,
742 sizeof(receiveTimeout));
743 if (rc != 0)
744 {
745 throw std::system_error(errno, std::system_category(),
746 "Can't set socket receive timemout");
747 }
748
749 sd = _sd;
750}
751
752MCTPInterface::~MCTPInterface()
753{
754 close(sd);
755}
756
Jeremy Kerra42a8652024-12-05 11:15:35 +0800757/* Small fd wrapper to provide RAII semantics, closing the IID file descriptor
758 * when we go out of scope.
759 */
760struct IidFd
761{
762 int fd;
763 IidFd(int _fd) : fd(_fd) {};
764 ~IidFd()
765 {
766 close(fd);
767 };
768};
769
770std::optional<uint8_t> MCTPInterface::allocateIID()
771{
772 int fd = open(mctp_iid_path, O_RDWR | O_CREAT, 0600);
773 if (fd < 0)
774 {
775 lg2::warning("Error opening IID database {FILE}: {ERROR}", "FILE",
776 mctp_iid_path, "ERROR", strerror(errno));
777 return {};
778 }
779
780 IidFd iidFd(fd);
781
782 /* lock while we read/modity/write; the lock will be short-lived, so
783 * we keep it simple and lock the entire file range
784 */
785 struct flock flock = {
786 .l_type = F_WRLCK,
787 .l_whence = SEEK_SET,
788 .l_start = 0,
789 .l_len = 0,
790 .l_pid = 0,
791 };
792
793 int rc = fcntl(iidFd.fd, F_OFD_SETLKW, &flock);
794 if (rc)
795 {
796 lg2::warning("Error locking IID database {FILE}: {ERROR}", "FILE",
797 mctp_iid_path, "ERROR", strerror(errno));
798 return {};
799 }
800
801 /* An EOF (rc == 0) would indicate that we don't yet have an entry for that
802 * eid, which we handle as iid = 0.
803 */
804 uint8_t iid = 0;
805 rc = pread(iidFd.fd, &iid, sizeof(iid), eid);
806 if (rc < 0)
807 {
808 lg2::warning("Error reading IID database {FILE}: {ERROR}", "FILE",
809 mctp_iid_path, "ERROR", strerror(errno));
810 return {};
811 }
812
813 /* DSP0222 defines valid IIDs in the range [1, 0xff], so manually wrap */
814 if (iid == 0xff)
815 {
816 iid = 1;
817 }
818 else
819 {
820 iid++;
821 }
822
823 rc = pwrite(iidFd.fd, &iid, sizeof(iid), eid);
824 if (rc != sizeof(iid))
825 {
826 lg2::warning("Error writing IID database {FILE}: {ERROR}", "FILE",
827 mctp_iid_path, "ERROR", strerror(errno));
828 return {};
829 }
830
831 return iid;
832}
833
Gunnar Mills57d9c502018-09-14 14:42:34 -0500834} // namespace ncsi
835} // namespace network
836} // namespace phosphor