Skip to content
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.

Commit 24ac045

Browse files
committedJun 24, 2025·
mctp-client: add a generic mctp client
Adds a generic mctp client to use from the cli. Currently does not support extended addressing. Testing --- control commands ``` mctp-client type control eid 8 data 0x80 0x02 0x00 0x02 0x00 0x08 0x01 0x00 ``` corner case testing ``` mctp-client type control eid 10 data 0x80 0x02 mctp-client: sendto(2): No route to host mctp-client type control eid 8 data mctp-client: no data to send mctp-client type control eid data 0x80 0x02 usage: mctp-client [net <net>] eid <eid> type <type> data <data> net defaults to MCTP_NET_ANY, data is space delimited hexadecimal. data must be the last parameter possible types: control: control mctp messages per DSP0236 pldm: platform level data model per DSP0241 nc-si: NC-SI traffic over MCTP per DSP0261 ethernet: Ethernet traffic over MCTP per DSP0261 nvme: nvme over mctp per DSP0235 spdm: spdm over mctp per DSP0275 secured: secured messages using spdm over mctp per DSP0276 pci: vdm using a pci based vendor id per DSP0236 iana: vdm using an iana based vendor id per DSP0236 mctp-client type control eid data 0x80 0x02 usage: mctp-client [net <net>] eid <eid> type <type> data <data> net defaults to MCTP_NET_ANY, data is space delimited hexadecimal. data must be the last parameter possible types: control: control mctp messages per DSP0236 pldm: platform level data model per DSP0241 nc-si: NC-SI traffic over MCTP per DSP0261 ethernet: Ethernet traffic over MCTP per DSP0261 nvme: nvme over mctp per DSP0235 spdm: spdm over mctp per DSP0275 secured: secured messages using spdm over mctp per DSP0276 pci: vdm using a pci based vendor id per DSP0236 iana: vdm using an iana based vendor id per DSP0236 echo $? 1 mctp-client eid 8 data 0x80 0x02 usage: mctp-client [net <net>] eid <eid> type <type> data <data> net defaults to MCTP_NET_ANY, data is space delimited hexadecimal. data must be the last parameter possible types: control: control mctp messages per DSP0236 pldm: platform level data model per DSP0241 nc-si: NC-SI traffic over MCTP per DSP0261 ethernet: Ethernet traffic over MCTP per DSP0261 nvme: nvme over mctp per DSP0235 spdm: spdm over mctp per DSP0275 secured: secured messages using spdm over mctp per DSP0276 pci: vdm using a pci based vendor id per DSP0236 iana: vdm using an iana based vendor id per DSP0236 mctp-client type eid 8 data 0x80 0x02 mctp-client: invalid type: eid ```
1 parent 380608e commit 24ac045

File tree

2 files changed

+261
-0
lines changed

2 files changed

+261
-0
lines changed
 

‎meson.build

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,11 @@ executable('mctp-bench',
6565
sources: ['src/mctp-bench.c'] + util_sources,
6666
)
6767

