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 * 49 pldm_transport_af_mctp_core(struct pldm_transport_af_mctp *ctx) 50 { 51 return &ctx->transport; 52 } 53 54 LIBPLDM_ABI_STABLE 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 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 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 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 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 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 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 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 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 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