xref: /openbmc/libpldm/src/transport/mctp-demux.c (revision d10c6b0ce56c531f5a1a1c3b7f4e7b5e0e2c06db)
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_TESTING
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_TESTING
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 LIBPLDM_ABI_TESTING
95 int pldm_transport_mctp_demux_map_tid(struct pldm_transport_mctp_demux *ctx,
96 				      pldm_tid_t tid, mctp_eid_t eid)
97 {
98 	ctx->tid_eid_map[eid] = tid;
99 
100 	return 0;
101 }
102 
103 LIBPLDM_ABI_TESTING
104 int pldm_transport_mctp_demux_unmap_tid(struct pldm_transport_mctp_demux *ctx,
105 					__attribute__((unused)) pldm_tid_t tid,
106 					mctp_eid_t eid)
107 {
108 	ctx->tid_eid_map[eid] = 0;
109 
110 	return 0;
111 }
112 
113 static pldm_requester_rc_t
114 pldm_transport_mctp_demux_recv(struct pldm_transport *t, pldm_tid_t tid,
115 			       void **pldm_resp_msg, size_t *resp_msg_len)
116 {
117 	struct pldm_transport_mctp_demux *demux = transport_to_demux(t);
118 	mctp_eid_t eid = 0;
119 	int rc = pldm_transport_mctp_demux_get_eid(demux, tid, &eid);
120 	if (rc) {
121 		return PLDM_REQUESTER_RECV_FAIL;
122 	}
123 
124 	ssize_t min_len = sizeof(eid) + sizeof(mctp_msg_type) +
125 			  sizeof(struct pldm_msg_hdr);
126 	ssize_t length = recv(demux->socket, NULL, 0, MSG_PEEK | MSG_TRUNC);
127 	if (length <= 0) {
128 		return PLDM_REQUESTER_RECV_FAIL;
129 	}
130 	uint8_t *buf = malloc(length);
131 	if (buf == NULL) {
132 		return PLDM_REQUESTER_RECV_FAIL;
133 	}
134 	if (length < min_len) {
135 		/* read and discard */
136 		recv(demux->socket, buf, length, 0);
137 		free(buf);
138 		return PLDM_REQUESTER_INVALID_RECV_LEN;
139 	}
140 	struct iovec iov[2];
141 	uint8_t mctp_prefix[2];
142 	size_t mctp_prefix_len = 2;
143 	size_t pldm_len = length - mctp_prefix_len;
144 	iov[0].iov_len = mctp_prefix_len;
145 	iov[0].iov_base = mctp_prefix;
146 	iov[1].iov_len = pldm_len;
147 	iov[1].iov_base = buf;
148 	struct msghdr msg = { 0 };
149 	msg.msg_iov = iov;
150 	msg.msg_iovlen = sizeof(iov) / sizeof(iov[0]);
151 	ssize_t bytes = recvmsg(demux->socket, &msg, 0);
152 	if (length != bytes) {
153 		free(buf);
154 		return PLDM_REQUESTER_INVALID_RECV_LEN;
155 	}
156 	if ((mctp_prefix[0] != eid) || (mctp_prefix[1] != mctp_msg_type)) {
157 		free(buf);
158 		return PLDM_REQUESTER_NOT_PLDM_MSG;
159 	}
160 	*pldm_resp_msg = buf;
161 	*resp_msg_len = pldm_len;
162 	return PLDM_REQUESTER_SUCCESS;
163 }
164 
165 static pldm_requester_rc_t
166 pldm_transport_mctp_demux_send(struct pldm_transport *t, pldm_tid_t tid,
167 			       const void *pldm_req_msg, size_t req_msg_len)
168 {
169 	struct pldm_transport_mctp_demux *demux = transport_to_demux(t);
170 	mctp_eid_t eid = 0;
171 	if (pldm_transport_mctp_demux_get_eid(demux, tid, &eid)) {
172 		return PLDM_REQUESTER_SEND_FAIL;
173 	}
174 
175 	uint8_t hdr[2] = { eid, mctp_msg_type };
176 
177 	struct iovec iov[2];
178 	iov[0].iov_base = hdr;
179 	iov[0].iov_len = sizeof(hdr);
180 	iov[1].iov_base = (uint8_t *)pldm_req_msg;
181 	iov[1].iov_len = req_msg_len;
182 
183 	struct msghdr msg = { 0 };
184 	msg.msg_iov = iov;
185 	msg.msg_iovlen = sizeof(iov) / sizeof(iov[0]);
186 
187 	if (req_msg_len > INT_MAX ||
188 	    pldm_socket_sndbuf_accomodate(&(demux->socket_send_buf),
189 					  (int)req_msg_len)) {
190 		return PLDM_REQUESTER_SEND_FAIL;
191 	}
192 
193 	ssize_t rc = sendmsg(demux->socket, &msg, 0);
194 	if (rc == -1) {
195 		return PLDM_REQUESTER_SEND_FAIL;
196 	}
197 	return PLDM_REQUESTER_SUCCESS;
198 }
199 
200 LIBPLDM_ABI_TESTING
201 int pldm_transport_mctp_demux_init(struct pldm_transport_mctp_demux **ctx)
202 {
203 	if (!ctx || *ctx) {
204 		return -EINVAL;
205 	}
206 
207 	struct pldm_transport_mctp_demux *demux =
208 		calloc(1, sizeof(struct pldm_transport_mctp_demux));
209 	if (!demux) {
210 		return -ENOMEM;
211 	}
212 
213 	demux->transport.name = MCTP_DEMUX_NAME;
214 	demux->transport.version = 1;
215 	demux->transport.recv = pldm_transport_mctp_demux_recv;
216 	demux->transport.send = pldm_transport_mctp_demux_send;
217 	demux->transport.init_pollfd = pldm_transport_mctp_demux_init_pollfd;
218 	demux->socket = pldm_transport_mctp_demux_open();
219 	if (demux->socket == -1) {
220 		free(demux);
221 		return -1;
222 	}
223 
224 	if (pldm_socket_sndbuf_init(&demux->socket_send_buf, demux->socket)) {
225 		close(demux->socket);
226 		free(demux);
227 		return -1;
228 	}
229 
230 	*ctx = demux;
231 	return 0;
232 }
233 
234 LIBPLDM_ABI_TESTING
235 void pldm_transport_mctp_demux_destroy(struct pldm_transport_mctp_demux *ctx)
236 {
237 	if (!ctx) {
238 		return;
239 	}
240 	close(ctx->socket);
241 	free(ctx);
242 }
243 
244 /* Temporary for old API */
245 LIBPLDM_ABI_TESTING
246 struct pldm_transport_mctp_demux *
247 pldm_transport_mctp_demux_init_with_fd(int mctp_fd)
248 {
249 	struct pldm_transport_mctp_demux *demux =
250 		calloc(1, sizeof(struct pldm_transport_mctp_demux));
251 	if (!demux) {
252 		return NULL;
253 	}
254 
255 	demux->transport.name = MCTP_DEMUX_NAME;
256 	demux->transport.version = 1;
257 	demux->transport.recv = pldm_transport_mctp_demux_recv;
258 	demux->transport.send = pldm_transport_mctp_demux_send;
259 	demux->transport.init_pollfd = pldm_transport_mctp_demux_init_pollfd;
260 	/* dup is so we can call pldm_transport_mctp_demux_destroy which closes
261 	 * the socket, without closing the fd that is being used by the consumer
262 	 */
263 	demux->socket = dup(mctp_fd);
264 	if (demux->socket == -1) {
265 		free(demux);
266 		return NULL;
267 	}
268 
269 	if (pldm_socket_sndbuf_init(&demux->socket_send_buf, demux->socket)) {
270 		close(demux->socket);
271 		free(demux);
272 		return NULL;
273 	}
274 
275 	return demux;
276 }
277 
278 LIBPLDM_ABI_TESTING
279 int pldm_transport_mctp_demux_get_socket_fd(
280 	struct pldm_transport_mctp_demux *ctx)
281 {
282 	if (ctx) {
283 		return ctx->socket;
284 	}
285 
286 	return -1;
287 }
288