xref: /openbmc/libpldm/src/transport/af-mctp.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 "responder.h"
6 #include "socket.h"
7 #include "transport.h"
8 
9 #include <libpldm/base.h>
10 #include <libpldm/pldm.h>
11 #include <libpldm/transport.h>
12 #include <libpldm/transport/af-mctp.h>
13 
14 #include <errno.h>
15 #include <limits.h>
16 #include <linux/mctp.h>
17 #include <poll.h>
18 #include <stdbool.h>
19 #include <stdlib.h>
20 #include <string.h>
21 #include <sys/socket.h>
22 #include <sys/types.h>
23 #include <sys/un.h>
24 #include <unistd.h>
25 
26 struct pldm_responder_cookie_af_mctp {
27 	struct pldm_responder_cookie req;
28 	struct sockaddr_mctp smctp;
29 };
30 
31 #define cookie_to_af_mctp(c)                                                   \
32 	container_of((c), struct pldm_responder_cookie_af_mctp, req)
33 
34 #define AF_MCTP_NAME "AF_MCTP"
35 struct pldm_transport_af_mctp {
36 	struct pldm_transport transport;
37 	int socket;
38 	pldm_tid_t tid_eid_map[MCTP_MAX_NUM_EID];
39 	struct pldm_socket_sndbuf socket_send_buf;
40 	bool bound;
41 	struct pldm_responder_cookie cookie_jar;
42 };
43 
44 #define transport_to_af_mctp(ptr)                                              \
45 	container_of(ptr, struct pldm_transport_af_mctp, transport)
46 
47 LIBPLDM_ABI_STABLE
48 struct pldm_transport *
pldm_transport_af_mctp_core(struct pldm_transport_af_mctp * ctx)49 pldm_transport_af_mctp_core(struct pldm_transport_af_mctp *ctx)
50 {
51 	return &ctx->transport;
52 }
53 
54 LIBPLDM_ABI_STABLE
pldm_transport_af_mctp_init_pollfd(struct pldm_transport * t,struct pollfd * pollfd)55 int pldm_transport_af_mctp_init_pollfd(struct pldm_transport *t,
56 				       struct pollfd *pollfd)
57 {
58 	struct pldm_transport_af_mctp *ctx = transport_to_af_mctp(t);
59 	pollfd->fd = ctx->socket;
60 	pollfd->events = POLLIN;
61 	return 0;
62 }
63 
pldm_transport_af_mctp_get_eid(struct pldm_transport_af_mctp * ctx,pldm_tid_t tid,mctp_eid_t * eid)64 static int pldm_transport_af_mctp_get_eid(struct pldm_transport_af_mctp *ctx,
65 					  pldm_tid_t tid, mctp_eid_t *eid)
66 {
67 	int i;
68 	for (i = 0; i < MCTP_MAX_NUM_EID; i++) {
69 		if (ctx->tid_eid_map[i] == tid) {
70 			*eid = i;
71 			return 0;
72 		}
73 	}
74 	*eid = -1;
75 	return -1;
76 }
77 
pldm_transport_af_mctp_get_tid(struct pldm_transport_af_mctp * ctx,mctp_eid_t eid,pldm_tid_t * tid)78 static int pldm_transport_af_mctp_get_tid(struct pldm_transport_af_mctp *ctx,
79 					  mctp_eid_t eid, pldm_tid_t *tid)
80 {
81 	if (ctx->tid_eid_map[eid] != 0) {
82 		*tid = ctx->tid_eid_map[eid];
83 		return 0;
84 	}
85 	return -1;
86 }
87 
88 LIBPLDM_ABI_STABLE
pldm_transport_af_mctp_map_tid(struct pldm_transport_af_mctp * ctx,pldm_tid_t tid,mctp_eid_t eid)89 int pldm_transport_af_mctp_map_tid(struct pldm_transport_af_mctp *ctx,
90 				   pldm_tid_t tid, mctp_eid_t eid)
91 {
92 	ctx->tid_eid_map[eid] = tid;
93 
94 	return 0;
95 }
96 
97 LIBPLDM_ABI_STABLE
pldm_transport_af_mctp_unmap_tid(struct pldm_transport_af_mctp * ctx,LIBPLDM_CC_UNUSED pldm_tid_t tid,mctp_eid_t eid)98 int pldm_transport_af_mctp_unmap_tid(struct pldm_transport_af_mctp *ctx,
99 				     LIBPLDM_CC_UNUSED pldm_tid_t tid,
100 				     mctp_eid_t eid)
101 {
102 	ctx->tid_eid_map[eid] = 0;
103 
104 	return 0;
105 }
106 
pldm_transport_af_mctp_recv(struct pldm_transport * t,pldm_tid_t * tid,void ** pldm_msg,size_t * msg_len)107 static pldm_requester_rc_t pldm_transport_af_mctp_recv(struct pldm_transport *t,
108 						       pldm_tid_t *tid,
109 						       void **pldm_msg,
110 						       size_t *msg_len)
111 {
112 	struct pldm_transport_af_mctp *af_mctp = transport_to_af_mctp(t);
113 	struct sockaddr_mctp addr = { 0 };
114 	socklen_t addrlen = sizeof(addr);
115 	struct pldm_msg_hdr *hdr;
116 	pldm_requester_rc_t res;
117 	mctp_eid_t eid = 0;
118 	ssize_t length;
119 	void *msg;
120 	int rc;
121 
122 	length = recv(af_mctp->socket, NULL, 0, MSG_PEEK | MSG_TRUNC);
123 	if (length <= 0) {
124 		return PLDM_REQUESTER_RECV_FAIL;
125 	}
126 
127 	msg = malloc(length);
128 	if (!msg) {
129 		return PLDM_REQUESTER_RECV_FAIL;
130 	}
131 
132 	length = recvfrom(af_mctp->socket, msg, length, MSG_TRUNC,
133 			  (struct sockaddr *)&addr, &addrlen);
134 	if (length < (ssize_t)sizeof(struct pldm_msg_hdr)) {
135 		res = PLDM_REQUESTER_INVALID_RECV_LEN;
136 		goto cleanup_msg;
137 	}
138 
139 	eid = addr.smctp_addr.s_addr;
140 	rc = pldm_transport_af_mctp_get_tid(af_mctp, eid, tid);
141 	if (rc) {
142 		res = PLDM_REQUESTER_RECV_FAIL;
143 		goto cleanup_msg;
144 	}
145 
146 	hdr = msg;
147 
148 	if (af_mctp->bound && hdr->request) {
149 		struct pldm_responder_cookie_af_mctp *cookie;
150 
151 		cookie = malloc(sizeof(*cookie));
152 		if (!cookie) {
153 			res = PLDM_REQUESTER_RECV_FAIL;
154 			goto cleanup_msg;
155 		}
156 
157 		cookie->req.tid = *tid,
158 		cookie->req.instance_id = hdr->instance_id,
159 		cookie->req.type = hdr->type,
160 		cookie->req.command = hdr->command;
161 		cookie->smctp = addr;
162 
163 		rc = pldm_responder_cookie_track(&af_mctp->cookie_jar,
164 						 &cookie->req);
165 		if (rc) {
166 			res = PLDM_REQUESTER_RECV_FAIL;
167 			goto cleanup_msg;
168 		}
169 	}
170 
171 	*pldm_msg = msg;
172 	*msg_len = length;
173 
174 	return PLDM_REQUESTER_SUCCESS;
175 
176 cleanup_msg:
177 	free(msg);
178 
179 	return res;
180 }
181 
pldm_transport_af_mctp_send(struct pldm_transport * t,pldm_tid_t tid,const void * pldm_msg,size_t msg_len)182 static pldm_requester_rc_t pldm_transport_af_mctp_send(struct pldm_transport *t,
183 						       pldm_tid_t tid,
184 						       const void *pldm_msg,
185 						       size_t msg_len)
186 {
187 	struct pldm_transport_af_mctp *af_mctp = transport_to_af_mctp(t);
188 	const struct pldm_msg_hdr *hdr;
189 	struct sockaddr_mctp addr = { 0 };
190 
191 	if (msg_len < (ssize_t)sizeof(struct pldm_msg_hdr)) {
192 		return PLDM_REQUESTER_SEND_FAIL;
193 	}
194 
195 	hdr = pldm_msg;
196 	if (af_mctp->bound && !hdr->request) {
197 		struct pldm_responder_cookie_af_mctp *cookie;
198 		struct pldm_responder_cookie *req;
199 
200 		req = pldm_responder_cookie_untrack(&af_mctp->cookie_jar, tid,
201 						    hdr->instance_id, hdr->type,
202 						    hdr->command);
203 		if (!req) {
204 			return PLDM_REQUESTER_SEND_FAIL;
205 		}
206 
207 		cookie = cookie_to_af_mctp(req);
208 		addr = cookie->smctp;
209 		/* Clear the TO to indicate a response */
210 		addr.smctp_tag &= ~MCTP_TAG_OWNER;
211 		free(cookie);
212 	} else {
213 		mctp_eid_t eid = 0;
214 		if (pldm_transport_af_mctp_get_eid(af_mctp, tid, &eid)) {
215 			return PLDM_REQUESTER_SEND_FAIL;
216 		}
217 
218 		addr.smctp_family = AF_MCTP;
219 		addr.smctp_addr.s_addr = eid;
220 		addr.smctp_type = MCTP_MSG_TYPE_PLDM;
221 		addr.smctp_tag = MCTP_TAG_OWNER;
222 	}
223 
224 	if (msg_len > INT_MAX ||
225 	    pldm_socket_sndbuf_accomodate(&(af_mctp->socket_send_buf),
226 					  (int)msg_len)) {
227 		return PLDM_REQUESTER_SEND_FAIL;
228 	}
229 
230 	ssize_t rc = sendto(af_mctp->socket, pldm_msg, msg_len, 0,
231 			    (struct sockaddr *)&addr, sizeof(addr));
232 	if (rc == -1) {
233 		return PLDM_REQUESTER_SEND_FAIL;
234 	}
235 
236 	return PLDM_REQUESTER_SUCCESS;
237 }
238 
239 LIBPLDM_ABI_STABLE
pldm_transport_af_mctp_init(struct pldm_transport_af_mctp ** ctx)240 int pldm_transport_af_mctp_init(struct pldm_transport_af_mctp **ctx)
241 {
242 	if (!ctx || *ctx) {
243 		return -EINVAL;
244 	}
245 
246 	struct pldm_transport_af_mctp *af_mctp =
247 		calloc(1, sizeof(struct pldm_transport_af_mctp));
248 	if (!af_mctp) {
249 		return -ENOMEM;
250 	}
251 
252 	af_mctp->transport.name = AF_MCTP_NAME;
253 	af_mctp->transport.version = 1;
254 	af_mctp->transport.recv = pldm_transport_af_mctp_recv;
255 	af_mctp->transport.send = pldm_transport_af_mctp_send;
256 	af_mctp->transport.init_pollfd = pldm_transport_af_mctp_init_pollfd;
257 	af_mctp->bound = false;
258 	af_mctp->cookie_jar.next = NULL;
259 	af_mctp->socket = socket(AF_MCTP, SOCK_DGRAM, 0);
260 	if (af_mctp->socket == -1) {
261 		free(af_mctp);
262 		return -1;
263 	}
264 
265 	if (pldm_socket_sndbuf_init(&af_mctp->socket_send_buf,
266 				    af_mctp->socket)) {
267 		close(af_mctp->socket);
268 		free(af_mctp);
269 		return -1;
270 	}
271 
272 	*ctx = af_mctp;
273 	return 0;
274 }
275 
276 LIBPLDM_ABI_STABLE
pldm_transport_af_mctp_destroy(struct pldm_transport_af_mctp * ctx)277 void pldm_transport_af_mctp_destroy(struct pldm_transport_af_mctp *ctx)
278 {
279 	if (!ctx) {
280 		return;
281 	}
282 	close(ctx->socket);
283 	free(ctx);
284 }
285 
286 LIBPLDM_ABI_STABLE
pldm_transport_af_mctp_bind(struct pldm_transport_af_mctp * transport,const struct sockaddr_mctp * smctp,size_t len)287 int pldm_transport_af_mctp_bind(struct pldm_transport_af_mctp *transport,
288 				const struct sockaddr_mctp *smctp, size_t len)
289 {
290 	struct sockaddr_mctp lsmctp = { 0 };
291 	int rc;
292 
293 	if (!transport) {
294 		return PLDM_REQUESTER_INVALID_SETUP;
295 	}
296 
297 	if (!smctp && len) {
298 		return PLDM_REQUESTER_INVALID_SETUP;
299 	}
300 
301 	if (!smctp) {
302 		lsmctp.smctp_family = AF_MCTP;
303 		lsmctp.smctp_network = MCTP_NET_ANY;
304 		lsmctp.smctp_addr.s_addr = MCTP_ADDR_ANY;
305 		lsmctp.smctp_type = MCTP_MSG_TYPE_PLDM;
306 		lsmctp.smctp_tag = MCTP_TAG_OWNER;
307 		smctp = &lsmctp;
308 		len = sizeof(lsmctp);
309 	}
310 
311 	if (smctp->smctp_family != AF_MCTP ||
312 	    smctp->smctp_type != MCTP_MSG_TYPE_PLDM ||
313 	    smctp->smctp_tag != MCTP_TAG_OWNER) {
314 		return PLDM_REQUESTER_INVALID_SETUP;
315 	}
316 
317 	if (len != sizeof(*smctp)) {
318 		return PLDM_REQUESTER_INVALID_SETUP;
319 	}
320 
321 	rc = bind(transport->socket, (const struct sockaddr *)smctp,
322 		  sizeof(*smctp));
323 	if (rc) {
324 		return PLDM_REQUESTER_SETUP_FAIL;
325 	}
326 
327 	transport->bound = true;
328 
329 	return PLDM_REQUESTER_SUCCESS;
330 }
331