blob: 1f505f0fcc763808a1aad2a2d9c24831b4d75dc3 [file] [log] [blame]
Patrick Venture189d44e2018-07-09 12:30:59 -07001#include "ncsi_util.hpp"
2
Jeremy Kerrca9d8672024-09-16 14:22:02 +08003#include <linux/mctp.h>
Ratan Guptabbe45792018-03-23 00:22:55 +05304#include <linux/ncsi.h>
Ratan Guptabbe45792018-03-23 00:22:55 +05305#include <netlink/genl/ctrl.h>
Gunnar Mills57d9c502018-09-14 14:42:34 -05006#include <netlink/genl/genl.h>
7#include <netlink/netlink.h>
Jeremy Kerrca9d8672024-09-16 14:22:02 +08008#include <unistd.h>
Ratan Guptabbe45792018-03-23 00:22:55 +05309
Patrick Williams89d734b2023-05-10 07:50:25 -050010#include <phosphor-logging/lg2.hpp>
11
Jeremy Kerrb7885242024-09-16 12:43:36 +080012#include <optional>
13#include <span>
Jeremy Kerrca9d8672024-09-16 14:22:02 +080014#include <system_error>
Jiaqing Zhao7c44a782022-04-10 15:30:04 +080015#include <vector>
Ratan Guptabbe45792018-03-23 00:22:55 +053016
17namespace phosphor
18{
19namespace network
20{
21namespace ncsi
22{
23
Jeremy Kerrb7885242024-09-16 12:43:36 +080024NCSICommand::NCSICommand(uint8_t opcode, uint8_t package,
25 std::optional<uint8_t> channel,
26 std::span<unsigned char> payload) :
27 opcode(opcode), package(package), channel(channel)
28{
29 this->payload.assign(payload.begin(), payload.end());
30}
31
32uint8_t NCSICommand::getChannel()
33{
34 return channel.value_or(CHANNEL_ID_NONE);
35}
36
Gunnar Mills57d9c502018-09-14 14:42:34 -050037using CallBack = int (*)(struct nl_msg* msg, void* arg);
Ratan Guptaaac603e2018-03-23 00:25:54 +053038
39namespace internal
40{
Ratan Guptabbe45792018-03-23 00:22:55 +053041
Eddie Jamesfa1f5c02020-09-17 15:12:46 -050042struct NCSIPacketHeader
43{
44 uint8_t MCID;
45 uint8_t revision;
46 uint8_t reserved;
47 uint8_t id;
48 uint8_t type;
49 uint8_t channel;
50 uint16_t length;
51 uint32_t rsvd[2];
52};
53
Jeremy Kerrb7885242024-09-16 12:43:36 +080054struct NCSIResponsePayload
55{
56 uint16_t response;
57 uint16_t reason;
58};
59
Jeremy Kerr3f34ff62024-09-12 13:00:27 +080060class NetlinkCommand
Eddie Jamesfa1f5c02020-09-17 15:12:46 -050061{
62 public:
Jeremy Kerr3f34ff62024-09-12 13:00:27 +080063 NetlinkCommand() = delete;
64 ~NetlinkCommand() = default;
65 NetlinkCommand(const NetlinkCommand&) = delete;
66 NetlinkCommand& operator=(const NetlinkCommand&) = delete;
67 NetlinkCommand(NetlinkCommand&&) = default;
68 NetlinkCommand& operator=(NetlinkCommand&&) = default;
69 NetlinkCommand(
Johnathan Mantey1ebea282024-02-15 10:26:06 -080070 int ncsiCmd, int operation = DEFAULT_VALUE,
Eddie Jamesfa1f5c02020-09-17 15:12:46 -050071 std::span<const unsigned char> p = std::span<const unsigned char>()) :
Patrick Williamsad205022024-08-16 15:20:07 -040072 ncsi_cmd(ncsiCmd), operation(operation), payload(p)
Patrick Williams89d734b2023-05-10 07:50:25 -050073 {}
Eddie Jamesfa1f5c02020-09-17 15:12:46 -050074
Eddie Jamesfa1f5c02020-09-17 15:12:46 -050075 int ncsi_cmd;
Johnathan Mantey1ebea282024-02-15 10:26:06 -080076 int operation;
Eddie Jamesfa1f5c02020-09-17 15:12:46 -050077 std::span<const unsigned char> payload;
78};
79
Ratan Guptabbe45792018-03-23 00:22:55 +053080using nlMsgPtr = std::unique_ptr<nl_msg, decltype(&::nlmsg_free)>;
81using nlSocketPtr = std::unique_ptr<nl_sock, decltype(&::nl_socket_free)>;
82
Jeremy Kerr7f7c0852024-08-08 11:32:55 +080083struct infoCallBackContext
84{
85 InterfaceInfo* info;
86};
87
88CallBack infoCallBack = [](struct nl_msg* msg, void* arg) {
89 if (arg == nullptr)
90 {
91 lg2::error("Internal error: invalid info callback context");
92 return -1;
93 }
94
95 struct infoCallBackContext* info = (struct infoCallBackContext*)arg;
Ratan Guptaaac603e2018-03-23 00:25:54 +053096 using namespace phosphor::network::ncsi;
97 auto nlh = nlmsg_hdr(msg);
98
Gunnar Mills57d9c502018-09-14 14:42:34 -050099 struct nlattr* tb[NCSI_ATTR_MAX + 1] = {nullptr};
100 struct nla_policy ncsiPolicy[NCSI_ATTR_MAX + 1] = {
William A. Kennington III05368f12021-05-13 18:40:47 -0700101 {NLA_UNSPEC, 0, 0}, {NLA_U32, 0, 0}, {NLA_NESTED, 0, 0},
102 {NLA_U32, 0, 0}, {NLA_U32, 0, 0},
Ratan Guptaaac603e2018-03-23 00:25:54 +0530103 };
104
Gunnar Mills57d9c502018-09-14 14:42:34 -0500105 struct nlattr* packagetb[NCSI_PKG_ATTR_MAX + 1] = {nullptr};
106 struct nla_policy packagePolicy[NCSI_PKG_ATTR_MAX + 1] = {
William A. Kennington III05368f12021-05-13 18:40:47 -0700107 {NLA_UNSPEC, 0, 0}, {NLA_NESTED, 0, 0}, {NLA_U32, 0, 0},
108 {NLA_FLAG, 0, 0}, {NLA_NESTED, 0, 0},
Ratan Guptaaac603e2018-03-23 00:25:54 +0530109 };
110
Gunnar Mills57d9c502018-09-14 14:42:34 -0500111 struct nlattr* channeltb[NCSI_CHANNEL_ATTR_MAX + 1] = {nullptr};
112 struct nla_policy channelPolicy[NCSI_CHANNEL_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}, {NLA_UNSPEC, 0, 0},
Ratan Guptaaac603e2018-03-23 00:25:54 +0530115 };
116
117 auto ret = genlmsg_parse(nlh, 0, tb, NCSI_ATTR_MAX, ncsiPolicy);
118 if (!tb[NCSI_ATTR_PACKAGE_LIST])
119 {
Jagpal Singh Gilld423beb2023-04-18 11:28:03 -0700120 lg2::error("No Packages");
Ratan Guptaaac603e2018-03-23 00:25:54 +0530121 return -1;
122 }
123
124 auto attrTgt = static_cast<nlattr*>(nla_data(tb[NCSI_ATTR_PACKAGE_LIST]));
125 if (!attrTgt)
126 {
Jagpal Singh Gilld423beb2023-04-18 11:28:03 -0700127 lg2::error("Package list attribute is null");
Ratan Guptaaac603e2018-03-23 00:25:54 +0530128 return -1;
129 }
130
131 auto rem = nla_len(tb[NCSI_ATTR_PACKAGE_LIST]);
132 nla_for_each_nested(attrTgt, tb[NCSI_ATTR_PACKAGE_LIST], rem)
133 {
134 ret = nla_parse_nested(packagetb, NCSI_PKG_ATTR_MAX, attrTgt,
135 packagePolicy);
136 if (ret < 0)
137 {
Jagpal Singh Gilld423beb2023-04-18 11:28:03 -0700138 lg2::error("Failed to parse package nested");
Ratan Guptaaac603e2018-03-23 00:25:54 +0530139 return -1;
140 }
141
Jeremy Kerr7f7c0852024-08-08 11:32:55 +0800142 PackageInfo pkg;
143
Ratan Guptaaac603e2018-03-23 00:25:54 +0530144 if (packagetb[NCSI_PKG_ATTR_ID])
145 {
146 auto attrID = nla_get_u32(packagetb[NCSI_PKG_ATTR_ID]);
Jeremy Kerr7f7c0852024-08-08 11:32:55 +0800147 pkg.id = attrID;
Ratan Guptaaac603e2018-03-23 00:25:54 +0530148 }
149 else
150 {
Jagpal Singh Gilld423beb2023-04-18 11:28:03 -0700151 lg2::debug("Package with no id");
Ratan Guptaaac603e2018-03-23 00:25:54 +0530152 }
153
154 if (packagetb[NCSI_PKG_ATTR_FORCED])
155 {
Jeremy Kerr7f7c0852024-08-08 11:32:55 +0800156 pkg.forced = true;
Ratan Guptaaac603e2018-03-23 00:25:54 +0530157 }
158
159 auto channelListTarget = static_cast<nlattr*>(
Gunnar Mills57d9c502018-09-14 14:42:34 -0500160 nla_data(packagetb[NCSI_PKG_ATTR_CHANNEL_LIST]));
Ratan Guptaaac603e2018-03-23 00:25:54 +0530161
162 auto channelrem = nla_len(packagetb[NCSI_PKG_ATTR_CHANNEL_LIST]);
163 nla_for_each_nested(channelListTarget,
164 packagetb[NCSI_PKG_ATTR_CHANNEL_LIST], channelrem)
165 {
166 ret = nla_parse_nested(channeltb, NCSI_CHANNEL_ATTR_MAX,
167 channelListTarget, channelPolicy);
168 if (ret < 0)
169 {
Jagpal Singh Gilld423beb2023-04-18 11:28:03 -0700170 lg2::error("Failed to parse channel nested");
Jeremy Kerr7f7c0852024-08-08 11:32:55 +0800171 continue;
Ratan Guptaaac603e2018-03-23 00:25:54 +0530172 }
173
Jeremy Kerr7f7c0852024-08-08 11:32:55 +0800174 ChannelInfo chan;
175
Ratan Guptaaac603e2018-03-23 00:25:54 +0530176 if (channeltb[NCSI_CHANNEL_ATTR_ID])
177 {
Jeremy Kerr7f7c0852024-08-08 11:32:55 +0800178 chan.id = nla_get_u32(channeltb[NCSI_CHANNEL_ATTR_ID]);
179 chan.active = !!channeltb[NCSI_CHANNEL_ATTR_ACTIVE];
180 chan.forced = !!channeltb[NCSI_CHANNEL_ATTR_FORCED];
Ratan Guptaaac603e2018-03-23 00:25:54 +0530181 }
182 else
183 {
Jagpal Singh Gilld423beb2023-04-18 11:28:03 -0700184 lg2::debug("Channel with no ID");
Jeremy Kerr7f7c0852024-08-08 11:32:55 +0800185 continue;
Ratan Guptaaac603e2018-03-23 00:25:54 +0530186 }
Ratan Guptaed5d7ff2018-03-23 00:27:52 +0530187
Ratan Guptaaac603e2018-03-23 00:25:54 +0530188 if (channeltb[NCSI_CHANNEL_ATTR_VERSION_MAJOR])
189 {
Jeremy Kerr7f7c0852024-08-08 11:32:55 +0800190 chan.version_major =
Gunnar Mills57d9c502018-09-14 14:42:34 -0500191 nla_get_u32(channeltb[NCSI_CHANNEL_ATTR_VERSION_MAJOR]);
Ratan Guptaaac603e2018-03-23 00:25:54 +0530192 }
193 if (channeltb[NCSI_CHANNEL_ATTR_VERSION_MINOR])
194 {
Jeremy Kerr7f7c0852024-08-08 11:32:55 +0800195 chan.version_minor =
Gunnar Mills57d9c502018-09-14 14:42:34 -0500196 nla_get_u32(channeltb[NCSI_CHANNEL_ATTR_VERSION_MINOR]);
Ratan Guptaaac603e2018-03-23 00:25:54 +0530197 }
198 if (channeltb[NCSI_CHANNEL_ATTR_VERSION_STR])
199 {
Jeremy Kerr7f7c0852024-08-08 11:32:55 +0800200 chan.version =
Gunnar Mills57d9c502018-09-14 14:42:34 -0500201 nla_get_string(channeltb[NCSI_CHANNEL_ATTR_VERSION_STR]);
Ratan Guptaaac603e2018-03-23 00:25:54 +0530202 }
203 if (channeltb[NCSI_CHANNEL_ATTR_LINK_STATE])
204 {
Jeremy Kerr7f7c0852024-08-08 11:32:55 +0800205 chan.link_state =
Gunnar Mills57d9c502018-09-14 14:42:34 -0500206 nla_get_u32(channeltb[NCSI_CHANNEL_ATTR_LINK_STATE]);
Ratan Guptaaac603e2018-03-23 00:25:54 +0530207 }
208 if (channeltb[NCSI_CHANNEL_ATTR_VLAN_LIST])
209 {
Ratan Guptaaac603e2018-03-23 00:25:54 +0530210 auto vids = channeltb[NCSI_CHANNEL_ATTR_VLAN_LIST];
211 auto vid = static_cast<nlattr*>(nla_data(vids));
212 auto len = nla_len(vids);
213 while (nla_ok(vid, len))
214 {
215 auto id = nla_get_u16(vid);
Jeremy Kerr7f7c0852024-08-08 11:32:55 +0800216 chan.vlan_ids.push_back(id);
Ratan Guptaaac603e2018-03-23 00:25:54 +0530217 vid = nla_next(vid, &len);
218 }
219 }
Jeremy Kerr7f7c0852024-08-08 11:32:55 +0800220 pkg.channels.push_back(chan);
Ratan Guptaaac603e2018-03-23 00:25:54 +0530221 }
Jeremy Kerr7f7c0852024-08-08 11:32:55 +0800222
223 info->info->packages.push_back(pkg);
Ratan Guptaaac603e2018-03-23 00:25:54 +0530224 }
Jeremy Kerrbea6cde2024-08-01 15:15:42 +0800225 return static_cast<int>(NL_STOP);
Ratan Guptaaac603e2018-03-23 00:25:54 +0530226};
227
Jeremy Kerr147086d2024-08-27 13:46:38 +0800228struct sendCallBackContext
229{
Jeremy Kerrb7885242024-09-16 12:43:36 +0800230 NCSIResponse resp;
Jeremy Kerr147086d2024-08-27 13:46:38 +0800231};
232
233CallBack sendCallBack = [](struct nl_msg* msg, void* arg) {
Eddie Jamesfa1f5c02020-09-17 15:12:46 -0500234 using namespace phosphor::network::ncsi;
235 auto nlh = nlmsg_hdr(msg);
236 struct nlattr* tb[NCSI_ATTR_MAX + 1] = {nullptr};
237 static struct nla_policy ncsiPolicy[NCSI_ATTR_MAX + 1] = {
238 {NLA_UNSPEC, 0, 0}, {NLA_U32, 0, 0}, {NLA_NESTED, 0, 0},
239 {NLA_U32, 0, 0}, {NLA_U32, 0, 0}, {NLA_BINARY, 0, 0},
240 {NLA_FLAG, 0, 0}, {NLA_U32, 0, 0}, {NLA_U32, 0, 0},
241 };
242
Jeremy Kerr147086d2024-08-27 13:46:38 +0800243 if (arg == nullptr)
244 {
245 lg2::error("Internal error: invalid send callback context");
246 return -1;
247 }
248
249 struct sendCallBackContext* ctx = (struct sendCallBackContext*)arg;
250
Eddie Jamesfa1f5c02020-09-17 15:12:46 -0500251 auto ret = genlmsg_parse(nlh, 0, tb, NCSI_ATTR_MAX, ncsiPolicy);
252 if (ret)
253 {
Jeremy Kerrde8d6662024-08-01 11:33:52 +0800254 lg2::error("Failed to parse message");
Eddie Jamesfa1f5c02020-09-17 15:12:46 -0500255 return ret;
256 }
257
Jian Zhang442d9e52022-10-20 22:11:14 +0800258 if (tb[NCSI_ATTR_DATA] == nullptr)
259 {
Jagpal Singh Gilld423beb2023-04-18 11:28:03 -0700260 lg2::error("Response: No data");
Jian Zhang442d9e52022-10-20 22:11:14 +0800261 return -1;
262 }
263
Jeremy Kerrb7885242024-09-16 12:43:36 +0800264 size_t data_len = nla_len(tb[NCSI_ATTR_DATA]);
265 unsigned char* data = (unsigned char*)nla_data(tb[NCSI_ATTR_DATA]);
Eddie Jamesfa1f5c02020-09-17 15:12:46 -0500266
Jeremy Kerrb7885242024-09-16 12:43:36 +0800267 ctx->resp.full_payload.assign(data, data + data_len);
268
269 int rc = ctx->resp.parseFullPayload();
270 if (rc)
271 {
272 return -1;
273 }
Eddie Jamesfa1f5c02020-09-17 15:12:46 -0500274
Jeremy Kerrbea6cde2024-08-01 15:15:42 +0800275 return static_cast<int>(NL_STOP);
Eddie Jamesfa1f5c02020-09-17 15:12:46 -0500276};
277
Jeremy Kerr2d0b48d2024-09-16 13:03:26 +0800278int applyCmd(NetlinkInterface& interface, const NetlinkCommand& cmd,
Jeremy Kerr8d9af022024-07-26 16:47:16 +0800279 int package = DEFAULT_VALUE, int channel = DEFAULT_VALUE,
Jeremy Kerr7f7c0852024-08-08 11:32:55 +0800280 int flags = NONE, CallBack function = nullptr, void* arg = nullptr)
Ratan Guptabbe45792018-03-23 00:22:55 +0530281{
Gunnar Mills57d9c502018-09-14 14:42:34 -0500282 nlSocketPtr socket(nl_socket_alloc(), &::nl_socket_free);
Johnathan Manteyd49c5c62021-06-23 09:14:42 -0700283 if (socket == nullptr)
284 {
Jagpal Singh Gilld423beb2023-04-18 11:28:03 -0700285 lg2::error("Unable to allocate memory for the socket");
Johnathan Manteyd49c5c62021-06-23 09:14:42 -0700286 return -ENOMEM;
287 }
288
Jeremy Kerrbea6cde2024-08-01 15:15:42 +0800289 nl_socket_disable_auto_ack(socket.get());
290
Ratan Guptabbe45792018-03-23 00:22:55 +0530291 auto ret = genl_connect(socket.get());
292 if (ret < 0)
293 {
Jagpal Singh Gilld423beb2023-04-18 11:28:03 -0700294 lg2::error("Failed to open the socket , RC : {RC}", "RC", ret);
Ratan Guptabbe45792018-03-23 00:22:55 +0530295 return ret;
296 }
297
298 auto driverID = genl_ctrl_resolve(socket.get(), "NCSI");
299 if (driverID < 0)
300 {
Jagpal Singh Gilld423beb2023-04-18 11:28:03 -0700301 lg2::error("Failed to resolve, RC : {RC}", "RC", ret);
Ratan Guptabbe45792018-03-23 00:22:55 +0530302 return driverID;
303 }
304
305 nlMsgPtr msg(nlmsg_alloc(), &::nlmsg_free);
Johnathan Manteyd49c5c62021-06-23 09:14:42 -0700306 if (msg == nullptr)
307 {
Jagpal Singh Gilld423beb2023-04-18 11:28:03 -0700308 lg2::error("Unable to allocate memory for the message");
Johnathan Manteyd49c5c62021-06-23 09:14:42 -0700309 return -ENOMEM;
310 }
Ratan Guptabbe45792018-03-23 00:22:55 +0530311
Johnathan Mantey1ebea282024-02-15 10:26:06 -0800312 auto msgHdr = genlmsg_put(msg.get(), NL_AUTO_PORT, NL_AUTO_SEQ, driverID, 0,
313 flags, cmd.ncsi_cmd, 0);
Ratan Guptabbe45792018-03-23 00:22:55 +0530314 if (!msgHdr)
315 {
Jagpal Singh Gilld423beb2023-04-18 11:28:03 -0700316 lg2::error("Unable to add the netlink headers , COMMAND : {COMMAND}",
Johnathan Mantey1ebea282024-02-15 10:26:06 -0800317 "COMMAND", cmd.ncsi_cmd);
Johnathan Manteyd49c5c62021-06-23 09:14:42 -0700318 return -ENOMEM;
Ratan Guptabbe45792018-03-23 00:22:55 +0530319 }
320
321 if (package != DEFAULT_VALUE)
322 {
323 ret = nla_put_u32(msg.get(), ncsi_nl_attrs::NCSI_ATTR_PACKAGE_ID,
324 package);
325 if (ret < 0)
326 {
Jagpal Singh Gilld423beb2023-04-18 11:28:03 -0700327 lg2::error("Failed to set the attribute , RC : {RC} PACKAGE "
328 "{PACKAGE}",
329 "RC", ret, "PACKAGE", lg2::hex, package);
Ratan Guptabbe45792018-03-23 00:22:55 +0530330 return ret;
331 }
332 }
333
334 if (channel != DEFAULT_VALUE)
335 {
336 ret = nla_put_u32(msg.get(), ncsi_nl_attrs::NCSI_ATTR_CHANNEL_ID,
337 channel);
338 if (ret < 0)
339 {
Jagpal Singh Gilld423beb2023-04-18 11:28:03 -0700340 lg2::error("Failed to set the attribute , RC : {RC} CHANNEL : "
341 "{CHANNEL}",
342 "RC", ret, "CHANNEL", lg2::hex, channel);
Ratan Guptabbe45792018-03-23 00:22:55 +0530343 return ret;
344 }
345 }
346
Jeremy Kerr8d9af022024-07-26 16:47:16 +0800347 ret = nla_put_u32(msg.get(), ncsi_nl_attrs::NCSI_ATTR_IFINDEX,
348 interface.ifindex);
Ratan Guptabbe45792018-03-23 00:22:55 +0530349 if (ret < 0)
350 {
Jagpal Singh Gilld423beb2023-04-18 11:28:03 -0700351 lg2::error("Failed to set the attribute , RC : {RC} INTERFACE : "
352 "{INTERFACE}",
Jeremy Kerr8a76d892024-07-26 17:19:57 +0800353 "RC", ret, "INTERFACE", interface);
Ratan Guptabbe45792018-03-23 00:22:55 +0530354 return ret;
355 }
356
Johnathan Mantey5a456062024-02-15 08:45:08 -0800357 if ((cmd.ncsi_cmd == ncsi_nl_commands::NCSI_CMD_SET_PACKAGE_MASK) ||
358 (cmd.ncsi_cmd == ncsi_nl_commands::NCSI_CMD_SET_CHANNEL_MASK))
359 {
360 if (cmd.payload.size() != sizeof(unsigned int))
361 {
362 lg2::error("Package/Channel mask must be 32-bits");
363 return -EINVAL;
364 }
365 int maskAttr =
366 cmd.ncsi_cmd == ncsi_nl_commands::NCSI_CMD_SET_PACKAGE_MASK
367 ? NCSI_ATTR_PACKAGE_MASK
368 : NCSI_ATTR_CHANNEL_MASK;
369 ret = nla_put_u32(
370 msg.get(), maskAttr,
371 *(reinterpret_cast<const unsigned int*>(cmd.payload.data())));
372 if (ret < 0)
373 {
374 lg2::error("Failed to set the mask attribute, RC : {RC}", "RC",
375 ret);
376 return ret;
377 }
378 }
379 else if (cmd.ncsi_cmd == ncsi_nl_commands::NCSI_CMD_SEND_CMD)
Eddie Jamesfa1f5c02020-09-17 15:12:46 -0500380 {
Patrick Williamsad205022024-08-16 15:20:07 -0400381 std::vector<unsigned char> pl(
382 sizeof(NCSIPacketHeader) + cmd.payload.size());
Eddie Jamesfa1f5c02020-09-17 15:12:46 -0500383 NCSIPacketHeader* hdr = (NCSIPacketHeader*)pl.data();
384
385 std::copy(cmd.payload.begin(), cmd.payload.end(),
386 pl.begin() + sizeof(NCSIPacketHeader));
387
Johnathan Mantey1ebea282024-02-15 10:26:06 -0800388 hdr->type = cmd.operation;
Eddie Jamesfa1f5c02020-09-17 15:12:46 -0500389 hdr->length = htons(cmd.payload.size());
390
391 ret = nla_put(msg.get(), ncsi_nl_attrs::NCSI_ATTR_DATA, pl.size(),
392 pl.data());
393 if (ret < 0)
394 {
Jagpal Singh Gilld423beb2023-04-18 11:28:03 -0700395 lg2::error("Failed to set the data attribute, RC : {RC}", "RC",
396 ret);
Eddie Jamesfa1f5c02020-09-17 15:12:46 -0500397 return ret;
398 }
399
400 nl_socket_disable_seq_check(socket.get());
401 }
402
Jeremy Kerr67b159a2024-08-01 15:23:57 +0800403 // Add a callback function to the socket
404 enum nl_cb_kind cb_kind = function ? NL_CB_CUSTOM : NL_CB_DEFAULT;
Jeremy Kerr7f7c0852024-08-08 11:32:55 +0800405 nl_socket_modify_cb(socket.get(), NL_CB_VALID, cb_kind, function, arg);
Ratan Guptabbe45792018-03-23 00:22:55 +0530406
407 ret = nl_send_auto(socket.get(), msg.get());
408 if (ret < 0)
409 {
Jagpal Singh Gilld423beb2023-04-18 11:28:03 -0700410 lg2::error("Failed to send the message , RC : {RC}", "RC", ret);
Ratan Guptabbe45792018-03-23 00:22:55 +0530411 return ret;
412 }
413
Jeremy Kerrbea6cde2024-08-01 15:15:42 +0800414 ret = nl_recvmsgs_default(socket.get());
415 if (ret < 0)
Ratan Guptabbe45792018-03-23 00:22:55 +0530416 {
Jeremy Kerrbea6cde2024-08-01 15:15:42 +0800417 lg2::error("Failed to receive the message , RC : {RC}", "RC", ret);
418 return ret;
419 }
Eddie Jamesfa1f5c02020-09-17 15:12:46 -0500420
Jeremy Kerrbea6cde2024-08-01 15:15:42 +0800421 return 0;
Ratan Guptabbe45792018-03-23 00:22:55 +0530422}
423
Gunnar Mills57d9c502018-09-14 14:42:34 -0500424} // namespace internal
Ratan Guptabbe45792018-03-23 00:22:55 +0530425
Jeremy Kerr8a76d892024-07-26 17:19:57 +0800426std::string to_string(Interface& interface)
427{
Jeremy Kerr2d0b48d2024-09-16 13:03:26 +0800428 return interface.toString();
Jeremy Kerr8a76d892024-07-26 17:19:57 +0800429}
430
Jeremy Kerr2d0b48d2024-09-16 13:03:26 +0800431NetlinkInterface::NetlinkInterface(int ifindex) : ifindex(ifindex) {}
432
433std::string NetlinkInterface::toString()
434{
435 return std::to_string(ifindex);
436}
437
438std::optional<NCSIResponse> NetlinkInterface::sendCommand(NCSICommand& cmd)
Eddie Jamesfa1f5c02020-09-17 15:12:46 -0500439{
Jeremy Kerrb7885242024-09-16 12:43:36 +0800440 lg2::debug("Send Command, CHANNEL : {CHANNEL} , PACKAGE : {PACKAGE}, "
Jeremy Kerr8a76d892024-07-26 17:19:57 +0800441 "INTERFACE: {INTERFACE}",
Jeremy Kerrb7885242024-09-16 12:43:36 +0800442 "CHANNEL", lg2::hex, cmd.getChannel(), "PACKAGE", lg2::hex,
443 cmd.package, "INTERFACE", this);
Eddie Jamesfa1f5c02020-09-17 15:12:46 -0500444
Jeremy Kerr147086d2024-08-27 13:46:38 +0800445 internal::sendCallBackContext ctx{};
446
Jeremy Kerrb7885242024-09-16 12:43:36 +0800447 internal::NetlinkCommand nl_cmd(ncsi_nl_commands::NCSI_CMD_SEND_CMD,
448 cmd.opcode, cmd.payload);
Jeremy Kerr3f34ff62024-09-12 13:00:27 +0800449
Jeremy Kerrb7885242024-09-16 12:43:36 +0800450 int rc = internal::applyCmd(*this, nl_cmd, cmd.package, cmd.getChannel(),
451 NONE, internal::sendCallBack, &ctx);
Jeremy Kerr147086d2024-08-27 13:46:38 +0800452
453 if (rc < 0)
454 {
455 return {};
456 }
457
Jeremy Kerrb7885242024-09-16 12:43:36 +0800458 return ctx.resp;
Eddie Jamesfa1f5c02020-09-17 15:12:46 -0500459}
460
Jeremy Kerr2d0b48d2024-09-16 13:03:26 +0800461int NetlinkInterface::setChannel(int package, int channel)
Ratan Guptabbe45792018-03-23 00:22:55 +0530462{
Jeremy Kerr8a76d892024-07-26 17:19:57 +0800463 lg2::debug("Set CHANNEL : {CHANNEL} , PACKAGE : {PACKAGE}, INTERFACE : "
464 "{INTERFACE}",
465 "CHANNEL", lg2::hex, channel, "PACKAGE", lg2::hex, package,
Jeremy Kerrbc22f812024-07-29 17:43:35 +0800466 "INTERFACE", this);
Jeremy Kerr3f34ff62024-09-12 13:00:27 +0800467
468 internal::NetlinkCommand cmd(ncsi_nl_commands::NCSI_CMD_SET_INTERFACE);
469
470 return internal::applyCmd(*this, cmd, package, channel);
Ratan Guptabbe45792018-03-23 00:22:55 +0530471}
472
Jeremy Kerr2d0b48d2024-09-16 13:03:26 +0800473int NetlinkInterface::clearInterface()
Ratan Guptabbe45792018-03-23 00:22:55 +0530474{
Jeremy Kerrbc22f812024-07-29 17:43:35 +0800475 lg2::debug("ClearInterface , INTERFACE : {INTERFACE}", "INTERFACE", this);
Jeremy Kerr3f34ff62024-09-12 13:00:27 +0800476
477 internal::NetlinkCommand cmd(ncsi_nl_commands::NCSI_CMD_CLEAR_INTERFACE);
478 return internal::applyCmd(*this, cmd);
Ratan Guptabbe45792018-03-23 00:22:55 +0530479}
480
Jeremy Kerr2d0b48d2024-09-16 13:03:26 +0800481std::optional<InterfaceInfo> NetlinkInterface::getInfo(int package)
Ratan Guptaaac603e2018-03-23 00:25:54 +0530482{
Jeremy Kerr7f7c0852024-08-08 11:32:55 +0800483 int rc, flags = package == DEFAULT_VALUE ? NLM_F_DUMP : NONE;
484 InterfaceInfo info;
485
Jeremy Kerr8a76d892024-07-26 17:19:57 +0800486 lg2::debug("Get Info , PACKAGE : {PACKAGE}, INTERFACE: {INTERFACE}",
Jeremy Kerrbc22f812024-07-29 17:43:35 +0800487 "PACKAGE", lg2::hex, package, "INTERFACE", this);
Jeremy Kerr7f7c0852024-08-08 11:32:55 +0800488
489 struct internal::infoCallBackContext ctx = {
490 .info = &info,
491 };
492
Jeremy Kerr3f34ff62024-09-12 13:00:27 +0800493 internal::NetlinkCommand cmd(ncsi_nl_commands::NCSI_CMD_PKG_INFO);
494
495 rc = internal::applyCmd(*this, cmd, package, DEFAULT_VALUE, flags,
496 internal::infoCallBack, &ctx);
Jeremy Kerr7f7c0852024-08-08 11:32:55 +0800497
498 if (rc < 0)
Ratan Guptaaac603e2018-03-23 00:25:54 +0530499 {
Jeremy Kerr7f7c0852024-08-08 11:32:55 +0800500 return {};
Ratan Guptaaac603e2018-03-23 00:25:54 +0530501 }
Jeremy Kerr7f7c0852024-08-08 11:32:55 +0800502
503 return info;
Ratan Guptaaac603e2018-03-23 00:25:54 +0530504}
505
Jeremy Kerr2d0b48d2024-09-16 13:03:26 +0800506int NetlinkInterface::setPackageMask(unsigned int mask)
Johnathan Mantey5a456062024-02-15 08:45:08 -0800507{
Jeremy Kerr8a76d892024-07-26 17:19:57 +0800508 lg2::debug("Set Package Mask , INTERFACE: {INTERFACE} MASK: {MASK}",
Jeremy Kerrbc22f812024-07-29 17:43:35 +0800509 "INTERFACE", this, "MASK", lg2::hex, mask);
Johnathan Mantey5a456062024-02-15 08:45:08 -0800510 auto payload = std::span<const unsigned char>(
511 reinterpret_cast<const unsigned char*>(&mask),
512 reinterpret_cast<const unsigned char*>(&mask) + sizeof(decltype(mask)));
Jeremy Kerr3f34ff62024-09-12 13:00:27 +0800513
514 internal::NetlinkCommand cmd(ncsi_nl_commands::NCSI_CMD_SET_PACKAGE_MASK, 0,
515 payload);
516 return internal::applyCmd(*this, cmd);
Johnathan Mantey5a456062024-02-15 08:45:08 -0800517}
518
Jeremy Kerr2d0b48d2024-09-16 13:03:26 +0800519int NetlinkInterface::setChannelMask(int package, unsigned int mask)
Johnathan Mantey5a456062024-02-15 08:45:08 -0800520{
521 lg2::debug(
Jeremy Kerr8a76d892024-07-26 17:19:57 +0800522 "Set Channel Mask , INTERFACE: {INTERFACE}, PACKAGE : {PACKAGE} MASK: {MASK}",
Jeremy Kerrbc22f812024-07-29 17:43:35 +0800523 "INTERFACE", this, "PACKAGE", lg2::hex, package, "MASK", lg2::hex,
Jeremy Kerr8a76d892024-07-26 17:19:57 +0800524 mask);
Johnathan Mantey5a456062024-02-15 08:45:08 -0800525 auto payload = std::span<const unsigned char>(
526 reinterpret_cast<const unsigned char*>(&mask),
527 reinterpret_cast<const unsigned char*>(&mask) + sizeof(decltype(mask)));
Jeremy Kerr3f34ff62024-09-12 13:00:27 +0800528
529 internal::NetlinkCommand cmd(ncsi_nl_commands::NCSI_CMD_SET_CHANNEL_MASK, 0,
530 payload);
531 return internal::applyCmd(*this, cmd);
Johnathan Mantey5a456062024-02-15 08:45:08 -0800532}
533
Jeremy Kerrb7885242024-09-16 12:43:36 +0800534int NCSIResponse::parseFullPayload()
535{
536 if (this->full_payload.size() < sizeof(internal::NCSIPacketHeader) +
537 sizeof(internal::NCSIResponsePayload))
538 {
539 lg2::error("Response: Not enough data for a response message");
540 return -1;
541 }
542
543 internal::NCSIPacketHeader* respHeader =
544 reinterpret_cast<decltype(respHeader)>(this->full_payload.data());
545
546 unsigned int payloadLen = ntohs(respHeader->length & htons(0x0fff));
547 /* we have determined that the payload size is larger than *respHeader,
548 * so cannot underflow here */
549 if (payloadLen > this->full_payload.size() - sizeof(*respHeader))
550 {
551 lg2::error("Invalid header length {HDRLEN} (vs {LEN}) in response",
552 "HDRLEN", payloadLen, "LEN",
553 this->full_payload.size() - sizeof(*respHeader));
554 return -1;
555 }
556
557 this->opcode = respHeader->type;
558 this->payload =
559 std::span(this->full_payload.begin() + sizeof(*respHeader), payloadLen);
560
561 internal::NCSIResponsePayload* respPayload =
562 reinterpret_cast<decltype(respPayload)>(this->payload.data());
563 this->response = ntohs(respPayload->response);
564 this->reason = ntohs(respPayload->reason);
565
566 return 0;
567}
568
Jeremy Kerrca9d8672024-09-16 14:22:02 +0800569static const uint8_t MCTP_TYPE_NCSI = 2;
570
571struct NCSIResponsePayload
572{
573 uint16_t response;
574 uint16_t reason;
575};
576
577std::optional<NCSIResponse> MCTPInterface::sendCommand(NCSICommand& cmd)
578{
579 static constexpr uint8_t iid = 0; /* we only have one cmd outstanding */
580 static constexpr uint8_t mcid = 0; /* no need to distinguish controllers */
581 static constexpr size_t maxRespLen = 16384;
582 size_t payloadLen, padLen;
583 ssize_t wlen, rlen;
584
585 payloadLen = cmd.payload.size();
586
587 internal::NCSIPacketHeader cmdHeader{};
588 cmdHeader.MCID = mcid;
589 cmdHeader.revision = 1;
590 cmdHeader.id = iid;
591 cmdHeader.type = cmd.opcode;
592 cmdHeader.channel = (uint8_t)(cmd.package << 5 | cmd.getChannel());
593 cmdHeader.length = htons(payloadLen);
594
595 struct iovec iov[3];
596 iov[0].iov_base = &cmdHeader;
597 iov[0].iov_len = sizeof(cmdHeader);
598 iov[1].iov_base = cmd.payload.data();
599 iov[1].iov_len = payloadLen;
600
601 /* the checksum must appear on a 4-byte boundary */
602 padLen = 4 - (payloadLen & 0x3);
603 if (padLen == 4)
604 {
605 padLen = 0;
606 }
607 uint8_t crc32buf[8] = {};
608 /* todo: set csum; zeros currently indicate no checksum present */
609 uint32_t crc32 = 0;
610
611 memcpy(crc32buf + padLen, &crc32, sizeof(crc32));
612 padLen += sizeof(crc32);
613
614 iov[2].iov_base = crc32buf;
615 iov[2].iov_len = padLen;
616
617 struct sockaddr_mctp addr = {};
618 addr.smctp_family = AF_MCTP;
619 addr.smctp_network = net;
620 addr.smctp_addr.s_addr = eid;
621 addr.smctp_tag = MCTP_TAG_OWNER;
622 addr.smctp_type = MCTP_TYPE_NCSI;
623
624 struct msghdr msg = {};
625 msg.msg_name = &addr;
626 msg.msg_namelen = sizeof(addr);
627 msg.msg_iov = iov;
628 msg.msg_iovlen = 3;
629
630 wlen = sendmsg(sd, &msg, 0);
631 if (wlen < 0)
632 {
633 lg2::error("Failed to send MCTP message, ERRNO: {ERRNO}", "ERRNO",
634 -wlen);
635 return {};
636 }
637 else if ((size_t)wlen != sizeof(cmdHeader) + payloadLen + padLen)
638 {
639 lg2::error("Short write sending MCTP message, LEN: {LEN}", "LEN", wlen);
640 return {};
641 }
642
643 internal::NCSIPacketHeader* respHeader;
644 NCSIResponsePayload* respPayload;
645 NCSIResponse resp{};
646
647 resp.full_payload.resize(maxRespLen);
648 iov[0].iov_len = resp.full_payload.size();
649 iov[0].iov_base = resp.full_payload.data();
650
651 msg.msg_name = &addr;
652 msg.msg_namelen = sizeof(addr);
653 msg.msg_iov = iov;
654 msg.msg_iovlen = 1;
655
656 /* we have set SO_RCVTIMEO, so this won't block forever... */
657 rlen = recvmsg(sd, &msg, MSG_TRUNC);
658 if (rlen < 0)
659 {
660 lg2::error("Failed to read MCTP response, ERRNO: {ERRNO}", "ERRNO",
661 -rlen);
662 return {};
663 }
664 else if ((size_t)rlen < sizeof(*respHeader) + sizeof(*respPayload))
665 {
666 lg2::error("Short read receiving MCTP message, LEN: {LEN}", "LEN",
667 rlen);
668 return {};
669 }
670 else if ((size_t)rlen > maxRespLen)
671 {
672 lg2::error("MCTP response is too large, LEN: {LEN}", "LEN", rlen);
673 return {};
674 }
675
676 resp.full_payload.resize(rlen);
677
678 respHeader =
679 reinterpret_cast<decltype(respHeader)>(resp.full_payload.data());
680
681 /* header validation */
682 if (respHeader->MCID != mcid)
683 {
684 lg2::error("Invalid MCID {MCID} in response", "MCID", lg2::hex,
685 respHeader->MCID);
686 return {};
687 }
688
689 if (respHeader->id != iid)
690 {
691 lg2::error("Invalid IID {IID} in response", "IID", lg2::hex,
692 respHeader->id);
693 return {};
694 }
695
696 if (respHeader->type != (cmd.opcode | 0x80))
697 {
698 lg2::error("Invalid opcode {OPCODE} in response", "OPCODE", lg2::hex,
699 respHeader->type);
700 return {};
701 }
702
703 int rc = resp.parseFullPayload();
704 if (rc)
705 {
706 return {};
707 }
708
709 return resp;
710}
711
712std::string MCTPInterface::toString()
713{
714 return std::to_string(net) + "," + std::to_string(eid);
715}
716
717MCTPInterface::MCTPInterface(int net, uint8_t eid) : net(net), eid(eid)
718{
719 static const struct timeval receiveTimeout = {
720 .tv_sec = 1,
721 .tv_usec = 0,
722 };
723
724 int _sd = socket(AF_MCTP, SOCK_DGRAM, 0);
725 if (_sd < 0)
726 {
727 throw std::system_error(errno, std::system_category(),
728 "Can't create MCTP socket");
729 }
730
731 int rc = setsockopt(_sd, SOL_SOCKET, SO_RCVTIMEO, &receiveTimeout,
732 sizeof(receiveTimeout));
733 if (rc != 0)
734 {
735 throw std::system_error(errno, std::system_category(),
736 "Can't set socket receive timemout");
737 }
738
739 sd = _sd;
740}
741
742MCTPInterface::~MCTPInterface()
743{
744 close(sd);
745}
746
Gunnar Mills57d9c502018-09-14 14:42:34 -0500747} // namespace ncsi
748} // namespace network
749} // namespace phosphor