xref: /openbmc/libpldm/src/requester/pldm.c (revision 9a8e4975)
1 #include "pldm.h"
2 #include "base.h"
3 
4 #include <bits/types/struct_iovec.h>
5 #include <stdlib.h>
6 #include <string.h>
7 #include <sys/socket.h>
8 #include <sys/un.h>
9 #include <unistd.h>
10 
11 const uint8_t MCTP_MSG_TYPE_PLDM = 1;
12 
13 pldm_requester_rc_t pldm_open()
14 {
15 	int fd = -1;
16 	int rc = -1;
17 
18 	fd = socket(AF_UNIX, SOCK_SEQPACKET, 0);
19 	if (-1 == fd) {
20 		return fd;
21 	}
22 
23 	const char path[] = "\0mctp-mux";
24 	struct sockaddr_un addr;
25 	addr.sun_family = AF_UNIX;
26 	memcpy(addr.sun_path, path, sizeof(path) - 1);
27 	rc = connect(fd, (struct sockaddr *)&addr,
28 		     sizeof(path) + sizeof(addr.sun_family) - 1);
29 	if (-1 == rc) {
30 		return PLDM_REQUESTER_OPEN_FAIL;
31 	}
32 	rc = write(fd, &MCTP_MSG_TYPE_PLDM, sizeof(MCTP_MSG_TYPE_PLDM));
33 	if (-1 == rc) {
34 		return PLDM_REQUESTER_OPEN_FAIL;
35 	}
36 
37 	return fd;
38 }
39 
40 /**
41  * @brief Read MCTP socket. If there's data available, return success only if
42  *        data is a PLDM message.
43  *
44  * @param[in] eid - destination MCTP eid
45  * @param[in] mctp_fd - MCTP socket fd
46  * @param[out] pldm_resp_msg - *pldm_resp_msg will point to PLDM msg,
47  *             this function allocates memory, caller to free(*pldm_resp_msg) on
48  *             success.
49  * @param[out] resp_msg_len - caller owned pointer that will be made point to
50  *             the size of the PLDM msg.
51  *
52  * @return pldm_requester_rc_t (errno may be set). failure is returned even
53  *         when data was read, but wasn't a PLDM response message
54  */
55 static pldm_requester_rc_t mctp_recv(mctp_eid_t eid, int mctp_fd,
56 				     uint8_t **pldm_resp_msg,
57 				     size_t *resp_msg_len)
58 {
59 	ssize_t min_len = sizeof(eid) + sizeof(MCTP_MSG_TYPE_PLDM) +
60 			  sizeof(struct pldm_msg_hdr);
61 	ssize_t length = recv(mctp_fd, NULL, 0, MSG_PEEK | MSG_TRUNC);
62 	if (length <= 0) {
63 		return PLDM_REQUESTER_RECV_FAIL;
64 	} else if (length < min_len) {
65 		/* read and discard */
66 		uint8_t buf[length];
67 		recv(mctp_fd, buf, length, 0);
68 		return PLDM_REQUESTER_INVALID_RECV_LEN;
69 	} else {
70 		struct iovec iov[2];
71 		size_t mctp_prefix_len =
72 		    sizeof(eid) + sizeof(MCTP_MSG_TYPE_PLDM);
73 		uint8_t mctp_prefix[mctp_prefix_len];
74 		size_t pldm_len = length - mctp_prefix_len;
75 		iov[0].iov_len = mctp_prefix_len;
76 		iov[0].iov_base = mctp_prefix;
77 		*pldm_resp_msg = malloc(pldm_len);
78 		iov[1].iov_len = pldm_len;
79 		iov[1].iov_base = *pldm_resp_msg;
80 		struct msghdr msg = {0};
81 		msg.msg_iov = iov;
82 		msg.msg_iovlen = sizeof(iov) / sizeof(iov[0]);
83 		ssize_t bytes = recvmsg(mctp_fd, &msg, 0);
84 		if (length != bytes) {
85 			free(*pldm_resp_msg);
86 			return PLDM_REQUESTER_INVALID_RECV_LEN;
87 		}
88 		if ((mctp_prefix[0] != eid) ||
89 		    (mctp_prefix[1] != MCTP_MSG_TYPE_PLDM)) {
90 			free(*pldm_resp_msg);
91 			return PLDM_REQUESTER_NOT_PLDM_MSG;
92 		}
93 		*resp_msg_len = pldm_len;
94 		return PLDM_REQUESTER_SUCCESS;
95 	}
96 }
97 
98 pldm_requester_rc_t pldm_recv_any(mctp_eid_t eid, int mctp_fd,
99 				  uint8_t **pldm_resp_msg, size_t *resp_msg_len)
100 {
101 	pldm_requester_rc_t rc =
102 	    mctp_recv(eid, mctp_fd, pldm_resp_msg, resp_msg_len);
103 	if (rc != PLDM_REQUESTER_SUCCESS) {
104 		return rc;
105 	}
106 
107 	struct pldm_msg_hdr *hdr = (struct pldm_msg_hdr *)(*pldm_resp_msg);
108 	if (hdr->request != PLDM_RESPONSE) {
109 		free(*pldm_resp_msg);
110 		return PLDM_REQUESTER_NOT_RESP_MSG;
111 	}
112 
113 	uint8_t pldm_rc = 0;
114 	if (*resp_msg_len < (sizeof(struct pldm_msg_hdr) + sizeof(pldm_rc))) {
115 		free(*pldm_resp_msg);
116 		return PLDM_REQUESTER_RESP_MSG_TOO_SMALL;
117 	}
118 
119 	return PLDM_REQUESTER_SUCCESS;
120 }
121 
122 pldm_requester_rc_t pldm_recv(mctp_eid_t eid, int mctp_fd, uint8_t instance_id,
123 			      uint8_t **pldm_resp_msg, size_t *resp_msg_len)
124 {
125 	pldm_requester_rc_t rc =
126 	    pldm_recv_any(eid, mctp_fd, pldm_resp_msg, resp_msg_len);
127 	if (rc != PLDM_REQUESTER_SUCCESS) {
128 		return rc;
129 	}
130 
131 	struct pldm_msg_hdr *hdr = (struct pldm_msg_hdr *)(*pldm_resp_msg);
132 	if (hdr->instance_id != instance_id) {
133 		free(*pldm_resp_msg);
134 		return PLDM_REQUESTER_INSTANCE_ID_MISMATCH;
135 	}
136 
137 	return PLDM_REQUESTER_SUCCESS;
138 }
139 
140 pldm_requester_rc_t pldm_send_recv(mctp_eid_t eid, int mctp_fd,
141 				   const uint8_t *pldm_req_msg,
142 				   size_t req_msg_len, uint8_t **pldm_resp_msg,
143 				   size_t *resp_msg_len)
144 {
145 	struct pldm_msg_hdr *hdr = (struct pldm_msg_hdr *)pldm_req_msg;
146 	if ((hdr->request != PLDM_REQUEST) &&
147 	    (hdr->request != PLDM_ASYNC_REQUEST_NOTIFY)) {
148 		return PLDM_REQUESTER_NOT_REQ_MSG;
149 	}
150 
151 	pldm_requester_rc_t rc =
152 	    pldm_send(eid, mctp_fd, pldm_req_msg, req_msg_len);
153 	if (rc != PLDM_REQUESTER_SUCCESS) {
154 		return rc;
155 	}
156 
157 	while (1) {
158 		rc = pldm_recv(eid, mctp_fd, hdr->instance_id, pldm_resp_msg,
159 			       resp_msg_len);
160 		if (rc == PLDM_REQUESTER_SUCCESS) {
161 			break;
162 		}
163 	}
164 
165 	return rc;
166 }
167 
168 pldm_requester_rc_t pldm_send(mctp_eid_t eid, int mctp_fd,
169 			      const uint8_t *pldm_req_msg, size_t req_msg_len)
170 {
171 	uint8_t hdr[2] = {eid, MCTP_MSG_TYPE_PLDM};
172 
173 	struct iovec iov[2];
174 	iov[0].iov_base = hdr;
175 	iov[0].iov_len = sizeof(hdr);
176 	iov[1].iov_base = (uint8_t *)pldm_req_msg;
177 	iov[1].iov_len = req_msg_len;
178 
179 	struct msghdr msg = {0};
180 	msg.msg_iov = iov;
181 	msg.msg_iovlen = sizeof(iov) / sizeof(iov[0]);
182 
183 	ssize_t rc = sendmsg(mctp_fd, &msg, 0);
184 	if (rc == -1) {
185 		return PLDM_REQUESTER_SEND_FAIL;
186 	}
187 	return PLDM_REQUESTER_SUCCESS;
188 }
189