-
Notifications
You must be signed in to change notification settings - Fork 22
mctp-req: Make mctp-req a generic mctp client #82
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -20,22 +20,18 @@ | |
|
||
static const int DEFAULT_NET = 1; | ||
static const mctp_eid_t DEFAULT_EID = 8; | ||
static const size_t DEFAULT_LEN = 1; | ||
|
||
// Code Construct allocation | ||
static const uint8_t VENDOR_TYPE_ECHO[3] = { 0xcc, 0xde, 0xf0 }; | ||
static const uint8_t MCTP_TYPE_VENDOR_PCIE = 0x7e; | ||
|
||
/* lladdrlen != -1 to ignore ifindex/lladdr */ | ||
static int mctp_req(unsigned int net, mctp_eid_t eid, | ||
unsigned int ifindex, uint8_t *lladdr, int lladdrlen, | ||
uint8_t *data, size_t len) | ||
uint8_t *data, size_t len, uint8_t type) | ||
{ | ||
struct sockaddr_mctp_ext addr; | ||
unsigned char *payload, *buf; | ||
unsigned char *rxbuf; | ||
socklen_t addrlen; | ||
int rc, sd, val; | ||
size_t i, buf_len; | ||
size_t recvlen; | ||
size_t i; | ||
|
||
sd = socket(AF_MCTP, SOCK_DGRAM, 0); | ||
if (sd < 0) | ||
|
@@ -46,34 +42,15 @@ static int mctp_req(unsigned int net, mctp_eid_t eid, | |
addr.smctp_base.smctp_family = AF_MCTP; | ||
addr.smctp_base.smctp_network = net; | ||
addr.smctp_base.smctp_addr.s_addr = eid; | ||
addr.smctp_base.smctp_type = MCTP_TYPE_VENDOR_PCIE; | ||
addr.smctp_base.smctp_type = type; | ||
addr.smctp_base.smctp_tag = MCTP_TAG_OWNER; | ||
printf("req: sending to (net %d, eid %d), type 0x%x\n", | ||
net, eid, addr.smctp_base.smctp_type); | ||
|
||
buf_len = len + sizeof(VENDOR_TYPE_ECHO); | ||
buf = malloc(buf_len); | ||
if (!buf) | ||
err(EXIT_FAILURE, "malloc"); | ||
memcpy(buf, VENDOR_TYPE_ECHO, sizeof(VENDOR_TYPE_ECHO)); | ||
payload = &buf[sizeof(VENDOR_TYPE_ECHO)]; | ||
|
||
if (data) { | ||
memcpy(payload, data, len); | ||
} else { | ||
for (i = 0; i < len; i++) | ||
payload[i] = i & 0xff; | ||
} | ||
|
||
/* extended addressing */ | ||
if (lladdrlen != -1) { | ||
addrlen = sizeof(struct sockaddr_mctp_ext); | ||
addr.smctp_halen = lladdrlen; | ||
memcpy(addr.smctp_haddr, lladdr, lladdrlen); | ||
addr.smctp_ifindex = ifindex; | ||
printf(" ext ifindex %d ha[0]=0x%02x len %hhu\n", | ||
addr.smctp_ifindex, | ||
addr.smctp_haddr[0], addr.smctp_halen); | ||
val = 1; | ||
rc = setsockopt(sd, SOL_MCTP, MCTP_OPT_ADDR_EXT, &val, sizeof(val)); | ||
if (rc < 0) | ||
|
@@ -83,71 +60,64 @@ static int mctp_req(unsigned int net, mctp_eid_t eid, | |
|
||
|
||
/* send data */ | ||
rc = sendto(sd, buf, buf_len, 0, | ||
rc = sendto(sd, data, len, 0, | ||
(struct sockaddr *)&addr, addrlen); | ||
if (rc != (int)buf_len) | ||
err(EXIT_FAILURE, "sendto(%zd)", buf_len); | ||
if (rc != (int)len) | ||
err(EXIT_FAILURE, "sendto(%zd)", len); | ||
|
||
/* receive response */ | ||
addrlen = sizeof(addr); | ||
rc = recvfrom(sd, buf, buf_len, MSG_TRUNC, | ||
recvlen = recvfrom(sd, NULL, 0, MSG_PEEK | MSG_TRUNC, | ||
NULL, 0); | ||
if (recvlen < 0) | ||
err(EXIT_FAILURE, "recvfrom(MSG_PEEK)"); | ||
|
||
rxbuf = calloc(recvlen, sizeof(*rxbuf)); | ||
if (!rxbuf) | ||
err(EXIT_FAILURE, "calloc"); | ||
|
||
rc = recvfrom(sd, rxbuf, recvlen, MSG_TRUNC, | ||
(struct sockaddr *)&addr, &addrlen); | ||
if (rc < 0) | ||
err(EXIT_FAILURE, "recvfrom"); | ||
else if ((size_t)rc != buf_len) | ||
else if ((size_t)rc != recvlen) | ||
errx(EXIT_FAILURE, "unexpected length: got %d, exp %zd", | ||
rc, buf_len); | ||
rc, recvlen); | ||
|
||
if (!(addrlen == sizeof(struct sockaddr_mctp_ext) || | ||
addrlen == sizeof(struct sockaddr_mctp))) | ||
errx(EXIT_FAILURE, "unknown recv address length %d, exp %zu or %zu)", | ||
addrlen, sizeof(struct sockaddr_mctp_ext), | ||
sizeof(struct sockaddr_mctp)); | ||
|
||
|
||
printf("req: message from (net %d, eid %d) type 0x%x len %zd\n", | ||
addr.smctp_base.smctp_network, addr.smctp_base.smctp_addr.s_addr, | ||
addr.smctp_base.smctp_type, | ||
len); | ||
if (addrlen == sizeof(struct sockaddr_mctp_ext)) { | ||
printf(" ext ifindex %d ha[0]=0x%02x len %hhu\n", | ||
addr.smctp_ifindex, | ||
addr.smctp_haddr[0], addr.smctp_halen); | ||
} | ||
|
||
if (memcmp(buf, VENDOR_TYPE_ECHO, sizeof(VENDOR_TYPE_ECHO)) != 0) { | ||
errx(EXIT_FAILURE, "unexpected vendor ID"); | ||
} | ||
|
||
for (i = 0; i < len; i++) { | ||
uint8_t exp = data ? data[i] : i & 0xff; | ||
if (payload[i] != exp) | ||
errx(EXIT_FAILURE, | ||
"payload mismatch at byte 0x%zx; " | ||
"sent 0x%02x, received 0x%02x", | ||
i, exp, buf[i]); | ||
for (i = 0; i < recvlen; ++i) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. There was some back and forth on whether we should use space delimited or colon delimited hex strings. This uses colon delimited strings for the sake of consistency between send and receive side, as the send side was already using colon delimited strings. Part of me thinks that json would be a better interface on both sides as we can just use jq to parse it instead of handrolled bash, but open to thoughts. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I find the proposed output fairly difficult to scan - there are a lot of superfluous characters in:
would it work to use If you're looking for programmatic interfaces here (to support json or scripted automation), I would suggest that we would want something other than (that's why we're not currently installing it by default...) There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Sure that's fine There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I didn't really have production flows in mind per se, but mostly for things like hardware bring up where you need to prove out functionality quickly. Having scripts that you can hand out to people before full firmware support is up is a boon and in my mind, a hard to use interface won't stop people from sneaking in bad hacks like using this in a production flow. That said I'm not going to die on this hill and json isn't the end all be all, my thought was that it would allow the user to see the debug info we currently output while not making scripting more difficult than it has to be. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. ok, sounds like we're on the same page then; I have seen a lot of scripting hacks escape into production though! For the bringup support objective, I would suggest focus on a human-readable interface, provided we don't stray too far from "possible to parse". Consistency is good; would space-separated work for both input and output? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think that's a reasonable compromise. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. ... and maybe assume hex in There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I don't really have a strong opinion, it would be a little odd to use decimal here in any case, but we should probably spell that out in the usage text |
||
printf("0x%02x", rxbuf[i]); | ||
if (i != (recvlen - 1)) | ||
printf(":"); | ||
} | ||
|
||
printf("\n"); | ||
return 0; | ||
} | ||
|
||
static void usage(void) | ||
{ | ||
fprintf(stderr, "mctp-req [eid <eid>] [net <net>] [ifindex <ifindex> lladdr <hwaddr>] [len <len>]\n"); | ||
fprintf(stderr, "default eid %d net %d len %zd\n", | ||
DEFAULT_EID, DEFAULT_NET, DEFAULT_LEN); | ||
fprintf(stderr, "mctp-req [eid <eid>] [net <net>] type <type> [ifindex <ifindex> lladdr <hwaddr>] data <data>\n"); | ||
fprintf(stderr, "default eid %d net %d\n", | ||
DEFAULT_EID, DEFAULT_NET); | ||
} | ||
|
||
int main(int argc, char ** argv) | ||
{ | ||
uint8_t *data, lladdr[MAX_ADDR_LEN]; | ||
int lladdrlen = -1, datalen = -1; | ||
unsigned int net = DEFAULT_NET; | ||
mctp_eid_t eid = DEFAULT_EID; | ||
size_t len = DEFAULT_LEN, sz; | ||
char *endp, *optname, *optval; | ||
mctp_eid_t eid = DEFAULT_EID; | ||
bool valid_parse, valid_type; | ||
unsigned int tmp, ifindex; | ||
bool valid_parse; | ||
uint8_t type; | ||
size_t sz; | ||
int i; | ||
|
||
if (!(argc % 2)) { | ||
|
@@ -156,8 +126,14 @@ int main(int argc, char ** argv) | |
return 255; | ||
} | ||
|
||
data = NULL; | ||
if (argc < 5) { | ||
usage(); | ||
return EXIT_FAILURE; | ||
} | ||
|
||
ifindex = 0; | ||
data = NULL; | ||
valid_type = false; | ||
|
||
for (i = 1; i < argc; i += 2) { | ||
optname = argv[i]; | ||
|
@@ -174,12 +150,13 @@ int main(int argc, char ** argv) | |
if (tmp > 0xff) | ||
errx(EXIT_FAILURE, "Bad net"); | ||
net = tmp; | ||
} else if (!strcmp(optname, "type")) { | ||
type = tmp; | ||
if (type > 0xff) | ||
errx(EXIT_FAILURE, "Bad type"); | ||
valid_type = true; | ||
} else if (!strcmp(optname, "ifindex")) { | ||
ifindex = tmp; | ||
} else if (!strcmp(optname, "len")) { | ||
if (tmp > 64 * 1024) | ||
errx(EXIT_FAILURE, "Bad len"); | ||
len = tmp; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'd like to retain the There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Are there other use cases than echo for when we don't care about the data? This was mostly trying to make it difficult to misuse, i.e accidentally truncating data with length for example There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Not really, just echo. However, |
||
} else if (!strcmp(optname, "data")) { | ||
sz = (strlen(optval) + 2) / 3; | ||
data = malloc(sz); | ||
|
@@ -207,8 +184,16 @@ int main(int argc, char ** argv) | |
} | ||
} | ||
|
||
if (data) | ||
len = datalen; | ||
if (!datalen || !data) { | ||
printf("data is a required field\n"); | ||
usage(); | ||
return EXIT_FAILURE; | ||
} | ||
if (!valid_type) { | ||
printf("type is a required field\n"); | ||
usage(); | ||
return EXIT_FAILURE; | ||
} | ||
|
||
return mctp_req(net, eid, ifindex, lladdr, lladdrlen, data, len); | ||
return mctp_req(net, eid, ifindex, lladdr, lladdrlen, data, datalen, type); | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We still want to be able to interact with the echo service; I would prefer if we can still describe vendor types here - or even retaining a shortcut for sending an echo request.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm fine with having an echo shortcut, my original thinking was that its not terribly difficult to script the existing behavior but would something like mctp-req echo instead of specifying data and type make sense to you?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think so. I was initially imagining something like
... but then we also need the subtype byte (0xf0), which is vendor specific. We can get around that by using the
data
parameter, but now you have to specify all the data :/ So using a generic format for vendor types isn't really practical.Would it work to allow named type aliases? like:
and even extending this to existing types:
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sure that works for me.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Could probably skip the
type
keyword for those cases?There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yep, the primary purpose of
req
is to be an echo client. It's now sounding like a separate program would be a better approach.I'm happy to rename
mctp-req
if you'd like to use that for a "generic send-a-message" utility.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ah, yes, there is that too. Yes, I would like to keep that.
Uh oh!
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah, I'll gather the feedback here and put up a cleaned up pr for a generic send a message client. Let's call it mctp-client, also let's get the bike shedding on the name done now :)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sorry if you were up for more bikeshedding,
mctp-client
sounds great :DThere was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is it even a sub one hundred line change on an obscure utility if it doesn't have thirty comments? :P
Seriously though, I appreciate all the feedback