xref: /spdk/lib/iscsi/portal_grp.c (revision d73077b84a71985da1db1c9847ea7c042189bae2)
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 int
216 iscsi_parse_redirect_addr(struct sockaddr_storage *sa,
217 			  const char *host, const char *port)
218 {
219 	struct addrinfo hints, *res;
220 	int rc;
221 
222 	if (host == NULL || port == NULL) {
223 		return -EINVAL;
224 	}
225 
226 	memset(&hints, 0, sizeof(hints));
227 	hints.ai_family = PF_UNSPEC;
228 	hints.ai_socktype = SOCK_STREAM;
229 	hints.ai_flags = AI_NUMERICSERV;
230 	hints.ai_flags |= AI_NUMERICHOST;
231 	rc = getaddrinfo(host, port, &hints, &res);
232 	if (rc != 0) {
233 		SPDK_ERRLOG("getaddinrfo failed: %s (%d)\n", gai_strerror(rc), rc);
234 		return -EINVAL;
235 	}
236 
237 	if (res->ai_addrlen > sizeof(*sa)) {
238 		SPDK_ERRLOG("getaddrinfo() ai_addrlen %zu too large\n",
239 			    (size_t)res->ai_addrlen);
240 		rc = -EINVAL;
241 	} else {
242 		memcpy(sa, res->ai_addr, res->ai_addrlen);
243 	}
244 
245 	freeaddrinfo(res);
246 	return rc;
247 }
248 
249 struct spdk_iscsi_portal_grp *
250 iscsi_portal_grp_create(int tag, bool is_private)
251 {
252 	struct spdk_iscsi_portal_grp *pg = malloc(sizeof(*pg));
253 
254 	if (!pg) {
255 		SPDK_ERRLOG("malloc() failed for portal group\n");
256 		return NULL;
257 	}
258 
259 	pg->ref = 0;
260 	pg->tag = tag;
261 	pg->is_private = is_private;
262 
263 	pthread_mutex_lock(&g_iscsi.mutex);
264 	pg->disable_chap = g_iscsi.disable_chap;
265 	pg->require_chap = g_iscsi.require_chap;
266 	pg->mutual_chap = g_iscsi.mutual_chap;
267 	pg->chap_group = g_iscsi.chap_group;
268 	pthread_mutex_unlock(&g_iscsi.mutex);
269 
270 	TAILQ_INIT(&pg->head);
271 
272 	return pg;
273 }
274 
275 void
276 iscsi_portal_grp_destroy(struct spdk_iscsi_portal_grp *pg)
277 {
278 	struct spdk_iscsi_portal	*p;
279 
280 	assert(pg != NULL);
281 
282 	SPDK_DEBUGLOG(iscsi, "iscsi_portal_grp_destroy\n");
283 	while (!TAILQ_EMPTY(&pg->head)) {
284 		p = TAILQ_FIRST(&pg->head);
285 		TAILQ_REMOVE(&pg->head, p, per_pg_tailq);
286 		iscsi_portal_destroy(p);
287 	}
288 	free(pg);
289 }
290 
291 int
292 iscsi_portal_grp_register(struct spdk_iscsi_portal_grp *pg)
293 {
294 	int rc = -1;
295 	struct spdk_iscsi_portal_grp *tmp;
296 
297 	assert(pg != NULL);
298 
299 	pthread_mutex_lock(&g_iscsi.mutex);
300 	tmp = iscsi_portal_grp_find_by_tag(pg->tag);
301 	if (tmp == NULL) {
302 		TAILQ_INSERT_TAIL(&g_iscsi.pg_head, pg, tailq);
303 		rc = 0;
304 	}
305 	pthread_mutex_unlock(&g_iscsi.mutex);
306 	return rc;
307 }
308 
309 void
310 iscsi_portal_grp_add_portal(struct spdk_iscsi_portal_grp *pg,
311 			    struct spdk_iscsi_portal *p)
312 {
313 	assert(pg != NULL);
314 	assert(p != NULL);
315 
316 	p->group = pg;
317 	TAILQ_INSERT_TAIL(&pg->head, p, per_pg_tailq);
318 }
319 
320 struct spdk_iscsi_portal *
321 iscsi_portal_grp_find_portal_by_addr(struct spdk_iscsi_portal_grp *pg,
322 				     const char *host, const char *port)
323 {
324 	struct spdk_iscsi_portal *p;
325 
326 	TAILQ_FOREACH(p, &pg->head, per_pg_tailq) {
327 		if (!strcmp(p->host, host) && !strcmp(p->port, port)) {
328 			return p;
329 		}
330 	}
331 
332 	return NULL;
333 }
334 
335 int
336 iscsi_portal_grp_set_chap_params(struct spdk_iscsi_portal_grp *pg,
337 				 bool disable_chap, bool require_chap,
338 				 bool mutual_chap, int32_t chap_group)
339 {
340 	if (!iscsi_check_chap_params(disable_chap, require_chap,
341 				     mutual_chap, chap_group)) {
342 		return -EINVAL;
343 	}
344 
345 	pg->disable_chap = disable_chap;
346 	pg->require_chap = require_chap;
347 	pg->mutual_chap = mutual_chap;
348 	pg->chap_group = chap_group;
349 
350 	return 0;
351 }
352 
353 struct spdk_iscsi_portal_grp *
354 iscsi_portal_grp_find_by_tag(int tag)
355 {
356 	struct spdk_iscsi_portal_grp *pg;
357 
358 	TAILQ_FOREACH(pg, &g_iscsi.pg_head, tailq) {
359 		if (pg->tag == tag) {
360 			return pg;
361 		}
362 	}
363 
364 	return NULL;
365 }
366 
367 void
368 iscsi_portal_grps_destroy(void)
369 {
370 	struct spdk_iscsi_portal_grp *pg;
371 
372 	SPDK_DEBUGLOG(iscsi, "iscsi_portal_grps_destroy\n");
373 	pthread_mutex_lock(&g_iscsi.mutex);
374 	while (!TAILQ_EMPTY(&g_iscsi.pg_head)) {
375 		pg = TAILQ_FIRST(&g_iscsi.pg_head);
376 		TAILQ_REMOVE(&g_iscsi.pg_head, pg, tailq);
377 		pthread_mutex_unlock(&g_iscsi.mutex);
378 		iscsi_portal_grp_destroy(pg);
379 		pthread_mutex_lock(&g_iscsi.mutex);
380 	}
381 	pthread_mutex_unlock(&g_iscsi.mutex);
382 }
383 
384 int
385 iscsi_portal_grp_open(struct spdk_iscsi_portal_grp *pg)
386 {
387 	struct spdk_iscsi_portal *p;
388 	int rc;
389 
390 	TAILQ_FOREACH(p, &pg->head, per_pg_tailq) {
391 		rc = iscsi_portal_open(p);
392 		if (rc < 0) {
393 			return rc;
394 		}
395 	}
396 	return 0;
397 }
398 
399 static void
400 iscsi_portal_grp_close(struct spdk_iscsi_portal_grp *pg)
401 {
402 	struct spdk_iscsi_portal *p;
403 
404 	TAILQ_FOREACH(p, &pg->head, per_pg_tailq) {
405 		iscsi_portal_close(p);
406 	}
407 }
408 
409 void
410 iscsi_portal_grp_close_all(void)
411 {
412 	struct spdk_iscsi_portal_grp *pg;
413 
414 	SPDK_DEBUGLOG(iscsi, "iscsi_portal_grp_close_all\n");
415 	pthread_mutex_lock(&g_iscsi.mutex);
416 	TAILQ_FOREACH(pg, &g_iscsi.pg_head, tailq) {
417 		iscsi_portal_grp_close(pg);
418 	}
419 	pthread_mutex_unlock(&g_iscsi.mutex);
420 }
421 
422 struct spdk_iscsi_portal_grp *
423 iscsi_portal_grp_unregister(int tag)
424 {
425 	struct spdk_iscsi_portal_grp *pg;
426 
427 	pthread_mutex_lock(&g_iscsi.mutex);
428 	TAILQ_FOREACH(pg, &g_iscsi.pg_head, tailq) {
429 		if (pg->tag == tag) {
430 			TAILQ_REMOVE(&g_iscsi.pg_head, pg, tailq);
431 			pthread_mutex_unlock(&g_iscsi.mutex);
432 			return pg;
433 		}
434 	}
435 	pthread_mutex_unlock(&g_iscsi.mutex);
436 	return NULL;
437 }
438 
439 void
440 iscsi_portal_grp_release(struct spdk_iscsi_portal_grp *pg)
441 {
442 	iscsi_portal_grp_close(pg);
443 	iscsi_portal_grp_destroy(pg);
444 }
445 
446 static void
447 iscsi_portal_grp_info_json(struct spdk_iscsi_portal_grp *pg,
448 			   struct spdk_json_write_ctx *w)
449 {
450 	struct spdk_iscsi_portal *portal;
451 
452 	spdk_json_write_object_begin(w);
453 
454 	spdk_json_write_named_int32(w, "tag", pg->tag);
455 
456 	spdk_json_write_named_array_begin(w, "portals");
457 	TAILQ_FOREACH(portal, &pg->head, per_pg_tailq) {
458 		spdk_json_write_object_begin(w);
459 
460 		spdk_json_write_named_string(w, "host", portal->host);
461 		spdk_json_write_named_string(w, "port", portal->port);
462 
463 		spdk_json_write_object_end(w);
464 	}
465 	spdk_json_write_array_end(w);
466 
467 	spdk_json_write_named_bool(w, "private", pg->is_private);
468 
469 	spdk_json_write_object_end(w);
470 }
471 
472 static void
473 iscsi_portal_grp_config_json(struct spdk_iscsi_portal_grp *pg,
474 			     struct spdk_json_write_ctx *w)
475 {
476 	spdk_json_write_object_begin(w);
477 
478 	spdk_json_write_named_string(w, "method", "iscsi_create_portal_group");
479 
480 	spdk_json_write_name(w, "params");
481 	iscsi_portal_grp_info_json(pg, w);
482 
483 	spdk_json_write_object_end(w);
484 }
485 
486 void
487 iscsi_portal_grps_info_json(struct spdk_json_write_ctx *w)
488 {
489 	struct spdk_iscsi_portal_grp *pg;
490 
491 	TAILQ_FOREACH(pg, &g_iscsi.pg_head, tailq) {
492 		iscsi_portal_grp_info_json(pg, w);
493 	}
494 }
495 
496 void
497 iscsi_portal_grps_config_json(struct spdk_json_write_ctx *w)
498 {
499 	struct spdk_iscsi_portal_grp *pg;
500 
501 	TAILQ_FOREACH(pg, &g_iscsi.pg_head, tailq) {
502 		iscsi_portal_grp_config_json(pg, w);
503 	}
504 }
505