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