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