Skip to content

Commit 5167bc3

Browse files
committedJun 26, 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 00 02 00 08 01 00 ``` 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 ``` Signed-off-by: Marc Olberding <marcolberding@gmail.com>
1 parent 380608e commit 5167bc3

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+
*/
7+
8+
#include <stdint.h>
9+
#include <stdio.h>
10+
#include <stdlib.h>
11+
#include <unistd.h>
12+
#include <stdbool.h>
13+
#include <string.h>
14+
#include <err.h>
15+
#include <sys/socket.h>
16+
#include <errno.h>
17+
#include <limits.h>
18+
19+
#include "mctp.h"
20+
#include "mctp-util.h"
21+
22+
struct data_t {
23+
uint8_t *data;
24+
size_t len;
25+
};
26+
27+
/* Types and values taken from DSP0239 */
28+
static struct type_lookup_t {
29+
const char *name;
30+
uint8_t type;
31+
const char *description;
32+
} type_lookup[] = {
33+
{
34+
.name = "control",
35+
.type = 0,
36+
.description = "control mctp messages per DSP0236",
37+
},
38+
{
39+
.name = "pldm",
40+
.type = 1,
41+
.description = "platform level data model per DSP0241",
42+
},
43+
{
44+
.name = "nc-si",
45+
.type = 2,
46+
.description = "NC-SI traffic over MCTP per DSP0261",
47+
},
48+
{
49+
.name = "ethernet",
50+
.type = 3,
51+
.description = "Ethernet traffic over MCTP per DSP0261",
52+
},
53+
{
54+
.name = "nvme",
55+
.type = 4,
56+
.description = "nvme over mctp per DSP0235",
57+
},
58+
{
59+
.name = "spdm",
60+
.type = 5,
61+
.description = "spdm over mctp per DSP0275",
62+
},
63+
{
64+
.name = "secured",
65+
.type = 6,
66+
.description = "secured messages using spdm over mctp per DSP0276",
67+
},
68+
{
69+
.name = "pci",
70+
.type = 0x7e,
71+
.description = "vdm using a pci based vendor id per DSP0236",
72+
},
73+
{
74+
.name = "iana",
75+
.type = 0x7f,
76+
.description = "vdm using an iana based vendor id per DSP0236",
77+
},
78+
};
79+
80+
static int do_type_lookup(char *type_str) {
81+
int ctr;
82+
83+
for (ctr = 0; ctr < ARRAY_SIZE(type_lookup); ++ctr) {
84+
if (!strcmp(type_str, type_lookup[ctr].name))
85+
return type_lookup[ctr].type;
86+
}
87+
88+
return -ENOENT;
89+
};
90+
91+
static int do_send_recv(unsigned int net, mctp_eid_t eid,
92+
uint8_t type, struct data_t *data)
93+
{
94+
int sd, rc, recvlen, ctr;
95+
struct sockaddr_mctp addr;
96+
uint8_t *recv_buffer;
97+
socklen_t addrlen;
98+
99+
sd = socket(AF_MCTP, SOCK_DGRAM, 0);
100+
if (sd < 0)
101+
err(EXIT_FAILURE, "socket");
102+
103+
104+
memset(&addr, 0, sizeof(addr));
105+
addr.smctp_tag = MCTP_TAG_OWNER;
106+
addr.smctp_family = AF_MCTP;
107+
addr.smctp_network = net;
108+
addr.smctp_type = type;
109+
addr.smctp_addr.s_addr = eid;
110+
111+
rc = sendto(sd, data->data, data->len, 0,
112+
(struct sockaddr *) &addr, sizeof(addr));
113+
114+
if (rc != data->len)
115+
err(EXIT_FAILURE, "sendto(%zd)", data->len);
116+
117+
recvlen = recvfrom(sd, NULL, 0, MSG_TRUNC | MSG_PEEK,
118+
(struct sockaddr *) &addr, &addrlen);
119+
120+
if (recvlen < 0)
121+
err(EXIT_FAILURE, "receive failed %d", recvlen);
122+
123+
recv_buffer = malloc(recvlen);
124+
if (!recv_buffer)
125+
errx(EXIT_FAILURE, "malloc failed for recv");
126+
127+
rc = recvfrom(sd, recv_buffer, recvlen, MSG_TRUNC,
128+
(struct sockaddr *) &addr, &addrlen);
129+
130+
if (rc < 0)
131+
err(EXIT_FAILURE, "receive failed %d", recvlen);
132+
133+
if (recvlen != rc)
134+
errx(EXIT_FAILURE, "invalid bytes received: %d, expected %d", rc, recvlen);
135+
136+
for (ctr = 0; ctr < rc; ++ctr) {
137+
printf("%02X", recv_buffer[ctr]);
138+
if (ctr != (rc - 1))
139+
printf(" ");
140+
}
141+
142+
printf("\n");
143+
144+
return 0;
145+
}
146+
147+
static void print_usage() {
148+
int ctr;
149+
printf("usage:\n\tmctp-client [net <net>] eid <eid> type <type> data <data>\n");
150+
printf("net defaults to MCTP_NET_ANY, data is space delimited hexadecimal. ");
151+
printf("data must be the last parameter\n");
152+
printf("possible types:\n");
153+
for (ctr = 0; ctr < ARRAY_SIZE(type_lookup); ++ctr) {
154+
printf("\t%s: %s\n", type_lookup[ctr].name, type_lookup[ctr].description);
155+
}
156+
printf("return data is always output as space delimited hexadecimal\n");
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)