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 "socket.h" 7 #include "transport.h" 8 9 #include <errno.h> 10 #include <limits.h> 11 #include <poll.h> 12 #include <stdlib.h> 13 #include <string.h> 14 #include <sys/socket.h> 15 #include <sys/types.h> 16 #include <sys/un.h> 17 #include <unistd.h> 18 19 #define MCTP_DEMUX_NAME "libmctp-demux-daemon" 20 const uint8_t mctp_msg_type = MCTP_MSG_TYPE_PLDM; 21 22 struct pldm_transport_mctp_demux { 23 struct pldm_transport transport; 24 int socket; 25 /* In the future this probably needs to move to a tid-eid-uuid/network 26 * id mapping for multi mctp networks */ 27 pldm_tid_t tid_eid_map[MCTP_MAX_NUM_EID]; 28 struct pldm_socket_sndbuf socket_send_buf; 29 }; 30 31 #define transport_to_demux(ptr) \ 32 container_of(ptr, struct pldm_transport_mctp_demux, transport) 33 34 LIBPLDM_ABI_TESTING 35 struct pldm_transport * 36 pldm_transport_mctp_demux_core(struct pldm_transport_mctp_demux *ctx) 37 { 38 return &ctx->transport; 39 } 40 41 static pldm_requester_rc_t pldm_transport_mctp_demux_open(void) 42 { 43 int fd = -1; 44 ssize_t rc = -1; 45 46 fd = socket(AF_UNIX, SOCK_SEQPACKET, 0); 47 if (fd == -1) { 48 return fd; 49 } 50 51 const char path[] = "\0mctp-mux"; 52 struct sockaddr_un addr; 53 addr.sun_family = AF_UNIX; 54 memcpy(addr.sun_path, path, sizeof(path) - 1); 55 rc = connect(fd, (struct sockaddr *)&addr, 56 sizeof(path) + sizeof(addr.sun_family) - 1); 57 if (rc == -1) { 58 return PLDM_REQUESTER_OPEN_FAIL; 59 } 60 rc = write(fd, &mctp_msg_type, sizeof(mctp_msg_type)); 61 if (rc == -1) { 62 return PLDM_REQUESTER_OPEN_FAIL; 63 } 64 65 return fd; 66 } 67 68 LIBPLDM_ABI_TESTING 69 int pldm_transport_mctp_demux_init_pollfd(struct pldm_transport *t, 70 struct pollfd *pollfd) 71 { 72 struct pldm_transport_mctp_demux *ctx = transport_to_demux(t); 73 pollfd->fd = ctx->socket; 74 pollfd->events = POLLIN; 75 return 0; 76 } 77 78 static int 79 pldm_transport_mctp_demux_get_eid(struct pldm_transport_mctp_demux *ctx, 80 pldm_tid_t tid, mctp_eid_t *eid) 81 { 82 int i; 83 for (i = 0; i < MCTP_MAX_NUM_EID; i++) { 84 if (ctx->tid_eid_map[i] == tid) { 85 *eid = i; 86 return 0; 87 } 88 } 89 *eid = -1; 90 return -1; 91 } 92 93 LIBPLDM_ABI_TESTING 94 int pldm_transport_mctp_demux_map_tid(struct pldm_transport_mctp_demux *ctx, 95 pldm_tid_t tid, mctp_eid_t eid) 96 { 97 ctx->tid_eid_map[eid] = tid; 98 99 return 0; 100 } 101 102 LIBPLDM_ABI_TESTING 103 int pldm_transport_mctp_demux_unmap_tid(struct pldm_transport_mctp_demux *ctx, 104 __attribute__((unused)) pldm_tid_t tid, 105 mctp_eid_t eid) 106 { 107 ctx->tid_eid_map[eid] = 0; 108 109 return 0; 110 } 111 112 static pldm_requester_rc_t 113 pldm_transport_mctp_demux_recv(struct pldm_transport *t, pldm_tid_t tid, 114 void **pldm_resp_msg, size_t *resp_msg_len) 115 { 116 struct pldm_transport_mctp_demux *demux = transport_to_demux(t); 117 mctp_eid_t eid = 0; 118 int rc = pldm_transport_mctp_demux_get_eid(demux, tid, &eid); 119 if (rc) { 120 return PLDM_REQUESTER_RECV_FAIL; 121 } 122 123 ssize_t min_len = sizeof(eid) + sizeof(mctp_msg_type) + 124 sizeof(struct pldm_msg_hdr); 125 ssize_t length = recv(demux->socket, NULL, 0, MSG_PEEK | MSG_TRUNC); 126 if (length <= 0) { 127 return PLDM_REQUESTER_RECV_FAIL; 128 } 129 uint8_t *buf = malloc(length); 130 if (buf == NULL) { 131 return PLDM_REQUESTER_RECV_FAIL; 132 } 133 if (length < min_len) { 134 /* read and discard */ 135 recv(demux->socket, buf, length, 0); 136 free(buf); 137 return PLDM_REQUESTER_INVALID_RECV_LEN; 138 } 139 struct iovec iov[2]; 140 uint8_t mctp_prefix[2]; 141 size_t mctp_prefix_len = 2; 142 size_t pldm_len = length - mctp_prefix_len; 143 iov[0].iov_len = mctp_prefix_len; 144 iov[0].iov_base = mctp_prefix; 145 iov[1].iov_len = pldm_len; 146 iov[1].iov_base = buf; 147 struct msghdr msg = { 0 }; 148 msg.msg_iov = iov; 149 msg.msg_iovlen = sizeof(iov) / sizeof(iov[0]); 150 ssize_t bytes = recvmsg(demux->socket, &msg, 0); 151 if (length != bytes) { 152 free(buf); 153 return PLDM_REQUESTER_INVALID_RECV_LEN; 154 } 155 if ((mctp_prefix[0] != eid) || (mctp_prefix[1] != mctp_msg_type)) { 156 free(buf); 157 return PLDM_REQUESTER_NOT_PLDM_MSG; 158 } 159 *pldm_resp_msg = buf; 160 *resp_msg_len = pldm_len; 161 return PLDM_REQUESTER_SUCCESS; 162 } 163 164 static pldm_requester_rc_t 165 pldm_transport_mctp_demux_send(struct pldm_transport *t, pldm_tid_t tid, 166 const void *pldm_req_msg, size_t req_msg_len) 167 { 168 struct pldm_transport_mctp_demux *demux = transport_to_demux(t); 169 mctp_eid_t eid = 0; 170 if (pldm_transport_mctp_demux_get_eid(demux, tid, &eid)) { 171 return PLDM_REQUESTER_SEND_FAIL; 172 } 173 174 uint8_t hdr[2] = { eid, mctp_msg_type }; 175 176 struct iovec iov[2]; 177 iov[0].iov_base = hdr; 178 iov[0].iov_len = sizeof(hdr); 179 iov[1].iov_base = (uint8_t *)pldm_req_msg; 180 iov[1].iov_len = req_msg_len; 181 182 struct msghdr msg = { 0 }; 183 msg.msg_iov = iov; 184 msg.msg_iovlen = sizeof(iov) / sizeof(iov[0]); 185 186 if (req_msg_len > INT_MAX || 187 pldm_socket_sndbuf_accomodate(&(demux->socket_send_buf), 188 (int)req_msg_len)) { 189 return PLDM_REQUESTER_SEND_FAIL; 190 } 191 192 ssize_t rc = sendmsg(demux->socket, &msg, 0); 193 if (rc == -1) { 194 return PLDM_REQUESTER_SEND_FAIL; 195 } 196 return PLDM_REQUESTER_SUCCESS; 197 } 198 199 LIBPLDM_ABI_TESTING 200 int pldm_transport_mctp_demux_init(struct pldm_transport_mctp_demux **ctx) 201 { 202 if (!ctx || *ctx) { 203 return -EINVAL; 204 } 205 206 struct pldm_transport_mctp_demux *demux = 207 calloc(1, sizeof(struct pldm_transport_mctp_demux)); 208 if (!demux) { 209 return -ENOMEM; 210 } 211 212 demux->transport.name = MCTP_DEMUX_NAME; 213 demux->transport.version = 1; 214 demux->transport.recv = pldm_transport_mctp_demux_recv; 215 demux->transport.send = pldm_transport_mctp_demux_send; 216 demux->transport.init_pollfd = pldm_transport_mctp_demux_init_pollfd; 217 demux->socket = pldm_transport_mctp_demux_open(); 218 if (demux->socket == -1) { 219 free(demux); 220 return -1; 221 } 222 223 if (pldm_socket_sndbuf_init(&demux->socket_send_buf, demux->socket)) { 224 close(demux->socket); 225 free(demux); 226 return -1; 227 } 228 229 *ctx = demux; 230 return 0; 231 } 232 233 LIBPLDM_ABI_TESTING 234 void pldm_transport_mctp_demux_destroy(struct pldm_transport_mctp_demux *ctx) 235 { 236 if (!ctx) { 237 return; 238 } 239 close(ctx->socket); 240 free(ctx); 241 } 242 243 /* Temporary for old API */ 244 LIBPLDM_ABI_TESTING 245 struct pldm_transport_mctp_demux * 246 pldm_transport_mctp_demux_init_with_fd(int mctp_fd) 247 { 248 struct pldm_transport_mctp_demux *demux = 249 calloc(1, sizeof(struct pldm_transport_mctp_demux)); 250 if (!demux) { 251 return NULL; 252 } 253 254 demux->transport.name = MCTP_DEMUX_NAME; 255 demux->transport.version = 1; 256 demux->transport.recv = pldm_transport_mctp_demux_recv; 257 demux->transport.send = pldm_transport_mctp_demux_send; 258 demux->transport.init_pollfd = pldm_transport_mctp_demux_init_pollfd; 259 /* dup is so we can call pldm_transport_mctp_demux_destroy which closes 260 * the socket, without closing the fd that is being used by the consumer 261 */ 262 demux->socket = dup(mctp_fd); 263 if (demux->socket == -1) { 264 free(demux); 265 return NULL; 266 } 267 268 if (pldm_socket_sndbuf_init(&demux->socket_send_buf, demux->socket)) { 269 close(demux->socket); 270 free(demux); 271 return NULL; 272 } 273 274 return demux; 275 } 276 277 LIBPLDM_ABI_TESTING 278 int pldm_transport_mctp_demux_get_socket_fd( 279 struct pldm_transport_mctp_demux *ctx) 280 { 281 if (ctx && ctx->socket) { 282 return ctx->socket; 283 } 284 return -1; 285 } 286