1 /* SPDX-License-Identifier: BSD-3-Clause 2 * Copyright (C) 2018 Intel Corporation. 3 * All rights reserved. 4 */ 5 6 #include "spdk/stdinc.h" 7 #include "spdk/thread.h" 8 #include "spdk/env.h" 9 #include "spdk/event.h" 10 #include "spdk/log.h" 11 #include "spdk/string.h" 12 13 #include "spdk/sock.h" 14 15 #define ACCEPT_TIMEOUT_US 1000 16 #define CLOSE_TIMEOUT_US 1000000 17 #define BUFFER_SIZE 1024 18 #define ADDR_STR_LEN INET6_ADDRSTRLEN 19 20 static bool g_is_running; 21 22 static char *g_host; 23 static char *g_sock_impl_name; 24 static int g_port; 25 static bool g_is_server; 26 static int g_zcopy; 27 static int g_ktls; 28 static int g_tls_version; 29 static bool g_verbose; 30 static char *g_psk_key; 31 static char *g_psk_identity; 32 33 /* 34 * We'll use this struct to gather housekeeping hello_context to pass between 35 * our events and callbacks. 36 */ 37 struct hello_context_t { 38 bool is_server; 39 char *host; 40 char *sock_impl_name; 41 int port; 42 int zcopy; 43 int ktls; 44 int tls_version; 45 char *psk_key; 46 char *psk_identity; 47 48 bool verbose; 49 int bytes_in; 50 int bytes_out; 51 52 struct spdk_sock *sock; 53 54 struct spdk_sock_group *group; 55 struct spdk_poller *poller_in; 56 struct spdk_poller *poller_out; 57 struct spdk_poller *time_out; 58 59 int rc; 60 }; 61 62 /* 63 * Usage function for printing parameters that are specific to this application 64 */ 65 static void 66 hello_sock_usage(void) 67 { 68 printf(" -E psk_key Default PSK KEY in hexadecimal digits, e.g. 1234567890ABCDEF (only applies when sock_impl == ssl)\n"); 69 printf(" -H host_addr host address\n"); 70 printf(" -I psk_id Default PSK ID, e.g. psk.spdk.io (only applies when sock_impl == ssl)\n"); 71 printf(" -P port port number\n"); 72 printf(" -N sock_impl socket implementation, e.g., -N posix or -N uring\n"); 73 printf(" -S start in server mode\n"); 74 printf(" -T tls_ver TLS version, e.g., -T 12 or -T 13. If omitted, auto-negotiation will take place\n"); 75 printf(" -k disable KTLS for the given sock implementation (default)\n"); 76 printf(" -K enable KTLS for the given sock implementation\n"); 77 printf(" -V print out additional information\n"); 78 printf(" -z disable zero copy send for the given sock implementation\n"); 79 printf(" -Z enable zero copy send for the given sock implementation\n"); 80 } 81 82 /* 83 * This function is called to parse the parameters that are specific to this application 84 */ 85 static int 86 hello_sock_parse_arg(int ch, char *arg) 87 { 88 switch (ch) { 89 case 'E': 90 g_psk_key = arg; 91 break; 92 case 'H': 93 g_host = arg; 94 break; 95 case 'I': 96 g_psk_identity = arg; 97 break; 98 case 'N': 99 g_sock_impl_name = arg; 100 break; 101 case 'P': 102 g_port = spdk_strtol(arg, 10); 103 if (g_port < 0) { 104 fprintf(stderr, "Invalid port ID\n"); 105 return g_port; 106 } 107 break; 108 case 'S': 109 g_is_server = 1; 110 break; 111 case 'K': 112 g_ktls = 1; 113 break; 114 case 'k': 115 g_ktls = 0; 116 break; 117 case 'T': 118 g_tls_version = spdk_strtol(arg, 10); 119 if (g_tls_version < 0) { 120 fprintf(stderr, "Invalid TLS version\n"); 121 return g_tls_version; 122 } 123 break; 124 case 'V': 125 g_verbose = true; 126 break; 127 case 'Z': 128 g_zcopy = 1; 129 break; 130 case 'z': 131 g_zcopy = 0; 132 break; 133 default: 134 return -EINVAL; 135 } 136 return 0; 137 } 138 139 static int 140 hello_sock_close_timeout_poll(void *arg) 141 { 142 struct hello_context_t *ctx = arg; 143 SPDK_NOTICELOG("Connection closed\n"); 144 145 spdk_poller_unregister(&ctx->time_out); 146 spdk_poller_unregister(&ctx->poller_in); 147 spdk_sock_close(&ctx->sock); 148 spdk_sock_group_close(&ctx->group); 149 150 spdk_app_stop(ctx->rc); 151 return SPDK_POLLER_BUSY; 152 } 153 154 static int 155 hello_sock_quit(struct hello_context_t *ctx, int rc) 156 { 157 ctx->rc = rc; 158 spdk_poller_unregister(&ctx->poller_out); 159 if (!ctx->time_out) { 160 ctx->time_out = SPDK_POLLER_REGISTER(hello_sock_close_timeout_poll, ctx, 161 CLOSE_TIMEOUT_US); 162 } 163 return 0; 164 } 165 166 static int 167 hello_sock_recv_poll(void *arg) 168 { 169 struct hello_context_t *ctx = arg; 170 int rc; 171 char buf_in[BUFFER_SIZE]; 172 173 /* 174 * Get response 175 */ 176 rc = spdk_sock_recv(ctx->sock, buf_in, sizeof(buf_in) - 1); 177 178 if (rc <= 0) { 179 if (errno == EAGAIN || errno == EWOULDBLOCK) { 180 return SPDK_POLLER_IDLE; 181 } 182 183 SPDK_ERRLOG("spdk_sock_recv() failed, errno %d: %s\n", 184 errno, spdk_strerror(errno)); 185 return SPDK_POLLER_BUSY; 186 } 187 188 if (rc > 0) { 189 ctx->bytes_in += rc; 190 buf_in[rc] = '\0'; 191 printf("%s", buf_in); 192 } 193 194 return SPDK_POLLER_BUSY; 195 } 196 197 static int 198 hello_sock_writev_poll(void *arg) 199 { 200 struct hello_context_t *ctx = arg; 201 int rc = 0; 202 char buf_out[BUFFER_SIZE]; 203 struct iovec iov; 204 ssize_t n; 205 206 n = read(STDIN_FILENO, buf_out, sizeof(buf_out)); 207 if (n == 0 || !g_is_running) { 208 /* EOF */ 209 SPDK_NOTICELOG("Closing connection...\n"); 210 hello_sock_quit(ctx, 0); 211 return SPDK_POLLER_IDLE; 212 } 213 if (n > 0) { 214 /* 215 * Send message to the server 216 */ 217 iov.iov_base = buf_out; 218 iov.iov_len = n; 219 rc = spdk_sock_writev(ctx->sock, &iov, 1); 220 if (rc > 0) { 221 ctx->bytes_out += rc; 222 } 223 } 224 return rc > 0 ? SPDK_POLLER_BUSY : SPDK_POLLER_IDLE; 225 } 226 227 static int 228 hello_sock_connect(struct hello_context_t *ctx) 229 { 230 int rc; 231 char saddr[ADDR_STR_LEN], caddr[ADDR_STR_LEN]; 232 uint16_t cport, sport; 233 struct spdk_sock_impl_opts impl_opts; 234 size_t impl_opts_size = sizeof(impl_opts); 235 struct spdk_sock_opts opts; 236 237 spdk_sock_impl_get_opts(ctx->sock_impl_name, &impl_opts, &impl_opts_size); 238 impl_opts.enable_ktls = ctx->ktls; 239 impl_opts.tls_version = ctx->tls_version; 240 impl_opts.psk_key = ctx->psk_key; 241 impl_opts.psk_identity = ctx->psk_identity; 242 243 opts.opts_size = sizeof(opts); 244 spdk_sock_get_default_opts(&opts); 245 opts.zcopy = ctx->zcopy; 246 opts.impl_opts = &impl_opts; 247 opts.impl_opts_size = sizeof(impl_opts); 248 249 SPDK_NOTICELOG("Connecting to the server on %s:%d with sock_impl(%s)\n", ctx->host, ctx->port, 250 ctx->sock_impl_name); 251 252 ctx->sock = spdk_sock_connect_ext(ctx->host, ctx->port, ctx->sock_impl_name, &opts); 253 if (ctx->sock == NULL) { 254 SPDK_ERRLOG("connect error(%d): %s\n", errno, spdk_strerror(errno)); 255 return -1; 256 } 257 258 rc = spdk_sock_getaddr(ctx->sock, saddr, sizeof(saddr), &sport, caddr, sizeof(caddr), &cport); 259 if (rc < 0) { 260 SPDK_ERRLOG("Cannot get connection addresses\n"); 261 spdk_sock_close(&ctx->sock); 262 return -1; 263 } 264 265 SPDK_NOTICELOG("Connection accepted from (%s, %hu) to (%s, %hu)\n", caddr, cport, saddr, sport); 266 267 fcntl(STDIN_FILENO, F_SETFL, fcntl(STDIN_FILENO, F_GETFL) | O_NONBLOCK); 268 269 g_is_running = true; 270 ctx->poller_in = SPDK_POLLER_REGISTER(hello_sock_recv_poll, ctx, 0); 271 ctx->poller_out = SPDK_POLLER_REGISTER(hello_sock_writev_poll, ctx, 0); 272 273 return 0; 274 } 275 276 static void 277 hello_sock_cb(void *arg, struct spdk_sock_group *group, struct spdk_sock *sock) 278 { 279 ssize_t n; 280 char buf[BUFFER_SIZE]; 281 struct iovec iov; 282 struct hello_context_t *ctx = arg; 283 284 n = spdk_sock_recv(sock, buf, sizeof(buf)); 285 if (n < 0) { 286 if (errno == EAGAIN || errno == EWOULDBLOCK) { 287 SPDK_ERRLOG("spdk_sock_recv() failed, errno %d: %s\n", 288 errno, spdk_strerror(errno)); 289 return; 290 } 291 292 SPDK_ERRLOG("spdk_sock_recv() failed, errno %d: %s\n", 293 errno, spdk_strerror(errno)); 294 } 295 296 if (n > 0) { 297 ctx->bytes_in += n; 298 iov.iov_base = buf; 299 iov.iov_len = n; 300 n = spdk_sock_writev(sock, &iov, 1); 301 if (n > 0) { 302 ctx->bytes_out += n; 303 } 304 return; 305 } 306 307 /* Connection closed */ 308 SPDK_NOTICELOG("Connection closed\n"); 309 spdk_sock_group_remove_sock(group, sock); 310 spdk_sock_close(&sock); 311 } 312 313 static int 314 hello_sock_accept_poll(void *arg) 315 { 316 struct hello_context_t *ctx = arg; 317 struct spdk_sock *sock; 318 int rc; 319 int count = 0; 320 char saddr[ADDR_STR_LEN], caddr[ADDR_STR_LEN]; 321 uint16_t cport, sport; 322 323 if (!g_is_running) { 324 hello_sock_quit(ctx, 0); 325 return SPDK_POLLER_IDLE; 326 } 327 328 while (1) { 329 sock = spdk_sock_accept(ctx->sock); 330 if (sock != NULL) { 331 rc = spdk_sock_getaddr(sock, saddr, sizeof(saddr), &sport, caddr, sizeof(caddr), &cport); 332 if (rc < 0) { 333 SPDK_ERRLOG("Cannot get connection addresses\n"); 334 spdk_sock_close(&sock); 335 return SPDK_POLLER_IDLE; 336 } 337 338 SPDK_NOTICELOG("Accepting a new connection from (%s, %hu) to (%s, %hu)\n", 339 caddr, cport, saddr, sport); 340 341 rc = spdk_sock_group_add_sock(ctx->group, sock, 342 hello_sock_cb, ctx); 343 344 if (rc < 0) { 345 spdk_sock_close(&sock); 346 SPDK_ERRLOG("failed\n"); 347 break; 348 } 349 350 count++; 351 } else { 352 if (errno != EAGAIN && errno != EWOULDBLOCK) { 353 SPDK_ERRLOG("accept error(%d): %s\n", errno, spdk_strerror(errno)); 354 } 355 break; 356 } 357 } 358 359 return count > 0 ? SPDK_POLLER_BUSY : SPDK_POLLER_IDLE; 360 } 361 362 static int 363 hello_sock_group_poll(void *arg) 364 { 365 struct hello_context_t *ctx = arg; 366 int rc; 367 368 rc = spdk_sock_group_poll(ctx->group); 369 if (rc < 0) { 370 SPDK_ERRLOG("Failed to poll sock_group=%p\n", ctx->group); 371 } 372 373 return rc > 0 ? SPDK_POLLER_BUSY : SPDK_POLLER_IDLE; 374 } 375 376 static int 377 hello_sock_listen(struct hello_context_t *ctx) 378 { 379 struct spdk_sock_impl_opts impl_opts; 380 size_t impl_opts_size = sizeof(impl_opts); 381 struct spdk_sock_opts opts; 382 383 spdk_sock_impl_get_opts(ctx->sock_impl_name, &impl_opts, &impl_opts_size); 384 impl_opts.enable_ktls = ctx->ktls; 385 impl_opts.tls_version = ctx->tls_version; 386 impl_opts.psk_key = ctx->psk_key; 387 impl_opts.psk_identity = ctx->psk_identity; 388 389 opts.opts_size = sizeof(opts); 390 spdk_sock_get_default_opts(&opts); 391 opts.zcopy = ctx->zcopy; 392 opts.impl_opts = &impl_opts; 393 opts.impl_opts_size = sizeof(impl_opts); 394 395 ctx->sock = spdk_sock_listen_ext(ctx->host, ctx->port, ctx->sock_impl_name, &opts); 396 if (ctx->sock == NULL) { 397 SPDK_ERRLOG("Cannot create server socket\n"); 398 return -1; 399 } 400 401 SPDK_NOTICELOG("Listening connection on %s:%d with sock_impl(%s)\n", ctx->host, ctx->port, 402 ctx->sock_impl_name); 403 404 /* 405 * Create sock group for server socket 406 */ 407 ctx->group = spdk_sock_group_create(NULL); 408 409 g_is_running = true; 410 411 /* 412 * Start acceptor and group poller 413 */ 414 ctx->poller_in = SPDK_POLLER_REGISTER(hello_sock_accept_poll, ctx, 415 ACCEPT_TIMEOUT_US); 416 ctx->poller_out = SPDK_POLLER_REGISTER(hello_sock_group_poll, ctx, 0); 417 418 return 0; 419 } 420 421 static void 422 hello_sock_shutdown_cb(void) 423 { 424 g_is_running = false; 425 } 426 427 /* 428 * Our initial event that kicks off everything from main(). 429 */ 430 static void 431 hello_start(void *arg1) 432 { 433 struct hello_context_t *ctx = arg1; 434 int rc; 435 436 SPDK_NOTICELOG("Successfully started the application\n"); 437 438 if (ctx->is_server) { 439 rc = hello_sock_listen(ctx); 440 } else { 441 rc = hello_sock_connect(ctx); 442 } 443 444 if (rc) { 445 spdk_app_stop(-1); 446 return; 447 } 448 } 449 450 int 451 main(int argc, char **argv) 452 { 453 struct spdk_app_opts opts = {}; 454 int rc = 0; 455 struct hello_context_t hello_context = {}; 456 457 /* Set default values in opts structure. */ 458 spdk_app_opts_init(&opts, sizeof(opts)); 459 opts.name = "hello_sock"; 460 opts.shutdown_cb = hello_sock_shutdown_cb; 461 462 if ((rc = spdk_app_parse_args(argc, argv, &opts, "E:H:I:kKN:P:ST:VzZ", NULL, hello_sock_parse_arg, 463 hello_sock_usage)) != SPDK_APP_PARSE_ARGS_SUCCESS) { 464 exit(rc); 465 } 466 hello_context.is_server = g_is_server; 467 hello_context.host = g_host; 468 hello_context.sock_impl_name = g_sock_impl_name; 469 hello_context.port = g_port; 470 hello_context.zcopy = g_zcopy; 471 hello_context.ktls = g_ktls; 472 hello_context.tls_version = g_tls_version; 473 hello_context.psk_key = g_psk_key; 474 hello_context.psk_identity = g_psk_identity; 475 hello_context.verbose = g_verbose; 476 477 rc = spdk_app_start(&opts, hello_start, &hello_context); 478 if (rc) { 479 SPDK_ERRLOG("ERROR starting application\n"); 480 } 481 482 SPDK_NOTICELOG("Exiting from application\n"); 483 484 if (hello_context.verbose) { 485 printf("** %d bytes received, %d bytes sent **\n", 486 hello_context.bytes_in, hello_context.bytes_out); 487 } 488 489 /* Gracefully close out all of the SPDK subsystems. */ 490 spdk_app_fini(); 491 return rc; 492 } 493