/* SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later */ #include "mctp-defines.h" #include "base.h" #include "container-of.h" #include "libpldm/pldm.h" #include "libpldm/transport.h" #include "libpldm/transport/af-mctp.h" #include "responder.h" #include "socket.h" #include "transport.h" #include #include #include #include #include #include #include #include #include #include #include struct pldm_responder_cookie_af_mctp { struct pldm_responder_cookie req; struct sockaddr_mctp smctp; }; #define cookie_to_af_mctp(c) \ container_of((c), struct pldm_responder_cookie_af_mctp, req) #define AF_MCTP_NAME "AF_MCTP" struct pldm_transport_af_mctp { struct pldm_transport transport; int socket; pldm_tid_t tid_eid_map[MCTP_MAX_NUM_EID]; struct pldm_socket_sndbuf socket_send_buf; bool bound; struct pldm_responder_cookie cookie_jar; }; #define transport_to_af_mctp(ptr) \ container_of(ptr, struct pldm_transport_af_mctp, transport) LIBPLDM_ABI_STABLE struct pldm_transport * pldm_transport_af_mctp_core(struct pldm_transport_af_mctp *ctx) { return &ctx->transport; } LIBPLDM_ABI_STABLE int pldm_transport_af_mctp_init_pollfd(struct pldm_transport *t, struct pollfd *pollfd) { struct pldm_transport_af_mctp *ctx = transport_to_af_mctp(t); pollfd->fd = ctx->socket; pollfd->events = POLLIN; return 0; } static int pldm_transport_af_mctp_get_eid(struct pldm_transport_af_mctp *ctx, pldm_tid_t tid, mctp_eid_t *eid) { int i; for (i = 0; i < MCTP_MAX_NUM_EID; i++) { if (ctx->tid_eid_map[i] == tid) { *eid = i; return 0; } } *eid = -1; return -1; } static int pldm_transport_af_mctp_get_tid(struct pldm_transport_af_mctp *ctx, mctp_eid_t eid, pldm_tid_t *tid) { if (ctx->tid_eid_map[eid] != 0) { *tid = ctx->tid_eid_map[eid]; return 0; } return -1; } LIBPLDM_ABI_STABLE int pldm_transport_af_mctp_map_tid(struct pldm_transport_af_mctp *ctx, pldm_tid_t tid, mctp_eid_t eid) { ctx->tid_eid_map[eid] = tid; return 0; } LIBPLDM_ABI_STABLE int pldm_transport_af_mctp_unmap_tid(struct pldm_transport_af_mctp *ctx, __attribute__((unused)) pldm_tid_t tid, mctp_eid_t eid) { ctx->tid_eid_map[eid] = 0; return 0; } static pldm_requester_rc_t pldm_transport_af_mctp_recv(struct pldm_transport *t, pldm_tid_t *tid, void **pldm_msg, size_t *msg_len) { struct pldm_transport_af_mctp *af_mctp = transport_to_af_mctp(t); struct sockaddr_mctp addr = { 0 }; socklen_t addrlen = sizeof(addr); struct pldm_msg_hdr *hdr; pldm_requester_rc_t res; mctp_eid_t eid = 0; ssize_t length; void *msg; int rc; length = recv(af_mctp->socket, NULL, 0, MSG_PEEK | MSG_TRUNC); if (length <= 0) { return PLDM_REQUESTER_RECV_FAIL; } msg = malloc(length); if (!msg) { return PLDM_REQUESTER_RECV_FAIL; } length = recvfrom(af_mctp->socket, msg, length, MSG_TRUNC, (struct sockaddr *)&addr, &addrlen); if (length < (ssize_t)sizeof(struct pldm_msg_hdr)) { res = PLDM_REQUESTER_INVALID_RECV_LEN; goto cleanup_msg; } eid = addr.smctp_addr.s_addr; rc = pldm_transport_af_mctp_get_tid(af_mctp, eid, tid); if (rc) { res = PLDM_REQUESTER_RECV_FAIL; goto cleanup_msg; } hdr = msg; if (af_mctp->bound && hdr->request) { struct pldm_responder_cookie_af_mctp *cookie; cookie = malloc(sizeof(*cookie)); if (!cookie) { res = PLDM_REQUESTER_RECV_FAIL; goto cleanup_msg; } cookie->req.tid = *tid, cookie->req.instance_id = hdr->instance_id, cookie->req.type = hdr->type, cookie->req.command = hdr->command; cookie->smctp = addr; rc = pldm_responder_cookie_track(&af_mctp->cookie_jar, &cookie->req); if (rc) { res = PLDM_REQUESTER_RECV_FAIL; goto cleanup_msg; } } *pldm_msg = msg; *msg_len = length; return PLDM_REQUESTER_SUCCESS; cleanup_msg: free(msg); return res; } static pldm_requester_rc_t pldm_transport_af_mctp_send(struct pldm_transport *t, pldm_tid_t tid, const void *pldm_msg, size_t msg_len) { struct pldm_transport_af_mctp *af_mctp = transport_to_af_mctp(t); const struct pldm_msg_hdr *hdr; struct sockaddr_mctp addr = { 0 }; if (msg_len < (ssize_t)sizeof(struct pldm_msg_hdr)) { return PLDM_REQUESTER_SEND_FAIL; } hdr = pldm_msg; if (af_mctp->bound && !hdr->request) { struct pldm_responder_cookie_af_mctp *cookie; struct pldm_responder_cookie *req; req = pldm_responder_cookie_untrack(&af_mctp->cookie_jar, tid, hdr->instance_id, hdr->type, hdr->command); if (!req) { return PLDM_REQUESTER_SEND_FAIL; } cookie = cookie_to_af_mctp(req); addr = cookie->smctp; /* Clear the TO to indicate a response */ addr.smctp_tag &= ~MCTP_TAG_OWNER; free(cookie); } else { mctp_eid_t eid = 0; if (pldm_transport_af_mctp_get_eid(af_mctp, tid, &eid)) { return PLDM_REQUESTER_SEND_FAIL; } addr.smctp_family = AF_MCTP; addr.smctp_addr.s_addr = eid; addr.smctp_type = MCTP_MSG_TYPE_PLDM; addr.smctp_tag = MCTP_TAG_OWNER; } if (msg_len > INT_MAX || pldm_socket_sndbuf_accomodate(&(af_mctp->socket_send_buf), (int)msg_len)) { return PLDM_REQUESTER_SEND_FAIL; } ssize_t rc = sendto(af_mctp->socket, pldm_msg, msg_len, 0, (struct sockaddr *)&addr, sizeof(addr)); if (rc == -1) { return PLDM_REQUESTER_SEND_FAIL; } return PLDM_REQUESTER_SUCCESS; } LIBPLDM_ABI_STABLE int pldm_transport_af_mctp_init(struct pldm_transport_af_mctp **ctx) { if (!ctx || *ctx) { return -EINVAL; } struct pldm_transport_af_mctp *af_mctp = calloc(1, sizeof(struct pldm_transport_af_mctp)); if (!af_mctp) { return -ENOMEM; } af_mctp->transport.name = AF_MCTP_NAME; af_mctp->transport.version = 1; af_mctp->transport.recv = pldm_transport_af_mctp_recv; af_mctp->transport.send = pldm_transport_af_mctp_send; af_mctp->transport.init_pollfd = pldm_transport_af_mctp_init_pollfd; af_mctp->bound = false; af_mctp->cookie_jar.next = NULL; af_mctp->socket = socket(AF_MCTP, SOCK_DGRAM, 0); if (af_mctp->socket == -1) { free(af_mctp); return -1; } if (pldm_socket_sndbuf_init(&af_mctp->socket_send_buf, af_mctp->socket)) { close(af_mctp->socket); free(af_mctp); return -1; } *ctx = af_mctp; return 0; } LIBPLDM_ABI_STABLE void pldm_transport_af_mctp_destroy(struct pldm_transport_af_mctp *ctx) { if (!ctx) { return; } close(ctx->socket); free(ctx); } LIBPLDM_ABI_STABLE int pldm_transport_af_mctp_bind(struct pldm_transport_af_mctp *transport, const struct sockaddr_mctp *smctp, size_t len) { struct sockaddr_mctp lsmctp = { 0 }; int rc; if (!transport) { return PLDM_REQUESTER_INVALID_SETUP; } if (!smctp && len) { return PLDM_REQUESTER_INVALID_SETUP; } if (!smctp) { lsmctp.smctp_family = AF_MCTP; lsmctp.smctp_network = MCTP_NET_ANY; lsmctp.smctp_addr.s_addr = MCTP_ADDR_ANY; lsmctp.smctp_type = MCTP_MSG_TYPE_PLDM; lsmctp.smctp_tag = MCTP_TAG_OWNER; smctp = &lsmctp; len = sizeof(lsmctp); } if (smctp->smctp_family != AF_MCTP || smctp->smctp_type != MCTP_MSG_TYPE_PLDM || smctp->smctp_tag != MCTP_TAG_OWNER) { return PLDM_REQUESTER_INVALID_SETUP; } if (len != sizeof(*smctp)) { return PLDM_REQUESTER_INVALID_SETUP; } rc = bind(transport->socket, (const struct sockaddr *)smctp, sizeof(*smctp)); if (rc) { return PLDM_REQUESTER_SETUP_FAIL; } transport->bound = true; return PLDM_REQUESTER_SUCCESS; }