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