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