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