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