xref: /spdk/lib/iscsi/portal_grp.c (revision b30d57cdad6d2bc75cc1e4e2ebbcebcb0d98dcfa)
1 /*-
2  *   BSD LICENSE
3  *
4  *   Copyright (C) 2008-2012 Daisuke Aoyama <aoyama@peach.ne.jp>.
5  *   Copyright (c) Intel Corporation.
6  *   All rights reserved.
7  *
8  *   Redistribution and use in source and binary forms, with or without
9  *   modification, are permitted provided that the following conditions
10  *   are met:
11  *
12  *     * Redistributions of source code must retain the above copyright
13  *       notice, this list of conditions and the following disclaimer.
14  *     * Redistributions in binary form must reproduce the above copyright
15  *       notice, this list of conditions and the following disclaimer in
16  *       the documentation and/or other materials provided with the
17  *       distribution.
18  *     * Neither the name of Intel Corporation nor the names of its
19  *       contributors may be used to endorse or promote products derived
20  *       from this software without specific prior written permission.
21  *
22  *   THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
23  *   "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
24  *   LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
25  *   A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
26  *   OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
27  *   SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
28  *   LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
29  *   DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
30  *   THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
31  *   (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
32  *   OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
33  */
34 
35 #include "spdk/stdinc.h"
36 
37 #include "spdk/sock.h"
38 #include "spdk/string.h"
39 
40 #include "spdk/log.h"
41 
42 #include "iscsi/iscsi.h"
43 #include "iscsi/conn.h"
44 #include "iscsi/portal_grp.h"
45 #include "iscsi/tgt_node.h"
46 
47 #define PORTNUMSTRLEN 32
48 #define ACCEPT_TIMEOUT_US 1000 /* 1ms */
49 
50 static int
51 iscsi_portal_accept(void *arg)
52 {
53 	struct spdk_iscsi_portal	*portal = arg;
54 	struct spdk_sock		*sock;
55 	int				rc;
56 	int				count = 0;
57 
58 	if (portal->sock == NULL) {
59 		return -1;
60 	}
61 
62 	while (1) {
63 		sock = spdk_sock_accept(portal->sock);
64 		if (sock != NULL) {
65 			rc = iscsi_conn_construct(portal, sock);
66 			if (rc < 0) {
67 				spdk_sock_close(&sock);
68 				SPDK_ERRLOG("spdk_iscsi_connection_construct() failed\n");
69 				break;
70 			}
71 			count++;
72 		} else {
73 			if (errno != EAGAIN && errno != EWOULDBLOCK) {
74 				SPDK_ERRLOG("accept error(%d): %s\n", errno, spdk_strerror(errno));
75 			}
76 			break;
77 		}
78 	}
79 
80 	return count;
81 }
82 
83 static struct spdk_iscsi_portal *
84 iscsi_portal_find_by_addr(const char *host, const char *port)
85 {
86 	struct spdk_iscsi_portal *p;
87 
88 	TAILQ_FOREACH(p, &g_iscsi.portal_head, g_tailq) {
89 		if (!strcmp(p->host, host) && !strcmp(p->port, port)) {
90 			return p;
91 		}
92 	}
93 
94 	return NULL;
95 }
96 
97 /* Assumes caller allocated host and port strings on the heap */
98 struct spdk_iscsi_portal *
99 iscsi_portal_create(const char *host, const char *port)
100 {
101 	struct spdk_iscsi_portal *p = NULL, *tmp;
102 
103 	assert(host != NULL);
104 	assert(port != NULL);
105 
106 	if (strlen(host) > MAX_PORTAL_ADDR || strlen(port) > MAX_PORTAL_PORT) {
107 		return NULL;
108 	}
109 
110 	p = calloc(1, sizeof(*p));
111 	if (!p) {
112 		SPDK_ERRLOG("calloc() failed for portal\n");
113 		return NULL;
114 	}
115 
116 	/* check and overwrite abbreviation of wildcard */
117 	if (strcasecmp(host, "[*]") == 0) {
118 		SPDK_WARNLOG("Please use \"[::]\" as IPv6 wildcard\n");
119 		SPDK_WARNLOG("Convert \"[*]\" to \"[::]\" automatically\n");
120 		SPDK_WARNLOG("(Use of \"[*]\" will be deprecated in a future release)");
121 		snprintf(p->host, sizeof(p->host), "[::]");
122 	} else if (strcasecmp(host, "*") == 0) {
123 		SPDK_WARNLOG("Please use \"0.0.0.0\" as IPv4 wildcard\n");
124 		SPDK_WARNLOG("Convert \"*\" to \"0.0.0.0\" automatically\n");
125 		SPDK_WARNLOG("(Use of \"[*]\" will be deprecated in a future release)");
126 		snprintf(p->host, sizeof(p->host), "0.0.0.0");
127 	} else {
128 		memcpy(p->host, host, strlen(host));
129 	}
130 
131 	memcpy(p->port, port, strlen(port));
132 
133 	p->sock = NULL;
134 	p->group = NULL; /* set at a later time by caller */
135 	p->acceptor_poller = NULL;
136 
137 	pthread_mutex_lock(&g_iscsi.mutex);
138 	tmp = iscsi_portal_find_by_addr(host, port);
139 	if (tmp != NULL) {
140 		pthread_mutex_unlock(&g_iscsi.mutex);
141 		SPDK_ERRLOG("portal (%s, %s) already exists\n", host, port);
142 		goto error_out;
143 	}
144 
145 	TAILQ_INSERT_TAIL(&g_iscsi.portal_head, p, g_tailq);
146 	pthread_mutex_unlock(&g_iscsi.mutex);
147 
148 	return p;
149 
150 error_out:
151 	free(p);
152 
153 	return NULL;
154 }
155 
156 void
157 iscsi_portal_destroy(struct spdk_iscsi_portal *p)
158 {
159 	assert(p != NULL);
160 
161 	SPDK_DEBUGLOG(iscsi, "iscsi_portal_destroy\n");
162 
163 	pthread_mutex_lock(&g_iscsi.mutex);
164 	TAILQ_REMOVE(&g_iscsi.portal_head, p, g_tailq);
165 	pthread_mutex_unlock(&g_iscsi.mutex);
166 
167 	free(p);
168 
169 }
170 
171 static int
172 iscsi_portal_open(struct spdk_iscsi_portal *p)
173 {
174 	struct spdk_sock *sock;
175 	int port;
176 
177 	if (p->sock != NULL) {
178 		SPDK_ERRLOG("portal (%s, %s) is already opened\n",
179 			    p->host, p->port);
180 		return -1;
181 	}
182 
183 	port = (int)strtol(p->port, NULL, 0);
184 	sock = spdk_sock_listen(p->host, port, NULL);
185 	if (sock == NULL) {
186 		SPDK_ERRLOG("listen error %.64s.%d\n", p->host, port);
187 		return -1;
188 	}
189 
190 	p->sock = sock;
191 
192 	/*
193 	 * When the portal is created by config file, incoming connection
194 	 * requests for the socket are pended to accept until reactors start.
195 	 * However the gap between listen() and accept() will be slight and
196 	 * the requests will be queued by the nonzero backlog of the socket
197 	 * or resend by TCP.
198 	 */
199 	p->acceptor_poller = SPDK_POLLER_REGISTER(iscsi_portal_accept, p, ACCEPT_TIMEOUT_US);
200 
201 	return 0;
202 }
203 
204 static void
205 iscsi_portal_close(struct spdk_iscsi_portal *p)
206 {
207 	if (p->sock) {
208 		SPDK_DEBUGLOG(iscsi, "close portal (%s, %s)\n",
209 			      p->host, p->port);
210 		spdk_poller_unregister(&p->acceptor_poller);
211 		spdk_sock_close(&p->sock);
212 	}
213 }
214 
215 static void
216 iscsi_portal_pause(struct spdk_iscsi_portal *p)
217 {
218 	assert(p->acceptor_poller != NULL);
219 
220 	spdk_poller_pause(p->acceptor_poller);
221 }
222 
223 static void
224 iscsi_portal_resume(struct spdk_iscsi_portal *p)
225 {
226 	assert(p->acceptor_poller != NULL);
227 
228 	spdk_poller_resume(p->acceptor_poller);
229 }
230 
231 int
232 iscsi_parse_redirect_addr(struct sockaddr_storage *sa,
233 			  const char *host, const char *port)
234 {
235 	struct addrinfo hints, *res;
236 	int rc;
237 
238 	if (host == NULL || port == NULL) {
239 		return -EINVAL;
240 	}
241 
242 	memset(&hints, 0, sizeof(hints));
243 	hints.ai_family = PF_UNSPEC;
244 	hints.ai_socktype = SOCK_STREAM;
245 	hints.ai_flags = AI_NUMERICSERV;
246 	hints.ai_flags |= AI_NUMERICHOST;
247 	rc = getaddrinfo(host, port, &hints, &res);
248 	if (rc != 0) {
249 		SPDK_ERRLOG("getaddinrfo failed: %s (%d)\n", gai_strerror(rc), rc);
250 		return -EINVAL;
251 	}
252 
253 	if (res->ai_addrlen > sizeof(*sa)) {
254 		SPDK_ERRLOG("getaddrinfo() ai_addrlen %zu too large\n",
255 			    (size_t)res->ai_addrlen);
256 		rc = -EINVAL;
257 	} else {
258 		memcpy(sa, res->ai_addr, res->ai_addrlen);
259 	}
260 
261 	freeaddrinfo(res);
262 	return rc;
263 }
264 
265 struct spdk_iscsi_portal_grp *
266 iscsi_portal_grp_create(int tag, bool is_private)
267 {
268 	struct spdk_iscsi_portal_grp *pg = malloc(sizeof(*pg));
269 
270 	if (!pg) {
271 		SPDK_ERRLOG("malloc() failed for portal group\n");
272 		return NULL;
273 	}
274 
275 	pg->ref = 0;
276 	pg->tag = tag;
277 	pg->is_private = is_private;
278 
279 	pthread_mutex_lock(&g_iscsi.mutex);
280 	pg->disable_chap = g_iscsi.disable_chap;
281 	pg->require_chap = g_iscsi.require_chap;
282 	pg->mutual_chap = g_iscsi.mutual_chap;
283 	pg->chap_group = g_iscsi.chap_group;
284 	pthread_mutex_unlock(&g_iscsi.mutex);
285 
286 	TAILQ_INIT(&pg->head);
287 
288 	return pg;
289 }
290 
291 void
292 iscsi_portal_grp_destroy(struct spdk_iscsi_portal_grp *pg)
293 {
294 	struct spdk_iscsi_portal	*p;
295 
296 	assert(pg != NULL);
297 
298 	SPDK_DEBUGLOG(iscsi, "iscsi_portal_grp_destroy\n");
299 	while (!TAILQ_EMPTY(&pg->head)) {
300 		p = TAILQ_FIRST(&pg->head);
301 		TAILQ_REMOVE(&pg->head, p, per_pg_tailq);
302 		iscsi_portal_destroy(p);
303 	}
304 	free(pg);
305 }
306 
307 int
308 iscsi_portal_grp_register(struct spdk_iscsi_portal_grp *pg)
309 {
310 	int rc = -1;
311 	struct spdk_iscsi_portal_grp *tmp;
312 
313 	assert(pg != NULL);
314 
315 	pthread_mutex_lock(&g_iscsi.mutex);
316 	tmp = iscsi_portal_grp_find_by_tag(pg->tag);
317 	if (tmp == NULL) {
318 		TAILQ_INSERT_TAIL(&g_iscsi.pg_head, pg, tailq);
319 		rc = 0;
320 	}
321 	pthread_mutex_unlock(&g_iscsi.mutex);
322 	return rc;
323 }
324 
325 void
326 iscsi_portal_grp_add_portal(struct spdk_iscsi_portal_grp *pg,
327 			    struct spdk_iscsi_portal *p)
328 {
329 	assert(pg != NULL);
330 	assert(p != NULL);
331 
332 	p->group = pg;
333 	TAILQ_INSERT_TAIL(&pg->head, p, per_pg_tailq);
334 }
335 
336 struct spdk_iscsi_portal *
337 iscsi_portal_grp_find_portal_by_addr(struct spdk_iscsi_portal_grp *pg,
338 				     const char *host, const char *port)
339 {
340 	struct spdk_iscsi_portal *p;
341 
342 	TAILQ_FOREACH(p, &pg->head, per_pg_tailq) {
343 		if (!strcmp(p->host, host) && !strcmp(p->port, port)) {
344 			return p;
345 		}
346 	}
347 
348 	return NULL;
349 }
350 
351 int
352 iscsi_portal_grp_set_chap_params(struct spdk_iscsi_portal_grp *pg,
353 				 bool disable_chap, bool require_chap,
354 				 bool mutual_chap, int32_t chap_group)
355 {
356 	if (!iscsi_check_chap_params(disable_chap, require_chap,
357 				     mutual_chap, chap_group)) {
358 		return -EINVAL;
359 	}
360 
361 	pg->disable_chap = disable_chap;
362 	pg->require_chap = require_chap;
363 	pg->mutual_chap = mutual_chap;
364 	pg->chap_group = chap_group;
365 
366 	return 0;
367 }
368 
369 struct spdk_iscsi_portal_grp *
370 iscsi_portal_grp_find_by_tag(int tag)
371 {
372 	struct spdk_iscsi_portal_grp *pg;
373 
374 	TAILQ_FOREACH(pg, &g_iscsi.pg_head, tailq) {
375 		if (pg->tag == tag) {
376 			return pg;
377 		}
378 	}
379 
380 	return NULL;
381 }
382 
383 void
384 iscsi_portal_grps_destroy(void)
385 {
386 	struct spdk_iscsi_portal_grp *pg;
387 
388 	SPDK_DEBUGLOG(iscsi, "iscsi_portal_grps_destroy\n");
389 	pthread_mutex_lock(&g_iscsi.mutex);
390 	while (!TAILQ_EMPTY(&g_iscsi.pg_head)) {
391 		pg = TAILQ_FIRST(&g_iscsi.pg_head);
392 		TAILQ_REMOVE(&g_iscsi.pg_head, pg, tailq);
393 		pthread_mutex_unlock(&g_iscsi.mutex);
394 		iscsi_portal_grp_destroy(pg);
395 		pthread_mutex_lock(&g_iscsi.mutex);
396 	}
397 	pthread_mutex_unlock(&g_iscsi.mutex);
398 }
399 
400 int
401 iscsi_portal_grp_open(struct spdk_iscsi_portal_grp *pg, bool pause)
402 {
403 	struct spdk_iscsi_portal *p;
404 	int rc;
405 
406 	TAILQ_FOREACH(p, &pg->head, per_pg_tailq) {
407 		rc = iscsi_portal_open(p);
408 		if (rc < 0) {
409 			return rc;
410 		}
411 
412 		if (pause) {
413 			iscsi_portal_pause(p);
414 		}
415 	}
416 	return 0;
417 }
418 
419 static void
420 iscsi_portal_grp_close(struct spdk_iscsi_portal_grp *pg)
421 {
422 	struct spdk_iscsi_portal *p;
423 
424 	TAILQ_FOREACH(p, &pg->head, per_pg_tailq) {
425 		iscsi_portal_close(p);
426 	}
427 }
428 
429 void
430 iscsi_portal_grp_resume(struct spdk_iscsi_portal_grp *pg)
431 {
432 	struct spdk_iscsi_portal *p;
433 
434 	TAILQ_FOREACH(p, &pg->head, per_pg_tailq) {
435 		iscsi_portal_resume(p);
436 	}
437 }
438 
439 void
440 iscsi_portal_grp_close_all(void)
441 {
442 	struct spdk_iscsi_portal_grp *pg;
443 
444 	SPDK_DEBUGLOG(iscsi, "iscsi_portal_grp_close_all\n");
445 	pthread_mutex_lock(&g_iscsi.mutex);
446 	TAILQ_FOREACH(pg, &g_iscsi.pg_head, tailq) {
447 		iscsi_portal_grp_close(pg);
448 	}
449 	pthread_mutex_unlock(&g_iscsi.mutex);
450 }
451 
452 struct spdk_iscsi_portal_grp *
453 iscsi_portal_grp_unregister(int tag)
454 {
455 	struct spdk_iscsi_portal_grp *pg;
456 
457 	pthread_mutex_lock(&g_iscsi.mutex);
458 	TAILQ_FOREACH(pg, &g_iscsi.pg_head, tailq) {
459 		if (pg->tag == tag) {
460 			TAILQ_REMOVE(&g_iscsi.pg_head, pg, tailq);
461 			pthread_mutex_unlock(&g_iscsi.mutex);
462 			return pg;
463 		}
464 	}
465 	pthread_mutex_unlock(&g_iscsi.mutex);
466 	return NULL;
467 }
468 
469 void
470 iscsi_portal_grp_release(struct spdk_iscsi_portal_grp *pg)
471 {
472 	iscsi_portal_grp_close(pg);
473 	iscsi_portal_grp_destroy(pg);
474 }
475 
476 static void
477 iscsi_portal_grp_info_json(struct spdk_iscsi_portal_grp *pg,
478 			   struct spdk_json_write_ctx *w)
479 {
480 	struct spdk_iscsi_portal *portal;
481 
482 	spdk_json_write_object_begin(w);
483 
484 	spdk_json_write_named_int32(w, "tag", pg->tag);
485 
486 	spdk_json_write_named_array_begin(w, "portals");
487 	TAILQ_FOREACH(portal, &pg->head, per_pg_tailq) {
488 		spdk_json_write_object_begin(w);
489 
490 		spdk_json_write_named_string(w, "host", portal->host);
491 		spdk_json_write_named_string(w, "port", portal->port);
492 
493 		spdk_json_write_object_end(w);
494 	}
495 	spdk_json_write_array_end(w);
496 
497 	spdk_json_write_named_bool(w, "private", pg->is_private);
498 
499 	spdk_json_write_object_end(w);
500 }
501 
502 static void
503 iscsi_portal_grp_config_json(struct spdk_iscsi_portal_grp *pg,
504 			     struct spdk_json_write_ctx *w)
505 {
506 	spdk_json_write_object_begin(w);
507 
508 	spdk_json_write_named_string(w, "method", "iscsi_create_portal_group");
509 
510 	spdk_json_write_name(w, "params");
511 	iscsi_portal_grp_info_json(pg, w);
512 
513 	spdk_json_write_object_end(w);
514 }
515 
516 void
517 iscsi_portal_grps_info_json(struct spdk_json_write_ctx *w)
518 {
519 	struct spdk_iscsi_portal_grp *pg;
520 
521 	TAILQ_FOREACH(pg, &g_iscsi.pg_head, tailq) {
522 		iscsi_portal_grp_info_json(pg, w);
523 	}
524 }
525 
526 void
527 iscsi_portal_grps_config_json(struct spdk_json_write_ctx *w)
528 {
529 	struct spdk_iscsi_portal_grp *pg;
530 
531 	TAILQ_FOREACH(pg, &g_iscsi.pg_head, tailq) {
532 		iscsi_portal_grp_config_json(pg, w);
533 	}
534 }
535