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