xref: /spdk/examples/sock/hello_world/hello_sock.c (revision 510f4c134a21b45ff3a5add9ebc6c6cf7e49aeab)
1 /*   SPDX-License-Identifier: BSD-3-Clause
2  *   Copyright (c) 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 informations\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(&ctx->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