xref: /openbmc/libpldm/src/transport/mctp-demux.c (revision 0a6d6821)
1 #include "mctp-defines.h"
2 #include "base.h"
3 #include "container-of.h"
4 #include "libpldm/pldm.h"
5 #include "libpldm/transport.h"
6 #include "libpldm/transport/mctp-demux.h"
7 #include "socket.h"
8 #include "transport.h"
9 
10 #include <errno.h>
11 #include <limits.h>
12 #include <poll.h>
13 #include <stdlib.h>
14 #include <string.h>
15 #include <sys/socket.h>
16 #include <sys/types.h>
17 #include <sys/un.h>
18 #include <unistd.h>
19 
20 #define MCTP_DEMUX_NAME "libmctp-demux-daemon"
21 const uint8_t mctp_msg_type = MCTP_MSG_TYPE_PLDM;
22 
23 struct pldm_transport_mctp_demux {
24 	struct pldm_transport transport;
25 	int socket;
26 	/* In the future this probably needs to move to a tid-eid-uuid/network
27 	 * id mapping for multi mctp networks */
28 	pldm_tid_t tid_eid_map[MCTP_MAX_NUM_EID];
29 	struct pldm_socket_sndbuf socket_send_buf;
30 };
31 
32 #define transport_to_demux(ptr)                                                \
33 	container_of(ptr, struct pldm_transport_mctp_demux, transport)
34 
35 LIBPLDM_ABI_STABLE
36 struct pldm_transport *
37 pldm_transport_mctp_demux_core(struct pldm_transport_mctp_demux *ctx)
38 {
39 	return &ctx->transport;
40 }
41 
42 static pldm_requester_rc_t pldm_transport_mctp_demux_open(void)
43 {
44 	int fd = -1;
45 	ssize_t rc = -1;
46 
47 	fd = socket(AF_UNIX, SOCK_SEQPACKET, 0);
48 	if (fd == -1) {
49 		return fd;
50 	}
51 
52 	const char path[] = "\0mctp-mux";
53 	struct sockaddr_un addr;
54 	addr.sun_family = AF_UNIX;
55 	memcpy(addr.sun_path, path, sizeof(path) - 1);
56 	rc = connect(fd, (struct sockaddr *)&addr,
57 		     sizeof(path) + sizeof(addr.sun_family) - 1);
58 	if (rc == -1) {
59 		return PLDM_REQUESTER_OPEN_FAIL;
60 	}
61 	rc = write(fd, &mctp_msg_type, sizeof(mctp_msg_type));
62 	if (rc == -1) {
63 		return PLDM_REQUESTER_OPEN_FAIL;
64 	}
65 
66 	return fd;
67 }
68 
69 LIBPLDM_ABI_STABLE
70 int pldm_transport_mctp_demux_init_pollfd(struct pldm_transport *t,
71 					  struct pollfd *pollfd)
72 {
73 	struct pldm_transport_mctp_demux *ctx = transport_to_demux(t);
74 	pollfd->fd = ctx->socket;
75 	pollfd->events = POLLIN;
76 	return 0;
77 }
78 
79 static int
80 pldm_transport_mctp_demux_get_eid(struct pldm_transport_mctp_demux *ctx,
81 				  pldm_tid_t tid, mctp_eid_t *eid)
82 {
83 	int i;
84 	for (i = 0; i < MCTP_MAX_NUM_EID; i++) {
85 		if (ctx->tid_eid_map[i] == tid) {
86 			*eid = i;
87 			return 0;
88 		}
89 	}
90 	*eid = -1;
91 	return -1;
92 }
93 
94 static int
95 pldm_transport_mctp_demux_get_tid(struct pldm_transport_mctp_demux *ctx,
96 				  mctp_eid_t eid, pldm_tid_t *tid)
97 {
98 	/* mapping exists */
99 	if (ctx->tid_eid_map[eid] != 0) {
100 		*tid = ctx->tid_eid_map[eid];
101 		return 0;
102 	}
103 	return -1;
104 }
105 
106 LIBPLDM_ABI_STABLE
107 int pldm_transport_mctp_demux_map_tid(struct pldm_transport_mctp_demux *ctx,
108 				      pldm_tid_t tid, mctp_eid_t eid)
109 {
110 	ctx->tid_eid_map[eid] = tid;
111 
112 	return 0;
113 }
114 
115 LIBPLDM_ABI_STABLE
116 int pldm_transport_mctp_demux_unmap_tid(struct pldm_transport_mctp_demux *ctx,
117 					__attribute__((unused)) pldm_tid_t tid,
118 					mctp_eid_t eid)
119 {
120 	ctx->tid_eid_map[eid] = 0;
121 
122 	return 0;
123 }
124 
125 static pldm_requester_rc_t
126 pldm_transport_mctp_demux_recv(struct pldm_transport *t, pldm_tid_t *tid,
127 			       void **pldm_msg, size_t *msg_len)
128 {
129 	struct pldm_transport_mctp_demux *demux = transport_to_demux(t);
130 	size_t mctp_prefix_len = 2;
131 	struct msghdr msg = { 0 };
132 	pldm_requester_rc_t res;
133 	uint8_t mctp_prefix[2];
134 	struct iovec iov[2];
135 	mctp_eid_t eid = 0;
136 	ssize_t min_len;
137 	size_t pldm_len;
138 	ssize_t length;
139 	ssize_t bytes;
140 	uint8_t *buf;
141 	int rc;
142 
143 	min_len = sizeof(eid) + sizeof(mctp_msg_type) +
144 		  sizeof(struct pldm_msg_hdr);
145 	length = recv(demux->socket, NULL, 0, MSG_PEEK | MSG_TRUNC);
146 	if (length <= 0) {
147 		return PLDM_REQUESTER_RECV_FAIL;
148 	}
149 
150 	buf = malloc(length);
151 	if (buf == NULL) {
152 		return PLDM_REQUESTER_RECV_FAIL;
153 	}
154 
155 	if (length < min_len) {
156 		/* read and discard */
157 		recv(demux->socket, buf, length, 0);
158 		res = PLDM_REQUESTER_INVALID_RECV_LEN;
159 		goto cleanup_buf;
160 	}
161 
162 	pldm_len = length - mctp_prefix_len;
163 	iov[0].iov_len = mctp_prefix_len;
164 	iov[0].iov_base = mctp_prefix;
165 	iov[1].iov_len = pldm_len;
166 	iov[1].iov_base = buf;
167 
168 	msg.msg_iov = iov;
169 	msg.msg_iovlen = sizeof(iov) / sizeof(iov[0]);
170 
171 	bytes = recvmsg(demux->socket, &msg, 0);
172 	if (length != bytes) {
173 		res = PLDM_REQUESTER_INVALID_RECV_LEN;
174 		goto cleanup_buf;
175 	}
176 
177 	if (mctp_prefix[1] != mctp_msg_type) {
178 		res = PLDM_REQUESTER_NOT_PLDM_MSG;
179 		goto cleanup_buf;
180 	}
181 
182 	eid = mctp_prefix[0];
183 	rc = pldm_transport_mctp_demux_get_tid(demux, eid, tid);
184 	if (rc) {
185 		res = PLDM_REQUESTER_RECV_FAIL;
186 		goto cleanup_buf;
187 	}
188 
189 	*pldm_msg = buf;
190 	*msg_len = pldm_len;
191 
192 	return PLDM_REQUESTER_SUCCESS;
193 
194 cleanup_buf:
195 	free(buf);
196 
197 	return res;
198 }
199 
200 static pldm_requester_rc_t
201 pldm_transport_mctp_demux_send(struct pldm_transport *t, pldm_tid_t tid,
202 			       const void *pldm_msg, size_t msg_len)
203 {
204 	struct pldm_transport_mctp_demux *demux = transport_to_demux(t);
205 	mctp_eid_t eid = 0;
206 	if (pldm_transport_mctp_demux_get_eid(demux, tid, &eid)) {
207 		return PLDM_REQUESTER_SEND_FAIL;
208 	}
209 
210 	uint8_t hdr[2] = { eid, mctp_msg_type };
211 
212 	struct iovec iov[2];
213 	iov[0].iov_base = hdr;
214 	iov[0].iov_len = sizeof(hdr);
215 	iov[1].iov_base = (uint8_t *)pldm_msg;
216 	iov[1].iov_len = msg_len;
217 
218 	struct msghdr msg = { 0 };
219 	msg.msg_iov = iov;
220 	msg.msg_iovlen = sizeof(iov) / sizeof(iov[0]);
221 
222 	if (msg_len > INT_MAX ||
223 	    pldm_socket_sndbuf_accomodate(&(demux->socket_send_buf),
224 					  (int)msg_len)) {
225 		return PLDM_REQUESTER_SEND_FAIL;
226 	}
227 
228 	ssize_t rc = sendmsg(demux->socket, &msg, 0);
229 	if (rc == -1) {
230 		return PLDM_REQUESTER_SEND_FAIL;
231 	}
232 	return PLDM_REQUESTER_SUCCESS;
233 }
234 
235 LIBPLDM_ABI_STABLE
236 int pldm_transport_mctp_demux_init(struct pldm_transport_mctp_demux **ctx)
237 {
238 	if (!ctx || *ctx) {
239 		return -EINVAL;
240 	}
241 
242 	struct pldm_transport_mctp_demux *demux =
243 		calloc(1, sizeof(struct pldm_transport_mctp_demux));
244 	if (!demux) {
245 		return -ENOMEM;
246 	}
247 
248 	demux->transport.name = MCTP_DEMUX_NAME;
249 	demux->transport.version = 1;
250 	demux->transport.recv = pldm_transport_mctp_demux_recv;
251 	demux->transport.send = pldm_transport_mctp_demux_send;
252 	demux->transport.init_pollfd = pldm_transport_mctp_demux_init_pollfd;
253 	demux->socket = pldm_transport_mctp_demux_open();
254 	if (demux->socket == -1) {
255 		free(demux);
256 		return -1;
257 	}
258 
259 	if (pldm_socket_sndbuf_init(&demux->socket_send_buf, demux->socket)) {
260 		close(demux->socket);
261 		free(demux);
262 		return -1;
263 	}
264 
265 	*ctx = demux;
266 	return 0;
267 }
268 
269 LIBPLDM_ABI_STABLE
270 void pldm_transport_mctp_demux_destroy(struct pldm_transport_mctp_demux *ctx)
271 {
272 	if (!ctx) {
273 		return;
274 	}
275 	close(ctx->socket);
276 	free(ctx);
277 }
278 
279 /* Temporary for old API */
280 struct pldm_transport_mctp_demux *
281 pldm_transport_mctp_demux_init_with_fd(int mctp_fd)
282 {
283 	struct pldm_transport_mctp_demux *demux =
284 		calloc(1, sizeof(struct pldm_transport_mctp_demux));
285 	if (!demux) {
286 		return NULL;
287 	}
288 
289 	demux->transport.name = MCTP_DEMUX_NAME;
290 	demux->transport.version = 1;
291 	demux->transport.recv = pldm_transport_mctp_demux_recv;
292 	demux->transport.send = pldm_transport_mctp_demux_send;
293 	demux->transport.init_pollfd = pldm_transport_mctp_demux_init_pollfd;
294 	/* dup is so we can call pldm_transport_mctp_demux_destroy which closes
295 	 * the socket, without closing the fd that is being used by the consumer
296 	 */
297 	demux->socket = dup(mctp_fd);
298 	if (demux->socket == -1) {
299 		free(demux);
300 		return NULL;
301 	}
302 
303 	if (pldm_socket_sndbuf_init(&demux->socket_send_buf, demux->socket)) {
304 		close(demux->socket);
305 		free(demux);
306 		return NULL;
307 	}
308 
309 	return demux;
310 }
311 
312 int pldm_transport_mctp_demux_get_socket_fd(
313 	struct pldm_transport_mctp_demux *ctx)
314 {
315 	if (ctx) {
316 		return ctx->socket;
317 	}
318 
319 	return -1;
320 }
321