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