68+
executable('mctp-client',
69+
sources: ['src/mctp-client.c'] + util_sources,
70+
install: true
71+
)
72+
6873
if libsystemd.found()
6974
executable('mctpd',
7075
sources: [

‎src/mctp-client.c

Lines changed: 256 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,256 @@
1+
/* SPDX-License-Identifier: GPL-2.0 */
2+
/*
3+
* mctp-client: a raw mctp client for the mctp kernel interface
4+
*
5+
* Copyright (c) 2025 Nvidia
6+
* Copyright (c) 2025 Code Construct
7+
*/
8+
9+
#include <stdint.h>
10+
#include <stdio.h>
11+
#include <stdlib.h>
12+
#include <unistd.h>
13+
#include <stdbool.h>
14+
#include <string.h>
15+
#include <err.h>
16+
#include <sys/socket.h>
17+
#include <errno.h>
18+
#include <limits.h>
19+
20+
#include "mctp.h"
21+
#include "mctp-util.h"
22+
23+
struct data_t {
24+
uint8_t *data;
25+
size_t len;
26+
};
27+
28+
/* Types and values taken from DSP0239 */
29+
static struct type_lookup_t {
30+
const char *name;
31+
uint8_t type;
32+
const char *description;
33+
} type_lookup[] = {
34+
{
35+
.name = "control",
36+
.type = 0,
37+
.description = "control mctp messages per DSP0236",
38+
},
39+
{
40+
.name = "pldm",
41+
.type = 1,
42+
.description = "platform level data model per DSP0241",
43+
},
44+
{
45+
.name = "nc-si",
46+
.type = 2,
47+
.description = "NC-SI traffic over MCTP per DSP0261",
48+
},
49+
{
50+
.name = "ethernet",
51+
.type = 3,
52+
.description = "Ethernet traffic over MCTP per DSP0261",
53+
},
54+
{
55+
.name = "nvme",
56+
.type = 4,
57+
.description = "nvme over mctp per DSP0235",
58+
},
59+
{
60+
.name = "spdm",
61+
.type = 5,
62+
.description = "spdm over mctp per DSP0275",
63+
},
64+
{
65+
.name = "secured",
66+
.type = 6,
67+
.description = "secured messages using spdm over mctp per DSP0276",
68+
},
69+
{
70+
.name = "pci",
71+
.type = 0x7e,
72+
.description = "vdm using a pci based vendor id per DSP0236",
73+
},
74+
{
75+
.name = "iana",
76+
.type = 0x7f,
77+
.description = "vdm using an iana based vendor id per DSP0236",
78+
},
79+
};
80+
81+
static int do_type_lookup(char *type_str) {
82+
int ctr;
83+
84+
for (ctr = 0; ctr < ARRAY_SIZE(type_lookup); ++ctr) {
85+
if (!strcmp(type_str, type_lookup[ctr].name))
86+
return type_lookup[ctr].type;
87+
}
88+
89+
return -ENOENT;
90+
};
91+
92+
static int do_send_recv(unsigned int net, mctp_eid_t eid,
93+
uint8_t type, struct data_t *data)
94+
{
95+
int sd, rc, recvlen, ctr;
96+
struct sockaddr_mctp addr;
97+
uint8_t *recv_buffer;
98+
socklen_t addrlen;
99+
100+
sd = socket(AF_MCTP, SOCK_DGRAM, 0);
101+
if (sd < 0)
102+
err(EXIT_FAILURE, "socket");
103+
104+
105+
memset(&addr, 0, sizeof(addr));
106+
addr.smctp_tag = MCTP_TAG_OWNER;
107+
addr.smctp_family = AF_MCTP;
108+
addr.smctp_network = net;
109+
addr.smctp_type = type;
110+
addr.smctp_addr.s_addr = eid;
111+
112+
rc = sendto(sd, data->data, data->len, 0,
113+
(struct sockaddr *) &addr, sizeof(addr));
114+
115+
if (rc != data->len)
116+
err(EXIT_FAILURE, "sendto(%zd)", data->len);
117+
118+
recvlen = recvfrom(sd, NULL, 0, MSG_TRUNC | MSG_PEEK,
119+
(struct sockaddr *) &addr, &addrlen);
120+
121+
if (recvlen < 0)
122+
err(EXIT_FAILURE, "receive failed %d", recvlen);
123+
124+
recv_buffer = malloc(recvlen);
125+
if (!recv_buffer)
126+
errx(EXIT_FAILURE, "malloc failed for recv");
127+
128+
rc = recvfrom(sd, recv_buffer, recvlen, MSG_TRUNC,
129+
(struct sockaddr *) &addr, &addrlen);
130+
131+
if (rc < 0)
132+
err(EXIT_FAILURE, "receive failed %d", recvlen);
133+
134+
if (recvlen != rc)
135+
errx(EXIT_FAILURE, "invalid bytes received: %d, expected %d", rc, recvlen);
136+
137+
for (ctr = 0; ctr < rc; ++ctr) {
138+
printf("0x%02X", recv_buffer[ctr]);
139+
if (ctr != (rc - 1))
140+
printf(" ");
141+
}
142+
143+
printf("\n");
144+
145+
return 0;
146+
}
147+
148+
static void print_usage() {
149+
int ctr;
150+
printf("usage:\n\tmctp-client [net <net>] eid <eid> type <type> data <data>\n");
151+
printf("net defaults to MCTP_NET_ANY, data is space delimited hexadecimal. ");
152+
printf("data must be the last parameter\n");
153+
printf("possible types:\n");
154+
for (ctr = 0; ctr < ARRAY_SIZE(type_lookup); ++ctr) {
155+
printf("\t%s: %s\n", type_lookup[ctr].name, type_lookup[ctr].description);
156+
}
157+
}
158+
159+
static struct data_t create_data(char **data_start, size_t count) {
160+
unsigned long int tmp;
161+
struct data_t data;
162+
char *endp;
163+
int ctr;
164+
165+
data.data = malloc(count);
166+
if (!data.data)
167+
errx(EXIT_FAILURE, "failed to malloc for data");
168+
169+
if (!count)
170+
errx(EXIT_FAILURE, "no data to send");
171+
172+
data.len = count;
173+
174+
for (ctr = 0; ctr < count; ++ctr) {
175+
tmp = strtoul(data_start[ctr], &endp, 16);
176+
if (endp == data_start[ctr])
177+
errx(EXIT_FAILURE, "data must be the last parameter");
178+
if (tmp == ULONG_MAX)
179+
errx(EXIT_FAILURE, "failed to parse: %s", data_start[ctr]);
180+
if (tmp > 0xff)
181+
errx(EXIT_FAILURE, "data parsed is invalid: %s", data_start[ctr]);
182+
data.data[ctr] = tmp;
183+
}
184+
185+
return data;
186+
}
187+
188+
static int find_data(int argc, char **argv) {
189+
int ctr;
190+
191+
for (ctr = argc - 1; ctr > 0; --ctr) {
192+
if (!strcmp(argv[ctr], "data"))
193+
return ctr;
194+
}
195+
196+
return -ENOENT;
197+
}
198+
199+
int main(int argc, char **argv) {
200+
bool valid_parse, valid_eid, valid_type;
201+
int ctr, data_idx, net = MCTP_NET_ANY;
202+
struct data_t send_data;
203+
char *tag, *endp, *val;
204+
unsigned long int tmp;
205+
mctp_eid_t eid;
206+
int type;
207+
208+
data_idx = find_data(argc, argv);
209+
if (data_idx < 0) {
210+
printf("unable to find data to send\n");
211+
print_usage();
212+
return EXIT_FAILURE;
213+
}
214+
215+
send_data = create_data(argv + data_idx + 1, argc - data_idx - 1);
216+
217+
/*
218+
* a little hacky, start by parsing the data since it needs to be last
219+
* then parse the rest of the params
220+
*/
221+
argc = data_idx;
222+
valid_eid = false;
223+
valid_type = false;
224+
225+
for (ctr = 1; ctr < argc; ctr += 2) {
226+
tag = argv[ctr];
227+
val = argv[ctr + 1];
228+
tmp = strtoul(val, &endp, 0);
229+
valid_parse = endp != val;
230+
if (!strcmp(tag, "eid")) {
231+
if (tmp > 0xff)
232+
errx(EXIT_FAILURE, "invalid eid: %s", val);
233+
eid = tmp;
234+
valid_eid = true;
235+
} else if (!strcmp(tag, "net")) {
236+
if (net > 0xff)
237+
errx(EXIT_FAILURE, "invalid net: %s", val);
238+
net = tmp;
239+
} else if (!strcmp(tag, "type")) {
240+
type = do_type_lookup(val);
241+
if (type < 0 || type > 0xff)
242+
errx(EXIT_FAILURE, "invalid type: %s", val);
243+
244+
valid_parse = true;
245+
valid_type = true;
246+
} else
247+
errx(EXIT_FAILURE, "invalid tag: %s", tag);
248+
}
249+
250+
if (!valid_parse || !valid_eid || !valid_type) {
251+
print_usage();
252+
return EXIT_FAILURE;
253+
}
254+
255+
return do_send_recv(net, eid, type, &send_data);
256+
}

0 commit comments

Comments
 (0)
Please sign in to comment.