blob: e5f8b0b4b0c0d831cd24059cc4b50c0f89dc8cb1 [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
Adi Fogel6c4859e2025-11-16 15:26:23 +020012#include <iostream>
13#include <memory>
Jeremy Kerrb7885242024-09-16 12:43:36 +080014#include <optional>
15#include <span>
Adi Fogel6c4859e2025-11-16 15:26:23 +020016#include <sstream>
Jeremy Kerrca9d8672024-09-16 14:22:02 +080017#include <system_error>
Jiaqing Zhao7c44a782022-04-10 15:30:04 +080018#include <vector>
Ratan Guptabbe45792018-03-23 00:22:55 +053019
20namespace phosphor
21{
22namespace network
23{
24namespace ncsi
25{
26
Jeremy Kerra42a8652024-12-05 11:15:35 +080027static const char* mctp_iid_path = "/run/ncsi-mctp-iids";
28
Jeremy Kerrb7885242024-09-16 12:43:36 +080029NCSICommand::NCSICommand(uint8_t opcode, uint8_t package,
30 std::optional<uint8_t> channel,
31 std::span<unsigned char> payload) :
32 opcode(opcode), package(package), channel(channel)
33{
34 this->payload.assign(payload.begin(), payload.end());
35}
36
37uint8_t NCSICommand::getChannel()
38{
39 return channel.value_or(CHANNEL_ID_NONE);
40}
41
Gunnar Mills57d9c502018-09-14 14:42:34 -050042using CallBack = int (*)(struct nl_msg* msg, void* arg);
Ratan Guptaaac603e2018-03-23 00:25:54 +053043
44namespace internal
45{
Ratan Guptabbe45792018-03-23 00:22:55 +053046
Eddie Jamesfa1f5c02020-09-17 15:12:46 -050047struct NCSIPacketHeader
48{
49 uint8_t MCID;
50 uint8_t revision;
51 uint8_t reserved;
52 uint8_t id;
53 uint8_t type;
54 uint8_t channel;
55 uint16_t length;
56 uint32_t rsvd[2];
57};
58
Jeremy Kerrb7885242024-09-16 12:43:36 +080059struct NCSIResponsePayload
60{
61 uint16_t response;
62 uint16_t reason;
63};
64
Jeremy Kerr3f34ff62024-09-12 13:00:27 +080065class NetlinkCommand
Eddie Jamesfa1f5c02020-09-17 15:12:46 -050066{
67 public:
Jeremy Kerr3f34ff62024-09-12 13:00:27 +080068 NetlinkCommand() = delete;
69 ~NetlinkCommand() = default;
70 NetlinkCommand(const NetlinkCommand&) = delete;
71 NetlinkCommand& operator=(const NetlinkCommand&) = delete;
72 NetlinkCommand(NetlinkCommand&&) = default;
73 NetlinkCommand& operator=(NetlinkCommand&&) = default;
74 NetlinkCommand(
Johnathan Mantey1ebea282024-02-15 10:26:06 -080075 int ncsiCmd, int operation = DEFAULT_VALUE,
Eddie Jamesfa1f5c02020-09-17 15:12:46 -050076 std::span<const unsigned char> p = std::span<const unsigned char>()) :
Patrick Williamsad205022024-08-16 15:20:07 -040077 ncsi_cmd(ncsiCmd), operation(operation), payload(p)
Patrick Williams89d734b2023-05-10 07:50:25 -050078 {}
Eddie Jamesfa1f5c02020-09-17 15:12:46 -050079
Eddie Jamesfa1f5c02020-09-17 15:12:46 -050080 int ncsi_cmd;
Johnathan Mantey1ebea282024-02-15 10:26:06 -080081 int operation;
Eddie Jamesfa1f5c02020-09-17 15:12:46 -050082 std::span<const unsigned char> payload;
83};
84
Ratan Guptabbe45792018-03-23 00:22:55 +053085using nlMsgPtr = std::unique_ptr<nl_msg, decltype(&::nlmsg_free)>;
86using nlSocketPtr = std::unique_ptr<nl_sock, decltype(&::nl_socket_free)>;
87
Jeremy Kerr7f7c0852024-08-08 11:32:55 +080088struct infoCallBackContext
89{
90 InterfaceInfo* info;
91};
92
93CallBack infoCallBack = [](struct nl_msg* msg, void* arg) {
94 if (arg == nullptr)
95 {
Adi Fogel6c4859e2025-11-16 15:26:23 +020096 std::cerr << "Internal error: invalid info callback context"
97 << std::endl;
Jeremy Kerr7f7c0852024-08-08 11:32:55 +080098 return -1;
99 }
100
101 struct infoCallBackContext* info = (struct infoCallBackContext*)arg;
Ratan Guptaaac603e2018-03-23 00:25:54 +0530102 using namespace phosphor::network::ncsi;
103 auto nlh = nlmsg_hdr(msg);
104
Gunnar Mills57d9c502018-09-14 14:42:34 -0500105 struct nlattr* tb[NCSI_ATTR_MAX + 1] = {nullptr};
106 struct nla_policy ncsiPolicy[NCSI_ATTR_MAX + 1] = {
William A. Kennington III05368f12021-05-13 18:40:47 -0700107 {NLA_UNSPEC, 0, 0}, {NLA_U32, 0, 0}, {NLA_NESTED, 0, 0},
108 {NLA_U32, 0, 0}, {NLA_U32, 0, 0},
Ratan Guptaaac603e2018-03-23 00:25:54 +0530109 };
110
Gunnar Mills57d9c502018-09-14 14:42:34 -0500111 struct nlattr* packagetb[NCSI_PKG_ATTR_MAX + 1] = {nullptr};
112 struct nla_policy packagePolicy[NCSI_PKG_ATTR_MAX + 1] = {
William A. Kennington III05368f12021-05-13 18:40:47 -0700113 {NLA_UNSPEC, 0, 0}, {NLA_NESTED, 0, 0}, {NLA_U32, 0, 0},
114 {NLA_FLAG, 0, 0}, {NLA_NESTED, 0, 0},
Ratan Guptaaac603e2018-03-23 00:25:54 +0530115 };
116
Gunnar Mills57d9c502018-09-14 14:42:34 -0500117 struct nlattr* channeltb[NCSI_CHANNEL_ATTR_MAX + 1] = {nullptr};
118 struct nla_policy channelPolicy[NCSI_CHANNEL_ATTR_MAX + 1] = {
William A. Kennington III05368f12021-05-13 18:40:47 -0700119 {NLA_UNSPEC, 0, 0}, {NLA_NESTED, 0, 0}, {NLA_U32, 0, 0},
120 {NLA_FLAG, 0, 0}, {NLA_NESTED, 0, 0}, {NLA_UNSPEC, 0, 0},
Ratan Guptaaac603e2018-03-23 00:25:54 +0530121 };
122
123 auto ret = genlmsg_parse(nlh, 0, tb, NCSI_ATTR_MAX, ncsiPolicy);
124 if (!tb[NCSI_ATTR_PACKAGE_LIST])
125 {
Adi Fogel6c4859e2025-11-16 15:26:23 +0200126 std::cerr << "No Packages" << std::endl;
Ratan Guptaaac603e2018-03-23 00:25:54 +0530127 return -1;
128 }
129
130 auto attrTgt = static_cast<nlattr*>(nla_data(tb[NCSI_ATTR_PACKAGE_LIST]));
131 if (!attrTgt)
132 {
Adi Fogel6c4859e2025-11-16 15:26:23 +0200133 std::cerr << "Package list attribute is null" << std::endl;
Ratan Guptaaac603e2018-03-23 00:25:54 +0530134 return -1;
135 }
136
137 auto rem = nla_len(tb[NCSI_ATTR_PACKAGE_LIST]);
138 nla_for_each_nested(attrTgt, tb[NCSI_ATTR_PACKAGE_LIST], rem)
139 {
140 ret = nla_parse_nested(packagetb, NCSI_PKG_ATTR_MAX, attrTgt,
141 packagePolicy);
142 if (ret < 0)
143 {
Adi Fogel6c4859e2025-11-16 15:26:23 +0200144 std::cerr << "Failed to parse package nested" << std::endl;
Ratan Guptaaac603e2018-03-23 00:25:54 +0530145 return -1;
146 }
147
Jeremy Kerr7f7c0852024-08-08 11:32:55 +0800148 PackageInfo pkg;
149
Ratan Guptaaac603e2018-03-23 00:25:54 +0530150 if (packagetb[NCSI_PKG_ATTR_ID])
151 {
152 auto attrID = nla_get_u32(packagetb[NCSI_PKG_ATTR_ID]);
Jeremy Kerr7f7c0852024-08-08 11:32:55 +0800153 pkg.id = attrID;
Ratan Guptaaac603e2018-03-23 00:25:54 +0530154 }
155 else
156 {
Adi Fogel6c4859e2025-11-16 15:26:23 +0200157 std::cout << "Package with no id" << std::endl;
Ratan Guptaaac603e2018-03-23 00:25:54 +0530158 }
159
160 if (packagetb[NCSI_PKG_ATTR_FORCED])
161 {
Jeremy Kerr7f7c0852024-08-08 11:32:55 +0800162 pkg.forced = true;
Ratan Guptaaac603e2018-03-23 00:25:54 +0530163 }
164
165 auto channelListTarget = static_cast<nlattr*>(
Gunnar Mills57d9c502018-09-14 14:42:34 -0500166 nla_data(packagetb[NCSI_PKG_ATTR_CHANNEL_LIST]));
Ratan Guptaaac603e2018-03-23 00:25:54 +0530167
168 auto channelrem = nla_len(packagetb[NCSI_PKG_ATTR_CHANNEL_LIST]);
169 nla_for_each_nested(channelListTarget,
170 packagetb[NCSI_PKG_ATTR_CHANNEL_LIST], channelrem)
171 {
172 ret = nla_parse_nested(channeltb, NCSI_CHANNEL_ATTR_MAX,
173 channelListTarget, channelPolicy);
174 if (ret < 0)
175 {
Adi Fogel6c4859e2025-11-16 15:26:23 +0200176 std::cerr << "Failed to parse channel nested" << std::endl;
Jeremy Kerr7f7c0852024-08-08 11:32:55 +0800177 continue;
Ratan Guptaaac603e2018-03-23 00:25:54 +0530178 }
179
Hariharan Rangasamy64dd8c92025-10-30 08:43:06 +0530180 ChannelInfo chan{};
Jeremy Kerr7f7c0852024-08-08 11:32:55 +0800181
Ratan Guptaaac603e2018-03-23 00:25:54 +0530182 if (channeltb[NCSI_CHANNEL_ATTR_ID])
183 {
Jeremy Kerr7f7c0852024-08-08 11:32:55 +0800184 chan.id = nla_get_u32(channeltb[NCSI_CHANNEL_ATTR_ID]);
185 chan.active = !!channeltb[NCSI_CHANNEL_ATTR_ACTIVE];
186 chan.forced = !!channeltb[NCSI_CHANNEL_ATTR_FORCED];
Ratan Guptaaac603e2018-03-23 00:25:54 +0530187 }
188 else
189 {
Adi Fogel6c4859e2025-11-16 15:26:23 +0200190 std::cout << "Channel with no ID" << std::endl;
Jeremy Kerr7f7c0852024-08-08 11:32:55 +0800191 continue;
Ratan Guptaaac603e2018-03-23 00:25:54 +0530192 }
Ratan Guptaed5d7ff2018-03-23 00:27:52 +0530193
Ratan Guptaaac603e2018-03-23 00:25:54 +0530194 if (channeltb[NCSI_CHANNEL_ATTR_VERSION_MAJOR])
195 {
Jeremy Kerr7f7c0852024-08-08 11:32:55 +0800196 chan.version_major =
Gunnar Mills57d9c502018-09-14 14:42:34 -0500197 nla_get_u32(channeltb[NCSI_CHANNEL_ATTR_VERSION_MAJOR]);
Ratan Guptaaac603e2018-03-23 00:25:54 +0530198 }
199 if (channeltb[NCSI_CHANNEL_ATTR_VERSION_MINOR])
200 {
Jeremy Kerr7f7c0852024-08-08 11:32:55 +0800201 chan.version_minor =
Gunnar Mills57d9c502018-09-14 14:42:34 -0500202 nla_get_u32(channeltb[NCSI_CHANNEL_ATTR_VERSION_MINOR]);
Ratan Guptaaac603e2018-03-23 00:25:54 +0530203 }
204 if (channeltb[NCSI_CHANNEL_ATTR_VERSION_STR])
205 {
Jeremy Kerr7f7c0852024-08-08 11:32:55 +0800206 chan.version =
Gunnar Mills57d9c502018-09-14 14:42:34 -0500207 nla_get_string(channeltb[NCSI_CHANNEL_ATTR_VERSION_STR]);
Ratan Guptaaac603e2018-03-23 00:25:54 +0530208 }
209 if (channeltb[NCSI_CHANNEL_ATTR_LINK_STATE])
210 {
Jeremy Kerr7f7c0852024-08-08 11:32:55 +0800211 chan.link_state =
Gunnar Mills57d9c502018-09-14 14:42:34 -0500212 nla_get_u32(channeltb[NCSI_CHANNEL_ATTR_LINK_STATE]);
Ratan Guptaaac603e2018-03-23 00:25:54 +0530213 }
214 if (channeltb[NCSI_CHANNEL_ATTR_VLAN_LIST])
215 {
Ratan Guptaaac603e2018-03-23 00:25:54 +0530216 auto vids = channeltb[NCSI_CHANNEL_ATTR_VLAN_LIST];
217 auto vid = static_cast<nlattr*>(nla_data(vids));
218 auto len = nla_len(vids);
219 while (nla_ok(vid, len))
220 {
221 auto id = nla_get_u16(vid);
Jeremy Kerr7f7c0852024-08-08 11:32:55 +0800222 chan.vlan_ids.push_back(id);
Ratan Guptaaac603e2018-03-23 00:25:54 +0530223 vid = nla_next(vid, &len);
224 }
225 }
Jeremy Kerr7f7c0852024-08-08 11:32:55 +0800226 pkg.channels.push_back(chan);
Ratan Guptaaac603e2018-03-23 00:25:54 +0530227 }
Jeremy Kerr7f7c0852024-08-08 11:32:55 +0800228
229 info->info->packages.push_back(pkg);
Ratan Guptaaac603e2018-03-23 00:25:54 +0530230 }
Jeremy Kerrbea6cde2024-08-01 15:15:42 +0800231 return static_cast<int>(NL_STOP);
Ratan Guptaaac603e2018-03-23 00:25:54 +0530232};
233
Jeremy Kerr147086d2024-08-27 13:46:38 +0800234struct sendCallBackContext
235{
Jeremy Kerrb7885242024-09-16 12:43:36 +0800236 NCSIResponse resp;
Jeremy Kerr147086d2024-08-27 13:46:38 +0800237};
238
239CallBack sendCallBack = [](struct nl_msg* msg, void* arg) {
Eddie Jamesfa1f5c02020-09-17 15:12:46 -0500240 using namespace phosphor::network::ncsi;
241 auto nlh = nlmsg_hdr(msg);
242 struct nlattr* tb[NCSI_ATTR_MAX + 1] = {nullptr};
243 static struct nla_policy ncsiPolicy[NCSI_ATTR_MAX + 1] = {
244 {NLA_UNSPEC, 0, 0}, {NLA_U32, 0, 0}, {NLA_NESTED, 0, 0},
245 {NLA_U32, 0, 0}, {NLA_U32, 0, 0}, {NLA_BINARY, 0, 0},
246 {NLA_FLAG, 0, 0}, {NLA_U32, 0, 0}, {NLA_U32, 0, 0},
247 };
248
Jeremy Kerr147086d2024-08-27 13:46:38 +0800249 if (arg == nullptr)
250 {
Adi Fogel6c4859e2025-11-16 15:26:23 +0200251 std::cerr << "Internal error: invalid send callback context"
252 << std::endl;
Jeremy Kerr147086d2024-08-27 13:46:38 +0800253 return -1;
254 }
255
256 struct sendCallBackContext* ctx = (struct sendCallBackContext*)arg;
257
Eddie Jamesfa1f5c02020-09-17 15:12:46 -0500258 auto ret = genlmsg_parse(nlh, 0, tb, NCSI_ATTR_MAX, ncsiPolicy);
259 if (ret)
260 {
Adi Fogel6c4859e2025-11-16 15:26:23 +0200261 std::cerr << "Failed to parse message" << std::endl;
Eddie Jamesfa1f5c02020-09-17 15:12:46 -0500262 return ret;
263 }
264
Jian Zhang442d9e52022-10-20 22:11:14 +0800265 if (tb[NCSI_ATTR_DATA] == nullptr)
266 {
Adi Fogel6c4859e2025-11-16 15:26:23 +0200267 std::cerr << "Response: No data" << std::endl;
Jian Zhang442d9e52022-10-20 22:11:14 +0800268 return -1;
269 }
270
Jeremy Kerrb7885242024-09-16 12:43:36 +0800271 size_t data_len = nla_len(tb[NCSI_ATTR_DATA]);
272 unsigned char* data = (unsigned char*)nla_data(tb[NCSI_ATTR_DATA]);
Eddie Jamesfa1f5c02020-09-17 15:12:46 -0500273
Jeremy Kerrb7885242024-09-16 12:43:36 +0800274 ctx->resp.full_payload.assign(data, data + data_len);
275
276 int rc = ctx->resp.parseFullPayload();
277 if (rc)
278 {
279 return -1;
280 }
Eddie Jamesfa1f5c02020-09-17 15:12:46 -0500281
Jeremy Kerrbea6cde2024-08-01 15:15:42 +0800282 return static_cast<int>(NL_STOP);
Eddie Jamesfa1f5c02020-09-17 15:12:46 -0500283};
284
Jeremy Kerr2d0b48d2024-09-16 13:03:26 +0800285int applyCmd(NetlinkInterface& interface, const NetlinkCommand& cmd,
Jeremy Kerr8d9af022024-07-26 16:47:16 +0800286 int package = DEFAULT_VALUE, int channel = DEFAULT_VALUE,
Jeremy Kerr7f7c0852024-08-08 11:32:55 +0800287 int flags = NONE, CallBack function = nullptr, void* arg = nullptr)
Ratan Guptabbe45792018-03-23 00:22:55 +0530288{
Gunnar Mills57d9c502018-09-14 14:42:34 -0500289 nlSocketPtr socket(nl_socket_alloc(), &::nl_socket_free);
Johnathan Manteyd49c5c62021-06-23 09:14:42 -0700290 if (socket == nullptr)
291 {
Adi Fogel6c4859e2025-11-16 15:26:23 +0200292 std::cerr << "Unable to allocate memory for the socket" << std::endl;
Johnathan Manteyd49c5c62021-06-23 09:14:42 -0700293 return -ENOMEM;
294 }
295
Jeremy Kerrbea6cde2024-08-01 15:15:42 +0800296 nl_socket_disable_auto_ack(socket.get());
297
Ratan Guptabbe45792018-03-23 00:22:55 +0530298 auto ret = genl_connect(socket.get());
299 if (ret < 0)
300 {
Adi Fogel6c4859e2025-11-16 15:26:23 +0200301 std::cerr << "Failed to open the socket , RC : " << ret << std::endl;
Ratan Guptabbe45792018-03-23 00:22:55 +0530302 return ret;
303 }
304
305 auto driverID = genl_ctrl_resolve(socket.get(), "NCSI");
306 if (driverID < 0)
307 {
Adi Fogel6c4859e2025-11-16 15:26:23 +0200308 std::cerr << "Failed to resolve, RC : " << ret << std::endl;
Ratan Guptabbe45792018-03-23 00:22:55 +0530309 return driverID;
310 }
311
312 nlMsgPtr msg(nlmsg_alloc(), &::nlmsg_free);
Johnathan Manteyd49c5c62021-06-23 09:14:42 -0700313 if (msg == nullptr)
314 {
Adi Fogel6c4859e2025-11-16 15:26:23 +0200315 std::cerr << "Unable to allocate memory for the message" << std::endl;
Johnathan Manteyd49c5c62021-06-23 09:14:42 -0700316 return -ENOMEM;
317 }
Ratan Guptabbe45792018-03-23 00:22:55 +0530318
Johnathan Mantey1ebea282024-02-15 10:26:06 -0800319 auto msgHdr = genlmsg_put(msg.get(), NL_AUTO_PORT, NL_AUTO_SEQ, driverID, 0,
320 flags, cmd.ncsi_cmd, 0);
Ratan Guptabbe45792018-03-23 00:22:55 +0530321 if (!msgHdr)
322 {
Adi Fogel6c4859e2025-11-16 15:26:23 +0200323 std::cerr << "Unable to add the netlink headers , COMMAND : "
324 << cmd.ncsi_cmd << std::endl;
Johnathan Manteyd49c5c62021-06-23 09:14:42 -0700325 return -ENOMEM;
Ratan Guptabbe45792018-03-23 00:22:55 +0530326 }
327
328 if (package != DEFAULT_VALUE)
329 {
330 ret = nla_put_u32(msg.get(), ncsi_nl_attrs::NCSI_ATTR_PACKAGE_ID,
331 package);
332 if (ret < 0)
333 {
Adi Fogel6c4859e2025-11-16 15:26:23 +0200334 std::cerr << "Failed to set the attribute , RC : " << ret
335 << " PACKAGE " << package << std::endl;
Ratan Guptabbe45792018-03-23 00:22:55 +0530336 return ret;
337 }
338 }
339
340 if (channel != DEFAULT_VALUE)
341 {
342 ret = nla_put_u32(msg.get(), ncsi_nl_attrs::NCSI_ATTR_CHANNEL_ID,
343 channel);
344 if (ret < 0)
345 {
Adi Fogel6c4859e2025-11-16 15:26:23 +0200346 std::cerr << "Failed to set the attribute , RC : " << ret
347 << " CHANNEL : " << channel << std::endl;
Ratan Guptabbe45792018-03-23 00:22:55 +0530348 return ret;
349 }
350 }
351
Jeremy Kerr8d9af022024-07-26 16:47:16 +0800352 ret = nla_put_u32(msg.get(), ncsi_nl_attrs::NCSI_ATTR_IFINDEX,
353 interface.ifindex);
Ratan Guptabbe45792018-03-23 00:22:55 +0530354 if (ret < 0)
355 {
Adi Fogel6c4859e2025-11-16 15:26:23 +0200356 std::cerr << "Failed to set the attribute , RC : " << ret
357 << " INTERFACE : " << interface.ifindex << std::endl;
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 {
Adi Fogel6c4859e2025-11-16 15:26:23 +0200366 std::cerr << "Package/Channel mask must be 32-bits" << std::endl;
Johnathan Mantey5a456062024-02-15 08:45:08 -0800367 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 {
Adi Fogel6c4859e2025-11-16 15:26:23 +0200378 std::cerr << "Failed to set the mask attribute, RC : " << ret
379 << std::endl;
Johnathan Mantey5a456062024-02-15 08:45:08 -0800380 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());
Rashid MP4a27fdc2025-11-17 12:29:32 +0530387 NCSIPacketHeader* hdr = reinterpret_cast<NCSIPacketHeader*>(pl.data());
Eddie Jamesfa1f5c02020-09-17 15:12:46 -0500388
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 {
Adi Fogel6c4859e2025-11-16 15:26:23 +0200399 std::cerr << "Failed to set the data attribute, RC : " << ret
400 << std::endl;
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 {
Adi Fogel6c4859e2025-11-16 15:26:23 +0200414 std::cerr << "Failed to send the message , RC : " << ret << std::endl;
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 {
Adi Fogel6c4859e2025-11-16 15:26:23 +0200421 std::cerr << "Failed to receive the message , RC : " << ret
422 << std::endl;
Jeremy Kerrbea6cde2024-08-01 15:15:42 +0800423 return ret;
424 }
Eddie Jamesfa1f5c02020-09-17 15:12:46 -0500425
Jeremy Kerrbea6cde2024-08-01 15:15:42 +0800426 return 0;
Ratan Guptabbe45792018-03-23 00:22:55 +0530427}
428
Gunnar Mills57d9c502018-09-14 14:42:34 -0500429} // namespace internal
Ratan Guptabbe45792018-03-23 00:22:55 +0530430
Jeremy Kerr8a76d892024-07-26 17:19:57 +0800431std::string to_string(Interface& interface)
432{
Jeremy Kerr2d0b48d2024-09-16 13:03:26 +0800433 return interface.toString();
Jeremy Kerr8a76d892024-07-26 17:19:57 +0800434}
435
Jeremy Kerr2d0b48d2024-09-16 13:03:26 +0800436NetlinkInterface::NetlinkInterface(int ifindex) : ifindex(ifindex) {}
437
438std::string NetlinkInterface::toString()
439{
440 return std::to_string(ifindex);
441}
442
443std::optional<NCSIResponse> NetlinkInterface::sendCommand(NCSICommand& cmd)
Eddie Jamesfa1f5c02020-09-17 15:12:46 -0500444{
Adi Fogel6c4859e2025-11-16 15:26:23 +0200445 std::cout << "Send Command, CHANNEL : " << std::hex << (int)cmd.getChannel()
446 << " , PACKAGE : " << (int)cmd.package << " , INTERFACE: " << this
447 << std::dec << std::endl;
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{
Adi Fogel6c4859e2025-11-16 15:26:23 +0200467 std::cout << "Set CHANNEL : " << std::hex << channel << " , PACKAGE : "
468 << package << " , INTERFACE : " << this << std::dec << std::endl;
Jeremy Kerr3f34ff62024-09-12 13:00:27 +0800469
470 internal::NetlinkCommand cmd(ncsi_nl_commands::NCSI_CMD_SET_INTERFACE);
471
472 return internal::applyCmd(*this, cmd, package, channel);
Ratan Guptabbe45792018-03-23 00:22:55 +0530473}
474
Jeremy Kerr2d0b48d2024-09-16 13:03:26 +0800475int NetlinkInterface::clearInterface()
Ratan Guptabbe45792018-03-23 00:22:55 +0530476{
Adi Fogel6c4859e2025-11-16 15:26:23 +0200477 std::cout << "ClearInterface , INTERFACE : " << this << std::endl;
Jeremy Kerr3f34ff62024-09-12 13:00:27 +0800478
479 internal::NetlinkCommand cmd(ncsi_nl_commands::NCSI_CMD_CLEAR_INTERFACE);
480 return internal::applyCmd(*this, cmd);
Ratan Guptabbe45792018-03-23 00:22:55 +0530481}
482
Jeremy Kerr2d0b48d2024-09-16 13:03:26 +0800483std::optional<InterfaceInfo> NetlinkInterface::getInfo(int package)
Ratan Guptaaac603e2018-03-23 00:25:54 +0530484{
Jeremy Kerr7f7c0852024-08-08 11:32:55 +0800485 int rc, flags = package == DEFAULT_VALUE ? NLM_F_DUMP : NONE;
486 InterfaceInfo info;
487
Adi Fogel6c4859e2025-11-16 15:26:23 +0200488 std::cout << "Get Info , PACKAGE : " << std::hex << package
489 << " , INTERFACE: " << this << std::dec << std::endl;
Jeremy Kerr7f7c0852024-08-08 11:32:55 +0800490
491 struct internal::infoCallBackContext ctx = {
492 .info = &info,
493 };
494
Jeremy Kerr3f34ff62024-09-12 13:00:27 +0800495 internal::NetlinkCommand cmd(ncsi_nl_commands::NCSI_CMD_PKG_INFO);
496
497 rc = internal::applyCmd(*this, cmd, package, DEFAULT_VALUE, flags,
498 internal::infoCallBack, &ctx);
Jeremy Kerr7f7c0852024-08-08 11:32:55 +0800499
500 if (rc < 0)
Ratan Guptaaac603e2018-03-23 00:25:54 +0530501 {
Jeremy Kerr7f7c0852024-08-08 11:32:55 +0800502 return {};
Ratan Guptaaac603e2018-03-23 00:25:54 +0530503 }
Jeremy Kerr7f7c0852024-08-08 11:32:55 +0800504
505 return info;
Ratan Guptaaac603e2018-03-23 00:25:54 +0530506}
507
Jeremy Kerr2d0b48d2024-09-16 13:03:26 +0800508int NetlinkInterface::setPackageMask(unsigned int mask)
Johnathan Mantey5a456062024-02-15 08:45:08 -0800509{
Adi Fogel6c4859e2025-11-16 15:26:23 +0200510 std::cout << "Set Package Mask , INTERFACE: " << this
511 << " MASK: " << std::hex << mask << std::dec << std::endl;
Johnathan Mantey5a456062024-02-15 08:45:08 -0800512 auto payload = std::span<const unsigned char>(
513 reinterpret_cast<const unsigned char*>(&mask),
514 reinterpret_cast<const unsigned char*>(&mask) + sizeof(decltype(mask)));
Jeremy Kerr3f34ff62024-09-12 13:00:27 +0800515
516 internal::NetlinkCommand cmd(ncsi_nl_commands::NCSI_CMD_SET_PACKAGE_MASK, 0,
517 payload);
518 return internal::applyCmd(*this, cmd);
Johnathan Mantey5a456062024-02-15 08:45:08 -0800519}
520
Jeremy Kerr2d0b48d2024-09-16 13:03:26 +0800521int NetlinkInterface::setChannelMask(int package, unsigned int mask)
Johnathan Mantey5a456062024-02-15 08:45:08 -0800522{
Adi Fogel6c4859e2025-11-16 15:26:23 +0200523 std::cout << "Set Channel Mask , INTERFACE: " << this
524 << " , PACKAGE : " << std::hex << package << " MASK: " << mask
525 << std::dec << std::endl;
Johnathan Mantey5a456062024-02-15 08:45:08 -0800526 auto payload = std::span<const unsigned char>(
527 reinterpret_cast<const unsigned char*>(&mask),
528 reinterpret_cast<const unsigned char*>(&mask) + sizeof(decltype(mask)));
Jeremy Kerr3f34ff62024-09-12 13:00:27 +0800529
530 internal::NetlinkCommand cmd(ncsi_nl_commands::NCSI_CMD_SET_CHANNEL_MASK, 0,
531 payload);
532 return internal::applyCmd(*this, cmd);
Johnathan Mantey5a456062024-02-15 08:45:08 -0800533}
534
Jeremy Kerrb7885242024-09-16 12:43:36 +0800535int NCSIResponse::parseFullPayload()
536{
537 if (this->full_payload.size() < sizeof(internal::NCSIPacketHeader) +
538 sizeof(internal::NCSIResponsePayload))
539 {
Adi Fogel6c4859e2025-11-16 15:26:23 +0200540 std::cerr << "Response: Not enough data for a response message"
541 << std::endl;
Jeremy Kerrb7885242024-09-16 12:43:36 +0800542 return -1;
543 }
544
545 internal::NCSIPacketHeader* respHeader =
546 reinterpret_cast<decltype(respHeader)>(this->full_payload.data());
547
548 unsigned int payloadLen = ntohs(respHeader->length & htons(0x0fff));
549 /* we have determined that the payload size is larger than *respHeader,
550 * so cannot underflow here */
551 if (payloadLen > this->full_payload.size() - sizeof(*respHeader))
552 {
Adi Fogel6c4859e2025-11-16 15:26:23 +0200553 std::cerr << "Invalid header length " << payloadLen << " (vs "
554 << (this->full_payload.size() - sizeof(*respHeader))
555 << ") in response" << std::endl;
Jeremy Kerrb7885242024-09-16 12:43:36 +0800556 return -1;
557 }
558
559 this->opcode = respHeader->type;
560 this->payload =
561 std::span(this->full_payload.begin() + sizeof(*respHeader), payloadLen);
562
563 internal::NCSIResponsePayload* respPayload =
564 reinterpret_cast<decltype(respPayload)>(this->payload.data());
565 this->response = ntohs(respPayload->response);
566 this->reason = ntohs(respPayload->reason);
567
568 return 0;
569}
570
Jeremy Kerrca9d8672024-09-16 14:22:02 +0800571static const uint8_t MCTP_TYPE_NCSI = 2;
572
573struct NCSIResponsePayload
574{
575 uint16_t response;
576 uint16_t reason;
577};
578
579std::optional<NCSIResponse> MCTPInterface::sendCommand(NCSICommand& cmd)
580{
Jeremy Kerrca9d8672024-09-16 14:22:02 +0800581 static constexpr uint8_t mcid = 0; /* no need to distinguish controllers */
582 static constexpr size_t maxRespLen = 16384;
583 size_t payloadLen, padLen;
584 ssize_t wlen, rlen;
585
586 payloadLen = cmd.payload.size();
587
Jeremy Kerra42a8652024-12-05 11:15:35 +0800588 auto tmp = allocateIID();
589 if (!tmp.has_value())
590 {
591 return {};
592 }
593 uint8_t iid = *tmp;
594
Jeremy Kerrca9d8672024-09-16 14:22:02 +0800595 internal::NCSIPacketHeader cmdHeader{};
596 cmdHeader.MCID = mcid;
597 cmdHeader.revision = 1;
598 cmdHeader.id = iid;
599 cmdHeader.type = cmd.opcode;
600 cmdHeader.channel = (uint8_t)(cmd.package << 5 | cmd.getChannel());
601 cmdHeader.length = htons(payloadLen);
602
603 struct iovec iov[3];
604 iov[0].iov_base = &cmdHeader;
605 iov[0].iov_len = sizeof(cmdHeader);
606 iov[1].iov_base = cmd.payload.data();
607 iov[1].iov_len = payloadLen;
608
609 /* the checksum must appear on a 4-byte boundary */
610 padLen = 4 - (payloadLen & 0x3);
611 if (padLen == 4)
612 {
613 padLen = 0;
614 }
615 uint8_t crc32buf[8] = {};
616 /* todo: set csum; zeros currently indicate no checksum present */
617 uint32_t crc32 = 0;
618
619 memcpy(crc32buf + padLen, &crc32, sizeof(crc32));
620 padLen += sizeof(crc32);
621
622 iov[2].iov_base = crc32buf;
623 iov[2].iov_len = padLen;
624
625 struct sockaddr_mctp addr = {};
626 addr.smctp_family = AF_MCTP;
627 addr.smctp_network = net;
628 addr.smctp_addr.s_addr = eid;
629 addr.smctp_tag = MCTP_TAG_OWNER;
630 addr.smctp_type = MCTP_TYPE_NCSI;
631
632 struct msghdr msg = {};
633 msg.msg_name = &addr;
634 msg.msg_namelen = sizeof(addr);
635 msg.msg_iov = iov;
636 msg.msg_iovlen = 3;
637
638 wlen = sendmsg(sd, &msg, 0);
639 if (wlen < 0)
640 {
Adi Fogel6c4859e2025-11-16 15:26:23 +0200641 std::stringstream ss;
642 std::cout << "Failed to send MCTP message, ERRNO: " << -errno
643 << std::endl;
644
Jeremy Kerrca9d8672024-09-16 14:22:02 +0800645 return {};
646 }
647 else if ((size_t)wlen != sizeof(cmdHeader) + payloadLen + padLen)
648 {
Adi Fogel6c4859e2025-11-16 15:26:23 +0200649 std::cout << "Short write sending MCTP message, LEN: " << wlen
650 << std::endl;
Jeremy Kerrca9d8672024-09-16 14:22:02 +0800651 return {};
652 }
653
654 internal::NCSIPacketHeader* respHeader;
Rashid MP4a27fdc2025-11-17 12:29:32 +0530655 NCSIResponsePayload* respPayload = nullptr;
Jeremy Kerrca9d8672024-09-16 14:22:02 +0800656 NCSIResponse resp{};
657
658 resp.full_payload.resize(maxRespLen);
659 iov[0].iov_len = resp.full_payload.size();
660 iov[0].iov_base = resp.full_payload.data();
661
662 msg.msg_name = &addr;
663 msg.msg_namelen = sizeof(addr);
664 msg.msg_iov = iov;
665 msg.msg_iovlen = 1;
666
667 /* we have set SO_RCVTIMEO, so this won't block forever... */
668 rlen = recvmsg(sd, &msg, MSG_TRUNC);
669 if (rlen < 0)
670 {
Adi Fogel6c4859e2025-11-16 15:26:23 +0200671 std::cerr << "Failed to read MCTP response, ERRNO: " << -rlen
672 << std::endl;
Jeremy Kerrca9d8672024-09-16 14:22:02 +0800673 return {};
674 }
675 else if ((size_t)rlen < sizeof(*respHeader) + sizeof(*respPayload))
676 {
Adi Fogel6c4859e2025-11-16 15:26:23 +0200677 std::cerr << "Short read receiving MCTP message, LEN: " << rlen
678 << std::endl;
Jeremy Kerrca9d8672024-09-16 14:22:02 +0800679 return {};
680 }
681 else if ((size_t)rlen > maxRespLen)
682 {
Adi Fogel6c4859e2025-11-16 15:26:23 +0200683 std::cerr << "MCTP response is too large, LEN: " << rlen << std::endl;
Jeremy Kerrca9d8672024-09-16 14:22:02 +0800684 return {};
685 }
686
687 resp.full_payload.resize(rlen);
688
689 respHeader =
690 reinterpret_cast<decltype(respHeader)>(resp.full_payload.data());
691
692 /* header validation */
693 if (respHeader->MCID != mcid)
694 {
Adi Fogel6c4859e2025-11-16 15:26:23 +0200695 std::cerr << "Invalid MCID " << std::hex << (int)respHeader->MCID
696 << std::dec << " in response" << std::endl;
Jeremy Kerrca9d8672024-09-16 14:22:02 +0800697 return {};
698 }
699
700 if (respHeader->id != iid)
701 {
Adi Fogel6c4859e2025-11-16 15:26:23 +0200702 std::cerr << "Invalid IID " << std::hex << (int)respHeader->id
703 << std::dec << " in response" << std::endl;
Jeremy Kerrca9d8672024-09-16 14:22:02 +0800704 return {};
705 }
706
707 if (respHeader->type != (cmd.opcode | 0x80))
708 {
Adi Fogel6c4859e2025-11-16 15:26:23 +0200709 std::cerr << "Invalid opcode " << std::hex << (int)respHeader->type
710 << std::dec << " in response" << std::endl;
Jeremy Kerrca9d8672024-09-16 14:22:02 +0800711 return {};
712 }
713
714 int rc = resp.parseFullPayload();
715 if (rc)
716 {
717 return {};
718 }
719
720 return resp;
721}
722
723std::string MCTPInterface::toString()
724{
725 return std::to_string(net) + "," + std::to_string(eid);
726}
727
728MCTPInterface::MCTPInterface(int net, uint8_t eid) : net(net), eid(eid)
729{
730 static const struct timeval receiveTimeout = {
731 .tv_sec = 1,
732 .tv_usec = 0,
733 };
734
735 int _sd = socket(AF_MCTP, SOCK_DGRAM, 0);
736 if (_sd < 0)
737 {
738 throw std::system_error(errno, std::system_category(),
739 "Can't create MCTP socket");
740 }
741
742 int rc = setsockopt(_sd, SOL_SOCKET, SO_RCVTIMEO, &receiveTimeout,
743 sizeof(receiveTimeout));
744 if (rc != 0)
745 {
746 throw std::system_error(errno, std::system_category(),
747 "Can't set socket receive timemout");
748 }
749
750 sd = _sd;
751}
752
753MCTPInterface::~MCTPInterface()
754{
755 close(sd);
756}
757
Jeremy Kerra42a8652024-12-05 11:15:35 +0800758/* Small fd wrapper to provide RAII semantics, closing the IID file descriptor
759 * when we go out of scope.
760 */
761struct IidFd
762{
763 int fd;
764 IidFd(int _fd) : fd(_fd) {};
765 ~IidFd()
766 {
767 close(fd);
768 };
769};
770
771std::optional<uint8_t> MCTPInterface::allocateIID()
772{
773 int fd = open(mctp_iid_path, O_RDWR | O_CREAT, 0600);
774 if (fd < 0)
775 {
Adi Fogel6c4859e2025-11-16 15:26:23 +0200776 std::cerr << "Error opening IID database " << mctp_iid_path << ": "
777 << strerror(errno) << std::endl;
Jeremy Kerra42a8652024-12-05 11:15:35 +0800778 return {};
779 }
780
781 IidFd iidFd(fd);
782
783 /* lock while we read/modity/write; the lock will be short-lived, so
784 * we keep it simple and lock the entire file range
785 */
786 struct flock flock = {
787 .l_type = F_WRLCK,
788 .l_whence = SEEK_SET,
789 .l_start = 0,
790 .l_len = 0,
791 .l_pid = 0,
792 };
793
794 int rc = fcntl(iidFd.fd, F_OFD_SETLKW, &flock);
795 if (rc)
796 {
Adi Fogel6c4859e2025-11-16 15:26:23 +0200797 std::cerr << "Error locking IID database " << mctp_iid_path << ": "
798 << strerror(errno) << std::endl;
Jeremy Kerra42a8652024-12-05 11:15:35 +0800799 return {};
800 }
801
802 /* An EOF (rc == 0) would indicate that we don't yet have an entry for that
803 * eid, which we handle as iid = 0.
804 */
805 uint8_t iid = 0;
806 rc = pread(iidFd.fd, &iid, sizeof(iid), eid);
807 if (rc < 0)
808 {
Adi Fogel6c4859e2025-11-16 15:26:23 +0200809 std::cerr << "Error reading IID database " << mctp_iid_path << ": "
810 << strerror(errno) << std::endl;
Jeremy Kerra42a8652024-12-05 11:15:35 +0800811 return {};
812 }
813
814 /* DSP0222 defines valid IIDs in the range [1, 0xff], so manually wrap */
815 if (iid == 0xff)
816 {
817 iid = 1;
818 }
819 else
820 {
821 iid++;
822 }
823
824 rc = pwrite(iidFd.fd, &iid, sizeof(iid), eid);
825 if (rc != sizeof(iid))
826 {
Adi Fogel6c4859e2025-11-16 15:26:23 +0200827 std::cerr << "Error writing IID database " << mctp_iid_path << ": "
828 << strerror(errno) << std::endl;
Jeremy Kerra42a8652024-12-05 11:15:35 +0800829 return {};
830 }
831
832 return iid;
833}
834
Gunnar Mills57d9c502018-09-14 14:42:34 -0500835} // namespace ncsi
836} // namespace network
837} // namespace phosphor