summaryrefslogtreecommitdiffstats
path: root/src/spdk/lib/iscsi
diff options
context:
space:
mode:
Diffstat (limited to 'src/spdk/lib/iscsi')
-rw-r--r--src/spdk/lib/iscsi/Makefile50
-rw-r--r--src/spdk/lib/iscsi/conn.c1714
-rw-r--r--src/spdk/lib/iscsi/conn.h237
-rw-r--r--src/spdk/lib/iscsi/init_grp.c787
-rw-r--r--src/spdk/lib/iscsi/init_grp.h81
-rw-r--r--src/spdk/lib/iscsi/iscsi.c4797
-rw-r--r--src/spdk/lib/iscsi/iscsi.h465
-rw-r--r--src/spdk/lib/iscsi/iscsi_rpc.c1639
-rw-r--r--src/spdk/lib/iscsi/iscsi_subsystem.c1577
-rw-r--r--src/spdk/lib/iscsi/md5.c75
-rw-r--r--src/spdk/lib/iscsi/md5.h52
-rw-r--r--src/spdk/lib/iscsi/param.c1216
-rw-r--r--src/spdk/lib/iscsi/param.h94
-rw-r--r--src/spdk/lib/iscsi/portal_grp.c655
-rw-r--r--src/spdk/lib/iscsi/portal_grp.h90
-rw-r--r--src/spdk/lib/iscsi/spdk_iscsi.map11
-rw-r--r--src/spdk/lib/iscsi/task.c98
-rw-r--r--src/spdk/lib/iscsi/task.h188
-rw-r--r--src/spdk/lib/iscsi/tgt_node.c1607
-rw-r--r--src/spdk/lib/iscsi/tgt_node.h147
20 files changed, 15580 insertions, 0 deletions
diff --git a/src/spdk/lib/iscsi/Makefile b/src/spdk/lib/iscsi/Makefile
new file mode 100644
index 000000000..2c663d880
--- /dev/null
+++ b/src/spdk/lib/iscsi/Makefile
@@ -0,0 +1,50 @@
+#
+# BSD LICENSE
+#
+# Copyright (c) Intel Corporation.
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+#
+# * Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# * Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in
+# the documentation and/or other materials provided with the
+# distribution.
+# * Neither the name of Intel Corporation nor the names of its
+# contributors may be used to endorse or promote products derived
+# from this software without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+#
+
+SPDK_ROOT_DIR := $(abspath $(CURDIR)/../..)
+include $(SPDK_ROOT_DIR)/mk/spdk.common.mk
+
+SO_VER := 3
+SO_MINOR := 0
+
+CFLAGS += -I$(SPDK_ROOT_DIR)/lib
+C_SRCS = conn.c \
+ init_grp.c iscsi.c md5.c param.c portal_grp.c \
+ tgt_node.c iscsi_subsystem.c \
+ iscsi_rpc.c task.c
+LIBNAME = iscsi
+LOCAL_SYS_LIBS = -lcrypto
+
+SPDK_MAP_FILE = $(abspath $(CURDIR)/spdk_iscsi.map)
+
+include $(SPDK_ROOT_DIR)/mk/spdk.lib.mk
diff --git a/src/spdk/lib/iscsi/conn.c b/src/spdk/lib/iscsi/conn.c
new file mode 100644
index 000000000..4c7a54fcf
--- /dev/null
+++ b/src/spdk/lib/iscsi/conn.c
@@ -0,0 +1,1714 @@
+/*-
+ * BSD LICENSE
+ *
+ * Copyright (C) 2008-2012 Daisuke Aoyama <aoyama@peach.ne.jp>.
+ * Copyright (c) Intel Corporation.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ * * Neither the name of Intel Corporation nor the names of its
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "spdk/stdinc.h"
+
+#include "spdk/endian.h"
+#include "spdk/env.h"
+#include "spdk/event.h"
+#include "spdk/likely.h"
+#include "spdk/thread.h"
+#include "spdk/queue.h"
+#include "spdk/trace.h"
+#include "spdk/net.h"
+#include "spdk/sock.h"
+#include "spdk/string.h"
+
+#include "spdk_internal/log.h"
+
+#include "iscsi/task.h"
+#include "iscsi/conn.h"
+#include "iscsi/tgt_node.h"
+#include "iscsi/portal_grp.h"
+
+#define MAKE_DIGEST_WORD(BUF, CRC32C) \
+ ( ((*((uint8_t *)(BUF)+0)) = (uint8_t)((uint32_t)(CRC32C) >> 0)), \
+ ((*((uint8_t *)(BUF)+1)) = (uint8_t)((uint32_t)(CRC32C) >> 8)), \
+ ((*((uint8_t *)(BUF)+2)) = (uint8_t)((uint32_t)(CRC32C) >> 16)), \
+ ((*((uint8_t *)(BUF)+3)) = (uint8_t)((uint32_t)(CRC32C) >> 24)))
+
+#define SPDK_ISCSI_CONNECTION_MEMSET(conn) \
+ memset(&(conn)->portal, 0, sizeof(*(conn)) - \
+ offsetof(struct spdk_iscsi_conn, portal));
+
+struct spdk_iscsi_conn *g_conns_array = MAP_FAILED;
+static int g_conns_array_fd = -1;
+static char g_shm_name[64];
+
+static TAILQ_HEAD(, spdk_iscsi_conn) g_free_conns = TAILQ_HEAD_INITIALIZER(g_free_conns);
+static TAILQ_HEAD(, spdk_iscsi_conn) g_active_conns = TAILQ_HEAD_INITIALIZER(g_active_conns);
+
+static pthread_mutex_t g_conns_mutex = PTHREAD_MUTEX_INITIALIZER;
+
+static struct spdk_poller *g_shutdown_timer = NULL;
+
+static void iscsi_conn_sock_cb(void *arg, struct spdk_sock_group *group,
+ struct spdk_sock *sock);
+
+static struct spdk_iscsi_conn *
+allocate_conn(void)
+{
+ struct spdk_iscsi_conn *conn;
+
+ pthread_mutex_lock(&g_conns_mutex);
+ conn = TAILQ_FIRST(&g_free_conns);
+ if (conn != NULL) {
+ assert(!conn->is_valid);
+ TAILQ_REMOVE(&g_free_conns, conn, conn_link);
+ SPDK_ISCSI_CONNECTION_MEMSET(conn);
+ conn->is_valid = 1;
+
+ TAILQ_INSERT_TAIL(&g_active_conns, conn, conn_link);
+ }
+ pthread_mutex_unlock(&g_conns_mutex);
+
+ return conn;
+}
+
+static void
+_free_conn(struct spdk_iscsi_conn *conn)
+{
+ TAILQ_REMOVE(&g_active_conns, conn, conn_link);
+
+ memset(conn->portal_host, 0, sizeof(conn->portal_host));
+ memset(conn->portal_port, 0, sizeof(conn->portal_port));
+ conn->is_valid = 0;
+
+ TAILQ_INSERT_TAIL(&g_free_conns, conn, conn_link);
+}
+
+static void
+free_conn(struct spdk_iscsi_conn *conn)
+{
+ pthread_mutex_lock(&g_conns_mutex);
+ _free_conn(conn);
+ pthread_mutex_unlock(&g_conns_mutex);
+}
+
+static void
+_iscsi_conns_cleanup(void)
+{
+ if (g_conns_array != MAP_FAILED) {
+ munmap(g_conns_array, sizeof(struct spdk_iscsi_conn) *
+ MAX_ISCSI_CONNECTIONS);
+ g_conns_array = MAP_FAILED;
+ }
+
+ if (g_conns_array_fd >= 0) {
+ close(g_conns_array_fd);
+ g_conns_array_fd = -1;
+ shm_unlink(g_shm_name);
+ }
+}
+
+int initialize_iscsi_conns(void)
+{
+ size_t conns_size = sizeof(struct spdk_iscsi_conn) * MAX_ISCSI_CONNECTIONS;
+ uint32_t i;
+
+ SPDK_DEBUGLOG(SPDK_LOG_ISCSI, "spdk_iscsi_init\n");
+
+ snprintf(g_shm_name, sizeof(g_shm_name), "/spdk_iscsi_conns.%d", spdk_app_get_shm_id());
+ g_conns_array_fd = shm_open(g_shm_name, O_RDWR | O_CREAT, 0600);
+ if (g_conns_array_fd < 0) {
+ SPDK_ERRLOG("could not shm_open %s\n", g_shm_name);
+ goto err;
+ }
+
+ if (ftruncate(g_conns_array_fd, conns_size) != 0) {
+ SPDK_ERRLOG("could not ftruncate\n");
+ goto err;
+ }
+ g_conns_array = mmap(0, conns_size, PROT_READ | PROT_WRITE, MAP_SHARED,
+ g_conns_array_fd, 0);
+
+ if (g_conns_array == MAP_FAILED) {
+ SPDK_ERRLOG("could not mmap cons array file %s (%d)\n", g_shm_name, errno);
+ goto err;
+ }
+
+ memset(g_conns_array, 0, conns_size);
+
+ for (i = 0; i < MAX_ISCSI_CONNECTIONS; i++) {
+ g_conns_array[i].id = i;
+ TAILQ_INSERT_TAIL(&g_free_conns, &g_conns_array[i], conn_link);
+ }
+
+ return 0;
+
+err:
+ _iscsi_conns_cleanup();
+
+ return -1;
+}
+
+static void
+iscsi_poll_group_add_conn(struct spdk_iscsi_poll_group *pg, struct spdk_iscsi_conn *conn)
+{
+ int rc;
+
+ rc = spdk_sock_group_add_sock(pg->sock_group, conn->sock, iscsi_conn_sock_cb, conn);
+ if (rc < 0) {
+ SPDK_ERRLOG("Failed to add sock=%p of conn=%p\n", conn->sock, conn);
+ return;
+ }
+
+ conn->is_stopped = false;
+ STAILQ_INSERT_TAIL(&pg->connections, conn, pg_link);
+}
+
+static void
+iscsi_poll_group_remove_conn(struct spdk_iscsi_poll_group *pg, struct spdk_iscsi_conn *conn)
+{
+ int rc;
+
+ assert(conn->sock != NULL);
+ rc = spdk_sock_group_remove_sock(pg->sock_group, conn->sock);
+ if (rc < 0) {
+ SPDK_ERRLOG("Failed to remove sock=%p of conn=%p\n", conn->sock, conn);
+ }
+
+ conn->is_stopped = true;
+ STAILQ_REMOVE(&pg->connections, conn, spdk_iscsi_conn, pg_link);
+}
+
+static void
+iscsi_conn_start(void *ctx)
+{
+ struct spdk_iscsi_conn *conn = ctx;
+
+ iscsi_poll_group_add_conn(conn->pg, conn);
+}
+
+int
+iscsi_conn_construct(struct spdk_iscsi_portal *portal,
+ struct spdk_sock *sock)
+{
+ struct spdk_iscsi_poll_group *pg;
+ struct spdk_iscsi_conn *conn;
+ int i, rc;
+
+ conn = allocate_conn();
+ if (conn == NULL) {
+ SPDK_ERRLOG("Could not allocate connection.\n");
+ return -1;
+ }
+
+ pthread_mutex_lock(&g_iscsi.mutex);
+ conn->timeout = g_iscsi.timeout * spdk_get_ticks_hz(); /* seconds to TSC */
+ conn->nopininterval = g_iscsi.nopininterval;
+ conn->nopininterval *= spdk_get_ticks_hz(); /* seconds to TSC */
+ conn->nop_outstanding = false;
+ conn->data_out_cnt = 0;
+ conn->data_in_cnt = 0;
+ conn->disable_chap = portal->group->disable_chap;
+ conn->require_chap = portal->group->require_chap;
+ conn->mutual_chap = portal->group->mutual_chap;
+ conn->chap_group = portal->group->chap_group;
+ pthread_mutex_unlock(&g_iscsi.mutex);
+ conn->MaxRecvDataSegmentLength = 8192; /* RFC3720(12.12) */
+
+ conn->portal = portal;
+ conn->pg_tag = portal->group->tag;
+ memcpy(conn->portal_host, portal->host, strlen(portal->host));
+ memcpy(conn->portal_port, portal->port, strlen(portal->port));
+ conn->sock = sock;
+
+ conn->state = ISCSI_CONN_STATE_INVALID;
+ conn->login_phase = ISCSI_SECURITY_NEGOTIATION_PHASE;
+ conn->ttt = 0;
+
+ conn->partial_text_parameter = NULL;
+
+ for (i = 0; i < MAX_CONNECTION_PARAMS; i++) {
+ conn->conn_param_state_negotiated[i] = false;
+ }
+
+ for (i = 0; i < MAX_SESSION_PARAMS; i++) {
+ conn->sess_param_state_negotiated[i] = false;
+ }
+
+ conn->pdu_recv_state = ISCSI_PDU_RECV_STATE_AWAIT_PDU_READY;
+
+ TAILQ_INIT(&conn->write_pdu_list);
+ TAILQ_INIT(&conn->snack_pdu_list);
+ TAILQ_INIT(&conn->queued_r2t_tasks);
+ TAILQ_INIT(&conn->active_r2t_tasks);
+ TAILQ_INIT(&conn->queued_datain_tasks);
+ memset(&conn->luns, 0, sizeof(conn->luns));
+
+ rc = spdk_sock_getaddr(sock, conn->target_addr, sizeof conn->target_addr, NULL,
+ conn->initiator_addr, sizeof conn->initiator_addr, NULL);
+ if (rc < 0) {
+ SPDK_ERRLOG("spdk_sock_getaddr() failed\n");
+ goto error_return;
+ }
+
+ /* set low water mark */
+ rc = spdk_sock_set_recvlowat(conn->sock, 1);
+ if (rc != 0) {
+ SPDK_ERRLOG("spdk_sock_set_recvlowat() failed\n");
+ goto error_return;
+ }
+
+ /* set default params */
+ rc = iscsi_conn_params_init(&conn->params);
+ if (rc < 0) {
+ SPDK_ERRLOG("iscsi_conn_params_init() failed\n");
+ goto error_return;
+ }
+ conn->logout_request_timer = NULL;
+ conn->logout_timer = NULL;
+ conn->shutdown_timer = NULL;
+ SPDK_DEBUGLOG(SPDK_LOG_ISCSI, "Launching connection on acceptor thread\n");
+ conn->pending_task_cnt = 0;
+
+ /* Get the first poll group. */
+ pg = TAILQ_FIRST(&g_iscsi.poll_group_head);
+ if (pg == NULL) {
+ SPDK_ERRLOG("There is no poll group.\n");
+ assert(false);
+ goto error_return;
+ }
+
+ conn->pg = pg;
+ spdk_thread_send_msg(spdk_io_channel_get_thread(spdk_io_channel_from_ctx(pg)),
+ iscsi_conn_start, conn);
+ return 0;
+
+error_return:
+ iscsi_param_free(conn->params);
+ free_conn(conn);
+ return -1;
+}
+
+void
+iscsi_conn_free_pdu(struct spdk_iscsi_conn *conn, struct spdk_iscsi_pdu *pdu)
+{
+ iscsi_conn_xfer_complete_cb cb_fn;
+ void *cb_arg;
+
+ cb_fn = pdu->cb_fn;
+ cb_arg = pdu->cb_arg;
+
+ assert(cb_fn != NULL);
+ pdu->cb_fn = NULL;
+
+ if (pdu->task) {
+ iscsi_task_put(pdu->task);
+ }
+ iscsi_put_pdu(pdu);
+
+ cb_fn(cb_arg);
+}
+
+static int
+iscsi_conn_free_tasks(struct spdk_iscsi_conn *conn)
+{
+ struct spdk_iscsi_pdu *pdu, *tmp_pdu;
+ struct spdk_iscsi_task *iscsi_task, *tmp_iscsi_task;
+
+ TAILQ_FOREACH_SAFE(pdu, &conn->snack_pdu_list, tailq, tmp_pdu) {
+ TAILQ_REMOVE(&conn->snack_pdu_list, pdu, tailq);
+ iscsi_conn_free_pdu(conn, pdu);
+ }
+
+ TAILQ_FOREACH_SAFE(iscsi_task, &conn->queued_datain_tasks, link, tmp_iscsi_task) {
+ if (!iscsi_task->is_queued) {
+ TAILQ_REMOVE(&conn->queued_datain_tasks, iscsi_task, link);
+ iscsi_task_put(iscsi_task);
+ }
+ }
+
+ /* We have to parse conn->write_pdu_list in the end. In iscsi_conn_free_pdu(),
+ * iscsi_conn_handle_queued_datain_tasks() may be called, and
+ * iscsi_conn_handle_queued_datain_tasks() will parse conn->queued_datain_tasks
+ * and may stack some PDUs to conn->write_pdu_list. Hence when we come here, we
+ * have to ensure there is no associated task in conn->queued_datain_tasks.
+ */
+ TAILQ_FOREACH_SAFE(pdu, &conn->write_pdu_list, tailq, tmp_pdu) {
+ TAILQ_REMOVE(&conn->write_pdu_list, pdu, tailq);
+ iscsi_conn_free_pdu(conn, pdu);
+ }
+
+ if (conn->pending_task_cnt) {
+ return -1;
+ }
+
+ return 0;
+}
+
+static void
+iscsi_conn_cleanup_backend(struct spdk_iscsi_conn *conn)
+{
+ int rc;
+ struct spdk_iscsi_tgt_node *target;
+
+ if (conn->sess->connections > 1) {
+ /* connection specific cleanup */
+ } else if (!g_iscsi.AllowDuplicateIsid) {
+ /* clean up all tasks to all LUNs for session */
+ target = conn->sess->target;
+ if (target != NULL) {
+ rc = iscsi_tgt_node_cleanup_luns(conn, target);
+ if (rc < 0) {
+ SPDK_ERRLOG("target abort failed\n");
+ }
+ }
+ }
+}
+
+static void
+iscsi_conn_free(struct spdk_iscsi_conn *conn)
+{
+ struct spdk_iscsi_sess *sess;
+ int idx;
+ uint32_t i;
+
+ pthread_mutex_lock(&g_conns_mutex);
+
+ if (conn->sess == NULL) {
+ goto end;
+ }
+
+ idx = -1;
+ sess = conn->sess;
+ conn->sess = NULL;
+
+ for (i = 0; i < sess->connections; i++) {
+ if (sess->conns[i] == conn) {
+ idx = i;
+ break;
+ }
+ }
+
+ if (idx < 0) {
+ SPDK_ERRLOG("remove conn not found\n");
+ } else {
+ for (i = idx; i < sess->connections - 1; i++) {
+ sess->conns[i] = sess->conns[i + 1];
+ }
+ sess->conns[sess->connections - 1] = NULL;
+ sess->connections--;
+
+ if (sess->connections == 0) {
+ /* cleanup last connection */
+ SPDK_DEBUGLOG(SPDK_LOG_ISCSI,
+ "cleanup last conn free sess\n");
+ iscsi_free_sess(sess);
+ }
+ }
+
+ SPDK_DEBUGLOG(SPDK_LOG_ISCSI, "Terminating connections(tsih %d): %d\n",
+ sess->tsih, sess->connections);
+
+end:
+ SPDK_DEBUGLOG(SPDK_LOG_ISCSI, "cleanup free conn\n");
+ iscsi_param_free(conn->params);
+ _free_conn(conn);
+
+ pthread_mutex_unlock(&g_conns_mutex);
+}
+
+static void
+iscsi_conn_close_lun(struct spdk_iscsi_conn *conn, int lun_id)
+{
+ struct spdk_iscsi_lun *iscsi_lun;
+
+ iscsi_lun = conn->luns[lun_id];
+ if (iscsi_lun == NULL) {
+ return;
+ }
+
+ spdk_scsi_lun_free_io_channel(iscsi_lun->desc);
+ spdk_scsi_lun_close(iscsi_lun->desc);
+ spdk_poller_unregister(&iscsi_lun->remove_poller);
+ free(iscsi_lun);
+
+ conn->luns[lun_id] = NULL;
+}
+
+static void
+iscsi_conn_close_luns(struct spdk_iscsi_conn *conn)
+{
+ int i;
+
+ for (i = 0; i < SPDK_SCSI_DEV_MAX_LUN; i++) {
+ iscsi_conn_close_lun(conn, i);
+ }
+}
+
+static bool
+iscsi_conn_check_tasks_for_lun(struct spdk_iscsi_conn *conn,
+ struct spdk_scsi_lun *lun)
+{
+ struct spdk_iscsi_pdu *pdu, *tmp_pdu;
+ struct spdk_iscsi_task *task;
+
+ assert(lun != NULL);
+
+ /* We can remove deferred PDUs safely because they are already flushed. */
+ TAILQ_FOREACH_SAFE(pdu, &conn->snack_pdu_list, tailq, tmp_pdu) {
+ if (lun == pdu->task->scsi.lun) {
+ TAILQ_REMOVE(&conn->snack_pdu_list, pdu, tailq);
+ iscsi_conn_free_pdu(conn, pdu);
+ }
+ }
+
+ TAILQ_FOREACH(task, &conn->queued_datain_tasks, link) {
+ if (lun == task->scsi.lun) {
+ return false;
+ }
+ }
+
+ /* This check loop works even when connection exits in the middle of LUN hotplug
+ * because all PDUs in write_pdu_list are removed in iscsi_conn_free_tasks().
+ */
+ TAILQ_FOREACH(pdu, &conn->write_pdu_list, tailq) {
+ if (pdu->task && lun == pdu->task->scsi.lun) {
+ return false;
+ }
+ }
+
+ return true;
+}
+
+static int
+iscsi_conn_remove_lun(void *ctx)
+{
+ struct spdk_iscsi_lun *iscsi_lun = ctx;
+ struct spdk_iscsi_conn *conn = iscsi_lun->conn;
+ struct spdk_scsi_lun *lun = iscsi_lun->lun;
+ int lun_id = spdk_scsi_lun_get_id(lun);
+
+ if (!iscsi_conn_check_tasks_for_lun(conn, lun)) {
+ return SPDK_POLLER_BUSY;
+ }
+ iscsi_conn_close_lun(conn, lun_id);
+ return SPDK_POLLER_BUSY;
+}
+
+static void
+_iscsi_conn_hotremove_lun(void *ctx)
+{
+ struct spdk_iscsi_lun *iscsi_lun = ctx;
+ struct spdk_iscsi_conn *conn = iscsi_lun->conn;
+ struct spdk_scsi_lun *lun = iscsi_lun->lun;
+
+ assert(spdk_io_channel_get_thread(spdk_io_channel_from_ctx(conn->pg)) ==
+ spdk_get_thread());
+
+ /* If a connection is already in stating status, just return */
+ if (conn->state >= ISCSI_CONN_STATE_EXITING) {
+ return;
+ }
+
+ iscsi_clear_all_transfer_task(conn, lun, NULL);
+
+ iscsi_lun->remove_poller = SPDK_POLLER_REGISTER(iscsi_conn_remove_lun, iscsi_lun,
+ 1000);
+}
+
+static void
+iscsi_conn_hotremove_lun(struct spdk_scsi_lun *lun, void *remove_ctx)
+{
+ struct spdk_iscsi_conn *conn = remove_ctx;
+ int lun_id = spdk_scsi_lun_get_id(lun);
+ struct spdk_iscsi_lun *iscsi_lun;
+
+ iscsi_lun = conn->luns[lun_id];
+ if (iscsi_lun == NULL) {
+ SPDK_ERRLOG("LUN hotplug was notified to the unallocated LUN %d.\n", lun_id);
+ return;
+ }
+
+ spdk_thread_send_msg(spdk_io_channel_get_thread(spdk_io_channel_from_ctx(conn->pg)),
+ _iscsi_conn_hotremove_lun, iscsi_lun);
+}
+
+static int
+iscsi_conn_open_lun(struct spdk_iscsi_conn *conn, int lun_id,
+ struct spdk_scsi_lun *lun)
+{
+ int rc;
+ struct spdk_iscsi_lun *iscsi_lun;
+
+ iscsi_lun = calloc(1, sizeof(*iscsi_lun));
+ if (iscsi_lun == NULL) {
+ return -ENOMEM;
+ }
+
+ iscsi_lun->conn = conn;
+ iscsi_lun->lun = lun;
+
+ rc = spdk_scsi_lun_open(lun, iscsi_conn_hotremove_lun, conn, &iscsi_lun->desc);
+ if (rc != 0) {
+ free(iscsi_lun);
+ return rc;
+ }
+
+ rc = spdk_scsi_lun_allocate_io_channel(iscsi_lun->desc);
+ if (rc != 0) {
+ spdk_scsi_lun_close(iscsi_lun->desc);
+ free(iscsi_lun);
+ return rc;
+ }
+
+ conn->luns[lun_id] = iscsi_lun;
+
+ return 0;
+}
+
+static void
+iscsi_conn_open_luns(struct spdk_iscsi_conn *conn)
+{
+ int i, rc;
+ struct spdk_scsi_lun *lun;
+
+ for (i = 0; i < SPDK_SCSI_DEV_MAX_LUN; i++) {
+ lun = spdk_scsi_dev_get_lun(conn->dev, i);
+ if (lun == NULL) {
+ continue;
+ }
+
+ rc = iscsi_conn_open_lun(conn, i, lun);
+ if (rc != 0) {
+ goto error;
+ }
+ }
+
+ return;
+
+error:
+ iscsi_conn_close_luns(conn);
+}
+
+/**
+ * This function will stop executing the specified connection.
+ */
+static void
+iscsi_conn_stop(struct spdk_iscsi_conn *conn)
+{
+ struct spdk_iscsi_tgt_node *target;
+
+ assert(conn->state == ISCSI_CONN_STATE_EXITED);
+ assert(conn->data_in_cnt == 0);
+ assert(conn->data_out_cnt == 0);
+
+ if (conn->sess != NULL &&
+ conn->sess->session_type == SESSION_TYPE_NORMAL &&
+ conn->full_feature) {
+ target = conn->sess->target;
+ pthread_mutex_lock(&target->mutex);
+ target->num_active_conns--;
+ pthread_mutex_unlock(&target->mutex);
+
+ iscsi_conn_close_luns(conn);
+ }
+
+ assert(spdk_io_channel_get_thread(spdk_io_channel_from_ctx(conn->pg)) ==
+ spdk_get_thread());
+}
+
+static int
+_iscsi_conn_check_shutdown(void *arg)
+{
+ struct spdk_iscsi_conn *conn = arg;
+ int rc;
+
+ rc = iscsi_conn_free_tasks(conn);
+ if (rc < 0) {
+ return SPDK_POLLER_BUSY;
+ }
+
+ spdk_poller_unregister(&conn->shutdown_timer);
+
+ iscsi_conn_stop(conn);
+ iscsi_conn_free(conn);
+
+ return SPDK_POLLER_BUSY;
+}
+
+static void
+_iscsi_conn_destruct(struct spdk_iscsi_conn *conn)
+{
+ int rc;
+
+ iscsi_poll_group_remove_conn(conn->pg, conn);
+ spdk_sock_close(&conn->sock);
+ iscsi_clear_all_transfer_task(conn, NULL, NULL);
+ spdk_poller_unregister(&conn->logout_request_timer);
+ spdk_poller_unregister(&conn->logout_timer);
+
+ rc = iscsi_conn_free_tasks(conn);
+ if (rc < 0) {
+ /* The connection cannot be freed yet. Check back later. */
+ conn->shutdown_timer = SPDK_POLLER_REGISTER(_iscsi_conn_check_shutdown, conn, 1000);
+ } else {
+ iscsi_conn_stop(conn);
+ iscsi_conn_free(conn);
+ }
+}
+
+static int
+_iscsi_conn_check_pending_tasks(void *arg)
+{
+ struct spdk_iscsi_conn *conn = arg;
+
+ if (conn->dev != NULL &&
+ spdk_scsi_dev_has_pending_tasks(conn->dev, conn->initiator_port)) {
+ return SPDK_POLLER_BUSY;
+ }
+
+ spdk_poller_unregister(&conn->shutdown_timer);
+
+ _iscsi_conn_destruct(conn);
+
+ return SPDK_POLLER_BUSY;
+}
+
+void
+iscsi_conn_destruct(struct spdk_iscsi_conn *conn)
+{
+ struct spdk_iscsi_pdu *pdu;
+ struct spdk_iscsi_task *task;
+ int opcode;
+
+ /* If a connection is already in exited status, just return */
+ if (conn->state >= ISCSI_CONN_STATE_EXITED) {
+ return;
+ }
+
+ conn->state = ISCSI_CONN_STATE_EXITED;
+
+ /*
+ * Each connection pre-allocates its next PDU - make sure these get
+ * freed here.
+ */
+ pdu = conn->pdu_in_progress;
+ if (pdu) {
+ /* remove the task left in the PDU too. */
+ task = pdu->task;
+ if (task) {
+ opcode = pdu->bhs.opcode;
+ switch (opcode) {
+ case ISCSI_OP_SCSI:
+ case ISCSI_OP_SCSI_DATAOUT:
+ spdk_scsi_task_process_abort(&task->scsi);
+ iscsi_task_cpl(&task->scsi);
+ break;
+ default:
+ SPDK_ERRLOG("unexpected opcode %x\n", opcode);
+ iscsi_task_put(task);
+ break;
+ }
+ }
+ iscsi_put_pdu(pdu);
+ conn->pdu_in_progress = NULL;
+ }
+
+ if (conn->sess != NULL && conn->pending_task_cnt > 0) {
+ iscsi_conn_cleanup_backend(conn);
+ }
+
+ if (conn->dev != NULL &&
+ spdk_scsi_dev_has_pending_tasks(conn->dev, conn->initiator_port)) {
+ conn->shutdown_timer = SPDK_POLLER_REGISTER(_iscsi_conn_check_pending_tasks, conn, 1000);
+ } else {
+ _iscsi_conn_destruct(conn);
+ }
+}
+
+int
+iscsi_get_active_conns(struct spdk_iscsi_tgt_node *target)
+{
+ struct spdk_iscsi_conn *conn;
+ int num = 0;
+
+ if (g_conns_array == MAP_FAILED) {
+ return 0;
+ }
+
+ pthread_mutex_lock(&g_conns_mutex);
+ TAILQ_FOREACH(conn, &g_active_conns, conn_link) {
+ if (target == NULL || conn->target == target) {
+ num++;
+ }
+ }
+ pthread_mutex_unlock(&g_conns_mutex);
+ return num;
+}
+
+static void
+iscsi_conn_check_shutdown_cb(void *arg1)
+{
+ _iscsi_conns_cleanup();
+ shutdown_iscsi_conns_done();
+}
+
+static int
+iscsi_conn_check_shutdown(void *arg)
+{
+ if (iscsi_get_active_conns(NULL) != 0) {
+ return SPDK_POLLER_BUSY;
+ }
+
+ spdk_poller_unregister(&g_shutdown_timer);
+
+ spdk_thread_send_msg(spdk_get_thread(), iscsi_conn_check_shutdown_cb, NULL);
+
+ return SPDK_POLLER_BUSY;
+}
+
+static void
+iscsi_send_logout_request(struct spdk_iscsi_conn *conn)
+{
+ struct spdk_iscsi_pdu *rsp_pdu;
+ struct iscsi_bhs_async *rsph;
+
+ rsp_pdu = iscsi_get_pdu(conn);
+ assert(rsp_pdu != NULL);
+
+ rsph = (struct iscsi_bhs_async *)&rsp_pdu->bhs;
+ rsp_pdu->data = NULL;
+
+ rsph->opcode = ISCSI_OP_ASYNC;
+ to_be32(&rsph->ffffffff, 0xFFFFFFFF);
+ rsph->async_event = 1;
+ to_be16(&rsph->param3, ISCSI_LOGOUT_REQUEST_TIMEOUT);
+
+ to_be32(&rsph->stat_sn, conn->StatSN);
+ to_be32(&rsph->exp_cmd_sn, conn->sess->ExpCmdSN);
+ to_be32(&rsph->max_cmd_sn, conn->sess->MaxCmdSN);
+
+ iscsi_conn_write_pdu(conn, rsp_pdu, iscsi_conn_pdu_generic_complete, NULL);
+}
+
+static int
+logout_request_timeout(void *arg)
+{
+ struct spdk_iscsi_conn *conn = arg;
+
+ if (conn->state < ISCSI_CONN_STATE_EXITING) {
+ conn->state = ISCSI_CONN_STATE_EXITING;
+ }
+
+ return SPDK_POLLER_BUSY;
+}
+
+/* If the connection is running and logout is not requested yet, request logout
+ * to initiator and wait for the logout process to start.
+ */
+static void
+_iscsi_conn_request_logout(void *ctx)
+{
+ struct spdk_iscsi_conn *conn = ctx;
+
+ if (conn->state > ISCSI_CONN_STATE_RUNNING ||
+ conn->logout_request_timer != NULL) {
+ return;
+ }
+
+ iscsi_send_logout_request(conn);
+
+ conn->logout_request_timer = SPDK_POLLER_REGISTER(logout_request_timeout,
+ conn, ISCSI_LOGOUT_REQUEST_TIMEOUT * 1000000);
+}
+
+static void
+iscsi_conn_request_logout(struct spdk_iscsi_conn *conn)
+{
+ struct spdk_thread *thread;
+
+ if (conn->state == ISCSI_CONN_STATE_INVALID) {
+ /* Move it to EXITING state if the connection is in login. */
+ conn->state = ISCSI_CONN_STATE_EXITING;
+ } else if (conn->state == ISCSI_CONN_STATE_RUNNING &&
+ conn->logout_request_timer == NULL) {
+ thread = spdk_io_channel_get_thread(spdk_io_channel_from_ctx(conn->pg));
+ spdk_thread_send_msg(thread, _iscsi_conn_request_logout, conn);
+ }
+}
+
+void
+iscsi_conns_request_logout(struct spdk_iscsi_tgt_node *target)
+{
+ struct spdk_iscsi_conn *conn;
+
+ if (g_conns_array == MAP_FAILED) {
+ return;
+ }
+
+ pthread_mutex_lock(&g_conns_mutex);
+ TAILQ_FOREACH(conn, &g_active_conns, conn_link) {
+ if (target == NULL || conn->target == target) {
+ iscsi_conn_request_logout(conn);
+ }
+ }
+ pthread_mutex_unlock(&g_conns_mutex);
+}
+
+void
+shutdown_iscsi_conns(void)
+{
+ iscsi_conns_request_logout(NULL);
+
+ g_shutdown_timer = SPDK_POLLER_REGISTER(iscsi_conn_check_shutdown, NULL, 1000);
+}
+
+/* Do not set conn->state if the connection has already started exiting.
+ * This ensures we do not move a connection from EXITED state back to EXITING.
+ */
+static void
+_iscsi_conn_drop(void *ctx)
+{
+ struct spdk_iscsi_conn *conn = ctx;
+
+ if (conn->state < ISCSI_CONN_STATE_EXITING) {
+ conn->state = ISCSI_CONN_STATE_EXITING;
+ }
+}
+
+int
+iscsi_drop_conns(struct spdk_iscsi_conn *conn, const char *conn_match,
+ int drop_all)
+{
+ struct spdk_iscsi_conn *xconn;
+ const char *xconn_match;
+ struct spdk_thread *thread;
+ int num;
+
+ SPDK_DEBUGLOG(SPDK_LOG_ISCSI, "iscsi_drop_conns\n");
+
+ num = 0;
+ pthread_mutex_lock(&g_conns_mutex);
+ if (g_conns_array == MAP_FAILED) {
+ goto exit;
+ }
+
+ TAILQ_FOREACH(xconn, &g_active_conns, conn_link) {
+ if (xconn == conn) {
+ continue;
+ }
+
+ if (!drop_all && xconn->initiator_port == NULL) {
+ continue;
+ }
+
+ xconn_match =
+ drop_all ? xconn->initiator_name : spdk_scsi_port_get_name(xconn->initiator_port);
+
+ if (!strcasecmp(conn_match, xconn_match) &&
+ conn->target == xconn->target) {
+
+ if (num == 0) {
+ /*
+ * Only print this message before we report the
+ * first dropped connection.
+ */
+ SPDK_ERRLOG("drop old connections %s by %s\n",
+ conn->target->name, conn_match);
+ }
+
+ SPDK_ERRLOG("exiting conn by %s (%s)\n",
+ xconn_match, xconn->initiator_addr);
+ if (xconn->sess != NULL) {
+ SPDK_DEBUGLOG(SPDK_LOG_ISCSI, "TSIH=%u\n", xconn->sess->tsih);
+ } else {
+ SPDK_DEBUGLOG(SPDK_LOG_ISCSI, "TSIH=xx\n");
+ }
+
+ SPDK_DEBUGLOG(SPDK_LOG_ISCSI, "CID=%u\n", xconn->cid);
+
+ thread = spdk_io_channel_get_thread(spdk_io_channel_from_ctx(xconn->pg));
+ spdk_thread_send_msg(thread, _iscsi_conn_drop, xconn);
+
+ num++;
+ }
+ }
+
+exit:
+ pthread_mutex_unlock(&g_conns_mutex);
+
+ if (num != 0) {
+ SPDK_ERRLOG("exiting %d conns\n", num);
+ }
+
+ return 0;
+}
+
+static int
+_iscsi_conn_abort_queued_datain_task(struct spdk_iscsi_conn *conn,
+ struct spdk_iscsi_task *task)
+{
+ struct spdk_iscsi_task *subtask;
+ uint32_t remaining_size;
+
+ if (conn->data_in_cnt >= MAX_LARGE_DATAIN_PER_CONNECTION) {
+ return -1;
+ }
+
+ assert(task->current_datain_offset <= task->scsi.transfer_len);
+ /* Stop split and abort read I/O for remaining data. */
+ if (task->current_datain_offset < task->scsi.transfer_len) {
+ remaining_size = task->scsi.transfer_len - task->current_datain_offset;
+ subtask = iscsi_task_get(conn, task, iscsi_task_cpl);
+ assert(subtask != NULL);
+ subtask->scsi.offset = task->current_datain_offset;
+ subtask->scsi.length = remaining_size;
+ spdk_scsi_task_set_data(&subtask->scsi, NULL, 0);
+ task->current_datain_offset += subtask->scsi.length;
+
+ subtask->scsi.transfer_len = subtask->scsi.length;
+ spdk_scsi_task_process_abort(&subtask->scsi);
+ iscsi_task_cpl(&subtask->scsi);
+ }
+
+ /* Remove the primary task from the list because all subtasks are submitted
+ * or aborted.
+ */
+ assert(task->current_datain_offset == task->scsi.transfer_len);
+ TAILQ_REMOVE(&conn->queued_datain_tasks, task, link);
+ return 0;
+}
+
+int
+iscsi_conn_abort_queued_datain_task(struct spdk_iscsi_conn *conn,
+ uint32_t ref_task_tag)
+{
+ struct spdk_iscsi_task *task;
+
+ TAILQ_FOREACH(task, &conn->queued_datain_tasks, link) {
+ if (task->tag == ref_task_tag) {
+ return _iscsi_conn_abort_queued_datain_task(conn, task);
+ }
+ }
+
+ return 0;
+}
+
+int
+iscsi_conn_abort_queued_datain_tasks(struct spdk_iscsi_conn *conn,
+ struct spdk_scsi_lun *lun,
+ struct spdk_iscsi_pdu *pdu)
+{
+ struct spdk_iscsi_task *task, *task_tmp;
+ struct spdk_iscsi_pdu *pdu_tmp;
+ int rc;
+
+ TAILQ_FOREACH_SAFE(task, &conn->queued_datain_tasks, link, task_tmp) {
+ pdu_tmp = iscsi_task_get_pdu(task);
+ if ((lun == NULL || lun == task->scsi.lun) &&
+ (pdu == NULL || (spdk_sn32_lt(pdu_tmp->cmd_sn, pdu->cmd_sn)))) {
+ rc = _iscsi_conn_abort_queued_datain_task(conn, task);
+ if (rc != 0) {
+ return rc;
+ }
+ }
+ }
+
+ return 0;
+}
+
+int
+iscsi_conn_handle_queued_datain_tasks(struct spdk_iscsi_conn *conn)
+{
+ struct spdk_iscsi_task *task;
+
+ while (!TAILQ_EMPTY(&conn->queued_datain_tasks) &&
+ conn->data_in_cnt < MAX_LARGE_DATAIN_PER_CONNECTION) {
+ task = TAILQ_FIRST(&conn->queued_datain_tasks);
+ assert(task->current_datain_offset <= task->scsi.transfer_len);
+ if (task->current_datain_offset < task->scsi.transfer_len) {
+ struct spdk_iscsi_task *subtask;
+ uint32_t remaining_size = 0;
+
+ remaining_size = task->scsi.transfer_len - task->current_datain_offset;
+ subtask = iscsi_task_get(conn, task, iscsi_task_cpl);
+ assert(subtask != NULL);
+ subtask->scsi.offset = task->current_datain_offset;
+ spdk_scsi_task_set_data(&subtask->scsi, NULL, 0);
+
+ if (spdk_scsi_dev_get_lun(conn->dev, task->lun_id) == NULL) {
+ /* Stop submitting split read I/Os for remaining data. */
+ TAILQ_REMOVE(&conn->queued_datain_tasks, task, link);
+ task->current_datain_offset += remaining_size;
+ assert(task->current_datain_offset == task->scsi.transfer_len);
+ subtask->scsi.transfer_len = remaining_size;
+ spdk_scsi_task_process_null_lun(&subtask->scsi);
+ iscsi_task_cpl(&subtask->scsi);
+ return 0;
+ }
+
+ subtask->scsi.length = spdk_min(SPDK_BDEV_LARGE_BUF_MAX_SIZE, remaining_size);
+ task->current_datain_offset += subtask->scsi.length;
+ iscsi_queue_task(conn, subtask);
+ }
+ if (task->current_datain_offset == task->scsi.transfer_len) {
+ TAILQ_REMOVE(&conn->queued_datain_tasks, task, link);
+ }
+ }
+ return 0;
+}
+
+void
+iscsi_task_mgmt_cpl(struct spdk_scsi_task *scsi_task)
+{
+ struct spdk_iscsi_task *task = iscsi_task_from_scsi_task(scsi_task);
+
+ iscsi_task_mgmt_response(task->conn, task);
+ iscsi_task_put(task);
+}
+
+static void
+iscsi_task_copy_to_rsp_scsi_status(struct spdk_iscsi_task *primary,
+ struct spdk_scsi_task *task)
+{
+ memcpy(primary->rsp_sense_data, task->sense_data, task->sense_data_len);
+ primary->rsp_sense_data_len = task->sense_data_len;
+ primary->rsp_scsi_status = task->status;
+}
+
+static void
+iscsi_task_copy_from_rsp_scsi_status(struct spdk_scsi_task *task,
+ struct spdk_iscsi_task *primary)
+{
+ memcpy(task->sense_data, primary->rsp_sense_data,
+ primary->rsp_sense_data_len);
+ task->sense_data_len = primary->rsp_sense_data_len;
+ task->status = primary->rsp_scsi_status;
+}
+
+static void
+process_completed_read_subtask_list(struct spdk_iscsi_conn *conn,
+ struct spdk_iscsi_task *primary)
+{
+ struct spdk_iscsi_task *subtask, *tmp;
+
+ TAILQ_FOREACH_SAFE(subtask, &primary->subtask_list, subtask_link, tmp) {
+ if (subtask->scsi.offset == primary->bytes_completed) {
+ TAILQ_REMOVE(&primary->subtask_list, subtask, subtask_link);
+ primary->bytes_completed += subtask->scsi.length;
+ iscsi_task_response(conn, subtask);
+ iscsi_task_put(subtask);
+ } else {
+ break;
+ }
+ }
+
+ if (primary->bytes_completed == primary->scsi.transfer_len) {
+ iscsi_task_put(primary);
+ }
+}
+
+static void
+process_read_task_completion(struct spdk_iscsi_conn *conn,
+ struct spdk_iscsi_task *task,
+ struct spdk_iscsi_task *primary)
+{
+ struct spdk_iscsi_task *tmp;
+
+ /* If the status of the completed subtask is the first failure,
+ * copy it to out-of-order subtasks and remember it as the status
+ * of the command,
+ *
+ * Even if the status of the completed task is success,
+ * there are any failed subtask ever, copy the first failed status
+ * to it.
+ */
+ if (task->scsi.status != SPDK_SCSI_STATUS_GOOD) {
+ if (primary->rsp_scsi_status == SPDK_SCSI_STATUS_GOOD) {
+ TAILQ_FOREACH(tmp, &primary->subtask_list, subtask_link) {
+ spdk_scsi_task_copy_status(&tmp->scsi, &task->scsi);
+ }
+ iscsi_task_copy_to_rsp_scsi_status(primary, &task->scsi);
+ }
+ } else if (primary->rsp_scsi_status != SPDK_SCSI_STATUS_GOOD) {
+ iscsi_task_copy_from_rsp_scsi_status(&task->scsi, primary);
+ }
+
+ if (task == primary) {
+ primary->bytes_completed = task->scsi.length;
+ /* For non split read I/O */
+ assert(primary->bytes_completed == task->scsi.transfer_len);
+ iscsi_task_response(conn, task);
+ iscsi_task_put(task);
+ } else {
+ if (task->scsi.offset != primary->bytes_completed) {
+ TAILQ_FOREACH(tmp, &primary->subtask_list, subtask_link) {
+ if (task->scsi.offset < tmp->scsi.offset) {
+ TAILQ_INSERT_BEFORE(tmp, task, subtask_link);
+ return;
+ }
+ }
+
+ TAILQ_INSERT_TAIL(&primary->subtask_list, task, subtask_link);
+ } else {
+ TAILQ_INSERT_HEAD(&primary->subtask_list, task, subtask_link);
+ process_completed_read_subtask_list(conn, primary);
+ }
+ }
+}
+
+static void
+process_non_read_task_completion(struct spdk_iscsi_conn *conn,
+ struct spdk_iscsi_task *task,
+ struct spdk_iscsi_task *primary)
+{
+ primary->bytes_completed += task->scsi.length;
+
+ /* If the status of the subtask is the first failure, remember it as
+ * the status of the command and set it to the status of the primary
+ * task later.
+ *
+ * If the first failed task is the primary, two copies can be avoided
+ * but code simplicity is prioritized.
+ */
+ if (task->scsi.status == SPDK_SCSI_STATUS_GOOD) {
+ if (task != primary) {
+ primary->scsi.data_transferred += task->scsi.data_transferred;
+ }
+ } else if (primary->rsp_scsi_status == SPDK_SCSI_STATUS_GOOD) {
+ iscsi_task_copy_to_rsp_scsi_status(primary, &task->scsi);
+ }
+
+ if (primary->bytes_completed == primary->scsi.transfer_len) {
+ /*
+ * Check if this is the last task completed for an iSCSI write
+ * that required child subtasks. If task != primary, we know
+ * for sure that it was part of an iSCSI write with child subtasks.
+ * The trickier case is when the last task completed was the initial
+ * task - in this case the task will have a smaller length than
+ * the overall transfer length.
+ */
+ if (task != primary || task->scsi.length != task->scsi.transfer_len) {
+ /* If LUN is removed in the middle of the iSCSI write sequence,
+ * primary might complete the write to the initiator because it is not
+ * ensured that the initiator will send all data requested by R2Ts.
+ *
+ * We check it and skip the following if primary is completed. (see
+ * iscsi_clear_all_transfer_task() in iscsi.c.)
+ */
+ if (primary->is_r2t_active) {
+ if (primary->rsp_scsi_status != SPDK_SCSI_STATUS_GOOD) {
+ iscsi_task_copy_from_rsp_scsi_status(&primary->scsi, primary);
+ }
+ iscsi_task_response(conn, primary);
+ iscsi_del_transfer_task(conn, primary->tag);
+ }
+ } else {
+ iscsi_task_response(conn, task);
+ }
+ }
+ iscsi_task_put(task);
+}
+
+void
+iscsi_task_cpl(struct spdk_scsi_task *scsi_task)
+{
+ struct spdk_iscsi_task *primary;
+ struct spdk_iscsi_task *task = iscsi_task_from_scsi_task(scsi_task);
+ struct spdk_iscsi_conn *conn = task->conn;
+ struct spdk_iscsi_pdu *pdu = task->pdu;
+
+ spdk_trace_record(TRACE_ISCSI_TASK_DONE, conn->id, 0, (uintptr_t)task, 0);
+
+ task->is_queued = false;
+ primary = iscsi_task_get_primary(task);
+
+ if (iscsi_task_is_read(primary)) {
+ process_read_task_completion(conn, task, primary);
+ } else {
+ process_non_read_task_completion(conn, task, primary);
+ }
+ if (!task->parent) {
+ spdk_trace_record(TRACE_ISCSI_PDU_COMPLETED, 0, 0, (uintptr_t)pdu, 0);
+ }
+}
+
+static void
+iscsi_conn_send_nopin(struct spdk_iscsi_conn *conn)
+{
+ struct spdk_iscsi_pdu *rsp_pdu;
+ struct iscsi_bhs_nop_in *rsp;
+ /* Only send nopin if we have logged in and are in a normal session. */
+ if (conn->sess == NULL ||
+ !conn->full_feature ||
+ !iscsi_param_eq_val(conn->sess->params, "SessionType", "Normal")) {
+ return;
+ }
+ SPDK_DEBUGLOG(SPDK_LOG_ISCSI, "send NOPIN isid=%"PRIx64", tsih=%u, cid=%u\n",
+ conn->sess->isid, conn->sess->tsih, conn->cid);
+ SPDK_DEBUGLOG(SPDK_LOG_ISCSI, "StatSN=%u, ExpCmdSN=%u, MaxCmdSN=%u\n",
+ conn->StatSN, conn->sess->ExpCmdSN,
+ conn->sess->MaxCmdSN);
+ rsp_pdu = iscsi_get_pdu(conn);
+ rsp = (struct iscsi_bhs_nop_in *) &rsp_pdu->bhs;
+ rsp_pdu->data = NULL;
+ /*
+ * iscsi_get_pdu() memset's the PDU for us, so only fill out the needed
+ * fields.
+ */
+ rsp->opcode = ISCSI_OP_NOPIN;
+ rsp->flags = 0x80;
+ /*
+ * Technically the to_be32() is not needed here, since
+ * to_be32(0xFFFFFFFU) returns 0xFFFFFFFFU.
+ */
+ to_be32(&rsp->itt, 0xFFFFFFFFU);
+ to_be32(&rsp->ttt, conn->id);
+ to_be32(&rsp->stat_sn, conn->StatSN);
+ to_be32(&rsp->exp_cmd_sn, conn->sess->ExpCmdSN);
+ to_be32(&rsp->max_cmd_sn, conn->sess->MaxCmdSN);
+ iscsi_conn_write_pdu(conn, rsp_pdu, iscsi_conn_pdu_generic_complete, NULL);
+ conn->last_nopin = spdk_get_ticks();
+ conn->nop_outstanding = true;
+}
+
+void
+iscsi_conn_handle_nop(struct spdk_iscsi_conn *conn)
+{
+ uint64_t tsc;
+
+ /**
+ * This function will be executed by nop_poller of iSCSI polling group, so
+ * we need to check the connection state first, then do the nop interval
+ * expiration check work.
+ */
+ if ((conn->state == ISCSI_CONN_STATE_EXITED) ||
+ (conn->state == ISCSI_CONN_STATE_EXITING)) {
+ return;
+ }
+
+ /* Check for nop interval expiration */
+ tsc = spdk_get_ticks();
+ if (conn->nop_outstanding) {
+ if ((tsc - conn->last_nopin) > conn->timeout) {
+ SPDK_ERRLOG("Timed out waiting for NOP-Out response from initiator\n");
+ SPDK_ERRLOG(" tsc=0x%lx, last_nopin=0x%lx\n", tsc, conn->last_nopin);
+ SPDK_ERRLOG(" initiator=%s, target=%s\n", conn->initiator_name,
+ conn->target_short_name);
+ conn->state = ISCSI_CONN_STATE_EXITING;
+ }
+ } else if (tsc - conn->last_nopin > conn->nopininterval) {
+ iscsi_conn_send_nopin(conn);
+ }
+}
+
+/**
+ * \brief Reads data for the specified iSCSI connection from its TCP socket.
+ *
+ * The TCP socket is marked as non-blocking, so this function may not read
+ * all data requested.
+ *
+ * Returns SPDK_ISCSI_CONNECTION_FATAL if the recv() operation indicates a fatal
+ * error with the TCP connection (including if the TCP connection was closed
+ * unexpectedly.
+ *
+ * Otherwise returns the number of bytes successfully read.
+ */
+int
+iscsi_conn_read_data(struct spdk_iscsi_conn *conn, int bytes,
+ void *buf)
+{
+ int ret;
+
+ if (bytes == 0) {
+ return 0;
+ }
+
+ ret = spdk_sock_recv(conn->sock, buf, bytes);
+
+ if (ret > 0) {
+ spdk_trace_record(TRACE_ISCSI_READ_FROM_SOCKET_DONE, conn->id, ret, 0, 0);
+ return ret;
+ }
+
+ if (ret < 0) {
+ if (errno == EAGAIN || errno == EWOULDBLOCK) {
+ return 0;
+ }
+
+ /* For connect reset issue, do not output error log */
+ if (errno == ECONNRESET) {
+ SPDK_DEBUGLOG(SPDK_LOG_ISCSI, "spdk_sock_recv() failed, errno %d: %s\n",
+ errno, spdk_strerror(errno));
+ } else {
+ SPDK_ERRLOG("spdk_sock_recv() failed, errno %d: %s\n",
+ errno, spdk_strerror(errno));
+ }
+ }
+
+ /* connection closed */
+ return SPDK_ISCSI_CONNECTION_FATAL;
+}
+
+int
+iscsi_conn_readv_data(struct spdk_iscsi_conn *conn,
+ struct iovec *iov, int iovcnt)
+{
+ int ret;
+
+ if (iov == NULL || iovcnt == 0) {
+ return 0;
+ }
+
+ if (iovcnt == 1) {
+ return iscsi_conn_read_data(conn, iov[0].iov_len,
+ iov[0].iov_base);
+ }
+
+ ret = spdk_sock_readv(conn->sock, iov, iovcnt);
+
+ if (ret > 0) {
+ spdk_trace_record(TRACE_ISCSI_READ_FROM_SOCKET_DONE, conn->id, ret, 0, 0);
+ return ret;
+ }
+
+ if (ret < 0) {
+ if (errno == EAGAIN || errno == EWOULDBLOCK) {
+ return 0;
+ }
+
+ /* For connect reset issue, do not output error log */
+ if (errno == ECONNRESET) {
+ SPDK_DEBUGLOG(SPDK_LOG_ISCSI, "spdk_sock_readv() failed, errno %d: %s\n",
+ errno, spdk_strerror(errno));
+ } else {
+ SPDK_ERRLOG("spdk_sock_readv() failed, errno %d: %s\n",
+ errno, spdk_strerror(errno));
+ }
+ }
+
+ /* connection closed */
+ return SPDK_ISCSI_CONNECTION_FATAL;
+}
+
+static bool
+iscsi_is_free_pdu_deferred(struct spdk_iscsi_pdu *pdu)
+{
+ if (pdu == NULL) {
+ return false;
+ }
+
+ if (pdu->bhs.opcode == ISCSI_OP_R2T ||
+ pdu->bhs.opcode == ISCSI_OP_SCSI_DATAIN) {
+ return true;
+ }
+
+ return false;
+}
+
+static int
+iscsi_dif_verify(struct spdk_iscsi_pdu *pdu, struct spdk_dif_ctx *dif_ctx)
+{
+ struct iovec iov;
+ struct spdk_dif_error err_blk = {};
+ uint32_t num_blocks;
+ int rc;
+
+ iov.iov_base = pdu->data;
+ iov.iov_len = pdu->data_buf_len;
+ num_blocks = pdu->data_buf_len / dif_ctx->block_size;
+
+ rc = spdk_dif_verify(&iov, 1, num_blocks, dif_ctx, &err_blk);
+ if (rc != 0) {
+ SPDK_ERRLOG("DIF error detected. type=%d, offset=%" PRIu32 "\n",
+ err_blk.err_type, err_blk.err_offset);
+ }
+
+ return rc;
+}
+
+static void
+_iscsi_conn_pdu_write_done(void *cb_arg, int err)
+{
+ struct spdk_iscsi_pdu *pdu = cb_arg;
+ struct spdk_iscsi_conn *conn = pdu->conn;
+
+ assert(conn != NULL);
+
+ if (spdk_unlikely(conn->state >= ISCSI_CONN_STATE_EXITING)) {
+ /* The other policy will recycle the resource */
+ return;
+ }
+
+ TAILQ_REMOVE(&conn->write_pdu_list, pdu, tailq);
+
+ if (err != 0) {
+ conn->state = ISCSI_CONN_STATE_EXITING;
+ } else {
+ spdk_trace_record(TRACE_ISCSI_FLUSH_WRITEBUF_DONE, conn->id, pdu->mapped_length, (uintptr_t)pdu, 0);
+ }
+
+ if ((conn->full_feature) &&
+ (conn->sess->ErrorRecoveryLevel >= 1) &&
+ iscsi_is_free_pdu_deferred(pdu)) {
+ SPDK_DEBUGLOG(SPDK_LOG_ISCSI, "stat_sn=%d\n",
+ from_be32(&pdu->bhs.stat_sn));
+ TAILQ_INSERT_TAIL(&conn->snack_pdu_list, pdu,
+ tailq);
+ } else {
+ iscsi_conn_free_pdu(conn, pdu);
+ }
+}
+
+void
+iscsi_conn_pdu_generic_complete(void *cb_arg)
+{
+}
+
+void
+iscsi_conn_write_pdu(struct spdk_iscsi_conn *conn, struct spdk_iscsi_pdu *pdu,
+ iscsi_conn_xfer_complete_cb cb_fn,
+ void *cb_arg)
+{
+ uint32_t crc32c;
+ ssize_t rc;
+
+ if (spdk_unlikely(pdu->dif_insert_or_strip)) {
+ rc = iscsi_dif_verify(pdu, &pdu->dif_ctx);
+ if (rc != 0) {
+ iscsi_conn_free_pdu(conn, pdu);
+ conn->state = ISCSI_CONN_STATE_EXITING;
+ return;
+ }
+ }
+
+ if (pdu->bhs.opcode != ISCSI_OP_LOGIN_RSP) {
+ /* Header Digest */
+ if (conn->header_digest) {
+ crc32c = iscsi_pdu_calc_header_digest(pdu);
+ MAKE_DIGEST_WORD(pdu->header_digest, crc32c);
+ }
+
+ /* Data Digest */
+ if (conn->data_digest && DGET24(pdu->bhs.data_segment_len) != 0) {
+ crc32c = iscsi_pdu_calc_data_digest(pdu);
+ MAKE_DIGEST_WORD(pdu->data_digest, crc32c);
+ }
+ }
+
+ pdu->cb_fn = cb_fn;
+ pdu->cb_arg = cb_arg;
+ TAILQ_INSERT_TAIL(&conn->write_pdu_list, pdu, tailq);
+
+ if (spdk_unlikely(conn->state >= ISCSI_CONN_STATE_EXITING)) {
+ return;
+ }
+ pdu->sock_req.iovcnt = iscsi_build_iovs(conn, pdu->iov, SPDK_COUNTOF(pdu->iov), pdu,
+ &pdu->mapped_length);
+ pdu->sock_req.cb_fn = _iscsi_conn_pdu_write_done;
+ pdu->sock_req.cb_arg = pdu;
+
+ spdk_trace_record(TRACE_ISCSI_FLUSH_WRITEBUF_START, conn->id, pdu->mapped_length, (uintptr_t)pdu,
+ pdu->sock_req.iovcnt);
+ spdk_sock_writev_async(conn->sock, &pdu->sock_req);
+}
+
+static void
+iscsi_conn_sock_cb(void *arg, struct spdk_sock_group *group, struct spdk_sock *sock)
+{
+ struct spdk_iscsi_conn *conn = arg;
+ int rc;
+
+ assert(conn != NULL);
+
+ if ((conn->state == ISCSI_CONN_STATE_EXITED) ||
+ (conn->state == ISCSI_CONN_STATE_EXITING)) {
+ return;
+ }
+
+ /* Handle incoming PDUs */
+ rc = iscsi_handle_incoming_pdus(conn);
+ if (rc < 0) {
+ conn->state = ISCSI_CONN_STATE_EXITING;
+ }
+}
+
+static void
+iscsi_conn_full_feature_migrate(void *arg)
+{
+ struct spdk_iscsi_conn *conn = arg;
+
+ if (conn->state >= ISCSI_CONN_STATE_EXITING) {
+ /* Connection is being exited before this callback is executed. */
+ SPDK_DEBUGLOG(SPDK_LOG_ISCSI, "Connection is already exited.\n");
+ return;
+ }
+
+ if (conn->sess->session_type == SESSION_TYPE_NORMAL) {
+ iscsi_conn_open_luns(conn);
+ }
+
+ /* Add this connection to the assigned poll group. */
+ iscsi_poll_group_add_conn(conn->pg, conn);
+}
+
+static struct spdk_iscsi_poll_group *g_next_pg = NULL;
+
+void
+iscsi_conn_schedule(struct spdk_iscsi_conn *conn)
+{
+ struct spdk_iscsi_poll_group *pg;
+ struct spdk_iscsi_tgt_node *target;
+
+ if (conn->sess->session_type != SESSION_TYPE_NORMAL) {
+ /* Leave all non-normal sessions on the acceptor
+ * thread. */
+ return;
+ }
+ pthread_mutex_lock(&g_iscsi.mutex);
+
+ target = conn->sess->target;
+ pthread_mutex_lock(&target->mutex);
+ target->num_active_conns++;
+ if (target->num_active_conns == 1) {
+ /**
+ * This is the only active connection for this target node.
+ * Pick a poll group using round-robin.
+ */
+ if (g_next_pg == NULL) {
+ g_next_pg = TAILQ_FIRST(&g_iscsi.poll_group_head);
+ assert(g_next_pg != NULL);
+ }
+
+ pg = g_next_pg;
+ g_next_pg = TAILQ_NEXT(g_next_pg, link);
+
+ /* Save the pg in the target node so it can be used for any other connections to this target node. */
+ target->pg = pg;
+ } else {
+ /**
+ * There are other active connections for this target node.
+ */
+ pg = target->pg;
+ }
+
+ pthread_mutex_unlock(&target->mutex);
+ pthread_mutex_unlock(&g_iscsi.mutex);
+
+ assert(spdk_io_channel_get_thread(spdk_io_channel_from_ctx(conn->pg)) ==
+ spdk_get_thread());
+
+ /* Remove this connection from the previous poll group */
+ iscsi_poll_group_remove_conn(conn->pg, conn);
+
+ conn->last_nopin = spdk_get_ticks();
+ conn->pg = pg;
+
+ spdk_thread_send_msg(spdk_io_channel_get_thread(spdk_io_channel_from_ctx(pg)),
+ iscsi_conn_full_feature_migrate, conn);
+}
+
+static int
+logout_timeout(void *arg)
+{
+ struct spdk_iscsi_conn *conn = arg;
+
+ if (conn->state < ISCSI_CONN_STATE_EXITING) {
+ conn->state = ISCSI_CONN_STATE_EXITING;
+ }
+
+ return SPDK_POLLER_BUSY;
+}
+
+void
+iscsi_conn_logout(struct spdk_iscsi_conn *conn)
+{
+ conn->is_logged_out = true;
+ conn->logout_timer = SPDK_POLLER_REGISTER(logout_timeout, conn, ISCSI_LOGOUT_TIMEOUT * 1000000);
+}
+
+SPDK_TRACE_REGISTER_FN(iscsi_conn_trace, "iscsi_conn", TRACE_GROUP_ISCSI)
+{
+ spdk_trace_register_owner(OWNER_ISCSI_CONN, 'c');
+ spdk_trace_register_object(OBJECT_ISCSI_PDU, 'p');
+ spdk_trace_register_description("ISCSI_READ_DONE", TRACE_ISCSI_READ_FROM_SOCKET_DONE,
+ OWNER_ISCSI_CONN, OBJECT_NONE, 0, 0, "");
+ spdk_trace_register_description("ISCSI_WRITE_START", TRACE_ISCSI_FLUSH_WRITEBUF_START,
+ OWNER_ISCSI_CONN, OBJECT_NONE, 0, 0, "iovec: ");
+ spdk_trace_register_description("ISCSI_WRITE_DONE", TRACE_ISCSI_FLUSH_WRITEBUF_DONE,
+ OWNER_ISCSI_CONN, OBJECT_NONE, 0, 0, "");
+ spdk_trace_register_description("ISCSI_READ_PDU", TRACE_ISCSI_READ_PDU,
+ OWNER_ISCSI_CONN, OBJECT_ISCSI_PDU, 1, 0, "opc: ");
+ spdk_trace_register_description("ISCSI_TASK_DONE", TRACE_ISCSI_TASK_DONE,
+ OWNER_ISCSI_CONN, OBJECT_SCSI_TASK, 0, 0, "");
+ spdk_trace_register_description("ISCSI_TASK_QUEUE", TRACE_ISCSI_TASK_QUEUE,
+ OWNER_ISCSI_CONN, OBJECT_SCSI_TASK, 1, 1, "pdu: ");
+ spdk_trace_register_description("ISCSI_TASK_EXECUTED", TRACE_ISCSI_TASK_EXECUTED,
+ OWNER_ISCSI_CONN, OBJECT_ISCSI_PDU, 0, 0, "");
+ spdk_trace_register_description("ISCSI_PDU_COMPLETED", TRACE_ISCSI_PDU_COMPLETED,
+ OWNER_ISCSI_CONN, OBJECT_ISCSI_PDU, 0, 0, "");
+}
+
+void
+iscsi_conn_info_json(struct spdk_json_write_ctx *w, struct spdk_iscsi_conn *conn)
+{
+ uint16_t tsih;
+
+ if (!conn->is_valid) {
+ return;
+ }
+
+ spdk_json_write_object_begin(w);
+
+ spdk_json_write_named_int32(w, "id", conn->id);
+
+ spdk_json_write_named_int32(w, "cid", conn->cid);
+
+ /*
+ * If we try to return data for a connection that has not
+ * logged in yet, the session will not be set. So in this
+ * case, return -1 for the tsih rather than segfaulting
+ * on the null conn->sess.
+ */
+ if (conn->sess == NULL) {
+ tsih = -1;
+ } else {
+ tsih = conn->sess->tsih;
+ }
+ spdk_json_write_named_int32(w, "tsih", tsih);
+
+ spdk_json_write_named_string(w, "initiator_addr", conn->initiator_addr);
+
+ spdk_json_write_named_string(w, "target_addr", conn->target_addr);
+
+ spdk_json_write_named_string(w, "target_node_name", conn->target_short_name);
+
+ spdk_json_write_named_string(w, "thread_name",
+ spdk_thread_get_name(spdk_get_thread()));
+
+ spdk_json_write_object_end(w);
+}
diff --git a/src/spdk/lib/iscsi/conn.h b/src/spdk/lib/iscsi/conn.h
new file mode 100644
index 000000000..a85d2ddeb
--- /dev/null
+++ b/src/spdk/lib/iscsi/conn.h
@@ -0,0 +1,237 @@
+/*-
+ * BSD LICENSE
+ *
+ * Copyright (C) 2008-2012 Daisuke Aoyama <aoyama@peach.ne.jp>.
+ * Copyright (c) Intel Corporation.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ * * Neither the name of Intel Corporation nor the names of its
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef SPDK_ISCSI_CONN_H
+#define SPDK_ISCSI_CONN_H
+
+#include "spdk/stdinc.h"
+
+#include "iscsi/iscsi.h"
+#include "spdk/queue.h"
+#include "spdk/cpuset.h"
+#include "spdk/scsi.h"
+
+/*
+ * MAX_CONNECTION_PARAMS: The numbers of the params in conn_param_table
+ * MAX_SESSION_PARAMS: The numbers of the params in sess_param_table
+ */
+#define MAX_CONNECTION_PARAMS 14
+#define MAX_SESSION_PARAMS 19
+
+#define MAX_ADDRBUF 64
+#define MAX_INITIATOR_ADDR (MAX_ADDRBUF)
+#define MAX_TARGET_ADDR (MAX_ADDRBUF)
+
+#define OWNER_ISCSI_CONN 0x1
+
+#define OBJECT_ISCSI_PDU 0x1
+
+#define TRACE_GROUP_ISCSI 0x1
+#define TRACE_ISCSI_READ_FROM_SOCKET_DONE SPDK_TPOINT_ID(TRACE_GROUP_ISCSI, 0x0)
+#define TRACE_ISCSI_FLUSH_WRITEBUF_START SPDK_TPOINT_ID(TRACE_GROUP_ISCSI, 0x1)
+#define TRACE_ISCSI_FLUSH_WRITEBUF_DONE SPDK_TPOINT_ID(TRACE_GROUP_ISCSI, 0x2)
+#define TRACE_ISCSI_READ_PDU SPDK_TPOINT_ID(TRACE_GROUP_ISCSI, 0x3)
+#define TRACE_ISCSI_TASK_DONE SPDK_TPOINT_ID(TRACE_GROUP_ISCSI, 0x4)
+#define TRACE_ISCSI_TASK_QUEUE SPDK_TPOINT_ID(TRACE_GROUP_ISCSI, 0x5)
+#define TRACE_ISCSI_TASK_EXECUTED SPDK_TPOINT_ID(TRACE_GROUP_ISCSI, 0x6)
+#define TRACE_ISCSI_PDU_COMPLETED SPDK_TPOINT_ID(TRACE_GROUP_ISCSI, 0x7)
+
+enum iscsi_pdu_recv_state {
+ /* Ready to wait for PDU */
+ ISCSI_PDU_RECV_STATE_AWAIT_PDU_READY,
+
+ /* Active connection waiting for any PDU header */
+ ISCSI_PDU_RECV_STATE_AWAIT_PDU_HDR,
+
+ /* Active connection waiting for payload */
+ ISCSI_PDU_RECV_STATE_AWAIT_PDU_PAYLOAD,
+
+ /* Active connection does not wait for payload */
+ ISCSI_PDU_RECV_STATE_ERROR,
+};
+
+struct spdk_poller;
+struct spdk_iscsi_conn;
+
+struct spdk_iscsi_lun {
+ struct spdk_iscsi_conn *conn;
+ struct spdk_scsi_lun *lun;
+ struct spdk_scsi_lun_desc *desc;
+ struct spdk_poller *remove_poller;
+};
+
+struct spdk_iscsi_conn {
+ int id;
+ int is_valid;
+ /*
+ * All fields below this point are reinitialized each time the
+ * connection object is allocated. Make sure to update the
+ * SPDK_ISCSI_CONNECTION_MEMSET() macro if changing which fields
+ * are initialized when allocated.
+ */
+ struct spdk_iscsi_portal *portal;
+ int pg_tag;
+ char portal_host[MAX_PORTAL_ADDR + 1];
+ char portal_port[MAX_PORTAL_ADDR + 1];
+ struct spdk_iscsi_poll_group *pg;
+ struct spdk_sock *sock;
+ struct spdk_iscsi_sess *sess;
+
+ enum iscsi_connection_state state;
+ int login_phase;
+ bool is_logged_out;
+ struct spdk_iscsi_pdu *login_rsp_pdu;
+
+ uint64_t last_flush;
+ uint64_t last_fill;
+ uint64_t last_nopin;
+
+ /* Timer used to destroy connection after requesting logout if
+ * initiator does not send logout request.
+ */
+ struct spdk_poller *logout_request_timer;
+
+ /* Timer used to destroy connection after logout if initiator does
+ * not close the connection.
+ */
+ struct spdk_poller *logout_timer;
+
+ /* Timer used to wait for connection to close
+ */
+ struct spdk_poller *shutdown_timer;
+
+ struct spdk_iscsi_pdu *pdu_in_progress;
+ enum iscsi_pdu_recv_state pdu_recv_state;
+
+ TAILQ_HEAD(, spdk_iscsi_pdu) write_pdu_list;
+ TAILQ_HEAD(, spdk_iscsi_pdu) snack_pdu_list;
+
+ int pending_r2t;
+
+ uint16_t cid;
+
+ /* IP address */
+ char initiator_addr[MAX_INITIATOR_ADDR];
+ char target_addr[MAX_TARGET_ADDR];
+
+ /* Initiator/Target port binds */
+ char initiator_name[MAX_INITIATOR_NAME];
+ struct spdk_scsi_port *initiator_port;
+ char target_short_name[MAX_TARGET_NAME];
+ struct spdk_scsi_port *target_port;
+ struct spdk_iscsi_tgt_node *target;
+ struct spdk_scsi_dev *dev;
+
+ /* for fast access */
+ int header_digest;
+ int data_digest;
+ int full_feature;
+
+ struct iscsi_param *params;
+ bool sess_param_state_negotiated[MAX_SESSION_PARAMS];
+ bool conn_param_state_negotiated[MAX_CONNECTION_PARAMS];
+ struct iscsi_chap_auth auth;
+ bool authenticated;
+ bool disable_chap;
+ bool require_chap;
+ bool mutual_chap;
+ int32_t chap_group;
+ uint32_t pending_task_cnt;
+ uint32_t data_out_cnt;
+ uint32_t data_in_cnt;
+
+ uint64_t timeout;
+ uint64_t nopininterval;
+ bool nop_outstanding;
+
+ /*
+ * This is the maximum data segment length that iscsi target can send
+ * to the initiator on this connection. Not to be confused with the
+ * maximum data segment length that initiators can send to iscsi target, which
+ * is statically defined as SPDK_ISCSI_MAX_RECV_DATA_SEGMENT_LENGTH.
+ */
+ int MaxRecvDataSegmentLength;
+
+ uint32_t StatSN;
+ uint32_t exp_statsn;
+ uint32_t ttt; /* target transfer tag */
+ char *partial_text_parameter;
+
+ STAILQ_ENTRY(spdk_iscsi_conn) pg_link;
+ bool is_stopped; /* Set true when connection is stopped for migration */
+ TAILQ_HEAD(queued_r2t_tasks, spdk_iscsi_task) queued_r2t_tasks;
+ TAILQ_HEAD(active_r2t_tasks, spdk_iscsi_task) active_r2t_tasks;
+ TAILQ_HEAD(queued_datain_tasks, spdk_iscsi_task) queued_datain_tasks;
+
+ struct spdk_iscsi_lun *luns[SPDK_SCSI_DEV_MAX_LUN];
+
+ TAILQ_ENTRY(spdk_iscsi_conn) conn_link;
+};
+
+extern struct spdk_iscsi_conn *g_conns_array;
+
+void iscsi_task_cpl(struct spdk_scsi_task *scsi_task);
+void iscsi_task_mgmt_cpl(struct spdk_scsi_task *scsi_task);
+
+int initialize_iscsi_conns(void);
+void shutdown_iscsi_conns(void);
+void iscsi_conns_request_logout(struct spdk_iscsi_tgt_node *target);
+int iscsi_get_active_conns(struct spdk_iscsi_tgt_node *target);
+
+int iscsi_conn_construct(struct spdk_iscsi_portal *portal, struct spdk_sock *sock);
+void iscsi_conn_destruct(struct spdk_iscsi_conn *conn);
+void iscsi_conn_handle_nop(struct spdk_iscsi_conn *conn);
+void iscsi_conn_schedule(struct spdk_iscsi_conn *conn);
+void iscsi_conn_logout(struct spdk_iscsi_conn *conn);
+int iscsi_drop_conns(struct spdk_iscsi_conn *conn,
+ const char *conn_match, int drop_all);
+int iscsi_conn_handle_queued_datain_tasks(struct spdk_iscsi_conn *conn);
+int iscsi_conn_abort_queued_datain_task(struct spdk_iscsi_conn *conn,
+ uint32_t ref_task_tag);
+int iscsi_conn_abort_queued_datain_tasks(struct spdk_iscsi_conn *conn,
+ struct spdk_scsi_lun *lun,
+ struct spdk_iscsi_pdu *pdu);
+
+int iscsi_conn_read_data(struct spdk_iscsi_conn *conn, int len, void *buf);
+int iscsi_conn_readv_data(struct spdk_iscsi_conn *conn,
+ struct iovec *iov, int iovcnt);
+void iscsi_conn_write_pdu(struct spdk_iscsi_conn *conn, struct spdk_iscsi_pdu *pdu,
+ iscsi_conn_xfer_complete_cb cb_fn,
+ void *cb_arg);
+
+void iscsi_conn_free_pdu(struct spdk_iscsi_conn *conn, struct spdk_iscsi_pdu *pdu);
+
+void iscsi_conn_info_json(struct spdk_json_write_ctx *w, struct spdk_iscsi_conn *conn);
+void iscsi_conn_pdu_generic_complete(void *cb_arg);
+#endif /* SPDK_ISCSI_CONN_H */
diff --git a/src/spdk/lib/iscsi/init_grp.c b/src/spdk/lib/iscsi/init_grp.c
new file mode 100644
index 000000000..49e78d89d
--- /dev/null
+++ b/src/spdk/lib/iscsi/init_grp.c
@@ -0,0 +1,787 @@
+/*-
+ * BSD LICENSE
+ *
+ * Copyright (C) 2008-2012 Daisuke Aoyama <aoyama@peach.ne.jp>.
+ * Copyright (c) Intel Corporation.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ * * Neither the name of Intel Corporation nor the names of its
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "spdk/stdinc.h"
+
+#include "spdk/conf.h"
+#include "spdk/string.h"
+
+#include "spdk_internal/log.h"
+
+#include "iscsi/iscsi.h"
+#include "iscsi/init_grp.h"
+
+static struct spdk_iscsi_init_grp *
+iscsi_init_grp_create(int tag)
+{
+ struct spdk_iscsi_init_grp *ig;
+
+ ig = calloc(1, sizeof(*ig));
+ if (ig == NULL) {
+ SPDK_ERRLOG("calloc() failed for initiator group\n");
+ return NULL;
+ }
+
+ ig->tag = tag;
+ TAILQ_INIT(&ig->initiator_head);
+ TAILQ_INIT(&ig->netmask_head);
+ return ig;
+}
+
+static struct spdk_iscsi_initiator_name *
+iscsi_init_grp_find_initiator(struct spdk_iscsi_init_grp *ig, char *name)
+{
+ struct spdk_iscsi_initiator_name *iname;
+
+ TAILQ_FOREACH(iname, &ig->initiator_head, tailq) {
+ if (!strcmp(iname->name, name)) {
+ return iname;
+ }
+ }
+ return NULL;
+}
+
+static int
+iscsi_init_grp_add_initiator(struct spdk_iscsi_init_grp *ig, char *name)
+{
+ struct spdk_iscsi_initiator_name *iname;
+ char *p;
+ size_t len;
+
+ if (ig->ninitiators >= MAX_INITIATOR) {
+ SPDK_ERRLOG("> MAX_INITIATOR(=%d) is not allowed\n", MAX_INITIATOR);
+ return -EPERM;
+ }
+
+ len = strlen(name);
+ if (len > MAX_INITIATOR_NAME) {
+ SPDK_ERRLOG("Initiator Name is larger than 223 bytes\n");
+ return -EINVAL;
+ }
+
+ iname = iscsi_init_grp_find_initiator(ig, name);
+ if (iname != NULL) {
+ return -EEXIST;
+ }
+
+ iname = calloc(1, sizeof(*iname));
+ if (iname == NULL) {
+ SPDK_ERRLOG("malloc() failed for initiator name str\n");
+ return -ENOMEM;
+ }
+
+ memcpy(iname->name, name, len);
+
+ /* Replace "ALL" by "ANY" if set */
+ p = strstr(iname->name, "ALL");
+ if (p != NULL) {
+ SPDK_WARNLOG("Please use \"%s\" instead of \"%s\"\n", "ANY", "ALL");
+ SPDK_WARNLOG("Converting \"%s\" to \"%s\" automatically\n", "ALL", "ANY");
+ memcpy(p, "ANY", 3);
+ }
+
+ TAILQ_INSERT_TAIL(&ig->initiator_head, iname, tailq);
+ ig->ninitiators++;
+
+ SPDK_DEBUGLOG(SPDK_LOG_ISCSI, "InitiatorName %s\n", name);
+ return 0;
+}
+
+static int
+iscsi_init_grp_delete_initiator(struct spdk_iscsi_init_grp *ig, char *name)
+{
+ struct spdk_iscsi_initiator_name *iname;
+
+ iname = iscsi_init_grp_find_initiator(ig, name);
+ if (iname == NULL) {
+ return -ENOENT;
+ }
+
+ TAILQ_REMOVE(&ig->initiator_head, iname, tailq);
+ ig->ninitiators--;
+ free(iname);
+ return 0;
+}
+
+static int
+iscsi_init_grp_add_initiators(struct spdk_iscsi_init_grp *ig, int num_inames,
+ char **inames)
+{
+ int i;
+ int rc;
+
+ for (i = 0; i < num_inames; i++) {
+ rc = iscsi_init_grp_add_initiator(ig, inames[i]);
+ if (rc < 0) {
+ goto cleanup;
+ }
+ }
+ return 0;
+
+cleanup:
+ for (; i > 0; --i) {
+ iscsi_init_grp_delete_initiator(ig, inames[i - 1]);
+ }
+ return rc;
+}
+
+static void
+iscsi_init_grp_delete_all_initiators(struct spdk_iscsi_init_grp *ig)
+{
+ struct spdk_iscsi_initiator_name *iname, *tmp;
+
+ TAILQ_FOREACH_SAFE(iname, &ig->initiator_head, tailq, tmp) {
+ TAILQ_REMOVE(&ig->initiator_head, iname, tailq);
+ ig->ninitiators--;
+ free(iname);
+ }
+}
+
+static int
+iscsi_init_grp_delete_initiators(struct spdk_iscsi_init_grp *ig, int num_inames, char **inames)
+{
+ int i;
+ int rc;
+
+ for (i = 0; i < num_inames; i++) {
+ rc = iscsi_init_grp_delete_initiator(ig, inames[i]);
+ if (rc < 0) {
+ goto cleanup;
+ }
+ }
+ return 0;
+
+cleanup:
+ for (; i > 0; --i) {
+ rc = iscsi_init_grp_add_initiator(ig, inames[i - 1]);
+ if (rc != 0) {
+ iscsi_init_grp_delete_all_initiators(ig);
+ break;
+ }
+ }
+ return -1;
+}
+
+static struct spdk_iscsi_initiator_netmask *
+iscsi_init_grp_find_netmask(struct spdk_iscsi_init_grp *ig, const char *mask)
+{
+ struct spdk_iscsi_initiator_netmask *netmask;
+
+ TAILQ_FOREACH(netmask, &ig->netmask_head, tailq) {
+ if (!strcmp(netmask->mask, mask)) {
+ return netmask;
+ }
+ }
+ return NULL;
+}
+
+static int
+iscsi_init_grp_add_netmask(struct spdk_iscsi_init_grp *ig, char *mask)
+{
+ struct spdk_iscsi_initiator_netmask *imask;
+ char *p;
+ size_t len;
+
+ if (ig->nnetmasks >= MAX_NETMASK) {
+ SPDK_ERRLOG("> MAX_NETMASK(=%d) is not allowed\n", MAX_NETMASK);
+ return -EPERM;
+ }
+
+ len = strlen(mask);
+ if (len > MAX_INITIATOR_ADDR) {
+ SPDK_ERRLOG("Initiator Name is larger than %d bytes\n", MAX_INITIATOR_ADDR);
+ return -EINVAL;
+ }
+
+ imask = iscsi_init_grp_find_netmask(ig, mask);
+ if (imask != NULL) {
+ return -EEXIST;
+ }
+
+ imask = calloc(1, sizeof(*imask));
+ if (imask == NULL) {
+ SPDK_ERRLOG("malloc() failed for inititator mask str\n");
+ return -ENOMEM;
+ }
+
+ memcpy(imask->mask, mask, len);
+
+ /* Replace "ALL" by "ANY" if set */
+ p = strstr(imask->mask, "ALL");
+ if (p != NULL) {
+ SPDK_WARNLOG("Please use \"%s\" instead of \"%s\"\n", "ANY", "ALL");
+ SPDK_WARNLOG("Converting \"%s\" to \"%s\" automatically\n", "ALL", "ANY");
+ memcpy(p, "ANY", 3);
+ }
+
+ TAILQ_INSERT_TAIL(&ig->netmask_head, imask, tailq);
+ ig->nnetmasks++;
+
+ SPDK_DEBUGLOG(SPDK_LOG_ISCSI, "Netmask %s\n", mask);
+ return 0;
+}
+
+static int
+iscsi_init_grp_delete_netmask(struct spdk_iscsi_init_grp *ig, char *mask)
+{
+ struct spdk_iscsi_initiator_netmask *imask;
+
+ imask = iscsi_init_grp_find_netmask(ig, mask);
+ if (imask == NULL) {
+ return -ENOENT;
+ }
+
+ TAILQ_REMOVE(&ig->netmask_head, imask, tailq);
+ ig->nnetmasks--;
+ free(imask);
+ return 0;
+}
+
+static int
+iscsi_init_grp_add_netmasks(struct spdk_iscsi_init_grp *ig, int num_imasks, char **imasks)
+{
+ int i;
+ int rc;
+
+ for (i = 0; i < num_imasks; i++) {
+ rc = iscsi_init_grp_add_netmask(ig, imasks[i]);
+ if (rc != 0) {
+ goto cleanup;
+ }
+ }
+ return 0;
+
+cleanup:
+ for (; i > 0; --i) {
+ iscsi_init_grp_delete_netmask(ig, imasks[i - 1]);
+ }
+ return rc;
+}
+
+static void
+iscsi_init_grp_delete_all_netmasks(struct spdk_iscsi_init_grp *ig)
+{
+ struct spdk_iscsi_initiator_netmask *imask, *tmp;
+
+ TAILQ_FOREACH_SAFE(imask, &ig->netmask_head, tailq, tmp) {
+ TAILQ_REMOVE(&ig->netmask_head, imask, tailq);
+ ig->nnetmasks--;
+ free(imask);
+ }
+}
+
+static int
+iscsi_init_grp_delete_netmasks(struct spdk_iscsi_init_grp *ig, int num_imasks, char **imasks)
+{
+ int i;
+ int rc;
+
+ for (i = 0; i < num_imasks; i++) {
+ rc = iscsi_init_grp_delete_netmask(ig, imasks[i]);
+ if (rc != 0) {
+ goto cleanup;
+ }
+ }
+ return 0;
+
+cleanup:
+ for (; i > 0; --i) {
+ rc = iscsi_init_grp_add_netmask(ig, imasks[i - 1]);
+ if (rc != 0) {
+ iscsi_init_grp_delete_all_netmasks(ig);
+ break;
+ }
+ }
+ return -1;
+}
+
+/* Read spdk iscsi target's config file and create initiator group */
+static int
+iscsi_parse_init_grp(struct spdk_conf_section *sp)
+{
+ int i, rc = 0;
+ const char *val = NULL;
+ int num_initiator_names;
+ int num_initiator_masks;
+ char **initiators = NULL, **netmasks = NULL;
+ int tag = spdk_conf_section_get_num(sp);
+
+ SPDK_DEBUGLOG(SPDK_LOG_ISCSI, "add initiator group %d\n", tag);
+
+ val = spdk_conf_section_get_val(sp, "Comment");
+ if (val != NULL) {
+ SPDK_DEBUGLOG(SPDK_LOG_ISCSI, "Comment %s\n", val);
+ }
+
+ /* counts number of definitions */
+ for (i = 0; ; i++) {
+ val = spdk_conf_section_get_nval(sp, "InitiatorName", i);
+ if (val == NULL) {
+ break;
+ }
+ }
+ if (i == 0) {
+ SPDK_ERRLOG("num_initiator_names = 0\n");
+ return -EINVAL;
+ }
+ num_initiator_names = i;
+ if (num_initiator_names > MAX_INITIATOR) {
+ SPDK_ERRLOG("%d > MAX_INITIATOR\n", num_initiator_names);
+ return -E2BIG;
+ }
+ for (i = 0; ; i++) {
+ val = spdk_conf_section_get_nval(sp, "Netmask", i);
+ if (val == NULL) {
+ break;
+ }
+ }
+ if (i == 0) {
+ SPDK_ERRLOG("num_initiator_mask = 0\n");
+ return -EINVAL;
+ }
+ num_initiator_masks = i;
+ if (num_initiator_masks > MAX_NETMASK) {
+ SPDK_ERRLOG("%d > MAX_NETMASK\n", num_initiator_masks);
+ return -E2BIG;
+ }
+
+ initiators = calloc(num_initiator_names, sizeof(char *));
+ if (!initiators) {
+ SPDK_ERRLOG("calloc() failed for temp initiator name array\n");
+ return -ENOMEM;
+ }
+ for (i = 0; i < num_initiator_names; i++) {
+ val = spdk_conf_section_get_nval(sp, "InitiatorName", i);
+ if (!val) {
+ SPDK_ERRLOG("InitiatorName %d not found\n", i);
+ rc = -EINVAL;
+ goto cleanup;
+ }
+ SPDK_DEBUGLOG(SPDK_LOG_ISCSI, "InitiatorName %s\n", val);
+ initiators[i] = strdup(val);
+ if (!initiators[i]) {
+ SPDK_ERRLOG("strdup() failed for temp initiator name\n");
+ rc = -ENOMEM;
+ goto cleanup;
+ }
+ }
+ netmasks = calloc(num_initiator_masks, sizeof(char *));
+ if (!netmasks) {
+ SPDK_ERRLOG("malloc() failed for portal group\n");
+ rc = -ENOMEM;
+ goto cleanup;
+ }
+ for (i = 0; i < num_initiator_masks; i++) {
+ val = spdk_conf_section_get_nval(sp, "Netmask", i);
+ if (!val) {
+ SPDK_ERRLOG("Netmask %d not found\n", i);
+ rc = -EINVAL;
+ goto cleanup;
+ }
+ SPDK_DEBUGLOG(SPDK_LOG_ISCSI, "Netmask %s\n", val);
+ netmasks[i] = strdup(val);
+ if (!netmasks[i]) {
+ SPDK_ERRLOG("strdup() failed for temp initiator mask\n");
+ rc = -ENOMEM;
+ goto cleanup;
+ }
+ }
+
+ rc = iscsi_init_grp_create_from_initiator_list(tag,
+ num_initiator_names, initiators, num_initiator_masks, netmasks);
+
+cleanup:
+ if (initiators) {
+ for (i = 0; i < num_initiator_names; i++) {
+ if (initiators[i]) {
+ free(initiators[i]);
+ }
+ }
+ free(initiators);
+ }
+ if (netmasks) {
+ for (i = 0; i < num_initiator_masks; i++) {
+ if (netmasks[i]) {
+ free(netmasks[i]);
+ }
+ }
+ free(netmasks);
+ }
+ return rc;
+}
+
+int
+iscsi_init_grp_register(struct spdk_iscsi_init_grp *ig)
+{
+ struct spdk_iscsi_init_grp *tmp;
+ int rc = -1;
+
+ assert(ig != NULL);
+
+ pthread_mutex_lock(&g_iscsi.mutex);
+ tmp = iscsi_init_grp_find_by_tag(ig->tag);
+ if (tmp == NULL) {
+ TAILQ_INSERT_TAIL(&g_iscsi.ig_head, ig, tailq);
+ rc = 0;
+ }
+ pthread_mutex_unlock(&g_iscsi.mutex);
+
+ return rc;
+}
+
+/*
+ * Create initiator group from list of initiator ip/hostnames and netmasks
+ * The initiator hostname/netmask lists are allocated by the caller on the
+ * heap. Freed later by common initiator_group_destroy() code
+ */
+int
+iscsi_init_grp_create_from_initiator_list(int tag,
+ int num_initiator_names,
+ char **initiator_names,
+ int num_initiator_masks,
+ char **initiator_masks)
+{
+ int rc = -1;
+ struct spdk_iscsi_init_grp *ig = NULL;
+
+ SPDK_DEBUGLOG(SPDK_LOG_ISCSI,
+ "add initiator group (from initiator list) tag=%d, #initiators=%d, #masks=%d\n",
+ tag, num_initiator_names, num_initiator_masks);
+
+ ig = iscsi_init_grp_create(tag);
+ if (!ig) {
+ SPDK_ERRLOG("initiator group create error (%d)\n", tag);
+ return rc;
+ }
+
+ rc = iscsi_init_grp_add_initiators(ig, num_initiator_names,
+ initiator_names);
+ if (rc < 0) {
+ SPDK_ERRLOG("add initiator name error\n");
+ goto cleanup;
+ }
+
+ rc = iscsi_init_grp_add_netmasks(ig, num_initiator_masks,
+ initiator_masks);
+ if (rc < 0) {
+ SPDK_ERRLOG("add initiator netmask error\n");
+ goto cleanup;
+ }
+
+ rc = iscsi_init_grp_register(ig);
+ if (rc < 0) {
+ SPDK_ERRLOG("initiator group register error (%d)\n", tag);
+ goto cleanup;
+ }
+ return 0;
+
+cleanup:
+ iscsi_init_grp_destroy(ig);
+ return rc;
+}
+
+int
+iscsi_init_grp_add_initiators_from_initiator_list(int tag,
+ int num_initiator_names,
+ char **initiator_names,
+ int num_initiator_masks,
+ char **initiator_masks)
+{
+ int rc = -1;
+ struct spdk_iscsi_init_grp *ig;
+
+ SPDK_DEBUGLOG(SPDK_LOG_ISCSI,
+ "add initiator to initiator group: tag=%d, #initiators=%d, #masks=%d\n",
+ tag, num_initiator_names, num_initiator_masks);
+
+ pthread_mutex_lock(&g_iscsi.mutex);
+ ig = iscsi_init_grp_find_by_tag(tag);
+ if (!ig) {
+ pthread_mutex_unlock(&g_iscsi.mutex);
+ SPDK_ERRLOG("initiator group (%d) is not found\n", tag);
+ return rc;
+ }
+
+ rc = iscsi_init_grp_add_initiators(ig, num_initiator_names,
+ initiator_names);
+ if (rc < 0) {
+ SPDK_ERRLOG("add initiator name error\n");
+ goto error;
+ }
+
+ rc = iscsi_init_grp_add_netmasks(ig, num_initiator_masks,
+ initiator_masks);
+ if (rc < 0) {
+ SPDK_ERRLOG("add initiator netmask error\n");
+ iscsi_init_grp_delete_initiators(ig, num_initiator_names,
+ initiator_names);
+ }
+
+error:
+ pthread_mutex_unlock(&g_iscsi.mutex);
+ return rc;
+}
+
+int
+iscsi_init_grp_delete_initiators_from_initiator_list(int tag,
+ int num_initiator_names,
+ char **initiator_names,
+ int num_initiator_masks,
+ char **initiator_masks)
+{
+ int rc = -1;
+ struct spdk_iscsi_init_grp *ig;
+
+ SPDK_DEBUGLOG(SPDK_LOG_ISCSI,
+ "delete initiator from initiator group: tag=%d, #initiators=%d, #masks=%d\n",
+ tag, num_initiator_names, num_initiator_masks);
+
+ pthread_mutex_lock(&g_iscsi.mutex);
+ ig = iscsi_init_grp_find_by_tag(tag);
+ if (!ig) {
+ pthread_mutex_unlock(&g_iscsi.mutex);
+ SPDK_ERRLOG("initiator group (%d) is not found\n", tag);
+ return rc;
+ }
+
+ rc = iscsi_init_grp_delete_initiators(ig, num_initiator_names,
+ initiator_names);
+ if (rc < 0) {
+ SPDK_ERRLOG("delete initiator name error\n");
+ goto error;
+ }
+
+ rc = iscsi_init_grp_delete_netmasks(ig, num_initiator_masks,
+ initiator_masks);
+ if (rc < 0) {
+ SPDK_ERRLOG("delete initiator netmask error\n");
+ iscsi_init_grp_add_initiators(ig, num_initiator_names,
+ initiator_names);
+ goto error;
+ }
+
+error:
+ pthread_mutex_unlock(&g_iscsi.mutex);
+ return rc;
+}
+
+void
+iscsi_init_grp_destroy(struct spdk_iscsi_init_grp *ig)
+{
+ if (!ig) {
+ return;
+ }
+
+ iscsi_init_grp_delete_all_initiators(ig);
+ iscsi_init_grp_delete_all_netmasks(ig);
+ free(ig);
+};
+
+struct spdk_iscsi_init_grp *
+iscsi_init_grp_find_by_tag(int tag)
+{
+ struct spdk_iscsi_init_grp *ig;
+
+ TAILQ_FOREACH(ig, &g_iscsi.ig_head, tailq) {
+ if (ig->tag == tag) {
+ return ig;
+ }
+ }
+
+ return NULL;
+}
+
+int
+iscsi_parse_init_grps(void)
+{
+ struct spdk_conf_section *sp;
+ int rc;
+
+ sp = spdk_conf_first_section(NULL);
+ while (sp != NULL) {
+ if (spdk_conf_section_match_prefix(sp, "InitiatorGroup")) {
+ if (spdk_conf_section_get_num(sp) == 0) {
+ SPDK_ERRLOG("Group 0 is invalid\n");
+ return -1;
+ }
+ rc = iscsi_parse_init_grp(sp);
+ if (rc < 0) {
+ SPDK_ERRLOG("parse_init_group() failed\n");
+ return -1;
+ }
+ }
+ sp = spdk_conf_next_section(sp);
+ }
+ return 0;
+}
+
+void
+iscsi_init_grps_destroy(void)
+{
+ struct spdk_iscsi_init_grp *ig, *tmp;
+
+ SPDK_DEBUGLOG(SPDK_LOG_ISCSI, "iscsi_init_grp_array_destroy\n");
+ pthread_mutex_lock(&g_iscsi.mutex);
+ TAILQ_FOREACH_SAFE(ig, &g_iscsi.ig_head, tailq, tmp) {
+ TAILQ_REMOVE(&g_iscsi.ig_head, ig, tailq);
+ iscsi_init_grp_destroy(ig);
+ }
+ pthread_mutex_unlock(&g_iscsi.mutex);
+}
+
+struct spdk_iscsi_init_grp *
+iscsi_init_grp_unregister(int tag)
+{
+ struct spdk_iscsi_init_grp *ig;
+
+ pthread_mutex_lock(&g_iscsi.mutex);
+ TAILQ_FOREACH(ig, &g_iscsi.ig_head, tailq) {
+ if (ig->tag == tag) {
+ TAILQ_REMOVE(&g_iscsi.ig_head, ig, tailq);
+ pthread_mutex_unlock(&g_iscsi.mutex);
+ return ig;
+ }
+ }
+ pthread_mutex_unlock(&g_iscsi.mutex);
+ return NULL;
+}
+
+static const char *initiator_group_section = \
+ "\n"
+ "# Users must change the InitiatorGroup section(s) to match the IP\n"
+ "# addresses and initiator configuration in their environment.\n"
+ "# Netmask can be used to specify a single IP address or a range of IP addresses\n"
+ "# Netmask 192.168.1.20 <== single IP address\n"
+ "# Netmask 192.168.1.0/24 <== IP range 192.168.1.*\n";
+
+#define INITIATOR_GROUP_TMPL \
+"[InitiatorGroup%d]\n" \
+" Comment \"Initiator Group%d\"\n"
+
+#define INITIATOR_TMPL \
+" InitiatorName "
+
+#define NETMASK_TMPL \
+" Netmask "
+
+void
+iscsi_init_grps_config_text(FILE *fp)
+{
+ struct spdk_iscsi_init_grp *ig;
+ struct spdk_iscsi_initiator_name *iname;
+ struct spdk_iscsi_initiator_netmask *imask;
+
+ /* Create initiator group section */
+ fprintf(fp, "%s", initiator_group_section);
+
+ /* Dump initiator groups */
+ TAILQ_FOREACH(ig, &g_iscsi.ig_head, tailq) {
+ if (NULL == ig) { continue; }
+ fprintf(fp, INITIATOR_GROUP_TMPL, ig->tag, ig->tag);
+
+ /* Dump initiators */
+ fprintf(fp, INITIATOR_TMPL);
+ TAILQ_FOREACH(iname, &ig->initiator_head, tailq) {
+ fprintf(fp, "%s ", iname->name);
+ }
+ fprintf(fp, "\n");
+
+ /* Dump netmasks */
+ fprintf(fp, NETMASK_TMPL);
+ TAILQ_FOREACH(imask, &ig->netmask_head, tailq) {
+ fprintf(fp, "%s ", imask->mask);
+ }
+ fprintf(fp, "\n");
+ }
+}
+
+static void
+iscsi_init_grp_info_json(struct spdk_iscsi_init_grp *ig,
+ struct spdk_json_write_ctx *w)
+{
+ struct spdk_iscsi_initiator_name *iname;
+ struct spdk_iscsi_initiator_netmask *imask;
+
+ spdk_json_write_object_begin(w);
+
+ spdk_json_write_named_int32(w, "tag", ig->tag);
+
+ spdk_json_write_named_array_begin(w, "initiators");
+ TAILQ_FOREACH(iname, &ig->initiator_head, tailq) {
+ spdk_json_write_string(w, iname->name);
+ }
+ spdk_json_write_array_end(w);
+
+ spdk_json_write_named_array_begin(w, "netmasks");
+ TAILQ_FOREACH(imask, &ig->netmask_head, tailq) {
+ spdk_json_write_string(w, imask->mask);
+ }
+ spdk_json_write_array_end(w);
+
+ spdk_json_write_object_end(w);
+}
+
+static void
+iscsi_init_grp_config_json(struct spdk_iscsi_init_grp *ig,
+ struct spdk_json_write_ctx *w)
+{
+ spdk_json_write_object_begin(w);
+
+ spdk_json_write_named_string(w, "method", "iscsi_create_initiator_group");
+
+ spdk_json_write_name(w, "params");
+ iscsi_init_grp_info_json(ig, w);
+
+ spdk_json_write_object_end(w);
+}
+
+void
+iscsi_init_grps_info_json(struct spdk_json_write_ctx *w)
+{
+ struct spdk_iscsi_init_grp *ig;
+
+ TAILQ_FOREACH(ig, &g_iscsi.ig_head, tailq) {
+ iscsi_init_grp_info_json(ig, w);
+ }
+}
+
+void
+iscsi_init_grps_config_json(struct spdk_json_write_ctx *w)
+{
+ struct spdk_iscsi_init_grp *ig;
+
+ TAILQ_FOREACH(ig, &g_iscsi.ig_head, tailq) {
+ iscsi_init_grp_config_json(ig, w);
+ }
+}
diff --git a/src/spdk/lib/iscsi/init_grp.h b/src/spdk/lib/iscsi/init_grp.h
new file mode 100644
index 000000000..8913c98cd
--- /dev/null
+++ b/src/spdk/lib/iscsi/init_grp.h
@@ -0,0 +1,81 @@
+/*-
+ * BSD LICENSE
+ *
+ * Copyright (C) 2008-2012 Daisuke Aoyama <aoyama@peach.ne.jp>.
+ * Copyright (c) Intel Corporation.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ * * Neither the name of Intel Corporation nor the names of its
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef SPDK_INIT_GRP_H
+#define SPDK_INIT_GRP_H
+
+#include "spdk/conf.h"
+#include "iscsi/iscsi.h"
+#include "iscsi/conn.h"
+
+struct spdk_iscsi_initiator_name {
+ char name[MAX_INITIATOR_NAME + 1];
+ TAILQ_ENTRY(spdk_iscsi_initiator_name) tailq;
+};
+
+struct spdk_iscsi_initiator_netmask {
+ char mask[MAX_INITIATOR_ADDR + 1];
+ TAILQ_ENTRY(spdk_iscsi_initiator_netmask) tailq;
+};
+
+struct spdk_iscsi_init_grp {
+ int ninitiators;
+ TAILQ_HEAD(, spdk_iscsi_initiator_name) initiator_head;
+ int nnetmasks;
+ TAILQ_HEAD(, spdk_iscsi_initiator_netmask) netmask_head;
+ int ref;
+ int tag;
+ TAILQ_ENTRY(spdk_iscsi_init_grp) tailq;
+};
+
+/* SPDK iSCSI Initiator Group management API */
+int iscsi_init_grp_create_from_initiator_list(int tag,
+ int num_initiator_names, char **initiator_names,
+ int num_initiator_masks, char **initiator_masks);
+int iscsi_init_grp_add_initiators_from_initiator_list(int tag,
+ int num_initiator_names, char **initiator_names,
+ int num_initiator_masks, char **initiator_masks);
+int iscsi_init_grp_delete_initiators_from_initiator_list(int tag,
+ int num_initiator_names, char **initiator_names,
+ int num_initiator_masks, char **initiator_masks);
+int iscsi_init_grp_register(struct spdk_iscsi_init_grp *ig);
+struct spdk_iscsi_init_grp *iscsi_init_grp_unregister(int tag);
+struct spdk_iscsi_init_grp *iscsi_init_grp_find_by_tag(int tag);
+void iscsi_init_grp_destroy(struct spdk_iscsi_init_grp *ig);
+int iscsi_parse_init_grps(void);
+void iscsi_init_grps_destroy(void);
+void iscsi_init_grps_config_text(FILE *fp);
+void iscsi_init_grps_info_json(struct spdk_json_write_ctx *w);
+void iscsi_init_grps_config_json(struct spdk_json_write_ctx *w);
+#endif /* SPDK_INIT_GRP_H */
diff --git a/src/spdk/lib/iscsi/iscsi.c b/src/spdk/lib/iscsi/iscsi.c
new file mode 100644
index 000000000..febf4cac4
--- /dev/null
+++ b/src/spdk/lib/iscsi/iscsi.c
@@ -0,0 +1,4797 @@
+/*-
+ * BSD LICENSE
+ *
+ * Copyright (C) 2008-2012 Daisuke Aoyama <aoyama@peach.ne.jp>.
+ * Copyright (c) Intel Corporation.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ * * Neither the name of Intel Corporation nor the names of its
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "spdk/stdinc.h"
+
+#include "spdk/base64.h"
+#include "spdk/crc32.h"
+#include "spdk/endian.h"
+#include "spdk/env.h"
+#include "spdk/likely.h"
+#include "spdk/trace.h"
+#include "spdk/sock.h"
+#include "spdk/string.h"
+#include "spdk/queue.h"
+#include "spdk/net.h"
+
+#include "iscsi/md5.h"
+#include "iscsi/iscsi.h"
+#include "iscsi/param.h"
+#include "iscsi/tgt_node.h"
+#include "iscsi/task.h"
+#include "iscsi/conn.h"
+#include "spdk/scsi.h"
+#include "spdk/bdev.h"
+#include "iscsi/portal_grp.h"
+
+#include "spdk_internal/log.h"
+
+#define MAX_TMPBUF 1024
+
+#define SPDK_CRC32C_INITIAL 0xffffffffUL
+#define SPDK_CRC32C_XOR 0xffffffffUL
+
+#ifdef __FreeBSD__
+#define HAVE_SRANDOMDEV 1
+#define HAVE_ARC4RANDOM 1
+#endif
+
+struct spdk_iscsi_globals g_iscsi = {
+ .mutex = PTHREAD_MUTEX_INITIALIZER,
+ .portal_head = TAILQ_HEAD_INITIALIZER(g_iscsi.portal_head),
+ .pg_head = TAILQ_HEAD_INITIALIZER(g_iscsi.pg_head),
+ .ig_head = TAILQ_HEAD_INITIALIZER(g_iscsi.ig_head),
+ .target_head = TAILQ_HEAD_INITIALIZER(g_iscsi.target_head),
+ .auth_group_head = TAILQ_HEAD_INITIALIZER(g_iscsi.auth_group_head),
+ .poll_group_head = TAILQ_HEAD_INITIALIZER(g_iscsi.poll_group_head),
+};
+
+#define MATCH_DIGEST_WORD(BUF, CRC32C) \
+ ( ((((uint32_t) *((uint8_t *)(BUF)+0)) << 0) \
+ | (((uint32_t) *((uint8_t *)(BUF)+1)) << 8) \
+ | (((uint32_t) *((uint8_t *)(BUF)+2)) << 16) \
+ | (((uint32_t) *((uint8_t *)(BUF)+3)) << 24)) \
+ == (CRC32C))
+
+#ifndef HAVE_SRANDOMDEV
+static void
+srandomdev(void)
+{
+ unsigned long seed;
+ time_t now;
+ pid_t pid;
+
+ pid = getpid();
+ now = time(NULL);
+ seed = pid ^ now;
+ srandom(seed);
+}
+#endif /* HAVE_SRANDOMDEV */
+
+#ifndef HAVE_ARC4RANDOM
+static int g_arc4random_initialized = 0;
+
+static uint32_t
+arc4random(void)
+{
+ uint32_t r;
+ uint32_t r1, r2;
+
+ if (!g_arc4random_initialized) {
+ srandomdev();
+ g_arc4random_initialized = 1;
+ }
+ r1 = (uint32_t)(random() & 0xffff);
+ r2 = (uint32_t)(random() & 0xffff);
+ r = (r1 << 16) | r2;
+ return r;
+}
+#endif /* HAVE_ARC4RANDOM */
+
+static void
+gen_random(uint8_t *buf, size_t len)
+{
+ uint32_t r;
+ size_t idx;
+
+ for (idx = 0; idx < len; idx++) {
+ r = arc4random();
+ buf[idx] = (uint8_t) r;
+ }
+}
+
+static uint64_t
+iscsi_get_isid(const uint8_t isid[6])
+{
+ return (uint64_t)isid[0] << 40 |
+ (uint64_t)isid[1] << 32 |
+ (uint64_t)isid[2] << 24 |
+ (uint64_t)isid[3] << 16 |
+ (uint64_t)isid[4] << 8 |
+ (uint64_t)isid[5];
+}
+
+static int
+bin2hex(char *buf, size_t len, const uint8_t *data, size_t data_len)
+{
+ const char *digits = "0123456789ABCDEF";
+ size_t total = 0;
+ size_t idx;
+
+ if (len < 3) {
+ return -1;
+ }
+ buf[total] = '0';
+ total++;
+ buf[total] = 'x';
+ total++;
+ buf[total] = '\0';
+
+ for (idx = 0; idx < data_len; idx++) {
+ if (total + 3 > len) {
+ buf[total] = '\0';
+ return - 1;
+ }
+ buf[total] = digits[(data[idx] >> 4) & 0x0fU];
+ total++;
+ buf[total] = digits[data[idx] & 0x0fU];
+ total++;
+ }
+ buf[total] = '\0';
+ return total;
+}
+
+static int
+hex2bin(uint8_t *data, size_t data_len, const char *str)
+{
+ const char *digits = "0123456789ABCDEF";
+ const char *dp;
+ const char *p;
+ size_t total = 0;
+ int n0, n1;
+
+ p = str;
+ if (p[0] != '0' && (p[1] != 'x' && p[1] != 'X')) {
+ return -1;
+ }
+ p += 2;
+
+ while (p[0] != '\0' && p[1] != '\0') {
+ if (total >= data_len) {
+ return -1;
+ }
+ dp = strchr(digits, toupper((int) p[0]));
+ if (dp == NULL) {
+ return -1;
+ }
+ n0 = (int)(dp - digits);
+ dp = strchr(digits, toupper((int) p[1]));
+ if (dp == NULL) {
+ return -1;
+ }
+ n1 = (int)(dp - digits);
+
+ data[total] = (uint8_t)(((n0 & 0x0fU) << 4) | (n1 & 0x0fU));
+ total++;
+ p += 2;
+ }
+ return total;
+}
+
+static int
+iscsi_reject(struct spdk_iscsi_conn *conn, struct spdk_iscsi_pdu *pdu,
+ int reason)
+{
+ struct spdk_iscsi_pdu *rsp_pdu;
+ struct iscsi_bhs_reject *rsph;
+ uint8_t *data;
+ int total_ahs_len;
+ int data_len;
+ int alloc_len;
+
+ pdu->is_rejected = true;
+
+ total_ahs_len = pdu->bhs.total_ahs_len;
+ data_len = 0;
+ alloc_len = ISCSI_BHS_LEN + (4 * total_ahs_len);
+
+ if (conn->header_digest) {
+ alloc_len += ISCSI_DIGEST_LEN;
+ }
+
+ data = calloc(1, alloc_len);
+ if (!data) {
+ SPDK_ERRLOG("calloc() failed for data segment\n");
+ return -ENOMEM;
+ }
+
+ SPDK_DEBUGLOG(SPDK_LOG_ISCSI, "Reject PDU reason=%d\n", reason);
+
+ if (conn->sess != NULL) {
+ SPDK_DEBUGLOG(SPDK_LOG_ISCSI,
+ "StatSN=%u, ExpCmdSN=%u, MaxCmdSN=%u\n",
+ conn->StatSN, conn->sess->ExpCmdSN,
+ conn->sess->MaxCmdSN);
+ } else {
+ SPDK_DEBUGLOG(SPDK_LOG_ISCSI, "StatSN=%u\n", conn->StatSN);
+ }
+
+ memcpy(data, &pdu->bhs, ISCSI_BHS_LEN);
+ data_len += ISCSI_BHS_LEN;
+
+ if (total_ahs_len != 0) {
+ total_ahs_len = spdk_min((4 * total_ahs_len), ISCSI_AHS_LEN);
+ memcpy(data + data_len, pdu->ahs, total_ahs_len);
+ data_len += total_ahs_len;
+ }
+
+ if (conn->header_digest) {
+ memcpy(data + data_len, pdu->header_digest, ISCSI_DIGEST_LEN);
+ data_len += ISCSI_DIGEST_LEN;
+ }
+
+ rsp_pdu = iscsi_get_pdu(conn);
+ if (rsp_pdu == NULL) {
+ free(data);
+ return -ENOMEM;
+ }
+
+ rsph = (struct iscsi_bhs_reject *)&rsp_pdu->bhs;
+ rsp_pdu->data = data;
+ rsph->opcode = ISCSI_OP_REJECT;
+ rsph->flags |= 0x80; /* bit 0 is default to 1 */
+ rsph->reason = reason;
+ DSET24(rsph->data_segment_len, data_len);
+
+ rsph->ffffffff = 0xffffffffU;
+ to_be32(&rsph->stat_sn, conn->StatSN);
+ conn->StatSN++;
+
+ if (conn->sess != NULL) {
+ to_be32(&rsph->exp_cmd_sn, conn->sess->ExpCmdSN);
+ to_be32(&rsph->max_cmd_sn, conn->sess->MaxCmdSN);
+ } else {
+ to_be32(&rsph->exp_cmd_sn, 1);
+ to_be32(&rsph->max_cmd_sn, 1);
+ }
+
+ SPDK_LOGDUMP(SPDK_LOG_ISCSI, "PDU", (void *)&rsp_pdu->bhs, ISCSI_BHS_LEN);
+
+ iscsi_conn_write_pdu(conn, rsp_pdu, iscsi_conn_pdu_generic_complete, NULL);
+
+ return 0;
+}
+
+uint32_t
+iscsi_pdu_calc_header_digest(struct spdk_iscsi_pdu *pdu)
+{
+ uint32_t crc32c;
+ uint32_t ahs_len_bytes = pdu->bhs.total_ahs_len * 4;
+
+ crc32c = SPDK_CRC32C_INITIAL;
+ crc32c = spdk_crc32c_update(&pdu->bhs, ISCSI_BHS_LEN, crc32c);
+
+ if (ahs_len_bytes) {
+ crc32c = spdk_crc32c_update(pdu->ahs, ahs_len_bytes, crc32c);
+ }
+
+ /* BHS and AHS are always 4-byte multiples in length, so no padding is necessary. */
+ crc32c = crc32c ^ SPDK_CRC32C_XOR;
+ return crc32c;
+}
+
+uint32_t
+iscsi_pdu_calc_data_digest(struct spdk_iscsi_pdu *pdu)
+{
+ uint32_t data_len = DGET24(pdu->bhs.data_segment_len);
+ uint32_t crc32c;
+ uint32_t mod;
+ struct iovec iov;
+ uint32_t num_blocks;
+
+ crc32c = SPDK_CRC32C_INITIAL;
+ if (spdk_likely(!pdu->dif_insert_or_strip)) {
+ crc32c = spdk_crc32c_update(pdu->data, data_len, crc32c);
+ } else {
+ iov.iov_base = pdu->data_buf;
+ iov.iov_len = pdu->data_buf_len;
+ num_blocks = pdu->data_buf_len / pdu->dif_ctx.block_size;
+
+ spdk_dif_update_crc32c(&iov, 1, num_blocks, &crc32c, &pdu->dif_ctx);
+ }
+
+ mod = data_len % ISCSI_ALIGNMENT;
+ if (mod != 0) {
+ uint32_t pad_length = ISCSI_ALIGNMENT - mod;
+ uint8_t pad[3] = {0, 0, 0};
+
+ assert(pad_length > 0);
+ assert(pad_length <= sizeof(pad));
+ crc32c = spdk_crc32c_update(pad, pad_length, crc32c);
+ }
+
+ crc32c = crc32c ^ SPDK_CRC32C_XOR;
+ return crc32c;
+}
+
+static int
+iscsi_conn_read_data_segment(struct spdk_iscsi_conn *conn,
+ struct spdk_iscsi_pdu *pdu,
+ uint32_t segment_len)
+{
+ struct iovec buf_iov, iovs[32];
+ int rc, _rc;
+
+ if (spdk_likely(!pdu->dif_insert_or_strip)) {
+ return iscsi_conn_read_data(conn,
+ segment_len - pdu->data_valid_bytes,
+ pdu->data_buf + pdu->data_valid_bytes);
+ } else {
+ buf_iov.iov_base = pdu->data_buf;
+ buf_iov.iov_len = pdu->data_buf_len;
+ rc = spdk_dif_set_md_interleave_iovs(iovs, 32, &buf_iov, 1,
+ pdu->data_valid_bytes,
+ segment_len - pdu->data_valid_bytes, NULL,
+ &pdu->dif_ctx);
+ if (rc > 0) {
+ rc = iscsi_conn_readv_data(conn, iovs, rc);
+ if (rc > 0) {
+ _rc = spdk_dif_generate_stream(&buf_iov, 1,
+ pdu->data_valid_bytes, rc,
+ &pdu->dif_ctx);
+ if (_rc != 0) {
+ SPDK_ERRLOG("DIF generate failed\n");
+ rc = _rc;
+ }
+ }
+ } else {
+ SPDK_ERRLOG("Setup iovs for interleaved metadata failed\n");
+ }
+ return rc;
+ }
+}
+
+struct _iscsi_sgl {
+ struct iovec *iov;
+ int iovcnt;
+ uint32_t iov_offset;
+ uint32_t total_size;
+};
+
+static inline void
+_iscsi_sgl_init(struct _iscsi_sgl *s, struct iovec *iovs, int iovcnt,
+ uint32_t iov_offset)
+{
+ s->iov = iovs;
+ s->iovcnt = iovcnt;
+ s->iov_offset = iov_offset;
+ s->total_size = 0;
+}
+
+static inline bool
+_iscsi_sgl_append(struct _iscsi_sgl *s, uint8_t *data, uint32_t data_len)
+{
+ if (s->iov_offset >= data_len) {
+ s->iov_offset -= data_len;
+ } else {
+ assert(s->iovcnt > 0);
+ s->iov->iov_base = data + s->iov_offset;
+ s->iov->iov_len = data_len - s->iov_offset;
+ s->total_size += data_len - s->iov_offset;
+ s->iov_offset = 0;
+ s->iov++;
+ s->iovcnt--;
+ if (s->iovcnt == 0) {
+ return false;
+ }
+ }
+
+ return true;
+}
+
+/* Build iovec array to leave metadata space for every data block
+ * when reading data segment from socket.
+ */
+static inline bool
+_iscsi_sgl_append_with_md(struct _iscsi_sgl *s,
+ void *buf, uint32_t buf_len, uint32_t data_len,
+ struct spdk_dif_ctx *dif_ctx)
+{
+ int rc;
+ uint32_t total_size = 0;
+ struct iovec buf_iov;
+
+ if (s->iov_offset >= data_len) {
+ s->iov_offset -= data_len;
+ } else {
+ buf_iov.iov_base = buf;
+ buf_iov.iov_len = buf_len;
+ rc = spdk_dif_set_md_interleave_iovs(s->iov, s->iovcnt, &buf_iov, 1,
+ s->iov_offset, data_len - s->iov_offset,
+ &total_size, dif_ctx);
+ if (rc < 0) {
+ SPDK_ERRLOG("Failed to setup iovs for DIF strip\n");
+ return false;
+ }
+
+ s->total_size += total_size;
+ s->iov_offset = 0;
+ assert(s->iovcnt >= rc);
+ s->iovcnt -= rc;
+ s->iov += rc;
+
+ if (s->iovcnt == 0) {
+ return false;
+ }
+ }
+
+ return true;
+}
+
+int
+iscsi_build_iovs(struct spdk_iscsi_conn *conn, struct iovec *iovs, int iovcnt,
+ struct spdk_iscsi_pdu *pdu, uint32_t *_mapped_length)
+{
+ struct _iscsi_sgl sgl;
+ int enable_digest;
+ uint32_t total_ahs_len;
+ uint32_t data_len;
+
+ if (iovcnt == 0) {
+ return 0;
+ }
+
+ total_ahs_len = pdu->bhs.total_ahs_len;
+ data_len = DGET24(pdu->bhs.data_segment_len);
+ data_len = ISCSI_ALIGN(data_len);
+
+ enable_digest = 1;
+ if (pdu->bhs.opcode == ISCSI_OP_LOGIN_RSP) {
+ /* this PDU should be sent without digest */
+ enable_digest = 0;
+ }
+
+ _iscsi_sgl_init(&sgl, iovs, iovcnt, pdu->writev_offset);
+
+ /* BHS */
+ if (!_iscsi_sgl_append(&sgl, (uint8_t *)&pdu->bhs, ISCSI_BHS_LEN)) {
+ goto end;
+ }
+ /* AHS */
+ if (total_ahs_len > 0) {
+ if (!_iscsi_sgl_append(&sgl, pdu->ahs, 4 * total_ahs_len)) {
+ goto end;
+ }
+ }
+
+ /* Header Digest */
+ if (enable_digest && conn->header_digest) {
+ if (!_iscsi_sgl_append(&sgl, pdu->header_digest, ISCSI_DIGEST_LEN)) {
+ goto end;
+ }
+ }
+
+ /* Data Segment */
+ if (data_len > 0) {
+ if (!pdu->dif_insert_or_strip) {
+ if (!_iscsi_sgl_append(&sgl, pdu->data, data_len)) {
+ goto end;
+ }
+ } else {
+ if (!_iscsi_sgl_append_with_md(&sgl, pdu->data, pdu->data_buf_len,
+ data_len, &pdu->dif_ctx)) {
+ goto end;
+ }
+ }
+ }
+
+ /* Data Digest */
+ if (enable_digest && conn->data_digest && data_len != 0) {
+ _iscsi_sgl_append(&sgl, pdu->data_digest, ISCSI_DIGEST_LEN);
+ }
+
+end:
+ if (_mapped_length != NULL) {
+ *_mapped_length = sgl.total_size;
+ }
+
+ return iovcnt - sgl.iovcnt;
+}
+
+void iscsi_free_sess(struct spdk_iscsi_sess *sess)
+{
+ if (sess == NULL) {
+ return;
+ }
+
+ sess->tag = 0;
+ sess->target = NULL;
+ sess->session_type = SESSION_TYPE_INVALID;
+ iscsi_param_free(sess->params);
+ free(sess->conns);
+ spdk_scsi_port_free(&sess->initiator_port);
+ spdk_mempool_put(g_iscsi.session_pool, (void *)sess);
+}
+
+static int
+create_iscsi_sess(struct spdk_iscsi_conn *conn,
+ struct spdk_iscsi_tgt_node *target,
+ enum session_type session_type)
+{
+ struct spdk_iscsi_sess *sess;
+ int rc;
+
+ sess = spdk_mempool_get(g_iscsi.session_pool);
+ if (!sess) {
+ SPDK_ERRLOG("Unable to get session object\n");
+ SPDK_ERRLOG("MaxSessions set to %d\n", g_iscsi.MaxSessions);
+ return -ENOMEM;
+ }
+
+ /* configuration values */
+ pthread_mutex_lock(&g_iscsi.mutex);
+
+ sess->MaxConnections = g_iscsi.MaxConnectionsPerSession;
+ sess->MaxOutstandingR2T = DEFAULT_MAXOUTSTANDINGR2T;
+
+ sess->DefaultTime2Wait = g_iscsi.DefaultTime2Wait;
+ sess->DefaultTime2Retain = g_iscsi.DefaultTime2Retain;
+ sess->FirstBurstLength = g_iscsi.FirstBurstLength;
+ sess->MaxBurstLength = SPDK_ISCSI_MAX_BURST_LENGTH;
+ sess->InitialR2T = DEFAULT_INITIALR2T;
+ sess->ImmediateData = g_iscsi.ImmediateData;
+ sess->DataPDUInOrder = DEFAULT_DATAPDUINORDER;
+ sess->DataSequenceInOrder = DEFAULT_DATASEQUENCEINORDER;
+ sess->ErrorRecoveryLevel = g_iscsi.ErrorRecoveryLevel;
+
+ pthread_mutex_unlock(&g_iscsi.mutex);
+
+ sess->tag = conn->pg_tag;
+
+ sess->conns = calloc(sess->MaxConnections, sizeof(*sess->conns));
+ if (!sess->conns) {
+ SPDK_ERRLOG("calloc() failed for connection array\n");
+ return -ENOMEM;
+ }
+
+ sess->connections = 0;
+
+ sess->conns[sess->connections] = conn;
+ sess->connections++;
+
+ sess->params = NULL;
+ sess->target = target;
+ sess->isid = 0;
+ sess->session_type = session_type;
+ sess->current_text_itt = 0xffffffffU;
+
+ /* set default params */
+ rc = iscsi_sess_params_init(&sess->params);
+ if (rc < 0) {
+ SPDK_ERRLOG("iscsi_sess_params_init() failed\n");
+ goto error_return;
+ }
+ /* replace with config value */
+ rc = iscsi_param_set_int(sess->params, "MaxConnections",
+ sess->MaxConnections);
+ if (rc < 0) {
+ SPDK_ERRLOG("iscsi_param_set_int() failed\n");
+ goto error_return;
+ }
+
+ rc = iscsi_param_set_int(sess->params, "MaxOutstandingR2T",
+ sess->MaxOutstandingR2T);
+ if (rc < 0) {
+ SPDK_ERRLOG("iscsi_param_set_int() failed\n");
+ goto error_return;
+ }
+
+ rc = iscsi_param_set_int(sess->params, "DefaultTime2Wait",
+ sess->DefaultTime2Wait);
+ if (rc < 0) {
+ SPDK_ERRLOG("iscsi_param_set_int() failed\n");
+ goto error_return;
+ }
+
+ rc = iscsi_param_set_int(sess->params, "DefaultTime2Retain",
+ sess->DefaultTime2Retain);
+ if (rc < 0) {
+ SPDK_ERRLOG("iscsi_param_set_int() failed\n");
+ goto error_return;
+ }
+
+ rc = iscsi_param_set_int(sess->params, "FirstBurstLength",
+ sess->FirstBurstLength);
+ if (rc < 0) {
+ SPDK_ERRLOG("iscsi_param_set_int() failed\n");
+ goto error_return;
+ }
+
+ rc = iscsi_param_set_int(sess->params, "MaxBurstLength",
+ sess->MaxBurstLength);
+ if (rc < 0) {
+ SPDK_ERRLOG("iscsi_param_set_int() failed\n");
+ goto error_return;
+ }
+
+ rc = iscsi_param_set(sess->params, "InitialR2T",
+ sess->InitialR2T ? "Yes" : "No");
+ if (rc < 0) {
+ SPDK_ERRLOG("iscsi_param_set() failed\n");
+ goto error_return;
+ }
+
+ rc = iscsi_param_set(sess->params, "ImmediateData",
+ sess->ImmediateData ? "Yes" : "No");
+ if (rc < 0) {
+ SPDK_ERRLOG("iscsi_param_set() failed\n");
+ goto error_return;
+ }
+
+ rc = iscsi_param_set(sess->params, "DataPDUInOrder",
+ sess->DataPDUInOrder ? "Yes" : "No");
+ if (rc < 0) {
+ SPDK_ERRLOG("iscsi_param_set() failed\n");
+ goto error_return;
+ }
+
+ rc = iscsi_param_set(sess->params, "DataSequenceInOrder",
+ sess->DataSequenceInOrder ? "Yes" : "No");
+ if (rc < 0) {
+ SPDK_ERRLOG("iscsi_param_set() failed\n");
+ goto error_return;
+ }
+
+ rc = iscsi_param_set_int(sess->params, "ErrorRecoveryLevel",
+ sess->ErrorRecoveryLevel);
+ if (rc < 0) {
+ SPDK_ERRLOG("iscsi_param_set_int() failed\n");
+ goto error_return;
+ }
+
+ /* realloc buffer */
+ rc = iscsi_param_set_int(conn->params, "MaxRecvDataSegmentLength",
+ conn->MaxRecvDataSegmentLength);
+ if (rc < 0) {
+ SPDK_ERRLOG("iscsi_param_set_int() failed\n");
+ goto error_return;
+ }
+
+ /* sess for first connection of session */
+ conn->sess = sess;
+ return 0;
+
+error_return:
+ iscsi_free_sess(sess);
+ conn->sess = NULL;
+ return -1;
+}
+
+static struct spdk_iscsi_sess *
+get_iscsi_sess_by_tsih(uint16_t tsih)
+{
+ struct spdk_iscsi_sess *session;
+
+ if (tsih == 0 || tsih > g_iscsi.MaxSessions) {
+ return NULL;
+ }
+
+ session = g_iscsi.session[tsih - 1];
+ assert(tsih == session->tsih);
+
+ return session;
+}
+
+static uint8_t
+append_iscsi_sess(struct spdk_iscsi_conn *conn,
+ const char *initiator_port_name, uint16_t tsih, uint16_t cid)
+{
+ struct spdk_iscsi_sess *sess;
+
+ SPDK_DEBUGLOG(SPDK_LOG_ISCSI, "append session: init port name=%s, tsih=%u, cid=%u\n",
+ initiator_port_name, tsih, cid);
+
+ sess = get_iscsi_sess_by_tsih(tsih);
+ if (sess == NULL) {
+ SPDK_ERRLOG("spdk_get_iscsi_sess_by_tsih failed\n");
+ return ISCSI_LOGIN_CONN_ADD_FAIL;
+ }
+ if ((conn->pg_tag != sess->tag) ||
+ (strcasecmp(initiator_port_name, spdk_scsi_port_get_name(sess->initiator_port)) != 0) ||
+ (conn->target != sess->target)) {
+ /* no match */
+ SPDK_ERRLOG("no MCS session for init port name=%s, tsih=%d, cid=%d\n",
+ initiator_port_name, tsih, cid);
+ return ISCSI_LOGIN_CONN_ADD_FAIL;
+ }
+
+ if (sess->connections >= sess->MaxConnections) {
+ /* no slot for connection */
+ SPDK_ERRLOG("too many connections for init port name=%s, tsih=%d, cid=%d\n",
+ initiator_port_name, tsih, cid);
+ return ISCSI_LOGIN_TOO_MANY_CONNECTIONS;
+ }
+
+ SPDK_DEBUGLOG(SPDK_LOG_ISCSI, "Connections (tsih %d): %d\n", sess->tsih, sess->connections);
+ conn->sess = sess;
+
+ /*
+ * TODO: need a mutex or other sync mechanism to protect the session's
+ * connection list.
+ */
+ sess->conns[sess->connections] = conn;
+ sess->connections++;
+
+ return 0;
+}
+
+static int
+iscsi_append_text(struct spdk_iscsi_conn *conn __attribute__((__unused__)),
+ const char *key, const char *val, uint8_t *data,
+ int alloc_len, int data_len)
+{
+ int total;
+ int len;
+
+ total = data_len;
+ if (alloc_len < 1) {
+ return 0;
+ }
+ if (total > alloc_len) {
+ total = alloc_len;
+ data[total - 1] = '\0';
+ return total;
+ }
+
+ if (alloc_len - total < 1) {
+ SPDK_ERRLOG("data space small %d\n", alloc_len);
+ return total;
+ }
+ len = snprintf((char *) data + total, alloc_len - total, "%s=%s", key, val);
+ total += len + 1;
+
+ return total;
+}
+
+static int
+iscsi_append_param(struct spdk_iscsi_conn *conn, const char *key,
+ uint8_t *data, int alloc_len, int data_len)
+{
+ struct iscsi_param *param;
+ int rc;
+
+ param = iscsi_param_find(conn->params, key);
+ if (param == NULL) {
+ param = iscsi_param_find(conn->sess->params, key);
+ if (param == NULL) {
+ SPDK_DEBUGLOG(SPDK_LOG_ISCSI, "no key %.64s\n", key);
+ return data_len;
+ }
+ }
+ rc = iscsi_append_text(conn, param->key, param->val, data,
+ alloc_len, data_len);
+ return rc;
+}
+
+static int
+iscsi_auth_params(struct spdk_iscsi_conn *conn,
+ struct iscsi_param *params, const char *method, uint8_t *data,
+ int alloc_len, int data_len)
+{
+ char *in_val;
+ char *in_next;
+ char *new_val;
+ const char *algorithm;
+ const char *name;
+ const char *response;
+ const char *identifier;
+ const char *challenge;
+ int total;
+ int rc;
+
+ if (conn == NULL || params == NULL || method == NULL) {
+ return -1;
+ }
+ if (strcasecmp(method, "CHAP") == 0) {
+ /* method OK */
+ } else {
+ SPDK_ERRLOG("unsupported AuthMethod %.64s\n", method);
+ return -1;
+ }
+
+ total = data_len;
+ if (alloc_len < 1) {
+ return 0;
+ }
+ if (total > alloc_len) {
+ total = alloc_len;
+ data[total - 1] = '\0';
+ return total;
+ }
+
+ /* for temporary store */
+ in_val = malloc(ISCSI_TEXT_MAX_VAL_LEN + 1);
+ if (!in_val) {
+ SPDK_ERRLOG("malloc() failed for temporary store\n");
+ return -ENOMEM;
+ }
+
+ /* CHAP method (RFC1994) */
+ if ((algorithm = iscsi_param_get_val(params, "CHAP_A")) != NULL) {
+ if (conn->auth.chap_phase != ISCSI_CHAP_PHASE_WAIT_A) {
+ SPDK_ERRLOG("CHAP sequence error\n");
+ goto error_return;
+ }
+
+ /* CHAP_A is LIST type */
+ snprintf(in_val, ISCSI_TEXT_MAX_VAL_LEN + 1, "%s", algorithm);
+ in_next = in_val;
+ while ((new_val = spdk_strsepq(&in_next, ",")) != NULL) {
+ if (strcasecmp(new_val, "5") == 0) {
+ /* CHAP with MD5 */
+ break;
+ }
+ }
+ if (new_val == NULL) {
+ snprintf(in_val, ISCSI_TEXT_MAX_VAL_LEN + 1, "%s", "Reject");
+ new_val = in_val;
+ iscsi_append_text(conn, "CHAP_A", new_val,
+ data, alloc_len, total);
+ goto error_return;
+ }
+ /* selected algorithm is 5 (MD5) */
+ SPDK_DEBUGLOG(SPDK_LOG_ISCSI, "got CHAP_A=%s\n", new_val);
+ total = iscsi_append_text(conn, "CHAP_A", new_val,
+ data, alloc_len, total);
+
+ /* Identifier is one octet */
+ gen_random(conn->auth.chap_id, 1);
+ snprintf(in_val, ISCSI_TEXT_MAX_VAL_LEN, "%d",
+ (int) conn->auth.chap_id[0]);
+ total = iscsi_append_text(conn, "CHAP_I", in_val,
+ data, alloc_len, total);
+
+ /* Challenge Value is a variable stream of octets */
+ /* (binary length MUST not exceed 1024 bytes) */
+ conn->auth.chap_challenge_len = ISCSI_CHAP_CHALLENGE_LEN;
+ gen_random(conn->auth.chap_challenge, conn->auth.chap_challenge_len);
+ bin2hex(in_val, ISCSI_TEXT_MAX_VAL_LEN,
+ conn->auth.chap_challenge, conn->auth.chap_challenge_len);
+ total = iscsi_append_text(conn, "CHAP_C", in_val,
+ data, alloc_len, total);
+
+ conn->auth.chap_phase = ISCSI_CHAP_PHASE_WAIT_NR;
+ } else if ((name = iscsi_param_get_val(params, "CHAP_N")) != NULL) {
+ uint8_t resmd5[SPDK_MD5DIGEST_LEN];
+ uint8_t tgtmd5[SPDK_MD5DIGEST_LEN];
+ struct spdk_md5ctx md5ctx;
+ size_t decoded_len = 0;
+
+ if (conn->auth.chap_phase != ISCSI_CHAP_PHASE_WAIT_NR) {
+ SPDK_ERRLOG("CHAP sequence error\n");
+ goto error_return;
+ }
+
+ response = iscsi_param_get_val(params, "CHAP_R");
+ if (response == NULL) {
+ SPDK_ERRLOG("no response\n");
+ goto error_return;
+ }
+ if (response[0] == '0' &&
+ (response[1] == 'x' || response[1] == 'X')) {
+ rc = hex2bin(resmd5, SPDK_MD5DIGEST_LEN, response);
+ if (rc < 0 || rc != SPDK_MD5DIGEST_LEN) {
+ SPDK_ERRLOG("response format error\n");
+ goto error_return;
+ }
+ } else if (response[0] == '0' &&
+ (response[1] == 'b' || response[1] == 'B')) {
+ response += 2;
+ rc = spdk_base64_decode(resmd5, &decoded_len, response);
+ if (rc < 0 || decoded_len != SPDK_MD5DIGEST_LEN) {
+ SPDK_ERRLOG("response format error\n");
+ goto error_return;
+ }
+ } else {
+ SPDK_ERRLOG("response format error\n");
+ goto error_return;
+ }
+ SPDK_DEBUGLOG(SPDK_LOG_ISCSI, "got CHAP_N/CHAP_R\n");
+
+ SPDK_DEBUGLOG(SPDK_LOG_ISCSI, "ag_tag=%d\n", conn->chap_group);
+
+ rc = iscsi_chap_get_authinfo(&conn->auth, name, conn->chap_group);
+ if (rc < 0) {
+ /* SPDK_ERRLOG("auth user or secret is missing\n"); */
+ SPDK_ERRLOG("iscsi_chap_get_authinfo() failed\n");
+ goto error_return;
+ }
+ if (conn->auth.user[0] == '\0' || conn->auth.secret[0] == '\0') {
+ /* SPDK_ERRLOG("auth user or secret is missing\n"); */
+ SPDK_ERRLOG("auth failed (name %.64s)\n", name);
+ goto error_return;
+ }
+
+ md5init(&md5ctx);
+ /* Identifier */
+ md5update(&md5ctx, conn->auth.chap_id, 1);
+ /* followed by secret */
+ md5update(&md5ctx, conn->auth.secret,
+ strlen(conn->auth.secret));
+ /* followed by Challenge Value */
+ md5update(&md5ctx, conn->auth.chap_challenge,
+ conn->auth.chap_challenge_len);
+ /* tgtmd5 is expecting Response Value */
+ md5final(tgtmd5, &md5ctx);
+
+ bin2hex(in_val, ISCSI_TEXT_MAX_VAL_LEN, tgtmd5, SPDK_MD5DIGEST_LEN);
+
+#if 0
+ SPDK_DEBUGLOG(SPDK_LOG_ISCSI, "tgtmd5=%s, resmd5=%s\n", in_val, response);
+ spdk_dump("tgtmd5", tgtmd5, SPDK_MD5DIGEST_LEN);
+ spdk_dump("resmd5", resmd5, SPDK_MD5DIGEST_LEN);
+#endif
+
+ /* compare MD5 digest */
+ if (memcmp(tgtmd5, resmd5, SPDK_MD5DIGEST_LEN) != 0) {
+ /* not match */
+ /* SPDK_ERRLOG("auth user or secret is missing\n"); */
+ SPDK_ERRLOG("auth failed (name %.64s)\n", name);
+ goto error_return;
+ }
+ /* OK initiator's secret */
+ conn->authenticated = true;
+
+ /* mutual CHAP? */
+ identifier = iscsi_param_get_val(params, "CHAP_I");
+ if (identifier != NULL) {
+ conn->auth.chap_mid[0] = (uint8_t) strtol(identifier, NULL, 10);
+ challenge = iscsi_param_get_val(params, "CHAP_C");
+ if (challenge == NULL) {
+ SPDK_ERRLOG("CHAP sequence error\n");
+ goto error_return;
+ }
+ if (challenge[0] == '0' &&
+ (challenge[1] == 'x' || challenge[1] == 'X')) {
+ rc = hex2bin(conn->auth.chap_mchallenge,
+ ISCSI_CHAP_CHALLENGE_LEN, challenge);
+ if (rc < 0) {
+ SPDK_ERRLOG("challenge format error\n");
+ goto error_return;
+ }
+ conn->auth.chap_mchallenge_len = rc;
+ } else if (challenge[0] == '0' &&
+ (challenge[1] == 'b' || challenge[1] == 'B')) {
+ challenge += 2;
+ rc = spdk_base64_decode(conn->auth.chap_mchallenge,
+ &decoded_len, challenge);
+ if (rc < 0) {
+ SPDK_ERRLOG("challenge format error\n");
+ goto error_return;
+ }
+ conn->auth.chap_mchallenge_len = decoded_len;
+ } else {
+ SPDK_ERRLOG("challenge format error\n");
+ goto error_return;
+ }
+#if 0
+ spdk_dump("MChallenge", conn->auth.chap_mchallenge,
+ conn->auth.chap_mchallenge_len);
+#endif
+ SPDK_DEBUGLOG(SPDK_LOG_ISCSI, "got CHAP_I/CHAP_C\n");
+
+ if (conn->auth.muser[0] == '\0' || conn->auth.msecret[0] == '\0') {
+ /* SPDK_ERRLOG("mutual auth user or secret is missing\n"); */
+ SPDK_ERRLOG("auth failed (name %.64s)\n", name);
+ goto error_return;
+ }
+
+ md5init(&md5ctx);
+ /* Identifier */
+ md5update(&md5ctx, conn->auth.chap_mid, 1);
+ /* followed by secret */
+ md5update(&md5ctx, conn->auth.msecret,
+ strlen(conn->auth.msecret));
+ /* followed by Challenge Value */
+ md5update(&md5ctx, conn->auth.chap_mchallenge,
+ conn->auth.chap_mchallenge_len);
+ /* tgtmd5 is Response Value */
+ md5final(tgtmd5, &md5ctx);
+
+ bin2hex(in_val, ISCSI_TEXT_MAX_VAL_LEN, tgtmd5, SPDK_MD5DIGEST_LEN);
+
+ total = iscsi_append_text(conn, "CHAP_N",
+ conn->auth.muser, data, alloc_len, total);
+ total = iscsi_append_text(conn, "CHAP_R",
+ in_val, data, alloc_len, total);
+ } else {
+ /* not mutual */
+ if (conn->mutual_chap) {
+ SPDK_ERRLOG("required mutual CHAP\n");
+ goto error_return;
+ }
+ }
+
+ conn->auth.chap_phase = ISCSI_CHAP_PHASE_END;
+ } else {
+ /* not found CHAP keys */
+ SPDK_DEBUGLOG(SPDK_LOG_ISCSI, "start CHAP\n");
+ conn->auth.chap_phase = ISCSI_CHAP_PHASE_WAIT_A;
+ }
+
+ free(in_val);
+ return total;
+
+error_return:
+ conn->auth.chap_phase = ISCSI_CHAP_PHASE_WAIT_A;
+ free(in_val);
+ return -1;
+}
+
+static int
+iscsi_check_values(struct spdk_iscsi_conn *conn)
+{
+ if (conn->sess->FirstBurstLength > conn->sess->MaxBurstLength) {
+ SPDK_ERRLOG("FirstBurstLength(%d) > MaxBurstLength(%d)\n",
+ conn->sess->FirstBurstLength,
+ conn->sess->MaxBurstLength);
+ return -1;
+ }
+ if (conn->sess->FirstBurstLength > g_iscsi.FirstBurstLength) {
+ SPDK_ERRLOG("FirstBurstLength(%d) > iSCSI target restriction(%d)\n",
+ conn->sess->FirstBurstLength, g_iscsi.FirstBurstLength);
+ return -1;
+ }
+ if (conn->sess->MaxBurstLength > 0x00ffffff) {
+ SPDK_ERRLOG("MaxBurstLength(%d) > 0x00ffffff\n",
+ conn->sess->MaxBurstLength);
+ return -1;
+ }
+
+ if (conn->MaxRecvDataSegmentLength < 512) {
+ SPDK_ERRLOG("MaxRecvDataSegmentLength(%d) < 512\n",
+ conn->MaxRecvDataSegmentLength);
+ return -1;
+ }
+ if (conn->MaxRecvDataSegmentLength > 0x00ffffff) {
+ SPDK_ERRLOG("MaxRecvDataSegmentLength(%d) > 0x00ffffff\n",
+ conn->MaxRecvDataSegmentLength);
+ return -1;
+ }
+ return 0;
+}
+
+static int
+iscsi_conn_params_update(struct spdk_iscsi_conn *conn)
+{
+ int rc;
+ uint32_t recv_buf_size;
+
+ /* update internal variables */
+ rc = iscsi_copy_param2var(conn);
+ if (rc < 0) {
+ SPDK_ERRLOG("iscsi_copy_param2var() failed\n");
+ if (conn->state < ISCSI_CONN_STATE_EXITING) {
+ conn->state = ISCSI_CONN_STATE_EXITING;
+ }
+ return rc;
+ }
+
+ /* check value */
+ rc = iscsi_check_values(conn);
+ if (rc < 0) {
+ SPDK_ERRLOG("iscsi_check_values() failed\n");
+ if (conn->state < ISCSI_CONN_STATE_EXITING) {
+ conn->state = ISCSI_CONN_STATE_EXITING;
+ }
+ }
+
+ /* The socket receive buffer may need to be adjusted based on the new parameters */
+
+ /* Don't allow the recv buffer to be 0 or very large. */
+ recv_buf_size = spdk_max(0x1000, spdk_min(0x2000, conn->sess->FirstBurstLength));
+
+ /* Add in extra space for the PDU */
+ recv_buf_size += ISCSI_BHS_LEN + ISCSI_AHS_LEN;
+
+ if (conn->header_digest) {
+ recv_buf_size += ISCSI_DIGEST_LEN;
+ }
+
+ if (conn->data_digest) {
+ recv_buf_size += ISCSI_DIGEST_LEN;
+ }
+
+ /* Set up to buffer up to 4 commands with immediate data at once */
+ if (spdk_sock_set_recvbuf(conn->sock, recv_buf_size * 4) < 0) {
+ /* Not fatal. */
+ }
+
+ return rc;
+}
+
+static void
+iscsi_conn_login_pdu_err_complete(void *arg)
+{
+ struct spdk_iscsi_conn *conn = arg;
+
+ if (conn->full_feature) {
+ iscsi_conn_params_update(conn);
+ }
+}
+
+static void
+iscsi_conn_login_pdu_success_complete(void *arg)
+{
+ struct spdk_iscsi_conn *conn = arg;
+
+ if (conn->state >= ISCSI_CONN_STATE_EXITING) {
+ /* Connection is being exited before this callback is executed. */
+ SPDK_DEBUGLOG(SPDK_LOG_ISCSI, "Connection is already exited.\n");
+ return;
+ }
+ if (conn->full_feature) {
+ if (iscsi_conn_params_update(conn) != 0) {
+ return;
+ }
+ }
+ conn->state = ISCSI_CONN_STATE_RUNNING;
+ if (conn->full_feature != 0) {
+ iscsi_conn_schedule(conn);
+ }
+}
+
+/*
+ * The response function of spdk_iscsi_op_login
+ */
+static void
+iscsi_op_login_response(struct spdk_iscsi_conn *conn,
+ struct spdk_iscsi_pdu *rsp_pdu, struct iscsi_param *params,
+ iscsi_conn_xfer_complete_cb cb_fn)
+{
+ struct iscsi_bhs_login_rsp *rsph;
+
+ rsph = (struct iscsi_bhs_login_rsp *)&rsp_pdu->bhs;
+ rsph->version_max = ISCSI_VERSION;
+ rsph->version_act = ISCSI_VERSION;
+ DSET24(rsph->data_segment_len, rsp_pdu->data_segment_len);
+
+ to_be32(&rsph->stat_sn, conn->StatSN);
+ conn->StatSN++;
+
+ if (conn->sess != NULL) {
+ to_be32(&rsph->exp_cmd_sn, conn->sess->ExpCmdSN);
+ to_be32(&rsph->max_cmd_sn, conn->sess->MaxCmdSN);
+ } else {
+ to_be32(&rsph->exp_cmd_sn, rsp_pdu->cmd_sn);
+ to_be32(&rsph->max_cmd_sn, rsp_pdu->cmd_sn);
+ }
+
+ SPDK_LOGDUMP(SPDK_LOG_ISCSI, "PDU", (uint8_t *)rsph, ISCSI_BHS_LEN);
+ SPDK_LOGDUMP(SPDK_LOG_ISCSI, "DATA", rsp_pdu->data, rsp_pdu->data_segment_len);
+
+ /* Set T/CSG/NSG to reserved if login error. */
+ if (rsph->status_class != 0) {
+ rsph->flags &= ~ISCSI_LOGIN_TRANSIT;
+ rsph->flags &= ~ISCSI_LOGIN_CURRENT_STAGE_MASK;
+ rsph->flags &= ~ISCSI_LOGIN_NEXT_STAGE_MASK;
+ }
+ iscsi_param_free(params);
+ iscsi_conn_write_pdu(conn, rsp_pdu, cb_fn, conn);
+}
+
+/*
+ * The function which is used to initialize the internal response data
+ * structure of iscsi login function.
+ * return:
+ * 0, success;
+ * otherwise, error;
+ */
+static int
+iscsi_op_login_rsp_init(struct spdk_iscsi_conn *conn,
+ struct spdk_iscsi_pdu *pdu, struct spdk_iscsi_pdu *rsp_pdu)
+{
+ struct iscsi_bhs_login_req *reqh;
+ struct iscsi_bhs_login_rsp *rsph;
+
+ rsph = (struct iscsi_bhs_login_rsp *)&rsp_pdu->bhs;
+ rsph->opcode = ISCSI_OP_LOGIN_RSP;
+ rsph->status_class = ISCSI_CLASS_SUCCESS;
+ rsph->status_detail = ISCSI_LOGIN_ACCEPT;
+ rsp_pdu->data_segment_len = 0;
+
+ /* The default MaxRecvDataSegmentLength 8192 is used during login. - RFC3720 */
+ rsp_pdu->data = calloc(1, 8192);
+ if (!rsp_pdu->data) {
+ SPDK_ERRLOG("calloc() failed for data segment\n");
+ rsph->status_class = ISCSI_CLASS_TARGET_ERROR;
+ rsph->status_detail = ISCSI_LOGIN_STATUS_NO_RESOURCES;
+ return SPDK_ISCSI_LOGIN_ERROR_RESPONSE;
+ }
+ rsp_pdu->data_buf_len = 8192;
+
+ reqh = (struct iscsi_bhs_login_req *)&pdu->bhs;
+ rsph->flags |= (reqh->flags & ISCSI_LOGIN_TRANSIT);
+ rsph->flags |= (reqh->flags & ISCSI_LOGIN_CONTINUE);
+ rsph->flags |= (reqh->flags & ISCSI_LOGIN_CURRENT_STAGE_MASK);
+ if (ISCSI_BHS_LOGIN_GET_TBIT(rsph->flags)) {
+ rsph->flags |= (reqh->flags & ISCSI_LOGIN_NEXT_STAGE_MASK);
+ }
+
+ /* We don't need to convert from network byte order. Just store it */
+ memcpy(&rsph->isid, reqh->isid, 6);
+ rsph->tsih = reqh->tsih;
+ rsph->itt = reqh->itt;
+ rsp_pdu->cmd_sn = from_be32(&reqh->cmd_sn);
+
+ if (rsph->tsih) {
+ rsph->stat_sn = reqh->exp_stat_sn;
+ }
+
+ SPDK_LOGDUMP(SPDK_LOG_ISCSI, "PDU", (uint8_t *)&pdu->bhs, ISCSI_BHS_LEN);
+
+ SPDK_DEBUGLOG(SPDK_LOG_ISCSI,
+ "T=%d, C=%d, CSG=%d, NSG=%d, Min=%d, Max=%d, ITT=%x\n",
+ ISCSI_BHS_LOGIN_GET_TBIT(rsph->flags),
+ ISCSI_BHS_LOGIN_GET_CBIT(rsph->flags),
+ ISCSI_BHS_LOGIN_GET_CSG(rsph->flags),
+ ISCSI_BHS_LOGIN_GET_NSG(rsph->flags),
+ reqh->version_min, reqh->version_max, from_be32(&rsph->itt));
+
+ if (conn->sess != NULL) {
+ SPDK_DEBUGLOG(SPDK_LOG_ISCSI,
+ "CmdSN=%u, ExpStatSN=%u, StatSN=%u, ExpCmdSN=%u,"
+ "MaxCmdSN=%u\n", rsp_pdu->cmd_sn,
+ from_be32(&rsph->stat_sn), conn->StatSN,
+ conn->sess->ExpCmdSN,
+ conn->sess->MaxCmdSN);
+ } else {
+ SPDK_DEBUGLOG(SPDK_LOG_ISCSI,
+ "CmdSN=%u, ExpStatSN=%u, StatSN=%u\n",
+ rsp_pdu->cmd_sn, from_be32(&rsph->stat_sn),
+ conn->StatSN);
+ }
+
+ if (ISCSI_BHS_LOGIN_GET_TBIT(rsph->flags) &&
+ ISCSI_BHS_LOGIN_GET_CBIT(rsph->flags)) {
+ SPDK_ERRLOG("transit error\n");
+ rsph->status_class = ISCSI_CLASS_INITIATOR_ERROR;
+ rsph->status_detail = ISCSI_LOGIN_INITIATOR_ERROR;
+ return SPDK_ISCSI_LOGIN_ERROR_RESPONSE;
+ }
+ /* make sure reqh->version_max < ISCSI_VERSION */
+ if (reqh->version_min > ISCSI_VERSION) {
+ SPDK_ERRLOG("unsupported version min %d/max %d, expecting %d\n", reqh->version_min,
+ reqh->version_max, ISCSI_VERSION);
+ /* Unsupported version */
+ /* set all reserved flag to zero */
+ rsph->status_class = ISCSI_CLASS_INITIATOR_ERROR;
+ rsph->status_detail = ISCSI_LOGIN_UNSUPPORTED_VERSION;
+ return SPDK_ISCSI_LOGIN_ERROR_RESPONSE;
+ }
+
+ if ((ISCSI_BHS_LOGIN_GET_NSG(rsph->flags) == ISCSI_NSG_RESERVED_CODE) &&
+ ISCSI_BHS_LOGIN_GET_TBIT(rsph->flags)) {
+ /* set NSG to zero */
+ rsph->flags &= ~ISCSI_LOGIN_NEXT_STAGE_MASK;
+ /* also set other bits to zero */
+ rsph->flags &= ~ISCSI_LOGIN_TRANSIT;
+ rsph->flags &= ~ISCSI_LOGIN_CURRENT_STAGE_MASK;
+ SPDK_ERRLOG("Received reserved NSG code: %d\n", ISCSI_NSG_RESERVED_CODE);
+ /* Initiator error */
+ rsph->status_class = ISCSI_CLASS_INITIATOR_ERROR;
+ rsph->status_detail = ISCSI_LOGIN_INITIATOR_ERROR;
+ return SPDK_ISCSI_LOGIN_ERROR_RESPONSE;
+ }
+
+ return 0;
+}
+
+static int
+iscsi_op_login_store_incoming_params(struct spdk_iscsi_conn *conn,
+ struct spdk_iscsi_pdu *pdu, struct spdk_iscsi_pdu *rsp_pdu,
+ struct iscsi_param **params)
+{
+ struct iscsi_bhs_login_req *reqh;
+ struct iscsi_bhs_login_rsp *rsph;
+ int rc;
+
+ reqh = (struct iscsi_bhs_login_req *)&pdu->bhs;
+ rsph = (struct iscsi_bhs_login_rsp *)&rsp_pdu->bhs;
+
+ rc = iscsi_parse_params(params, pdu->data,
+ pdu->data_segment_len, ISCSI_BHS_LOGIN_GET_CBIT(reqh->flags),
+ &conn->partial_text_parameter);
+ if (rc < 0) {
+ SPDK_ERRLOG("iscsi_parse_params() failed\n");
+ iscsi_param_free(*params);
+ rsph->status_class = ISCSI_CLASS_INITIATOR_ERROR;
+ rsph->status_detail = ISCSI_LOGIN_INITIATOR_ERROR;
+ return SPDK_ISCSI_LOGIN_ERROR_PARAMETER;
+ }
+
+ return 0;
+}
+
+/*
+ * This function is used to initialize the port info
+ * return
+ * 0: success
+ * otherwise: error
+ */
+static int
+iscsi_op_login_initialize_port(struct spdk_iscsi_conn *conn,
+ struct spdk_iscsi_pdu *rsp_pdu,
+ char *initiator_port_name,
+ uint32_t name_length,
+ struct iscsi_param *params)
+{
+ const char *val;
+ struct iscsi_bhs_login_rsp *rsph;
+ rsph = (struct iscsi_bhs_login_rsp *)&rsp_pdu->bhs;
+
+ /* Initiator Name and Port */
+ val = iscsi_param_get_val(params, "InitiatorName");
+ if (val == NULL) {
+ SPDK_ERRLOG("InitiatorName is empty\n");
+ /* Missing parameter */
+ rsph->status_class = ISCSI_CLASS_INITIATOR_ERROR;
+ rsph->status_detail = ISCSI_LOGIN_MISSING_PARMS;
+ return SPDK_ISCSI_LOGIN_ERROR_RESPONSE;
+ }
+ snprintf(conn->initiator_name, sizeof(conn->initiator_name), "%s", val);
+ snprintf(initiator_port_name, name_length,
+ "%s,i,0x%12.12" PRIx64, val, iscsi_get_isid(rsph->isid));
+ spdk_strlwr(conn->initiator_name);
+ spdk_strlwr(initiator_port_name);
+ SPDK_DEBUGLOG(SPDK_LOG_ISCSI, "Initiator name: %s\n", conn->initiator_name);
+ SPDK_DEBUGLOG(SPDK_LOG_ISCSI, "Initiator port: %s\n", initiator_port_name);
+
+ return 0;
+}
+
+/*
+ * This function is used to judge the session type
+ * return
+ * 0: success
+ * Other value: error
+ */
+static int
+iscsi_op_login_session_type(struct spdk_iscsi_conn *conn,
+ struct spdk_iscsi_pdu *rsp_pdu,
+ enum session_type *session_type,
+ struct iscsi_param *params)
+{
+ const char *session_type_str;
+ struct iscsi_bhs_login_rsp *rsph;
+
+ rsph = (struct iscsi_bhs_login_rsp *)&rsp_pdu->bhs;
+ session_type_str = iscsi_param_get_val(params, "SessionType");
+ if (session_type_str == NULL) {
+ if (rsph->tsih != 0) {
+ *session_type = SESSION_TYPE_NORMAL;
+ } else {
+ SPDK_ERRLOG("SessionType is empty\n");
+ /* Missing parameter */
+ rsph->status_class = ISCSI_CLASS_INITIATOR_ERROR;
+ rsph->status_detail = ISCSI_LOGIN_MISSING_PARMS;
+ return SPDK_ISCSI_LOGIN_ERROR_RESPONSE;
+ }
+ } else {
+ if (strcasecmp(session_type_str, "Discovery") == 0) {
+ *session_type = SESSION_TYPE_DISCOVERY;
+ } else if (strcasecmp(session_type_str, "Normal") == 0) {
+ *session_type = SESSION_TYPE_NORMAL;
+ } else {
+ *session_type = SESSION_TYPE_INVALID;
+ SPDK_ERRLOG("SessionType is invalid\n");
+ /* Missing parameter */
+ rsph->status_class = ISCSI_CLASS_INITIATOR_ERROR;
+ rsph->status_detail = ISCSI_LOGIN_MISSING_PARMS;
+ return SPDK_ISCSI_LOGIN_ERROR_RESPONSE;
+ }
+ }
+ SPDK_DEBUGLOG(SPDK_LOG_ISCSI, "Session Type: %s\n", session_type_str);
+
+ return 0;
+}
+
+/*
+ * This function is used to check the target info
+ * return:
+ * 0: success
+ * otherwise: error
+ */
+static int
+iscsi_op_login_check_target(struct spdk_iscsi_conn *conn,
+ struct spdk_iscsi_pdu *rsp_pdu,
+ const char *target_name,
+ struct spdk_iscsi_tgt_node **target)
+{
+ bool result;
+ struct iscsi_bhs_login_rsp *rsph;
+
+ rsph = (struct iscsi_bhs_login_rsp *)&rsp_pdu->bhs;
+ *target = iscsi_find_tgt_node(target_name);
+ if (*target == NULL) {
+ SPDK_WARNLOG("target %s not found\n", target_name);
+ /* Not found */
+ rsph->status_class = ISCSI_CLASS_INITIATOR_ERROR;
+ rsph->status_detail = ISCSI_LOGIN_TARGET_NOT_FOUND;
+ return SPDK_ISCSI_LOGIN_ERROR_RESPONSE;
+ }
+ if (iscsi_tgt_node_is_destructed(*target)) {
+ SPDK_ERRLOG("target %s is removed\n", target_name);
+ rsph->status_class = ISCSI_CLASS_INITIATOR_ERROR;
+ rsph->status_detail = ISCSI_LOGIN_TARGET_REMOVED;
+ return SPDK_ISCSI_LOGIN_ERROR_RESPONSE;
+ }
+ result = iscsi_tgt_node_access(conn, *target,
+ conn->initiator_name,
+ conn->initiator_addr);
+ if (!result) {
+ SPDK_ERRLOG("access denied\n");
+ rsph->status_class = ISCSI_CLASS_INITIATOR_ERROR;
+ rsph->status_detail = ISCSI_LOGIN_AUTHORIZATION_FAIL;
+ return SPDK_ISCSI_LOGIN_ERROR_RESPONSE;
+ }
+
+ return 0;
+}
+
+/*
+ * This function use to check the session
+ * return:
+ * 0, success
+ * otherwise: error
+ */
+static int
+iscsi_op_login_check_session(struct spdk_iscsi_conn *conn,
+ struct spdk_iscsi_pdu *rsp_pdu,
+ char *initiator_port_name, int cid)
+
+{
+ int rc = 0;
+ struct iscsi_bhs_login_rsp *rsph;
+
+ rsph = (struct iscsi_bhs_login_rsp *)&rsp_pdu->bhs;
+ /* check existing session */
+ SPDK_DEBUGLOG(SPDK_LOG_ISCSI, "isid=%"PRIx64", tsih=%u, cid=%u\n",
+ iscsi_get_isid(rsph->isid), from_be16(&rsph->tsih), cid);
+ if (rsph->tsih != 0) {
+ /* multiple connections */
+ rc = append_iscsi_sess(conn, initiator_port_name,
+ from_be16(&rsph->tsih), cid);
+ if (rc != 0) {
+ SPDK_ERRLOG("isid=%"PRIx64", tsih=%u, cid=%u:"
+ "spdk_append_iscsi_sess() failed\n",
+ iscsi_get_isid(rsph->isid), from_be16(&rsph->tsih),
+ cid);
+ /* Can't include in session */
+ rsph->status_class = ISCSI_CLASS_INITIATOR_ERROR;
+ rsph->status_detail = rc;
+ return SPDK_ISCSI_LOGIN_ERROR_RESPONSE;
+ }
+ } else if (!g_iscsi.AllowDuplicateIsid) {
+ /* new session, drop old sess by the initiator */
+ iscsi_drop_conns(conn, initiator_port_name, 0 /* drop old */);
+ }
+
+ return rc;
+}
+
+/*
+ * This function is used to del the original param and update it with new
+ * value
+ * return:
+ * 0: success
+ * otherwise: error
+ */
+static int
+iscsi_op_login_update_param(struct spdk_iscsi_conn *conn,
+ const char *key, const char *value,
+ const char *list)
+{
+ int rc = 0;
+ struct iscsi_param *new_param, *orig_param;
+ int index;
+
+ orig_param = iscsi_param_find(conn->params, key);
+ if (orig_param == NULL) {
+ SPDK_ERRLOG("orig_param %s not found\n", key);
+ return SPDK_ISCSI_LOGIN_ERROR_PARAMETER;
+ }
+
+ index = orig_param->state_index;
+ rc = iscsi_param_del(&conn->params, key);
+ if (rc < 0) {
+ SPDK_ERRLOG("iscsi_param_del(%s) failed\n", key);
+ return SPDK_ISCSI_LOGIN_ERROR_PARAMETER;
+ }
+ rc = iscsi_param_add(&conn->params, key, value, list, ISPT_LIST);
+ if (rc < 0) {
+ SPDK_ERRLOG("iscsi_param_add() failed\n");
+ return SPDK_ISCSI_LOGIN_ERROR_PARAMETER;
+ }
+ new_param = iscsi_param_find(conn->params, key);
+ if (new_param == NULL) {
+ SPDK_ERRLOG("iscsi_param_find() failed\n");
+ return SPDK_ISCSI_LOGIN_ERROR_PARAMETER;
+ }
+ new_param->state_index = index;
+ return rc;
+}
+
+static int
+iscsi_negotiate_chap_param(struct spdk_iscsi_conn *conn)
+{
+ int rc = 0;
+
+ if (conn->disable_chap) {
+ rc = iscsi_op_login_update_param(conn, "AuthMethod", "None", "None");
+ } else if (conn->require_chap) {
+ rc = iscsi_op_login_update_param(conn, "AuthMethod", "CHAP", "CHAP");
+ }
+
+ return rc;
+}
+
+/*
+ * The function which is used to handle the part of session discovery
+ * return:
+ * 0, success;
+ * otherwise: error;
+ */
+static int
+iscsi_op_login_session_discovery_chap(struct spdk_iscsi_conn *conn)
+{
+ return iscsi_negotiate_chap_param(conn);
+}
+
+/*
+ * This function is used to update the param related with chap
+ * return:
+ * 0: success
+ * otherwise: error
+ */
+static int
+iscsi_op_login_negotiate_chap_param(struct spdk_iscsi_conn *conn,
+ struct spdk_iscsi_tgt_node *target)
+{
+ conn->disable_chap = target->disable_chap;
+ conn->require_chap = target->require_chap;
+ conn->mutual_chap = target->mutual_chap;
+ conn->chap_group = target->chap_group;
+
+ return iscsi_negotiate_chap_param(conn);
+}
+
+static int
+iscsi_op_login_negotiate_digest_param(struct spdk_iscsi_conn *conn,
+ struct spdk_iscsi_tgt_node *target)
+{
+ int rc;
+
+ if (target->header_digest) {
+ /*
+ * User specified header digests, so update the list of
+ * HeaderDigest values to remove "None" so that only
+ * initiators who support CRC32C can connect.
+ */
+ rc = iscsi_op_login_update_param(conn, "HeaderDigest", "CRC32C", "CRC32C");
+ if (rc < 0) {
+ return rc;
+ }
+ }
+
+ if (target->data_digest) {
+ /*
+ * User specified data digests, so update the list of
+ * DataDigest values to remove "None" so that only
+ * initiators who support CRC32C can connect.
+ */
+ rc = iscsi_op_login_update_param(conn, "DataDigest", "CRC32C", "CRC32C");
+ if (rc < 0) {
+ return rc;
+ }
+ }
+
+ return 0;
+}
+
+/*
+ * The function which is used to handle the part of normal login session
+ * return:
+ * 0, success;
+ * SPDK_ISCSI_LOGIN_ERROR_PARAMETER, parameter error;
+ */
+static int
+iscsi_op_login_session_normal(struct spdk_iscsi_conn *conn,
+ struct spdk_iscsi_pdu *rsp_pdu,
+ char *initiator_port_name,
+ struct iscsi_param *params,
+ int cid)
+{
+ struct spdk_iscsi_tgt_node *target = NULL;
+ const char *target_name;
+ const char *target_short_name;
+ struct iscsi_bhs_login_rsp *rsph;
+ int rc = 0;
+
+ rsph = (struct iscsi_bhs_login_rsp *)&rsp_pdu->bhs;
+ target_name = iscsi_param_get_val(params, "TargetName");
+
+ if (target_name == NULL) {
+ SPDK_ERRLOG("TargetName is empty\n");
+ /* Missing parameter */
+ rsph->status_class = ISCSI_CLASS_INITIATOR_ERROR;
+ rsph->status_detail = ISCSI_LOGIN_MISSING_PARMS;
+ return SPDK_ISCSI_LOGIN_ERROR_RESPONSE;
+ }
+
+ memset(conn->target_short_name, 0, MAX_TARGET_NAME);
+ target_short_name = strstr(target_name, ":");
+ if (target_short_name != NULL) {
+ target_short_name++; /* Advance past the ':' */
+ if (strlen(target_short_name) >= MAX_TARGET_NAME) {
+ SPDK_ERRLOG("Target Short Name (%s) is more than %u characters\n",
+ target_short_name, MAX_TARGET_NAME);
+ /* Invalid request */
+ rsph->status_class = ISCSI_CLASS_INITIATOR_ERROR;
+ rsph->status_detail = ISCSI_LOGIN_INVALID_LOGIN_REQUEST;
+ return SPDK_ISCSI_LOGIN_ERROR_RESPONSE;
+ }
+ snprintf(conn->target_short_name, MAX_TARGET_NAME, "%s",
+ target_short_name);
+ }
+
+ pthread_mutex_lock(&g_iscsi.mutex);
+ rc = iscsi_op_login_check_target(conn, rsp_pdu, target_name, &target);
+ pthread_mutex_unlock(&g_iscsi.mutex);
+
+ if (rc < 0) {
+ return rc;
+ }
+
+ conn->target = target;
+ conn->dev = target->dev;
+ conn->target_port = spdk_scsi_dev_find_port_by_id(target->dev,
+ conn->pg_tag);
+
+ rc = iscsi_op_login_check_session(conn, rsp_pdu,
+ initiator_port_name, cid);
+ if (rc < 0) {
+ return rc;
+ }
+
+ /* force target flags */
+ pthread_mutex_lock(&target->mutex);
+ rc = iscsi_op_login_negotiate_chap_param(conn, target);
+ pthread_mutex_unlock(&target->mutex);
+
+ if (rc == 0) {
+ rc = iscsi_op_login_negotiate_digest_param(conn, target);
+ }
+
+ if (rc != 0) {
+ /* Invalid request */
+ rsph->status_class = ISCSI_CLASS_INITIATOR_ERROR;
+ rsph->status_detail = ISCSI_LOGIN_INVALID_LOGIN_REQUEST;
+ }
+
+ return rc;
+}
+
+/*
+ * This function is used to set the info in the connection data structure
+ * return
+ * 0: success
+ * otherwise: error
+ */
+static int
+iscsi_op_login_set_conn_info(struct spdk_iscsi_conn *conn,
+ struct spdk_iscsi_pdu *rsp_pdu,
+ char *initiator_port_name,
+ enum session_type session_type, int cid)
+{
+ int rc = 0;
+ struct spdk_iscsi_tgt_node *target;
+ struct iscsi_bhs_login_rsp *rsph;
+ struct spdk_scsi_port *initiator_port;
+
+ target = conn->target;
+
+ rsph = (struct iscsi_bhs_login_rsp *)&rsp_pdu->bhs;
+ conn->authenticated = false;
+ conn->auth.chap_phase = ISCSI_CHAP_PHASE_WAIT_A;
+ conn->cid = cid;
+
+ if (conn->sess == NULL) {
+ /* create initiator port */
+ initiator_port = spdk_scsi_port_create(iscsi_get_isid(rsph->isid), 0, initiator_port_name);
+ if (initiator_port == NULL) {
+ SPDK_ERRLOG("create_port() failed\n");
+ rsph->status_class = ISCSI_CLASS_TARGET_ERROR;
+ rsph->status_detail = ISCSI_LOGIN_STATUS_NO_RESOURCES;
+ return SPDK_ISCSI_LOGIN_ERROR_RESPONSE;
+ }
+
+ /* new session */
+ rc = create_iscsi_sess(conn, target, session_type);
+ if (rc < 0) {
+ spdk_scsi_port_free(&initiator_port);
+ SPDK_ERRLOG("create_sess() failed\n");
+ rsph->status_class = ISCSI_CLASS_TARGET_ERROR;
+ rsph->status_detail = ISCSI_LOGIN_STATUS_NO_RESOURCES;
+ return SPDK_ISCSI_LOGIN_ERROR_RESPONSE;
+ }
+ /* initialize parameters */
+ conn->sess->initiator_port = initiator_port;
+ conn->StatSN = from_be32(&rsph->stat_sn);
+ conn->sess->isid = iscsi_get_isid(rsph->isid);
+
+ /* Initiator port TransportID */
+ spdk_scsi_port_set_iscsi_transport_id(conn->sess->initiator_port,
+ conn->initiator_name,
+ conn->sess->isid);
+
+ /* Discovery sessions will not have a target. */
+ if (target != NULL) {
+ conn->sess->queue_depth = target->queue_depth;
+ } else {
+ /*
+ * Assume discovery sessions have an effective command
+ * windows size of 1.
+ */
+ conn->sess->queue_depth = 1;
+ }
+ conn->sess->ExpCmdSN = rsp_pdu->cmd_sn;
+ conn->sess->MaxCmdSN = rsp_pdu->cmd_sn + conn->sess->queue_depth - 1;
+ }
+
+ conn->initiator_port = conn->sess->initiator_port;
+
+ return 0;
+}
+
+/*
+ * This function is used to set the target info
+ * return
+ * 0: success
+ * otherwise: error
+ */
+static int
+iscsi_op_login_set_target_info(struct spdk_iscsi_conn *conn,
+ struct spdk_iscsi_pdu *rsp_pdu,
+ enum session_type session_type)
+{
+ char buf[MAX_TMPBUF];
+ const char *val;
+ int rc = 0;
+ struct spdk_iscsi_tgt_node *target = conn->target;
+
+ /* declarative parameters */
+ if (target != NULL) {
+ pthread_mutex_lock(&target->mutex);
+ if (target->alias[0] != '\0') {
+ snprintf(buf, sizeof buf, "%s", target->alias);
+ } else {
+ snprintf(buf, sizeof buf, "%s", "");
+ }
+ pthread_mutex_unlock(&target->mutex);
+ rc = iscsi_param_set(conn->sess->params, "TargetAlias", buf);
+ if (rc < 0) {
+ SPDK_ERRLOG("iscsi_param_set() failed\n");
+ return SPDK_ISCSI_LOGIN_ERROR_PARAMETER;
+ }
+ }
+ snprintf(buf, sizeof buf, "%s:%s,%d", conn->portal_host, conn->portal_port,
+ conn->pg_tag);
+ rc = iscsi_param_set(conn->sess->params, "TargetAddress", buf);
+ if (rc < 0) {
+ SPDK_ERRLOG("iscsi_param_set() failed\n");
+ return SPDK_ISCSI_LOGIN_ERROR_PARAMETER;
+ }
+ snprintf(buf, sizeof buf, "%d", conn->pg_tag);
+ rc = iscsi_param_set(conn->sess->params, "TargetPortalGroupTag", buf);
+ if (rc < 0) {
+ SPDK_ERRLOG("iscsi_param_set() failed\n");
+ return SPDK_ISCSI_LOGIN_ERROR_PARAMETER;
+ }
+
+ /* write in response */
+ if (target != NULL) {
+ val = iscsi_param_get_val(conn->sess->params, "TargetAlias");
+ if (val != NULL && strlen(val) != 0) {
+ rsp_pdu->data_segment_len = iscsi_append_param(conn,
+ "TargetAlias",
+ rsp_pdu->data,
+ rsp_pdu->data_buf_len,
+ rsp_pdu->data_segment_len);
+ }
+ if (session_type == SESSION_TYPE_DISCOVERY) {
+ rsp_pdu->data_segment_len = iscsi_append_param(conn,
+ "TargetAddress",
+ rsp_pdu->data,
+ rsp_pdu->data_buf_len,
+ rsp_pdu->data_segment_len);
+ }
+ rsp_pdu->data_segment_len = iscsi_append_param(conn,
+ "TargetPortalGroupTag",
+ rsp_pdu->data,
+ rsp_pdu->data_buf_len,
+ rsp_pdu->data_segment_len);
+ }
+
+ return rc;
+}
+
+/*
+ * This function is used to handle the login of iscsi initiator when there is
+ * no session
+ * return:
+ * 0, success;
+ * SPDK_ISCSI_LOGIN_ERROR_PARAMETER, parameter error;
+ * SPDK_ISCSI_LOGIN_ERROR_RESPONSE, used to notify the login fail.
+ */
+static int
+iscsi_op_login_phase_none(struct spdk_iscsi_conn *conn,
+ struct spdk_iscsi_pdu *rsp_pdu,
+ struct iscsi_param *params, int cid)
+{
+ enum session_type session_type;
+ char initiator_port_name[MAX_INITIATOR_PORT_NAME];
+ struct iscsi_bhs_login_rsp *rsph;
+ int rc = 0;
+ rsph = (struct iscsi_bhs_login_rsp *)&rsp_pdu->bhs;
+
+ conn->target = NULL;
+ conn->dev = NULL;
+
+ rc = iscsi_op_login_initialize_port(conn, rsp_pdu, initiator_port_name,
+ MAX_INITIATOR_PORT_NAME, params);
+ if (rc < 0) {
+ return rc;
+ }
+
+ rc = iscsi_op_login_session_type(conn, rsp_pdu, &session_type, params);
+ if (rc < 0) {
+ return rc;
+ }
+
+ /* Target Name and Port */
+ if (session_type == SESSION_TYPE_NORMAL) {
+ rc = iscsi_op_login_session_normal(conn, rsp_pdu,
+ initiator_port_name,
+ params, cid);
+ if (rc < 0) {
+ return rc;
+ }
+
+ } else if (session_type == SESSION_TYPE_DISCOVERY) {
+ rsph->tsih = 0;
+
+ /* force target flags */
+ pthread_mutex_lock(&g_iscsi.mutex);
+ rc = iscsi_op_login_session_discovery_chap(conn);
+ pthread_mutex_unlock(&g_iscsi.mutex);
+ if (rc < 0) {
+ return rc;
+ }
+ } else {
+ SPDK_ERRLOG("unknown session type\n");
+ /* Missing parameter */
+ rsph->status_class = ISCSI_CLASS_INITIATOR_ERROR;
+ rsph->status_detail = ISCSI_LOGIN_MISSING_PARMS;
+ return SPDK_ISCSI_LOGIN_ERROR_RESPONSE;
+ }
+
+ rc = iscsi_op_login_set_conn_info(conn, rsp_pdu, initiator_port_name,
+ session_type, cid);
+ if (rc < 0) {
+ return rc;
+ }
+
+ /* limit conns on discovery session */
+ if (session_type == SESSION_TYPE_DISCOVERY) {
+ conn->sess->MaxConnections = 1;
+ rc = iscsi_param_set_int(conn->sess->params,
+ "MaxConnections",
+ conn->sess->MaxConnections);
+ if (rc < 0) {
+ SPDK_ERRLOG("iscsi_param_set_int() failed\n");
+ return SPDK_ISCSI_LOGIN_ERROR_PARAMETER;
+ }
+ }
+
+ return iscsi_op_login_set_target_info(conn, rsp_pdu, session_type);
+}
+
+/*
+ * This function is used to set the csg bit case in rsp
+ * return:
+ * 0, success
+ * otherwise: error
+ */
+static int
+iscsi_op_login_rsp_handle_csg_bit(struct spdk_iscsi_conn *conn,
+ struct spdk_iscsi_pdu *rsp_pdu,
+ struct iscsi_param *params)
+{
+ const char *auth_method;
+ int rc;
+ struct iscsi_bhs_login_rsp *rsph;
+ rsph = (struct iscsi_bhs_login_rsp *)&rsp_pdu->bhs;
+
+ switch (ISCSI_BHS_LOGIN_GET_CSG(rsph->flags)) {
+ case ISCSI_SECURITY_NEGOTIATION_PHASE:
+ /* SecurityNegotiation */
+ auth_method = iscsi_param_get_val(conn->params, "AuthMethod");
+ if (auth_method == NULL) {
+ SPDK_ERRLOG("AuthMethod is empty\n");
+ /* Missing parameter */
+ rsph->status_class = ISCSI_CLASS_INITIATOR_ERROR;
+ rsph->status_detail = ISCSI_LOGIN_MISSING_PARMS;
+ return SPDK_ISCSI_LOGIN_ERROR_RESPONSE;
+ }
+ if (strcasecmp(auth_method, "None") == 0) {
+ conn->authenticated = true;
+ } else {
+ rc = iscsi_auth_params(conn, params, auth_method,
+ rsp_pdu->data, rsp_pdu->data_buf_len,
+ rsp_pdu->data_segment_len);
+ if (rc < 0) {
+ SPDK_ERRLOG("iscsi_auth_params() failed\n");
+ /* Authentication failure */
+ rsph->status_class = ISCSI_CLASS_INITIATOR_ERROR;
+ rsph->status_detail = ISCSI_LOGIN_AUTHENT_FAIL;
+ return SPDK_ISCSI_LOGIN_ERROR_RESPONSE;
+ }
+ rsp_pdu->data_segment_len = rc;
+ if (!conn->authenticated) {
+ /* not complete */
+ rsph->flags &= ~ISCSI_LOGIN_TRANSIT;
+ } else {
+ if (conn->auth.chap_phase != ISCSI_CHAP_PHASE_END) {
+ SPDK_DEBUGLOG(SPDK_LOG_ISCSI, "CHAP phase not complete");
+ }
+ }
+
+ SPDK_LOGDUMP(SPDK_LOG_ISCSI, "Negotiated Auth Params",
+ rsp_pdu->data, rsp_pdu->data_segment_len);
+ }
+ break;
+
+ case ISCSI_OPERATIONAL_NEGOTIATION_PHASE:
+ /* LoginOperationalNegotiation */
+ if (conn->state == ISCSI_CONN_STATE_INVALID) {
+ if (conn->require_chap) {
+ /* Authentication failure */
+ rsph->status_class = ISCSI_CLASS_INITIATOR_ERROR;
+ rsph->status_detail = ISCSI_LOGIN_AUTHENT_FAIL;
+ return SPDK_ISCSI_LOGIN_ERROR_RESPONSE;
+ } else {
+ /* AuthMethod=None */
+ conn->authenticated = true;
+ }
+ }
+ if (!conn->authenticated) {
+ SPDK_ERRLOG("authentication error\n");
+ /* Authentication failure */
+ rsph->status_class = ISCSI_CLASS_INITIATOR_ERROR;
+ rsph->status_detail = ISCSI_LOGIN_AUTHENT_FAIL;
+ return SPDK_ISCSI_LOGIN_ERROR_RESPONSE;
+ }
+ break;
+
+ case ISCSI_FULL_FEATURE_PHASE:
+ /* FullFeaturePhase */
+ SPDK_ERRLOG("XXX Login in FullFeaturePhase\n");
+ /* Initiator error */
+ rsph->status_class = ISCSI_CLASS_INITIATOR_ERROR;
+ rsph->status_detail = ISCSI_LOGIN_INITIATOR_ERROR;
+ return SPDK_ISCSI_LOGIN_ERROR_RESPONSE;
+
+ default:
+ SPDK_ERRLOG("unknown stage\n");
+ /* Initiator error */
+ rsph->status_class = ISCSI_CLASS_INITIATOR_ERROR;
+ rsph->status_detail = ISCSI_LOGIN_INITIATOR_ERROR;
+ return SPDK_ISCSI_LOGIN_ERROR_RESPONSE;
+ }
+
+ return 0;
+}
+
+/* This function is used to notify the session info
+ * return
+ * 0: success
+ * otherwise: error
+ */
+static int
+iscsi_op_login_notify_session_info(struct spdk_iscsi_conn *conn,
+ struct spdk_iscsi_pdu *rsp_pdu)
+{
+ struct iscsi_bhs_login_rsp *rsph;
+
+ rsph = (struct iscsi_bhs_login_rsp *)&rsp_pdu->bhs;
+ if (conn->sess->session_type == SESSION_TYPE_NORMAL) {
+ /* normal session */
+ SPDK_DEBUGLOG(SPDK_LOG_ISCSI, "Login from %s (%s) on %s tgt_node%d"
+ " (%s:%s,%d), ISID=%"PRIx64", TSIH=%u,"
+ " CID=%u, HeaderDigest=%s, DataDigest=%s\n",
+ conn->initiator_name, conn->initiator_addr,
+ conn->target->name, conn->target->num,
+ conn->portal_host, conn->portal_port, conn->pg_tag,
+ conn->sess->isid, conn->sess->tsih, conn->cid,
+ (iscsi_param_eq_val(conn->params, "HeaderDigest", "CRC32C")
+ ? "on" : "off"),
+ (iscsi_param_eq_val(conn->params, "DataDigest", "CRC32C")
+ ? "on" : "off"));
+ } else if (conn->sess->session_type == SESSION_TYPE_DISCOVERY) {
+ /* discovery session */
+ SPDK_DEBUGLOG(SPDK_LOG_ISCSI, "Login(discovery) from %s (%s) on"
+ " (%s:%s,%d), ISID=%"PRIx64", TSIH=%u,"
+ " CID=%u, HeaderDigest=%s, DataDigest=%s\n",
+ conn->initiator_name, conn->initiator_addr,
+ conn->portal_host, conn->portal_port, conn->pg_tag,
+ conn->sess->isid, conn->sess->tsih, conn->cid,
+ (iscsi_param_eq_val(conn->params, "HeaderDigest", "CRC32C")
+ ? "on" : "off"),
+ (iscsi_param_eq_val(conn->params, "DataDigest", "CRC32C")
+ ? "on" : "off"));
+ } else {
+ SPDK_ERRLOG("unknown session type\n");
+ /* Initiator error */
+ rsph->status_class = ISCSI_CLASS_INITIATOR_ERROR;
+ rsph->status_detail = ISCSI_LOGIN_INITIATOR_ERROR;
+ return SPDK_ISCSI_LOGIN_ERROR_RESPONSE;
+ }
+
+ return 0;
+}
+
+/*
+ * This function is to handle the tbit cases
+ * return
+ * 0: success
+ * otherwise error
+ */
+static int
+iscsi_op_login_rsp_handle_t_bit(struct spdk_iscsi_conn *conn,
+ struct spdk_iscsi_pdu *rsp_pdu)
+{
+ int rc;
+ struct iscsi_bhs_login_rsp *rsph;
+ rsph = (struct iscsi_bhs_login_rsp *)&rsp_pdu->bhs;
+
+ switch (ISCSI_BHS_LOGIN_GET_NSG(rsph->flags)) {
+ case ISCSI_SECURITY_NEGOTIATION_PHASE:
+ /* SecurityNegotiation */
+ conn->login_phase = ISCSI_SECURITY_NEGOTIATION_PHASE;
+ break;
+
+ case ISCSI_OPERATIONAL_NEGOTIATION_PHASE:
+ /* LoginOperationalNegotiation */
+ conn->login_phase = ISCSI_OPERATIONAL_NEGOTIATION_PHASE;
+ break;
+
+ case ISCSI_FULL_FEATURE_PHASE:
+ /* FullFeaturePhase */
+ conn->login_phase = ISCSI_FULL_FEATURE_PHASE;
+ to_be16(&rsph->tsih, conn->sess->tsih);
+
+ rc = iscsi_op_login_notify_session_info(conn, rsp_pdu);
+ if (rc < 0) {
+ return rc;
+ }
+
+ conn->full_feature = 1;
+ break;
+
+ default:
+ SPDK_ERRLOG("unknown stage\n");
+ /* Initiator error */
+ rsph->status_class = ISCSI_CLASS_INITIATOR_ERROR;
+ rsph->status_detail = ISCSI_LOGIN_INITIATOR_ERROR;
+ return SPDK_ISCSI_LOGIN_ERROR_RESPONSE;
+ }
+
+ return 0;
+}
+
+/*
+ * This function is used to set the values of the internal data structure used
+ * by spdk_iscsi_op_login function
+ * return:
+ * 0, used to notify the a successful login
+ * SPDK_ISCSI_LOGIN_ERROR_RESPONSE, used to notify a failure login.
+ */
+static int
+iscsi_op_login_rsp_handle(struct spdk_iscsi_conn *conn,
+ struct spdk_iscsi_pdu *rsp_pdu, struct iscsi_param **params)
+{
+ int rc;
+ struct iscsi_bhs_login_rsp *rsph;
+ rsph = (struct iscsi_bhs_login_rsp *)&rsp_pdu->bhs;
+
+ /* negotiate parameters */
+ rc = iscsi_negotiate_params(conn, params, rsp_pdu->data,
+ rsp_pdu->data_buf_len,
+ rsp_pdu->data_segment_len);
+ if (rc < 0) {
+ /*
+ * iscsi_negotiate_params just returns -1 on failure,
+ * so translate this into meaningful response codes and
+ * return values.
+ */
+ rsph->status_class = ISCSI_CLASS_INITIATOR_ERROR;
+ rsph->status_detail = ISCSI_LOGIN_INITIATOR_ERROR;
+ return SPDK_ISCSI_LOGIN_ERROR_RESPONSE;
+ }
+
+ rsp_pdu->data_segment_len = rc;
+ SPDK_LOGDUMP(SPDK_LOG_ISCSI, "Negotiated Params", rsp_pdu->data, rc);
+
+ /* handle the CSG bit case */
+ rc = iscsi_op_login_rsp_handle_csg_bit(conn, rsp_pdu, *params);
+ if (rc < 0) {
+ return rc;
+ }
+
+ /* handle the T bit case */
+ if (ISCSI_BHS_LOGIN_GET_TBIT(rsph->flags)) {
+ rc = iscsi_op_login_rsp_handle_t_bit(conn, rsp_pdu);
+ }
+
+ return rc;
+}
+
+static int
+iscsi_pdu_hdr_op_login(struct spdk_iscsi_conn *conn, struct spdk_iscsi_pdu *pdu)
+{
+ int rc;
+ struct iscsi_bhs_login_req *reqh;
+ struct spdk_iscsi_pdu *rsp_pdu;
+
+ if (conn->full_feature && conn->sess != NULL &&
+ conn->sess->session_type == SESSION_TYPE_DISCOVERY) {
+ return SPDK_ISCSI_CONNECTION_FATAL;
+ }
+
+ reqh = (struct iscsi_bhs_login_req *)&pdu->bhs;
+ pdu->cmd_sn = from_be32(&reqh->cmd_sn);
+
+ /* During login processing, use the 8KB default FirstBurstLength as
+ * our maximum data segment length value.
+ */
+ if (pdu->data_segment_len > SPDK_ISCSI_FIRST_BURST_LENGTH) {
+ return iscsi_reject(conn, pdu, ISCSI_REASON_PROTOCOL_ERROR);
+ }
+
+ rsp_pdu = iscsi_get_pdu(conn);
+ if (rsp_pdu == NULL) {
+ return SPDK_ISCSI_CONNECTION_FATAL;
+ }
+ rc = iscsi_op_login_rsp_init(conn, pdu, rsp_pdu);
+ if (rc < 0) {
+ iscsi_op_login_response(conn, rsp_pdu, NULL, iscsi_conn_login_pdu_err_complete);
+ return 0;
+ }
+
+ conn->login_rsp_pdu = rsp_pdu;
+ return 0;
+}
+
+static int
+iscsi_pdu_payload_op_login(struct spdk_iscsi_conn *conn, struct spdk_iscsi_pdu *pdu)
+{
+ int rc;
+ struct iscsi_bhs_login_req *reqh;
+ struct spdk_iscsi_pdu *rsp_pdu;
+ struct iscsi_param *params = NULL;
+ int cid;
+
+ if (conn->login_rsp_pdu == NULL) {
+ return 0;
+ }
+
+ rsp_pdu = conn->login_rsp_pdu;
+
+ reqh = (struct iscsi_bhs_login_req *)&pdu->bhs;
+ cid = from_be16(&reqh->cid);
+
+ rc = iscsi_op_login_store_incoming_params(conn, pdu, rsp_pdu, &params);
+ if (rc < 0) {
+ iscsi_op_login_response(conn, rsp_pdu, NULL, iscsi_conn_login_pdu_err_complete);
+ return 0;
+ }
+
+ if (conn->state == ISCSI_CONN_STATE_INVALID) {
+ rc = iscsi_op_login_phase_none(conn, rsp_pdu, params, cid);
+ if (rc == SPDK_ISCSI_LOGIN_ERROR_RESPONSE || rc == SPDK_ISCSI_LOGIN_ERROR_PARAMETER) {
+ iscsi_op_login_response(conn, rsp_pdu, params, iscsi_conn_login_pdu_err_complete);
+ return 0;
+ }
+ }
+
+ rc = iscsi_op_login_rsp_handle(conn, rsp_pdu, &params);
+ if (rc == SPDK_ISCSI_LOGIN_ERROR_RESPONSE) {
+ iscsi_op_login_response(conn, rsp_pdu, params, iscsi_conn_login_pdu_err_complete);
+ return 0;
+ }
+
+ iscsi_op_login_response(conn, rsp_pdu, params, iscsi_conn_login_pdu_success_complete);
+ return 0;
+}
+
+static int
+iscsi_pdu_hdr_op_text(struct spdk_iscsi_conn *conn, struct spdk_iscsi_pdu *pdu)
+{
+ uint32_t task_tag;
+ uint32_t ExpStatSN;
+ int F_bit, C_bit;
+ struct iscsi_bhs_text_req *reqh;
+
+ if (pdu->data_segment_len > iscsi_get_max_immediate_data_size()) {
+ SPDK_ERRLOG("data segment len(=%zu) > immediate data len(=%"PRIu32")\n",
+ pdu->data_segment_len, iscsi_get_max_immediate_data_size());
+ return iscsi_reject(conn, pdu, ISCSI_REASON_PROTOCOL_ERROR);
+ }
+
+ reqh = (struct iscsi_bhs_text_req *)&pdu->bhs;
+
+ F_bit = !!(reqh->flags & ISCSI_FLAG_FINAL);
+ C_bit = !!(reqh->flags & ISCSI_TEXT_CONTINUE);
+ task_tag = from_be32(&reqh->itt);
+ ExpStatSN = from_be32(&reqh->exp_stat_sn);
+
+ SPDK_DEBUGLOG(SPDK_LOG_ISCSI, "I=%d, F=%d, C=%d, ITT=%x, TTT=%x\n",
+ reqh->immediate, F_bit, C_bit, task_tag, from_be32(&reqh->ttt));
+
+ SPDK_DEBUGLOG(SPDK_LOG_ISCSI,
+ "CmdSN=%u, ExpStatSN=%u, StatSN=%u, ExpCmdSN=%u, MaxCmdSN=%u\n",
+ pdu->cmd_sn, ExpStatSN, conn->StatSN, conn->sess->ExpCmdSN,
+ conn->sess->MaxCmdSN);
+
+ if (ExpStatSN != conn->StatSN) {
+#if 0
+ SPDK_ERRLOG("StatSN(%u) error\n", ExpStatSN);
+ return -1;
+#else
+ /* StarPort have a bug */
+ SPDK_DEBUGLOG(SPDK_LOG_ISCSI, "StatSN(%u) rewound\n", ExpStatSN);
+ conn->StatSN = ExpStatSN;
+#endif
+ }
+
+ if (F_bit && C_bit) {
+ SPDK_ERRLOG("final and continue\n");
+ return -1;
+ }
+
+ /*
+ * If this is the first text op in a sequence, save the ITT so we can
+ * compare it against the ITT for subsequent ops in the same sequence.
+ * If a subsequent text op in same sequence has a different ITT, reject
+ * that PDU.
+ */
+ if (conn->sess->current_text_itt == 0xffffffffU) {
+ conn->sess->current_text_itt = task_tag;
+ } else if (conn->sess->current_text_itt != task_tag) {
+ SPDK_ERRLOG("The correct itt is %u, and the current itt is %u...\n",
+ conn->sess->current_text_itt, task_tag);
+ return iscsi_reject(conn, pdu, ISCSI_REASON_PROTOCOL_ERROR);
+ }
+
+ return 0;
+}
+
+static void
+iscsi_conn_text_pdu_complete(void *arg)
+{
+ struct spdk_iscsi_conn *conn = arg;
+
+ iscsi_conn_params_update(conn);
+}
+
+static int
+iscsi_pdu_payload_op_text(struct spdk_iscsi_conn *conn, struct spdk_iscsi_pdu *pdu)
+{
+ struct iscsi_param *params = NULL;
+ struct spdk_iscsi_pdu *rsp_pdu;
+ uint8_t *data;
+ uint64_t lun;
+ uint32_t task_tag;
+ const char *val;
+ int F_bit, C_bit;
+ int data_len;
+ int alloc_len;
+ int rc;
+ struct iscsi_bhs_text_req *reqh;
+ struct iscsi_bhs_text_resp *rsph;
+
+ data_len = 0;
+ alloc_len = conn->MaxRecvDataSegmentLength;
+
+ reqh = (struct iscsi_bhs_text_req *)&pdu->bhs;
+
+ F_bit = !!(reqh->flags & ISCSI_FLAG_FINAL);
+ C_bit = !!(reqh->flags & ISCSI_TEXT_CONTINUE);
+ lun = from_be64(&reqh->lun);
+ task_tag = from_be32(&reqh->itt);
+
+ /* store incoming parameters */
+ rc = iscsi_parse_params(&params, pdu->data, pdu->data_segment_len,
+ C_bit, &conn->partial_text_parameter);
+ if (rc < 0) {
+ SPDK_ERRLOG("iscsi_parse_params() failed\n");
+ iscsi_param_free(params);
+ return -1;
+ }
+
+ data = calloc(1, alloc_len);
+ if (!data) {
+ SPDK_ERRLOG("calloc() failed for data segment\n");
+ iscsi_param_free(params);
+ return -ENOMEM;
+ }
+
+ /* negotiate parameters */
+ data_len = iscsi_negotiate_params(conn, &params,
+ data, alloc_len, data_len);
+ if (data_len < 0) {
+ SPDK_ERRLOG("iscsi_negotiate_params() failed\n");
+ iscsi_param_free(params);
+ free(data);
+ return -1;
+ }
+
+ /* sendtargets is special case */
+ val = iscsi_param_get_val(params, "SendTargets");
+ if (val != NULL) {
+ if (iscsi_param_eq_val(conn->sess->params,
+ "SessionType", "Discovery")) {
+ if (strcasecmp(val, "") == 0) {
+ val = "ALL";
+ }
+
+ data_len = iscsi_send_tgts(conn,
+ conn->initiator_name,
+ conn->initiator_addr,
+ val, data, alloc_len,
+ data_len);
+ } else {
+ if (strcasecmp(val, "") == 0) {
+ val = conn->target->name;
+ }
+
+ if (strcasecmp(val, "ALL") == 0) {
+ /* not in discovery session */
+ data_len = iscsi_append_text(conn,
+ "SendTargets",
+ "Reject", data,
+ alloc_len, data_len);
+ } else {
+ data_len = iscsi_send_tgts(conn,
+ conn->initiator_name,
+ conn->initiator_addr,
+ val, data, alloc_len,
+ data_len);
+ }
+ }
+ } else {
+ if (iscsi_param_eq_val(conn->sess->params, "SessionType", "Discovery")) {
+ iscsi_param_free(params);
+ free(data);
+ return SPDK_ISCSI_CONNECTION_FATAL;
+ }
+ }
+
+ iscsi_param_free(params);
+ SPDK_LOGDUMP(SPDK_LOG_ISCSI, "Negotiated Params", data, data_len);
+
+ /* response PDU */
+ rsp_pdu = iscsi_get_pdu(conn);
+ if (rsp_pdu == NULL) {
+ free(data);
+ return SPDK_ISCSI_CONNECTION_FATAL;
+ }
+ rsph = (struct iscsi_bhs_text_resp *)&rsp_pdu->bhs;
+
+ rsp_pdu->data = data;
+ rsph->opcode = ISCSI_OP_TEXT_RSP;
+
+ if (F_bit) {
+ rsph->flags |= ISCSI_FLAG_FINAL;
+ }
+
+ if (C_bit) {
+ rsph->flags |= ISCSI_TEXT_CONTINUE;
+ }
+
+ DSET24(rsph->data_segment_len, data_len);
+ to_be64(&rsph->lun, lun);
+ to_be32(&rsph->itt, task_tag);
+
+ if (F_bit) {
+ rsph->ttt = 0xffffffffU;
+ conn->sess->current_text_itt = 0xffffffffU;
+ } else {
+ to_be32(&rsph->ttt, 1 + conn->id);
+ }
+
+ to_be32(&rsph->stat_sn, conn->StatSN);
+ conn->StatSN++;
+
+ if (reqh->immediate == 0) {
+ conn->sess->MaxCmdSN++;
+ }
+
+ to_be32(&rsph->exp_cmd_sn, conn->sess->ExpCmdSN);
+ to_be32(&rsph->max_cmd_sn, conn->sess->MaxCmdSN);
+
+ iscsi_conn_write_pdu(conn, rsp_pdu, iscsi_conn_text_pdu_complete, conn);
+ return 0;
+}
+
+static void iscsi_conn_logout_pdu_complete(void *arg)
+{
+ struct spdk_iscsi_conn *conn = arg;
+
+ if (conn->sess == NULL) {
+ /*
+ * login failed but initiator still sent a logout rather than
+ * just closing the TCP connection.
+ */
+ SPDK_DEBUGLOG(SPDK_LOG_ISCSI, "Logout(login failed) from %s (%s) on"
+ " (%s:%s,%d)\n",
+ conn->initiator_name, conn->initiator_addr,
+ conn->portal_host, conn->portal_port, conn->pg_tag);
+ } else if (iscsi_param_eq_val(conn->sess->params, "SessionType", "Normal")) {
+ SPDK_DEBUGLOG(SPDK_LOG_ISCSI, "Logout from %s (%s) on %s tgt_node%d"
+ " (%s:%s,%d), ISID=%"PRIx64", TSIH=%u,"
+ " CID=%u, HeaderDigest=%s, DataDigest=%s\n",
+ conn->initiator_name, conn->initiator_addr,
+ conn->target->name, conn->target->num,
+ conn->portal_host, conn->portal_port, conn->pg_tag,
+ conn->sess->isid, conn->sess->tsih, conn->cid,
+ (iscsi_param_eq_val(conn->params, "HeaderDigest", "CRC32C")
+ ? "on" : "off"),
+ (iscsi_param_eq_val(conn->params, "DataDigest", "CRC32C")
+ ? "on" : "off"));
+ } else {
+ /* discovery session */
+ SPDK_DEBUGLOG(SPDK_LOG_ISCSI, "Logout(discovery) from %s (%s) on"
+ " (%s:%s,%d), ISID=%"PRIx64", TSIH=%u,"
+ " CID=%u, HeaderDigest=%s, DataDigest=%s\n",
+ conn->initiator_name, conn->initiator_addr,
+ conn->portal_host, conn->portal_port, conn->pg_tag,
+ conn->sess->isid, conn->sess->tsih, conn->cid,
+ (iscsi_param_eq_val(conn->params, "HeaderDigest", "CRC32C")
+ ? "on" : "off"),
+ (iscsi_param_eq_val(conn->params, "DataDigest", "CRC32C")
+ ? "on" : "off"));
+ }
+}
+
+static int
+iscsi_pdu_hdr_op_logout(struct spdk_iscsi_conn *conn, struct spdk_iscsi_pdu *pdu)
+{
+ struct spdk_iscsi_pdu *rsp_pdu;
+ uint32_t task_tag;
+ uint32_t ExpStatSN;
+ int response;
+ struct iscsi_bhs_logout_req *reqh;
+ struct iscsi_bhs_logout_resp *rsph;
+ uint16_t cid;
+
+ reqh = (struct iscsi_bhs_logout_req *)&pdu->bhs;
+
+ cid = from_be16(&reqh->cid);
+ task_tag = from_be32(&reqh->itt);
+ ExpStatSN = from_be32(&reqh->exp_stat_sn);
+
+ SPDK_DEBUGLOG(SPDK_LOG_ISCSI, "reason=%d, ITT=%x, cid=%d\n",
+ reqh->reason, task_tag, cid);
+
+ if (conn->sess != NULL) {
+ if (conn->sess->session_type == SESSION_TYPE_DISCOVERY &&
+ reqh->reason != ISCSI_LOGOUT_REASON_CLOSE_SESSION) {
+ SPDK_ERRLOG("Target can accept logout only with reason \"close the session\" "
+ "on discovery session. %d is not acceptable reason.\n",
+ reqh->reason);
+ return SPDK_ISCSI_CONNECTION_FATAL;
+ }
+
+ SPDK_DEBUGLOG(SPDK_LOG_ISCSI,
+ "CmdSN=%u, ExpStatSN=%u, StatSN=%u, ExpCmdSN=%u, MaxCmdSN=%u\n",
+ pdu->cmd_sn, ExpStatSN, conn->StatSN,
+ conn->sess->ExpCmdSN, conn->sess->MaxCmdSN);
+
+ if (pdu->cmd_sn != conn->sess->ExpCmdSN) {
+ SPDK_DEBUGLOG(SPDK_LOG_ISCSI, "CmdSN(%u) might have dropped\n", pdu->cmd_sn);
+ /* ignore error */
+ }
+ } else {
+ SPDK_DEBUGLOG(SPDK_LOG_ISCSI, "CmdSN=%u, ExpStatSN=%u, StatSN=%u\n",
+ pdu->cmd_sn, ExpStatSN, conn->StatSN);
+ }
+
+ if (ExpStatSN != conn->StatSN) {
+ SPDK_DEBUGLOG(SPDK_LOG_ISCSI, "StatSN(%u/%u) might have dropped\n",
+ ExpStatSN, conn->StatSN);
+ /* ignore error */
+ }
+
+ if (conn->id == cid) {
+ /* connection or session closed successfully */
+ response = 0;
+ iscsi_conn_logout(conn);
+ } else {
+ response = 1;
+ }
+
+ /* response PDU */
+ rsp_pdu = iscsi_get_pdu(conn);
+ if (rsp_pdu == NULL) {
+ return SPDK_ISCSI_CONNECTION_FATAL;
+ }
+ rsph = (struct iscsi_bhs_logout_resp *)&rsp_pdu->bhs;
+ rsp_pdu->data = NULL;
+ rsph->opcode = ISCSI_OP_LOGOUT_RSP;
+ rsph->flags |= 0x80; /* bit 0 must be 1 */
+ rsph->response = response;
+ DSET24(rsph->data_segment_len, 0);
+ to_be32(&rsph->itt, task_tag);
+
+ if (conn->sess != NULL) {
+ to_be32(&rsph->stat_sn, conn->StatSN);
+ conn->StatSN++;
+
+ if (conn->sess->connections == 1) {
+ conn->sess->MaxCmdSN++;
+ }
+
+ to_be32(&rsph->exp_cmd_sn, conn->sess->ExpCmdSN);
+ to_be32(&rsph->max_cmd_sn, conn->sess->MaxCmdSN);
+ } else {
+ to_be32(&rsph->stat_sn, conn->StatSN);
+ conn->StatSN++;
+ to_be32(&rsph->exp_cmd_sn, pdu->cmd_sn);
+ to_be32(&rsph->max_cmd_sn, pdu->cmd_sn);
+ }
+
+ rsph->time_2_wait = 0;
+ rsph->time_2_retain = 0;
+
+ iscsi_conn_write_pdu(conn, rsp_pdu, iscsi_conn_logout_pdu_complete, conn);
+
+ return 0;
+}
+
+static int
+iscsi_send_r2t(struct spdk_iscsi_conn *conn,
+ struct spdk_iscsi_task *task, int offset,
+ int len, uint32_t transfer_tag, uint32_t *R2TSN)
+{
+ struct spdk_iscsi_pdu *rsp_pdu;
+ struct iscsi_bhs_r2t *rsph;
+ uint64_t fmt_lun;
+
+ /* R2T PDU */
+ rsp_pdu = iscsi_get_pdu(conn);
+ if (rsp_pdu == NULL) {
+ return SPDK_ISCSI_CONNECTION_FATAL;
+ }
+ rsph = (struct iscsi_bhs_r2t *)&rsp_pdu->bhs;
+ rsp_pdu->data = NULL;
+ rsph->opcode = ISCSI_OP_R2T;
+ rsph->flags |= 0x80; /* bit 0 is default to 1 */
+ fmt_lun = spdk_scsi_lun_id_int_to_fmt(task->lun_id);
+ to_be64(&rsph->lun, fmt_lun);
+ to_be32(&rsph->itt, task->tag);
+ to_be32(&rsph->ttt, transfer_tag);
+
+ to_be32(&rsph->stat_sn, conn->StatSN);
+ to_be32(&rsph->exp_cmd_sn, conn->sess->ExpCmdSN);
+ to_be32(&rsph->max_cmd_sn, conn->sess->MaxCmdSN);
+
+ to_be32(&rsph->r2t_sn, *R2TSN);
+ *R2TSN += 1;
+
+ task->r2t_datasn = 0; /* next expected datasn to ack */
+
+ to_be32(&rsph->buffer_offset, (uint32_t)offset);
+ to_be32(&rsph->desired_xfer_len, (uint32_t)len);
+ task->desired_data_transfer_length = (size_t)len;
+
+ /* we need to hold onto this task/cmd because until the PDU has been
+ * written out */
+ rsp_pdu->task = task;
+ task->scsi.ref++;
+
+ iscsi_conn_write_pdu(conn, rsp_pdu, iscsi_conn_pdu_generic_complete, NULL);
+
+ return 0;
+}
+
+/* This function is used to remove the r2t pdu from snack_pdu_list by < task, r2t_sn> info */
+static struct spdk_iscsi_pdu *
+iscsi_remove_r2t_pdu_from_snack_list(struct spdk_iscsi_conn *conn,
+ struct spdk_iscsi_task *task,
+ uint32_t r2t_sn)
+{
+ struct spdk_iscsi_pdu *pdu;
+ struct iscsi_bhs_r2t *r2t_header;
+
+ TAILQ_FOREACH(pdu, &conn->snack_pdu_list, tailq) {
+ if (pdu->bhs.opcode == ISCSI_OP_R2T) {
+ r2t_header = (struct iscsi_bhs_r2t *)&pdu->bhs;
+ if (pdu->task == task &&
+ from_be32(&r2t_header->r2t_sn) == r2t_sn) {
+ TAILQ_REMOVE(&conn->snack_pdu_list, pdu, tailq);
+ return pdu;
+ }
+ }
+ }
+
+ return NULL;
+}
+
+/* This function is used re-send the r2t packet */
+static int
+iscsi_send_r2t_recovery(struct spdk_iscsi_conn *conn,
+ struct spdk_iscsi_task *task, uint32_t r2t_sn,
+ bool send_new_r2tsn)
+{
+ struct spdk_iscsi_pdu *pdu;
+ struct iscsi_bhs_r2t *rsph;
+ uint32_t transfer_len;
+ uint32_t len;
+ int rc;
+
+ /* remove the r2t pdu from the snack_list */
+ pdu = iscsi_remove_r2t_pdu_from_snack_list(conn, task, r2t_sn);
+ if (!pdu) {
+ SPDK_DEBUGLOG(SPDK_LOG_ISCSI, "No pdu is found\n");
+ return -1;
+ }
+
+ /* flag
+ * false: only need to re-send the old r2t with changing statsn
+ * true: we send a r2t with new r2tsn
+ */
+ if (!send_new_r2tsn) {
+ to_be32(&pdu->bhs.stat_sn, conn->StatSN);
+ iscsi_conn_write_pdu(conn, pdu, iscsi_conn_pdu_generic_complete, NULL);
+ } else {
+ rsph = (struct iscsi_bhs_r2t *)&pdu->bhs;
+ transfer_len = from_be32(&rsph->desired_xfer_len);
+
+ /* still need to increase the acked r2tsn */
+ task->acked_r2tsn++;
+ len = spdk_min(conn->sess->MaxBurstLength,
+ (transfer_len - task->next_expected_r2t_offset));
+
+ /* remove the old_r2t_pdu */
+ iscsi_conn_free_pdu(conn, pdu);
+
+ /* re-send a new r2t pdu */
+ rc = iscsi_send_r2t(conn, task, task->next_expected_r2t_offset,
+ len, task->ttt, &task->R2TSN);
+ if (rc < 0) {
+ return SPDK_ISCSI_CONNECTION_FATAL;
+ }
+ }
+
+ return 0;
+}
+
+static int
+add_transfer_task(struct spdk_iscsi_conn *conn, struct spdk_iscsi_task *task)
+{
+ uint32_t transfer_len;
+ size_t max_burst_len;
+ size_t segment_len;
+ size_t data_len;
+ int len;
+ int rc;
+ int data_out_req;
+
+ transfer_len = task->scsi.transfer_len;
+ data_len = iscsi_task_get_pdu(task)->data_segment_len;
+ max_burst_len = conn->sess->MaxBurstLength;
+ segment_len = SPDK_ISCSI_MAX_RECV_DATA_SEGMENT_LENGTH;
+ data_out_req = 1 + (transfer_len - data_len - 1) / segment_len;
+ task->data_out_cnt = data_out_req;
+
+ /*
+ * If we already have too many tasks using R2T, then queue this task
+ * and start sending R2T for it after some of the tasks using R2T/data
+ * out buffers complete.
+ */
+ if (conn->pending_r2t >= DEFAULT_MAXR2T) {
+ TAILQ_INSERT_TAIL(&conn->queued_r2t_tasks, task, link);
+ return 0;
+ }
+
+ conn->data_out_cnt += data_out_req;
+ conn->pending_r2t++;
+
+ task->next_expected_r2t_offset = data_len;
+ task->current_r2t_length = 0;
+ task->R2TSN = 0;
+ /* According to RFC3720 10.8.5, 0xffffffff is
+ * reserved for TTT in R2T.
+ */
+ if (++conn->ttt == 0xffffffffu) {
+ conn->ttt = 0;
+ }
+ task->ttt = conn->ttt;
+
+ while (data_len != transfer_len) {
+ len = spdk_min(max_burst_len, (transfer_len - data_len));
+ rc = iscsi_send_r2t(conn, task, data_len, len,
+ task->ttt, &task->R2TSN);
+ if (rc < 0) {
+ SPDK_ERRLOG("iscsi_send_r2t() failed\n");
+ return rc;
+ }
+ data_len += len;
+ task->next_r2t_offset = data_len;
+ task->outstanding_r2t++;
+ if (conn->sess->MaxOutstandingR2T == task->outstanding_r2t) {
+ break;
+ }
+ }
+
+ TAILQ_INSERT_TAIL(&conn->active_r2t_tasks, task, link);
+ task->is_r2t_active = true;
+ return 0;
+}
+
+/* If there are additional large writes queued for R2Ts, start them now.
+ * This is called when a large write is just completed or when multiple LUNs
+ * are attached and large write tasks for the specific LUN are cleared.
+ */
+static void
+start_queued_transfer_tasks(struct spdk_iscsi_conn *conn)
+{
+ struct spdk_iscsi_task *task, *tmp;
+
+ TAILQ_FOREACH_SAFE(task, &conn->queued_r2t_tasks, link, tmp) {
+ if (conn->pending_r2t < DEFAULT_MAXR2T) {
+ TAILQ_REMOVE(&conn->queued_r2t_tasks, task, link);
+ add_transfer_task(conn, task);
+ } else {
+ break;
+ }
+ }
+}
+
+bool
+iscsi_del_transfer_task(struct spdk_iscsi_conn *conn, uint32_t task_tag)
+{
+ struct spdk_iscsi_task *task, *tmp;
+
+ TAILQ_FOREACH_SAFE(task, &conn->active_r2t_tasks, link, tmp) {
+ if (task->tag == task_tag) {
+ assert(conn->data_out_cnt >= task->data_out_cnt);
+ conn->data_out_cnt -= task->data_out_cnt;
+
+ conn->pending_r2t--;
+
+ assert(task->is_r2t_active == true);
+ TAILQ_REMOVE(&conn->active_r2t_tasks, task, link);
+ task->is_r2t_active = false;
+ iscsi_task_put(task);
+
+ start_queued_transfer_tasks(conn);
+ return true;
+ }
+ }
+ return false;
+}
+
+void iscsi_clear_all_transfer_task(struct spdk_iscsi_conn *conn,
+ struct spdk_scsi_lun *lun,
+ struct spdk_iscsi_pdu *pdu)
+{
+ struct spdk_iscsi_task *task, *task_tmp;
+ struct spdk_iscsi_pdu *pdu_tmp;
+
+ TAILQ_FOREACH_SAFE(task, &conn->active_r2t_tasks, link, task_tmp) {
+ pdu_tmp = iscsi_task_get_pdu(task);
+ if ((lun == NULL || lun == task->scsi.lun) &&
+ (pdu == NULL || spdk_sn32_lt(pdu_tmp->cmd_sn, pdu->cmd_sn))) {
+ task->outstanding_r2t = 0;
+ task->next_r2t_offset = 0;
+ task->next_expected_r2t_offset = 0;
+ assert(conn->data_out_cnt >= task->data_out_cnt);
+ conn->data_out_cnt -= task->data_out_cnt;
+ conn->pending_r2t--;
+
+ TAILQ_REMOVE(&conn->active_r2t_tasks, task, link);
+ task->is_r2t_active = false;
+ if (lun != NULL && spdk_scsi_lun_is_removing(lun)) {
+ spdk_scsi_task_process_null_lun(&task->scsi);
+ iscsi_task_response(conn, task);
+ }
+ iscsi_task_put(task);
+ }
+ }
+
+ TAILQ_FOREACH_SAFE(task, &conn->queued_r2t_tasks, link, task_tmp) {
+ pdu_tmp = iscsi_task_get_pdu(task);
+ if ((lun == NULL || lun == task->scsi.lun) &&
+ (pdu == NULL || spdk_sn32_lt(pdu_tmp->cmd_sn, pdu->cmd_sn))) {
+ TAILQ_REMOVE(&conn->queued_r2t_tasks, task, link);
+ task->is_r2t_active = false;
+ if (lun != NULL && spdk_scsi_lun_is_removing(lun)) {
+ spdk_scsi_task_process_null_lun(&task->scsi);
+ iscsi_task_response(conn, task);
+ }
+ iscsi_task_put(task);
+ }
+ }
+
+ start_queued_transfer_tasks(conn);
+}
+
+static struct spdk_iscsi_task *
+get_transfer_task(struct spdk_iscsi_conn *conn, uint32_t transfer_tag)
+{
+ struct spdk_iscsi_task *task;
+
+ TAILQ_FOREACH(task, &conn->active_r2t_tasks, link) {
+ if (task->ttt == transfer_tag) {
+ return task;
+ }
+ }
+
+ return NULL;
+}
+
+static void
+iscsi_conn_datain_pdu_complete(void *arg)
+{
+ struct spdk_iscsi_conn *conn = arg;
+
+ iscsi_conn_handle_queued_datain_tasks(conn);
+}
+
+static int
+iscsi_send_datain(struct spdk_iscsi_conn *conn,
+ struct spdk_iscsi_task *task, int datain_flag,
+ int residual_len, int offset, int DataSN, int len)
+{
+ struct spdk_iscsi_pdu *rsp_pdu;
+ struct iscsi_bhs_data_in *rsph;
+ uint32_t task_tag;
+ uint32_t transfer_tag;
+ int F_bit, U_bit, O_bit, S_bit;
+ struct spdk_iscsi_task *primary;
+ struct spdk_scsi_lun *lun_dev;
+
+ primary = iscsi_task_get_primary(task);
+
+ /* DATA PDU */
+ rsp_pdu = iscsi_get_pdu(conn);
+ rsph = (struct iscsi_bhs_data_in *)&rsp_pdu->bhs;
+ rsp_pdu->data = task->scsi.iovs[0].iov_base + offset;
+ rsp_pdu->data_buf_len = task->scsi.iovs[0].iov_len - offset;
+ rsp_pdu->data_from_mempool = true;
+
+ task_tag = task->tag;
+ transfer_tag = 0xffffffffU;
+
+ F_bit = datain_flag & ISCSI_FLAG_FINAL;
+ O_bit = datain_flag & ISCSI_DATAIN_OVERFLOW;
+ U_bit = datain_flag & ISCSI_DATAIN_UNDERFLOW;
+ S_bit = datain_flag & ISCSI_DATAIN_STATUS;
+
+ /*
+ * we need to hold onto this task/cmd because until the
+ * PDU has been written out
+ */
+ rsp_pdu->task = task;
+ task->scsi.ref++;
+
+ rsph->opcode = ISCSI_OP_SCSI_DATAIN;
+
+ if (F_bit) {
+ rsph->flags |= ISCSI_FLAG_FINAL;
+ }
+
+ /* we leave the A_bit clear */
+
+ if (F_bit && S_bit) {
+ if (O_bit) {
+ rsph->flags |= ISCSI_DATAIN_OVERFLOW;
+ }
+
+ if (U_bit) {
+ rsph->flags |= ISCSI_DATAIN_UNDERFLOW;
+ }
+ }
+
+ if (S_bit) {
+ rsph->flags |= ISCSI_DATAIN_STATUS;
+ rsph->status = task->scsi.status;
+ }
+
+ DSET24(rsph->data_segment_len, len);
+
+ to_be32(&rsph->itt, task_tag);
+ to_be32(&rsph->ttt, transfer_tag);
+
+ if (S_bit) {
+ to_be32(&rsph->stat_sn, conn->StatSN);
+ conn->StatSN++;
+ }
+
+ if (F_bit && S_bit && !iscsi_task_is_immediate(primary)) {
+ conn->sess->MaxCmdSN++;
+ }
+
+ to_be32(&rsph->exp_cmd_sn, conn->sess->ExpCmdSN);
+ to_be32(&rsph->max_cmd_sn, conn->sess->MaxCmdSN);
+
+ to_be32(&rsph->data_sn, DataSN);
+
+ if (conn->sess->ErrorRecoveryLevel >= 1) {
+ primary->datain_datasn = DataSN;
+ }
+ DataSN++;
+
+ if (task->parent) {
+ offset += primary->scsi.data_transferred;
+ }
+ to_be32(&rsph->buffer_offset, (uint32_t)offset);
+ task->scsi.offset = offset;
+
+ if (F_bit && S_bit) {
+ to_be32(&rsph->res_cnt, residual_len);
+ }
+
+ lun_dev = spdk_scsi_dev_get_lun(conn->dev, task->lun_id);
+ if (spdk_likely(lun_dev != NULL)) {
+ if (spdk_unlikely(spdk_scsi_lun_get_dif_ctx(lun_dev, &task->scsi,
+ &rsp_pdu->dif_ctx))) {
+ rsp_pdu->dif_insert_or_strip = true;
+ }
+ }
+
+ iscsi_conn_write_pdu(conn, rsp_pdu, iscsi_conn_datain_pdu_complete, conn);
+
+ return DataSN;
+}
+
+static int
+iscsi_transfer_in(struct spdk_iscsi_conn *conn, struct spdk_iscsi_task *task)
+{
+ uint32_t DataSN;
+ uint32_t transfer_len;
+ uint32_t data_len;
+ uint32_t segment_len;
+ uint32_t offset;
+ uint32_t residual_len = 0;
+ int sent_status;
+ uint32_t len;
+ int datain_flag = 0;
+ int datain_seq_cnt;
+ int i;
+ uint32_t sequence_end;
+ struct spdk_iscsi_task *primary;
+
+ primary = iscsi_task_get_primary(task);
+ segment_len = conn->MaxRecvDataSegmentLength;
+ data_len = task->scsi.data_transferred;
+ transfer_len = task->scsi.length;
+
+ if (task->scsi.status != SPDK_SCSI_STATUS_GOOD) {
+ return 0;
+ }
+
+ if (data_len < transfer_len) {
+ /* underflow */
+ SPDK_DEBUGLOG(SPDK_LOG_ISCSI, "Underflow %u/%u\n", data_len, transfer_len);
+ residual_len = transfer_len - data_len;
+ transfer_len = data_len;
+ datain_flag |= ISCSI_DATAIN_UNDERFLOW;
+ } else if (data_len > transfer_len) {
+ /* overflow */
+ SPDK_DEBUGLOG(SPDK_LOG_ISCSI, "Overflow %u/%u\n", data_len, transfer_len);
+ residual_len = data_len - transfer_len;
+ datain_flag |= ISCSI_DATAIN_OVERFLOW;
+ } else {
+ SPDK_DEBUGLOG(SPDK_LOG_ISCSI, "Transfer %u\n", transfer_len);
+ residual_len = 0;
+ }
+
+ DataSN = primary->datain_datasn;
+ sent_status = 0;
+
+ /* calculate the number of sequences for all data-in pdus */
+ datain_seq_cnt = 1 + ((transfer_len - 1) / (int)conn->sess->MaxBurstLength);
+ for (i = 0; i < datain_seq_cnt; i++) {
+ offset = i * conn->sess->MaxBurstLength;
+ sequence_end = spdk_min(((i + 1) * conn->sess->MaxBurstLength),
+ transfer_len);
+
+ /* send data splitted by segment_len */
+ for (; offset < sequence_end; offset += segment_len) {
+ len = spdk_min(segment_len, (sequence_end - offset));
+
+ datain_flag &= ~ISCSI_FLAG_FINAL;
+ datain_flag &= ~ISCSI_DATAIN_STATUS;
+
+ if (offset + len == sequence_end) {
+ /* last PDU in a sequence */
+ datain_flag |= ISCSI_FLAG_FINAL;
+ if (task->scsi.sense_data_len == 0) {
+ /* The last pdu in all data-in pdus */
+ if ((offset + len) == transfer_len &&
+ (primary->bytes_completed == primary->scsi.transfer_len)) {
+ datain_flag |= ISCSI_DATAIN_STATUS;
+ sent_status = 1;
+ }
+ }
+ }
+
+ SPDK_DEBUGLOG(SPDK_LOG_ISCSI, "Transfer=%d, Offset=%d, Len=%d\n",
+ sequence_end, offset, len);
+ SPDK_DEBUGLOG(SPDK_LOG_ISCSI, "StatSN=%u, DataSN=%u, Offset=%u, Len=%d\n",
+ conn->StatSN, DataSN, offset, len);
+
+ DataSN = iscsi_send_datain(conn, task, datain_flag, residual_len,
+ offset, DataSN, len);
+ }
+ }
+
+ if (task != primary) {
+ primary->scsi.data_transferred += task->scsi.data_transferred;
+ }
+ primary->datain_datasn = DataSN;
+
+ return sent_status;
+}
+
+void iscsi_task_response(struct spdk_iscsi_conn *conn,
+ struct spdk_iscsi_task *task)
+{
+ struct spdk_iscsi_pdu *rsp_pdu;
+ struct iscsi_bhs_scsi_resp *rsph;
+ uint32_t task_tag;
+ uint32_t transfer_len;
+ size_t residual_len;
+ size_t data_len;
+ int O_bit, U_bit;
+ int rc;
+ struct spdk_iscsi_task *primary;
+
+ primary = iscsi_task_get_primary(task);
+
+ transfer_len = primary->scsi.transfer_len;
+ task_tag = task->tag;
+
+ /* transfer data from logical unit */
+ /* (direction is view of initiator side) */
+ if (iscsi_task_is_read(primary)) {
+ rc = iscsi_transfer_in(conn, task);
+ if (rc > 0) {
+ /* sent status by last DATAIN PDU */
+ return;
+ }
+
+ if (primary->bytes_completed != primary->scsi.transfer_len) {
+ return;
+ }
+ }
+
+ O_bit = U_bit = 0;
+ residual_len = 0;
+ data_len = primary->scsi.data_transferred;
+
+ if ((transfer_len != 0) &&
+ (task->scsi.status == SPDK_SCSI_STATUS_GOOD)) {
+ if (data_len < transfer_len) {
+ /* underflow */
+ SPDK_DEBUGLOG(SPDK_LOG_ISCSI, "Underflow %zu/%u\n", data_len, transfer_len);
+ residual_len = transfer_len - data_len;
+ U_bit = 1;
+ } else if (data_len > transfer_len) {
+ /* overflow */
+ SPDK_DEBUGLOG(SPDK_LOG_ISCSI, "Overflow %zu/%u\n", data_len, transfer_len);
+ residual_len = data_len - transfer_len;
+ O_bit = 1;
+ } else {
+ SPDK_DEBUGLOG(SPDK_LOG_ISCSI, "Transfer %u\n", transfer_len);
+ }
+ }
+
+ /* response PDU */
+ rsp_pdu = iscsi_get_pdu(conn);
+ assert(rsp_pdu != NULL);
+ rsph = (struct iscsi_bhs_scsi_resp *)&rsp_pdu->bhs;
+ assert(task->scsi.sense_data_len <= sizeof(rsp_pdu->sense.data));
+ memcpy(rsp_pdu->sense.data, task->scsi.sense_data, task->scsi.sense_data_len);
+ to_be16(&rsp_pdu->sense.length, task->scsi.sense_data_len);
+ rsp_pdu->data = (uint8_t *)&rsp_pdu->sense;
+ rsp_pdu->data_from_mempool = true;
+
+ /*
+ * we need to hold onto this task/cmd because until the
+ * PDU has been written out
+ */
+ rsp_pdu->task = task;
+ task->scsi.ref++;
+
+ rsph->opcode = ISCSI_OP_SCSI_RSP;
+ rsph->flags |= 0x80; /* bit 0 is default to 1 */
+
+ if (O_bit) {
+ rsph->flags |= ISCSI_SCSI_OVERFLOW;
+ }
+
+ if (U_bit) {
+ rsph->flags |= ISCSI_SCSI_UNDERFLOW;
+ }
+
+ rsph->status = task->scsi.status;
+ if (task->scsi.sense_data_len) {
+ /* SenseLength (2 bytes) + SenseData */
+ DSET24(rsph->data_segment_len, 2 + task->scsi.sense_data_len);
+ }
+ to_be32(&rsph->itt, task_tag);
+
+ to_be32(&rsph->stat_sn, conn->StatSN);
+ conn->StatSN++;
+
+ if (!iscsi_task_is_immediate(primary)) {
+ conn->sess->MaxCmdSN++;
+ }
+
+ to_be32(&rsph->exp_cmd_sn, conn->sess->ExpCmdSN);
+ to_be32(&rsph->max_cmd_sn, conn->sess->MaxCmdSN);
+
+ to_be32(&rsph->bi_read_res_cnt, 0);
+ to_be32(&rsph->res_cnt, residual_len);
+
+ iscsi_conn_write_pdu(conn, rsp_pdu, iscsi_conn_pdu_generic_complete, NULL);
+}
+
+/*
+ * This function compare the input pdu's bhs with the pdu's bhs associated by
+ * active_r2t_tasks and queued_r2t_tasks in a connection
+ */
+static bool
+iscsi_compare_pdu_bhs_within_existed_r2t_tasks(struct spdk_iscsi_conn *conn,
+ struct spdk_iscsi_pdu *pdu)
+{
+ struct spdk_iscsi_task *task;
+
+ TAILQ_FOREACH(task, &conn->active_r2t_tasks, link) {
+ if (!memcmp(&pdu->bhs, iscsi_task_get_bhs(task), ISCSI_BHS_LEN)) {
+ return true;
+ }
+ }
+
+ TAILQ_FOREACH(task, &conn->queued_r2t_tasks, link) {
+ if (!memcmp(&pdu->bhs, iscsi_task_get_bhs(task), ISCSI_BHS_LEN)) {
+ return true;
+ }
+ }
+
+ return false;
+}
+
+void
+iscsi_queue_task(struct spdk_iscsi_conn *conn, struct spdk_iscsi_task *task)
+{
+ spdk_trace_record(TRACE_ISCSI_TASK_QUEUE, conn->id, task->scsi.length,
+ (uintptr_t)task, (uintptr_t)task->pdu);
+ task->is_queued = true;
+ spdk_scsi_dev_queue_task(conn->dev, &task->scsi);
+}
+
+static int
+iscsi_pdu_payload_op_scsi_read(struct spdk_iscsi_conn *conn, struct spdk_iscsi_task *task)
+{
+ if (task->scsi.transfer_len <= SPDK_BDEV_LARGE_BUF_MAX_SIZE) {
+ task->parent = NULL;
+ task->scsi.offset = 0;
+ task->scsi.length = task->scsi.transfer_len;
+ spdk_scsi_task_set_data(&task->scsi, NULL, 0);
+
+ iscsi_queue_task(conn, task);
+ return 0;
+ } else {
+ TAILQ_INIT(&task->subtask_list);
+ task->current_datain_offset = 0;
+ TAILQ_INSERT_TAIL(&conn->queued_datain_tasks, task, link);
+
+ return iscsi_conn_handle_queued_datain_tasks(conn);
+ }
+}
+
+static int
+iscsi_pdu_payload_op_scsi_write(struct spdk_iscsi_conn *conn, struct spdk_iscsi_task *task)
+{
+ struct spdk_iscsi_pdu *pdu;
+ struct iscsi_bhs_scsi_req *reqh;
+ uint32_t transfer_len;
+ uint32_t scsi_data_len;
+ int rc;
+
+ pdu = iscsi_task_get_pdu(task);
+ reqh = (struct iscsi_bhs_scsi_req *)&pdu->bhs;
+
+ transfer_len = task->scsi.transfer_len;
+
+ if (spdk_likely(!pdu->dif_insert_or_strip)) {
+ scsi_data_len = pdu->data_segment_len;
+ } else {
+ scsi_data_len = pdu->data_buf_len;
+ }
+
+ if (reqh->final_bit &&
+ pdu->data_segment_len < transfer_len) {
+ /* needs R2T */
+ rc = add_transfer_task(conn, task);
+ if (rc < 0) {
+ SPDK_ERRLOG("add_transfer_task() failed\n");
+ iscsi_task_put(task);
+ return SPDK_ISCSI_CONNECTION_FATAL;
+ }
+
+ /* Non-immediate writes */
+ if (pdu->data_segment_len == 0) {
+ return 0;
+ } else {
+ /* we are doing the first partial write task */
+ task->scsi.ref++;
+ spdk_scsi_task_set_data(&task->scsi, pdu->data, scsi_data_len);
+ task->scsi.length = pdu->data_segment_len;
+ }
+ }
+
+ if (pdu->data_segment_len == transfer_len) {
+ /* we are doing small writes with no R2T */
+ spdk_scsi_task_set_data(&task->scsi, pdu->data, scsi_data_len);
+ task->scsi.length = transfer_len;
+ }
+
+ iscsi_queue_task(conn, task);
+ return 0;
+}
+
+static int
+iscsi_pdu_hdr_op_scsi(struct spdk_iscsi_conn *conn, struct spdk_iscsi_pdu *pdu)
+{
+ struct spdk_iscsi_task *task;
+ struct spdk_scsi_dev *dev;
+ uint8_t *cdb;
+ uint64_t lun;
+ uint32_t task_tag;
+ uint32_t transfer_len;
+ int R_bit, W_bit;
+ int lun_i;
+ struct iscsi_bhs_scsi_req *reqh;
+
+ if (conn->sess->session_type != SESSION_TYPE_NORMAL) {
+ SPDK_ERRLOG("ISCSI_OP_SCSI not allowed in discovery and invalid session\n");
+ return SPDK_ISCSI_CONNECTION_FATAL;
+ }
+
+ reqh = (struct iscsi_bhs_scsi_req *)&pdu->bhs;
+
+ R_bit = reqh->read_bit;
+ W_bit = reqh->write_bit;
+ lun = from_be64(&reqh->lun);
+ task_tag = from_be32(&reqh->itt);
+ transfer_len = from_be32(&reqh->expected_data_xfer_len);
+ cdb = reqh->cdb;
+
+ SPDK_LOGDUMP(SPDK_LOG_ISCSI, "CDB", cdb, 16);
+
+ task = iscsi_task_get(conn, NULL, iscsi_task_cpl);
+ if (!task) {
+ SPDK_ERRLOG("Unable to acquire task\n");
+ return SPDK_ISCSI_CONNECTION_FATAL;
+ }
+
+ iscsi_task_associate_pdu(task, pdu);
+ lun_i = spdk_scsi_lun_id_fmt_to_int(lun);
+ task->lun_id = lun_i;
+ dev = conn->dev;
+ task->scsi.lun = spdk_scsi_dev_get_lun(dev, lun_i);
+
+ if ((R_bit != 0) && (W_bit != 0)) {
+ SPDK_ERRLOG("Bidirectional CDB is not supported\n");
+ iscsi_task_put(task);
+ return SPDK_ISCSI_CONNECTION_FATAL;
+ }
+
+ task->scsi.cdb = cdb;
+ task->tag = task_tag;
+ task->scsi.transfer_len = transfer_len;
+ task->scsi.target_port = conn->target_port;
+ task->scsi.initiator_port = conn->initiator_port;
+ task->parent = NULL;
+ task->rsp_scsi_status = SPDK_SCSI_STATUS_GOOD;
+
+ if (task->scsi.lun == NULL) {
+ spdk_scsi_task_process_null_lun(&task->scsi);
+ iscsi_task_cpl(&task->scsi);
+ return 0;
+ }
+
+ /* no bi-directional support */
+ if (R_bit) {
+ task->scsi.dxfer_dir = SPDK_SCSI_DIR_FROM_DEV;
+ } else if (W_bit) {
+ task->scsi.dxfer_dir = SPDK_SCSI_DIR_TO_DEV;
+
+ if ((conn->sess->ErrorRecoveryLevel >= 1) &&
+ (iscsi_compare_pdu_bhs_within_existed_r2t_tasks(conn, pdu))) {
+ iscsi_task_response(conn, task);
+ iscsi_task_put(task);
+ return 0;
+ }
+
+ if (pdu->data_segment_len > iscsi_get_max_immediate_data_size()) {
+ SPDK_ERRLOG("data segment len(=%zu) > immediate data len(=%"PRIu32")\n",
+ pdu->data_segment_len, iscsi_get_max_immediate_data_size());
+ iscsi_task_put(task);
+ return iscsi_reject(conn, pdu, ISCSI_REASON_PROTOCOL_ERROR);
+ }
+
+ if (pdu->data_segment_len > transfer_len) {
+ SPDK_ERRLOG("data segment len(=%zu) > task transfer len(=%d)\n",
+ pdu->data_segment_len, transfer_len);
+ iscsi_task_put(task);
+ return iscsi_reject(conn, pdu, ISCSI_REASON_PROTOCOL_ERROR);
+ }
+
+ /* check the ImmediateData and also pdu->data_segment_len */
+ if ((!conn->sess->ImmediateData && (pdu->data_segment_len > 0)) ||
+ (pdu->data_segment_len > conn->sess->FirstBurstLength)) {
+ iscsi_task_put(task);
+ return iscsi_reject(conn, pdu, ISCSI_REASON_PROTOCOL_ERROR);
+ }
+
+ if (spdk_unlikely(spdk_scsi_lun_get_dif_ctx(task->scsi.lun, &task->scsi, &pdu->dif_ctx))) {
+ pdu->dif_insert_or_strip = true;
+ }
+ } else {
+ /* neither R nor W bit set */
+ task->scsi.dxfer_dir = SPDK_SCSI_DIR_NONE;
+ if (transfer_len > 0) {
+ iscsi_task_put(task);
+ SPDK_ERRLOG("Reject scsi cmd with EDTL > 0 but (R | W) == 0\n");
+ return iscsi_reject(conn, pdu, ISCSI_REASON_INVALID_PDU_FIELD);
+ }
+ }
+
+ pdu->task = task;
+ return 0;
+}
+
+static int
+iscsi_pdu_payload_op_scsi(struct spdk_iscsi_conn *conn, struct spdk_iscsi_pdu *pdu)
+{
+ struct spdk_iscsi_task *task;
+
+ if (pdu->task == NULL) {
+ return 0;
+ }
+
+ task = pdu->task;
+
+ if (spdk_scsi_dev_get_lun(conn->dev, task->lun_id) == NULL) {
+ spdk_scsi_task_process_null_lun(&task->scsi);
+ iscsi_task_cpl(&task->scsi);
+ return 0;
+ }
+
+ switch (task->scsi.dxfer_dir) {
+ case SPDK_SCSI_DIR_FROM_DEV:
+ return iscsi_pdu_payload_op_scsi_read(conn, task);
+ case SPDK_SCSI_DIR_TO_DEV:
+ return iscsi_pdu_payload_op_scsi_write(conn, task);
+ case SPDK_SCSI_DIR_NONE:
+ iscsi_queue_task(conn, task);
+ return 0;
+ default:
+ assert(false);
+ iscsi_task_put(task);
+ break;
+ }
+
+ return SPDK_ISCSI_CONNECTION_FATAL;
+}
+
+static void
+abort_transfer_task_in_task_mgmt_resp(struct spdk_iscsi_conn *conn,
+ struct spdk_iscsi_task *task)
+{
+ struct spdk_iscsi_pdu *pdu;
+
+ pdu = iscsi_task_get_pdu(task);
+
+ switch (task->scsi.function) {
+ /* abort task identified by Reference Task Tag field */
+ case ISCSI_TASK_FUNC_ABORT_TASK:
+ iscsi_del_transfer_task(conn, task->scsi.abort_id);
+ break;
+
+ /* abort all tasks issued via this session on the LUN */
+ case ISCSI_TASK_FUNC_ABORT_TASK_SET:
+ iscsi_clear_all_transfer_task(conn, task->scsi.lun, pdu);
+ break;
+
+ case ISCSI_TASK_FUNC_LOGICAL_UNIT_RESET:
+ iscsi_clear_all_transfer_task(conn, task->scsi.lun, pdu);
+ break;
+ }
+}
+
+void
+iscsi_task_mgmt_response(struct spdk_iscsi_conn *conn,
+ struct spdk_iscsi_task *task)
+{
+ struct spdk_iscsi_pdu *rsp_pdu;
+ struct iscsi_bhs_task_req *reqh;
+ struct iscsi_bhs_task_resp *rsph;
+
+ if (task->pdu == NULL) {
+ /*
+ * This was an internally generated task management command,
+ * usually from LUN cleanup when a connection closes.
+ */
+ return;
+ }
+
+ reqh = (struct iscsi_bhs_task_req *)&task->pdu->bhs;
+ /* response PDU */
+ rsp_pdu = iscsi_get_pdu(conn);
+ rsph = (struct iscsi_bhs_task_resp *)&rsp_pdu->bhs;
+ rsph->opcode = ISCSI_OP_TASK_RSP;
+ rsph->flags |= 0x80; /* bit 0 default to 1 */
+ switch (task->scsi.response) {
+ case SPDK_SCSI_TASK_MGMT_RESP_COMPLETE:
+ abort_transfer_task_in_task_mgmt_resp(conn, task);
+ rsph->response = ISCSI_TASK_FUNC_RESP_COMPLETE;
+ break;
+ case SPDK_SCSI_TASK_MGMT_RESP_SUCCESS:
+ abort_transfer_task_in_task_mgmt_resp(conn, task);
+ rsph->response = ISCSI_TASK_FUNC_RESP_COMPLETE;
+ break;
+ case SPDK_SCSI_TASK_MGMT_RESP_REJECT:
+ rsph->response = ISCSI_TASK_FUNC_REJECTED;
+ break;
+ case SPDK_SCSI_TASK_MGMT_RESP_INVALID_LUN:
+ rsph->response = ISCSI_TASK_FUNC_RESP_LUN_NOT_EXIST;
+ break;
+ case SPDK_SCSI_TASK_MGMT_RESP_TARGET_FAILURE:
+ rsph->response = ISCSI_TASK_FUNC_REJECTED;
+ break;
+ case SPDK_SCSI_TASK_MGMT_RESP_REJECT_FUNC_NOT_SUPPORTED:
+ rsph->response = ISCSI_TASK_FUNC_RESP_FUNC_NOT_SUPPORTED;
+ break;
+ }
+ rsph->itt = reqh->itt;
+
+ to_be32(&rsph->stat_sn, conn->StatSN);
+ conn->StatSN++;
+
+ if (reqh->immediate == 0) {
+ conn->sess->MaxCmdSN++;
+ }
+
+ to_be32(&rsph->exp_cmd_sn, conn->sess->ExpCmdSN);
+ to_be32(&rsph->max_cmd_sn, conn->sess->MaxCmdSN);
+
+ iscsi_conn_write_pdu(conn, rsp_pdu, iscsi_conn_pdu_generic_complete, NULL);
+}
+
+static void
+iscsi_queue_mgmt_task(struct spdk_iscsi_conn *conn, struct spdk_iscsi_task *task)
+{
+ struct spdk_scsi_lun *lun;
+
+ lun = spdk_scsi_dev_get_lun(conn->dev, task->lun_id);
+ if (lun == NULL) {
+ task->scsi.response = SPDK_SCSI_TASK_MGMT_RESP_INVALID_LUN;
+ iscsi_task_mgmt_response(conn, task);
+ iscsi_task_put(task);
+ return;
+ }
+
+ spdk_scsi_dev_queue_mgmt_task(conn->dev, &task->scsi);
+}
+
+static int
+_iscsi_op_abort_task(void *arg)
+{
+ struct spdk_iscsi_task *task = arg;
+ int rc;
+
+ rc = iscsi_conn_abort_queued_datain_task(task->conn, task->scsi.abort_id);
+ if (rc != 0) {
+ return SPDK_POLLER_BUSY;
+ }
+
+ spdk_poller_unregister(&task->mgmt_poller);
+ iscsi_queue_mgmt_task(task->conn, task);
+ return SPDK_POLLER_BUSY;
+}
+
+static void
+iscsi_op_abort_task(struct spdk_iscsi_task *task, uint32_t ref_task_tag)
+{
+ task->scsi.abort_id = ref_task_tag;
+ task->scsi.function = SPDK_SCSI_TASK_FUNC_ABORT_TASK;
+ task->mgmt_poller = SPDK_POLLER_REGISTER(_iscsi_op_abort_task, task, 10);
+}
+
+static int
+_iscsi_op_abort_task_set(void *arg)
+{
+ struct spdk_iscsi_task *task = arg;
+ int rc;
+
+ rc = iscsi_conn_abort_queued_datain_tasks(task->conn, task->scsi.lun,
+ task->pdu);
+ if (rc != 0) {
+ return SPDK_POLLER_BUSY;
+ }
+
+ spdk_poller_unregister(&task->mgmt_poller);
+ iscsi_queue_mgmt_task(task->conn, task);
+ return SPDK_POLLER_BUSY;
+}
+
+void
+iscsi_op_abort_task_set(struct spdk_iscsi_task *task, uint8_t function)
+{
+ task->scsi.function = function;
+ task->mgmt_poller = SPDK_POLLER_REGISTER(_iscsi_op_abort_task_set, task, 10);
+}
+
+static int
+iscsi_pdu_hdr_op_task(struct spdk_iscsi_conn *conn, struct spdk_iscsi_pdu *pdu)
+{
+ struct iscsi_bhs_task_req *reqh;
+ uint64_t lun;
+ uint32_t task_tag;
+ uint32_t ref_task_tag;
+ uint8_t function;
+ int lun_i;
+ struct spdk_iscsi_task *task;
+ struct spdk_scsi_dev *dev;
+
+ if (conn->sess->session_type != SESSION_TYPE_NORMAL) {
+ SPDK_ERRLOG("ISCSI_OP_TASK not allowed in discovery and invalid session\n");
+ return SPDK_ISCSI_CONNECTION_FATAL;
+ }
+
+ reqh = (struct iscsi_bhs_task_req *)&pdu->bhs;
+ function = reqh->flags & ISCSI_TASK_FUNCTION_MASK;
+ lun = from_be64(&reqh->lun);
+ task_tag = from_be32(&reqh->itt);
+ ref_task_tag = from_be32(&reqh->ref_task_tag);
+
+ SPDK_DEBUGLOG(SPDK_LOG_ISCSI, "I=%d, func=%d, ITT=%x, ref TT=%x, LUN=0x%16.16"PRIx64"\n",
+ reqh->immediate, function, task_tag, ref_task_tag, lun);
+
+ SPDK_DEBUGLOG(SPDK_LOG_ISCSI, "StatSN=%u, ExpCmdSN=%u, MaxCmdSN=%u\n",
+ conn->StatSN, conn->sess->ExpCmdSN, conn->sess->MaxCmdSN);
+
+ lun_i = spdk_scsi_lun_id_fmt_to_int(lun);
+ dev = conn->dev;
+
+ task = iscsi_task_get(conn, NULL, iscsi_task_mgmt_cpl);
+ if (!task) {
+ SPDK_ERRLOG("Unable to acquire task\n");
+ return SPDK_ISCSI_CONNECTION_FATAL;
+ }
+
+ iscsi_task_associate_pdu(task, pdu);
+ task->scsi.target_port = conn->target_port;
+ task->scsi.initiator_port = conn->initiator_port;
+ task->tag = task_tag;
+ task->scsi.lun = spdk_scsi_dev_get_lun(dev, lun_i);
+ task->lun_id = lun_i;
+
+ if (task->scsi.lun == NULL) {
+ task->scsi.response = SPDK_SCSI_TASK_MGMT_RESP_INVALID_LUN;
+ iscsi_task_mgmt_response(conn, task);
+ iscsi_task_put(task);
+ return 0;
+ }
+
+ switch (function) {
+ /* abort task identified by Referenced Task Tag field */
+ case ISCSI_TASK_FUNC_ABORT_TASK:
+ SPDK_NOTICELOG("ABORT_TASK\n");
+
+ iscsi_op_abort_task(task, ref_task_tag);
+ return 0;
+
+ /* abort all tasks issued via this session on the LUN */
+ case ISCSI_TASK_FUNC_ABORT_TASK_SET:
+ SPDK_NOTICELOG("ABORT_TASK_SET\n");
+
+ iscsi_op_abort_task_set(task, SPDK_SCSI_TASK_FUNC_ABORT_TASK_SET);
+ return 0;
+
+ case ISCSI_TASK_FUNC_CLEAR_TASK_SET:
+ task->scsi.response = SPDK_SCSI_TASK_MGMT_RESP_REJECT_FUNC_NOT_SUPPORTED;
+ SPDK_NOTICELOG("CLEAR_TASK_SET (Unsupported)\n");
+ break;
+
+ case ISCSI_TASK_FUNC_CLEAR_ACA:
+ task->scsi.response = SPDK_SCSI_TASK_MGMT_RESP_REJECT_FUNC_NOT_SUPPORTED;
+ SPDK_NOTICELOG("CLEAR_ACA (Unsupported)\n");
+ break;
+
+ case ISCSI_TASK_FUNC_LOGICAL_UNIT_RESET:
+ SPDK_NOTICELOG("LOGICAL_UNIT_RESET\n");
+
+ iscsi_op_abort_task_set(task, SPDK_SCSI_TASK_FUNC_LUN_RESET);
+ return 0;
+
+ case ISCSI_TASK_FUNC_TARGET_WARM_RESET:
+ SPDK_NOTICELOG("TARGET_WARM_RESET (Unsupported)\n");
+ task->scsi.response = SPDK_SCSI_TASK_MGMT_RESP_REJECT_FUNC_NOT_SUPPORTED;
+ break;
+
+ case ISCSI_TASK_FUNC_TARGET_COLD_RESET:
+ SPDK_NOTICELOG("TARGET_COLD_RESET (Unsupported)\n");
+ task->scsi.response = SPDK_SCSI_TASK_MGMT_RESP_REJECT_FUNC_NOT_SUPPORTED;
+ break;
+
+ case ISCSI_TASK_FUNC_TASK_REASSIGN:
+ SPDK_NOTICELOG("TASK_REASSIGN (Unsupported)\n");
+ task->scsi.response = SPDK_SCSI_TASK_MGMT_RESP_REJECT_FUNC_NOT_SUPPORTED;
+ break;
+
+ default:
+ SPDK_ERRLOG("unsupported function %d\n", function);
+ task->scsi.response = SPDK_SCSI_TASK_MGMT_RESP_REJECT;
+ break;
+ }
+
+ iscsi_task_mgmt_response(conn, task);
+ iscsi_task_put(task);
+ return 0;
+}
+
+static int
+iscsi_pdu_hdr_op_nopout(struct spdk_iscsi_conn *conn, struct spdk_iscsi_pdu *pdu)
+{
+ struct iscsi_bhs_nop_out *reqh;
+ uint32_t task_tag;
+ uint32_t transfer_tag;
+ int I_bit;
+
+ if (conn->sess->session_type == SESSION_TYPE_DISCOVERY) {
+ SPDK_ERRLOG("ISCSI_OP_NOPOUT not allowed in discovery session\n");
+ return SPDK_ISCSI_CONNECTION_FATAL;
+ }
+
+ reqh = (struct iscsi_bhs_nop_out *)&pdu->bhs;
+ I_bit = reqh->immediate;
+
+ if (pdu->data_segment_len > SPDK_ISCSI_MAX_RECV_DATA_SEGMENT_LENGTH) {
+ return iscsi_reject(conn, pdu, ISCSI_REASON_PROTOCOL_ERROR);
+ }
+
+ task_tag = from_be32(&reqh->itt);
+ transfer_tag = from_be32(&reqh->ttt);
+
+ SPDK_DEBUGLOG(SPDK_LOG_ISCSI, "I=%d, ITT=%x, TTT=%x\n",
+ I_bit, task_tag, transfer_tag);
+
+ SPDK_DEBUGLOG(SPDK_LOG_ISCSI, "CmdSN=%u, StatSN=%u, ExpCmdSN=%u, MaxCmdSN=%u\n",
+ pdu->cmd_sn, conn->StatSN, conn->sess->ExpCmdSN,
+ conn->sess->MaxCmdSN);
+
+ if (transfer_tag != 0xFFFFFFFF && transfer_tag != (uint32_t)conn->id) {
+ SPDK_ERRLOG("invalid transfer tag 0x%x\n", transfer_tag);
+ /*
+ * Technically we should probably fail the connection here, but for now
+ * just print the error message and continue.
+ */
+ }
+
+ if (task_tag == 0xffffffffU && I_bit == 0) {
+ SPDK_ERRLOG("got NOPOUT ITT=0xffffffff, I=0\n");
+ return SPDK_ISCSI_CONNECTION_FATAL;
+ }
+
+ return 0;
+}
+
+static int
+iscsi_pdu_payload_op_nopout(struct spdk_iscsi_conn *conn, struct spdk_iscsi_pdu *pdu)
+{
+ struct spdk_iscsi_pdu *rsp_pdu;
+ struct iscsi_bhs_nop_out *reqh;
+ struct iscsi_bhs_nop_in *rsph;
+ uint8_t *data;
+ uint64_t lun;
+ uint32_t task_tag;
+ int I_bit;
+ int data_len;
+
+ reqh = (struct iscsi_bhs_nop_out *)&pdu->bhs;
+ I_bit = reqh->immediate;
+
+ data_len = pdu->data_segment_len;
+ if (data_len > conn->MaxRecvDataSegmentLength) {
+ data_len = conn->MaxRecvDataSegmentLength;
+ }
+
+ lun = from_be64(&reqh->lun);
+ task_tag = from_be32(&reqh->itt);
+
+ /*
+ * We don't actually check to see if this is a response to the NOP-In
+ * that we sent. Our goal is to just verify that the initiator is
+ * alive and responding to commands, not to verify that it tags
+ * NOP-Outs correctly
+ */
+ conn->nop_outstanding = false;
+
+ if (task_tag == 0xffffffffU) {
+ assert(I_bit == 1);
+ SPDK_DEBUGLOG(SPDK_LOG_ISCSI, "got NOPOUT ITT=0xffffffff\n");
+ return 0;
+ }
+
+ data = calloc(1, data_len);
+ if (!data) {
+ SPDK_ERRLOG("calloc() failed for ping data\n");
+ return SPDK_ISCSI_CONNECTION_FATAL;
+ }
+
+ /* response of NOPOUT */
+ if (data_len > 0) {
+ /* copy ping data */
+ memcpy(data, pdu->data, data_len);
+ }
+
+ /* response PDU */
+ rsp_pdu = iscsi_get_pdu(conn);
+ assert(rsp_pdu != NULL);
+
+ rsph = (struct iscsi_bhs_nop_in *)&rsp_pdu->bhs;
+ rsp_pdu->data = data;
+ rsph->opcode = ISCSI_OP_NOPIN;
+ rsph->flags |= 0x80; /* bit 0 default to 1 */
+ DSET24(rsph->data_segment_len, data_len);
+ to_be64(&rsph->lun, lun);
+ to_be32(&rsph->itt, task_tag);
+ to_be32(&rsph->ttt, 0xffffffffU);
+
+ to_be32(&rsph->stat_sn, conn->StatSN);
+ conn->StatSN++;
+
+ if (I_bit == 0) {
+ conn->sess->MaxCmdSN++;
+ }
+
+ to_be32(&rsph->exp_cmd_sn, conn->sess->ExpCmdSN);
+ to_be32(&rsph->max_cmd_sn, conn->sess->MaxCmdSN);
+
+ iscsi_conn_write_pdu(conn, rsp_pdu, iscsi_conn_pdu_generic_complete, NULL);
+ conn->last_nopin = spdk_get_ticks();
+
+ return 0;
+}
+
+/* This function returns the spdk_scsi_task by searching the snack list via
+ * task transfertag and the pdu's opcode
+ */
+static struct spdk_iscsi_task *
+get_scsi_task_from_ttt(struct spdk_iscsi_conn *conn, uint32_t transfer_tag)
+{
+ struct spdk_iscsi_pdu *pdu;
+ struct iscsi_bhs_data_in *datain_bhs;
+
+ TAILQ_FOREACH(pdu, &conn->snack_pdu_list, tailq) {
+ if (pdu->bhs.opcode == ISCSI_OP_SCSI_DATAIN) {
+ datain_bhs = (struct iscsi_bhs_data_in *)&pdu->bhs;
+ if (from_be32(&datain_bhs->ttt) == transfer_tag) {
+ return pdu->task;
+ }
+ }
+ }
+
+ return NULL;
+}
+
+/* This function returns the spdk_scsi_task by searching the snack list via
+ * initiator task tag and the pdu's opcode
+ */
+static struct spdk_iscsi_task *
+get_scsi_task_from_itt(struct spdk_iscsi_conn *conn,
+ uint32_t task_tag, enum iscsi_op opcode)
+{
+ struct spdk_iscsi_pdu *pdu;
+
+ TAILQ_FOREACH(pdu, &conn->snack_pdu_list, tailq) {
+ if (pdu->bhs.opcode == opcode &&
+ pdu->task != NULL &&
+ pdu->task->tag == task_tag) {
+ return pdu->task;
+ }
+ }
+
+ return NULL;
+}
+
+/* This function is used to handle the r2t snack */
+static int
+iscsi_handle_r2t_snack(struct spdk_iscsi_conn *conn,
+ struct spdk_iscsi_task *task,
+ struct spdk_iscsi_pdu *pdu, uint32_t beg_run,
+ uint32_t run_length, int32_t task_tag)
+{
+ int32_t last_r2tsn;
+ int i;
+
+ if (beg_run < task->acked_r2tsn) {
+ SPDK_ERRLOG("ITT: 0x%08x, R2T SNACK requests retransmission of"
+ "R2TSN: from 0x%08x to 0x%08x. But it has already"
+ "ack to R2TSN:0x%08x, protocol error.\n",
+ task_tag, beg_run, (beg_run + run_length),
+ (task->acked_r2tsn - 1));
+ return iscsi_reject(conn, pdu, ISCSI_REASON_PROTOCOL_ERROR);
+ }
+
+ if (run_length) {
+ if ((beg_run + run_length) > task->R2TSN) {
+ SPDK_ERRLOG("ITT: 0x%08x, received R2T SNACK with"
+ "BegRun: 0x%08x, RunLength: 0x%08x, exceeds"
+ "current R2TSN: 0x%08x, protocol error.\n",
+ task_tag, beg_run, run_length,
+ task->R2TSN);
+
+ return iscsi_reject(conn, pdu, ISCSI_REASON_INVALID_PDU_FIELD);
+ }
+ last_r2tsn = (beg_run + run_length);
+ } else {
+ last_r2tsn = task->R2TSN;
+ }
+
+ for (i = beg_run; i < last_r2tsn; i++) {
+ if (iscsi_send_r2t_recovery(conn, task, i, false) < 0) {
+ SPDK_ERRLOG("The r2t_sn=%d of r2t_task=%p is not sent\n", i, task);
+ }
+ }
+ return 0;
+}
+
+/* This function is used to recover the data in packet */
+static int
+iscsi_handle_recovery_datain(struct spdk_iscsi_conn *conn,
+ struct spdk_iscsi_task *task,
+ struct spdk_iscsi_pdu *pdu, uint32_t beg_run,
+ uint32_t run_length, uint32_t task_tag)
+{
+ struct spdk_iscsi_pdu *old_pdu, *pdu_temp;
+ uint32_t i;
+ struct iscsi_bhs_data_in *datain_header;
+ uint32_t last_statsn;
+
+ task = iscsi_task_get_primary(task);
+
+ SPDK_DEBUGLOG(SPDK_LOG_ISCSI, "iscsi_handle_recovery_datain\n");
+
+ if (beg_run < task->acked_data_sn) {
+ SPDK_ERRLOG("ITT: 0x%08x, DATA IN SNACK requests retransmission of"
+ "DATASN: from 0x%08x to 0x%08x but already acked to "
+ "DATASN: 0x%08x protocol error\n",
+ task_tag, beg_run,
+ (beg_run + run_length), (task->acked_data_sn - 1));
+
+ return iscsi_reject(conn, pdu, ISCSI_REASON_PROTOCOL_ERROR);
+ }
+
+ if (run_length == 0) {
+ /* as the DataSN begins at 0 */
+ run_length = task->datain_datasn + 1;
+ }
+
+ if ((beg_run + run_length - 1) > task->datain_datasn) {
+ SPDK_ERRLOG("Initiator requests BegRun: 0x%08x, RunLength:"
+ "0x%08x greater than maximum DataSN: 0x%08x.\n",
+ beg_run, run_length, task->datain_datasn);
+
+ return -1;
+ } else {
+ last_statsn = beg_run + run_length - 1;
+ }
+
+ for (i = beg_run; i <= last_statsn; i++) {
+ TAILQ_FOREACH_SAFE(old_pdu, &conn->snack_pdu_list, tailq, pdu_temp) {
+ if (old_pdu->bhs.opcode == ISCSI_OP_SCSI_DATAIN) {
+ datain_header = (struct iscsi_bhs_data_in *)&old_pdu->bhs;
+ if (from_be32(&datain_header->itt) == task_tag &&
+ from_be32(&datain_header->data_sn) == i) {
+ TAILQ_REMOVE(&conn->snack_pdu_list, old_pdu, tailq);
+ iscsi_conn_write_pdu(conn, old_pdu, old_pdu->cb_fn, old_pdu->cb_arg);
+ break;
+ }
+ }
+ }
+ }
+ return 0;
+}
+
+/* This function is used to handle the status snack */
+static int
+iscsi_handle_status_snack(struct spdk_iscsi_conn *conn, struct spdk_iscsi_pdu *pdu)
+{
+ uint32_t beg_run;
+ uint32_t run_length;
+ struct iscsi_bhs_snack_req *reqh;
+ uint32_t i;
+ uint32_t last_statsn;
+ bool found_pdu;
+ struct spdk_iscsi_pdu *old_pdu;
+
+ reqh = (struct iscsi_bhs_snack_req *)&pdu->bhs;
+ beg_run = from_be32(&reqh->beg_run);
+ run_length = from_be32(&reqh->run_len);
+
+ SPDK_DEBUGLOG(SPDK_LOG_ISCSI, "beg_run=%d, run_length=%d, conn->StatSN="
+ "%d, conn->exp_statsn=%d\n", beg_run, run_length,
+ conn->StatSN, conn->exp_statsn);
+
+ if (!beg_run) {
+ beg_run = conn->exp_statsn;
+ } else if (beg_run < conn->exp_statsn) {
+ SPDK_ERRLOG("Got Status SNACK Begrun: 0x%08x, RunLength: 0x%08x "
+ "but already got ExpStatSN: 0x%08x on CID:%hu.\n",
+ beg_run, run_length, conn->StatSN, conn->cid);
+
+ return iscsi_reject(conn, pdu, ISCSI_REASON_INVALID_PDU_FIELD);
+ }
+
+ last_statsn = (!run_length) ? conn->StatSN : (beg_run + run_length);
+
+ for (i = beg_run; i < last_statsn; i++) {
+ found_pdu = false;
+ TAILQ_FOREACH(old_pdu, &conn->snack_pdu_list, tailq) {
+ if (from_be32(&old_pdu->bhs.stat_sn) == i) {
+ found_pdu = true;
+ break;
+ }
+ }
+
+ if (!found_pdu) {
+ SPDK_ERRLOG("Unable to find StatSN: 0x%08x. For a Status"
+ "SNACK, assuming this is a proactive SNACK "
+ "for an untransmitted StatSN, ignoring.\n",
+ beg_run);
+ } else {
+ TAILQ_REMOVE(&conn->snack_pdu_list, old_pdu, tailq);
+ iscsi_conn_write_pdu(conn, old_pdu, old_pdu->cb_fn, old_pdu->cb_arg);
+ }
+ }
+
+ return 0;
+}
+
+/* This function is used to handle the data ack snack */
+static int
+iscsi_handle_data_ack(struct spdk_iscsi_conn *conn, struct spdk_iscsi_pdu *pdu)
+{
+ uint32_t transfer_tag;
+ uint32_t beg_run;
+ uint32_t run_length;
+ struct spdk_iscsi_pdu *old_pdu;
+ uint32_t old_datasn;
+ struct iscsi_bhs_snack_req *reqh;
+ struct spdk_iscsi_task *task;
+ struct iscsi_bhs_data_in *datain_header;
+ struct spdk_iscsi_task *primary;
+
+ reqh = (struct iscsi_bhs_snack_req *)&pdu->bhs;
+ transfer_tag = from_be32(&reqh->ttt);
+ beg_run = from_be32(&reqh->beg_run);
+ run_length = from_be32(&reqh->run_len);
+ task = NULL;
+ datain_header = NULL;
+
+ SPDK_DEBUGLOG(SPDK_LOG_ISCSI, "beg_run=%d,transfer_tag=%d,run_len=%d\n",
+ beg_run, transfer_tag, run_length);
+
+ task = get_scsi_task_from_ttt(conn, transfer_tag);
+ if (!task) {
+ SPDK_ERRLOG("Data ACK SNACK for TTT: 0x%08x is invalid.\n",
+ transfer_tag);
+ goto reject_return;
+ }
+
+ primary = iscsi_task_get_primary(task);
+ if ((run_length != 0) || (beg_run < primary->acked_data_sn)) {
+ SPDK_ERRLOG("TTT: 0x%08x Data ACK SNACK BegRUN: %d is less than "
+ "the next expected acked DataSN: %d\n",
+ transfer_tag, beg_run, primary->acked_data_sn);
+ goto reject_return;
+ }
+
+ primary->acked_data_sn = beg_run;
+
+ /* To free the pdu */
+ TAILQ_FOREACH(old_pdu, &conn->snack_pdu_list, tailq) {
+ if (old_pdu->bhs.opcode == ISCSI_OP_SCSI_DATAIN) {
+ datain_header = (struct iscsi_bhs_data_in *) &old_pdu->bhs;
+ old_datasn = from_be32(&datain_header->data_sn);
+ if ((from_be32(&datain_header->ttt) == transfer_tag) &&
+ (old_datasn == beg_run - 1)) {
+ TAILQ_REMOVE(&conn->snack_pdu_list, old_pdu, tailq);
+ iscsi_conn_free_pdu(conn, old_pdu);
+ break;
+ }
+ }
+ }
+
+ SPDK_DEBUGLOG(SPDK_LOG_ISCSI, "Received Data ACK SNACK for TTT: 0x%08x,"
+ " updated acked DataSN to 0x%08x.\n", transfer_tag,
+ (task->acked_data_sn - 1));
+
+ return 0;
+
+reject_return:
+ return iscsi_reject(conn, pdu, ISCSI_REASON_INVALID_SNACK);
+}
+
+/* This function is used to handle the snack request from the initiator */
+static int
+iscsi_pdu_hdr_op_snack(struct spdk_iscsi_conn *conn, struct spdk_iscsi_pdu *pdu)
+{
+ struct iscsi_bhs_snack_req *reqh;
+ struct spdk_iscsi_task *task;
+ int type;
+ uint32_t task_tag;
+ uint32_t beg_run;
+ uint32_t run_length;
+ int rc;
+
+ if (conn->sess->session_type == SESSION_TYPE_DISCOVERY) {
+ SPDK_ERRLOG("ISCSI_OP_SNACK not allowed in discovery session\n");
+ return SPDK_ISCSI_CONNECTION_FATAL;
+ }
+
+ reqh = (struct iscsi_bhs_snack_req *)&pdu->bhs;
+ if (!conn->sess->ErrorRecoveryLevel) {
+ SPDK_ERRLOG("Got a SNACK request in ErrorRecoveryLevel=0\n");
+ return iscsi_reject(conn, pdu, ISCSI_REASON_PROTOCOL_ERROR);
+ }
+
+ type = reqh->flags & ISCSI_FLAG_SNACK_TYPE_MASK;
+ SPDK_DEBUGLOG(SPDK_LOG_ISCSI, "The value of type is %d\n", type);
+
+ switch (type) {
+ case 0:
+ reqh = (struct iscsi_bhs_snack_req *)&pdu->bhs;
+ task_tag = from_be32(&reqh->itt);
+ beg_run = from_be32(&reqh->beg_run);
+ run_length = from_be32(&reqh->run_len);
+
+ SPDK_DEBUGLOG(SPDK_LOG_ISCSI, "beg_run=%d, run_length=%d, "
+ "task_tag=%x, transfer_tag=%u\n", beg_run,
+ run_length, task_tag, from_be32(&reqh->ttt));
+
+ task = get_scsi_task_from_itt(conn, task_tag,
+ ISCSI_OP_SCSI_DATAIN);
+ if (task) {
+ return iscsi_handle_recovery_datain(conn, task, pdu,
+ beg_run, run_length, task_tag);
+ }
+ task = get_scsi_task_from_itt(conn, task_tag, ISCSI_OP_R2T);
+ if (task) {
+ return iscsi_handle_r2t_snack(conn, task, pdu, beg_run,
+ run_length, task_tag);
+ }
+ SPDK_ERRLOG("It is Neither datain nor r2t recovery request\n");
+ rc = -1;
+ break;
+ case ISCSI_FLAG_SNACK_TYPE_STATUS:
+ rc = iscsi_handle_status_snack(conn, pdu);
+ break;
+ case ISCSI_FLAG_SNACK_TYPE_DATA_ACK:
+ rc = iscsi_handle_data_ack(conn, pdu);
+ break;
+ case ISCSI_FLAG_SNACK_TYPE_RDATA:
+ SPDK_ERRLOG("R-Data SNACK is Not Supported int spdk\n");
+ rc = iscsi_reject(conn, pdu, ISCSI_REASON_PROTOCOL_ERROR);
+ break;
+ default:
+ SPDK_ERRLOG("Unknown SNACK type %d, protocol error\n", type);
+ rc = iscsi_reject(conn, pdu, ISCSI_REASON_PROTOCOL_ERROR);
+ break;
+ }
+
+ return rc;
+}
+
+static int
+iscsi_pdu_hdr_op_data(struct spdk_iscsi_conn *conn, struct spdk_iscsi_pdu *pdu)
+{
+ struct spdk_iscsi_task *task, *subtask;
+ struct iscsi_bhs_data_out *reqh;
+ struct spdk_scsi_lun *lun_dev;
+ uint32_t transfer_tag;
+ uint32_t task_tag;
+ uint32_t transfer_len;
+ uint32_t DataSN;
+ uint32_t buffer_offset;
+ uint32_t len;
+ int F_bit;
+ int rc;
+ int reject_reason = ISCSI_REASON_INVALID_PDU_FIELD;
+
+ if (conn->sess->session_type == SESSION_TYPE_DISCOVERY) {
+ SPDK_ERRLOG("ISCSI_OP_SCSI_DATAOUT not allowed in discovery session\n");
+ return SPDK_ISCSI_CONNECTION_FATAL;
+ }
+
+ reqh = (struct iscsi_bhs_data_out *)&pdu->bhs;
+ F_bit = !!(reqh->flags & ISCSI_FLAG_FINAL);
+ transfer_tag = from_be32(&reqh->ttt);
+ task_tag = from_be32(&reqh->itt);
+ DataSN = from_be32(&reqh->data_sn);
+ buffer_offset = from_be32(&reqh->buffer_offset);
+
+ if (pdu->data_segment_len > SPDK_ISCSI_MAX_RECV_DATA_SEGMENT_LENGTH) {
+ reject_reason = ISCSI_REASON_PROTOCOL_ERROR;
+ goto reject_return;
+ }
+
+ task = get_transfer_task(conn, transfer_tag);
+ if (task == NULL) {
+ SPDK_ERRLOG("Not found task for transfer_tag=%x\n", transfer_tag);
+ goto reject_return;
+ }
+
+ lun_dev = spdk_scsi_dev_get_lun(conn->dev, task->lun_id);
+
+ if (pdu->data_segment_len > task->desired_data_transfer_length) {
+ SPDK_ERRLOG("the dataout pdu data length is larger than the value sent by R2T PDU\n");
+ return SPDK_ISCSI_CONNECTION_FATAL;
+ }
+
+ if (task->tag != task_tag) {
+ SPDK_ERRLOG("The r2t task tag is %u, and the dataout task tag is %u\n",
+ task->tag, task_tag);
+ goto reject_return;
+ }
+
+ if (DataSN != task->r2t_datasn) {
+ SPDK_ERRLOG("DataSN(%u) exp=%d error\n", DataSN, task->r2t_datasn);
+ if (conn->sess->ErrorRecoveryLevel >= 1) {
+ goto send_r2t_recovery_return;
+ } else {
+ reject_reason = ISCSI_REASON_PROTOCOL_ERROR;
+ goto reject_return;
+ }
+ }
+
+ if (buffer_offset != task->next_expected_r2t_offset) {
+ SPDK_ERRLOG("offset(%u) error\n", buffer_offset);
+ return SPDK_ISCSI_CONNECTION_FATAL;
+ }
+
+ transfer_len = task->scsi.transfer_len;
+ task->current_r2t_length += pdu->data_segment_len;
+ task->next_expected_r2t_offset += pdu->data_segment_len;
+ task->r2t_datasn++;
+
+ if (task->current_r2t_length > conn->sess->MaxBurstLength) {
+ SPDK_ERRLOG("R2T burst(%u) > MaxBurstLength(%u)\n",
+ task->current_r2t_length,
+ conn->sess->MaxBurstLength);
+ return SPDK_ISCSI_CONNECTION_FATAL;
+ }
+
+ if (F_bit) {
+ /*
+ * This R2T burst is done. Clear the length before we
+ * receive a PDU for the next R2t burst.
+ */
+ task->current_r2t_length = 0;
+ }
+
+ subtask = iscsi_task_get(conn, task, iscsi_task_cpl);
+ if (subtask == NULL) {
+ SPDK_ERRLOG("Unable to acquire subtask\n");
+ return SPDK_ISCSI_CONNECTION_FATAL;
+ }
+ subtask->scsi.offset = buffer_offset;
+ subtask->scsi.length = pdu->data_segment_len;
+ iscsi_task_associate_pdu(subtask, pdu);
+
+ if (task->next_expected_r2t_offset == transfer_len) {
+ task->acked_r2tsn++;
+ } else if (F_bit && (task->next_r2t_offset < transfer_len)) {
+ task->acked_r2tsn++;
+ len = spdk_min(conn->sess->MaxBurstLength,
+ (transfer_len - task->next_r2t_offset));
+ rc = iscsi_send_r2t(conn, task, task->next_r2t_offset, len,
+ task->ttt, &task->R2TSN);
+ if (rc < 0) {
+ SPDK_ERRLOG("iscsi_send_r2t() failed\n");
+ }
+ task->next_r2t_offset += len;
+ }
+
+ if (lun_dev == NULL) {
+ SPDK_DEBUGLOG(SPDK_LOG_ISCSI, "LUN %d is removed, complete the task immediately\n",
+ task->lun_id);
+ subtask->scsi.transfer_len = subtask->scsi.length;
+ spdk_scsi_task_process_null_lun(&subtask->scsi);
+ iscsi_task_cpl(&subtask->scsi);
+ return 0;
+ }
+
+ if (spdk_unlikely(spdk_scsi_lun_get_dif_ctx(lun_dev, &subtask->scsi, &pdu->dif_ctx))) {
+ pdu->dif_insert_or_strip = true;
+ }
+
+ pdu->task = subtask;
+ return 0;
+
+send_r2t_recovery_return:
+ rc = iscsi_send_r2t_recovery(conn, task, task->acked_r2tsn, true);
+ if (rc == 0) {
+ return 0;
+ }
+
+reject_return:
+ return iscsi_reject(conn, pdu, reject_reason);
+}
+
+static int
+iscsi_pdu_payload_op_data(struct spdk_iscsi_conn *conn, struct spdk_iscsi_pdu *pdu)
+{
+ struct spdk_iscsi_task *subtask;
+ struct iscsi_bhs_data_out *reqh;
+ uint32_t transfer_tag;
+
+ if (pdu->task == NULL) {
+ return 0;
+ }
+
+ subtask = pdu->task;
+
+ reqh = (struct iscsi_bhs_data_out *)&pdu->bhs;
+ transfer_tag = from_be32(&reqh->ttt);
+
+ if (get_transfer_task(conn, transfer_tag) == NULL) {
+ SPDK_ERRLOG("Not found for transfer_tag=%x\n", transfer_tag);
+ subtask->scsi.transfer_len = subtask->scsi.length;
+ spdk_scsi_task_process_abort(&subtask->scsi);
+ iscsi_task_cpl(&subtask->scsi);
+ return 0;
+ }
+
+ if (spdk_likely(!pdu->dif_insert_or_strip)) {
+ spdk_scsi_task_set_data(&subtask->scsi, pdu->data, pdu->data_segment_len);
+ } else {
+ spdk_scsi_task_set_data(&subtask->scsi, pdu->data, pdu->data_buf_len);
+ }
+
+ if (spdk_scsi_dev_get_lun(conn->dev, subtask->lun_id) == NULL) {
+ SPDK_DEBUGLOG(SPDK_LOG_ISCSI, "LUN %d is removed, complete the task immediately\n",
+ subtask->lun_id);
+ subtask->scsi.transfer_len = subtask->scsi.length;
+ spdk_scsi_task_process_null_lun(&subtask->scsi);
+ iscsi_task_cpl(&subtask->scsi);
+ return 0;
+ }
+
+ iscsi_queue_task(conn, subtask);
+ return 0;
+}
+
+static void
+init_login_reject_response(struct spdk_iscsi_pdu *pdu, struct spdk_iscsi_pdu *rsp_pdu)
+{
+ struct iscsi_bhs_login_rsp *rsph;
+
+ memset(rsp_pdu, 0, sizeof(struct spdk_iscsi_pdu));
+ rsph = (struct iscsi_bhs_login_rsp *)&rsp_pdu->bhs;
+ rsph->version_max = ISCSI_VERSION;
+ rsph->version_act = ISCSI_VERSION;
+ rsph->opcode = ISCSI_OP_LOGIN_RSP;
+ rsph->status_class = ISCSI_CLASS_INITIATOR_ERROR;
+ rsph->status_detail = ISCSI_LOGIN_INVALID_LOGIN_REQUEST;
+ rsph->itt = pdu->bhs.itt;
+}
+
+static void
+iscsi_pdu_dump(struct spdk_iscsi_pdu *pdu)
+{
+ SPDK_ERRLOGDUMP("PDU", (uint8_t *)&pdu->bhs, ISCSI_BHS_LEN);
+}
+
+/* This function is used to refree the pdu when it is acknowledged */
+static void
+remove_acked_pdu(struct spdk_iscsi_conn *conn, uint32_t ExpStatSN)
+{
+ struct spdk_iscsi_pdu *pdu, *pdu_temp;
+ uint32_t stat_sn;
+
+ conn->exp_statsn = spdk_min(ExpStatSN, conn->StatSN);
+ TAILQ_FOREACH_SAFE(pdu, &conn->snack_pdu_list, tailq, pdu_temp) {
+ stat_sn = from_be32(&pdu->bhs.stat_sn);
+ if (spdk_sn32_lt(stat_sn, conn->exp_statsn)) {
+ TAILQ_REMOVE(&conn->snack_pdu_list, pdu, tailq);
+ iscsi_conn_free_pdu(conn, pdu);
+ }
+ }
+}
+
+static int
+iscsi_update_cmdsn(struct spdk_iscsi_conn *conn, struct spdk_iscsi_pdu *pdu)
+{
+ int opcode;
+ uint32_t ExpStatSN;
+ int I_bit;
+ struct spdk_iscsi_sess *sess;
+ struct iscsi_bhs_scsi_req *reqh;
+
+ sess = conn->sess;
+ if (!sess) {
+ SPDK_ERRLOG("Connection has no associated session!\n");
+ return SPDK_ISCSI_CONNECTION_FATAL;
+ }
+
+ opcode = pdu->bhs.opcode;
+ reqh = (struct iscsi_bhs_scsi_req *)&pdu->bhs;
+
+ pdu->cmd_sn = from_be32(&reqh->cmd_sn);
+
+ I_bit = reqh->immediate;
+ if (I_bit == 0) {
+ if (spdk_sn32_lt(pdu->cmd_sn, sess->ExpCmdSN) ||
+ spdk_sn32_gt(pdu->cmd_sn, sess->MaxCmdSN)) {
+ if (sess->session_type == SESSION_TYPE_NORMAL &&
+ opcode != ISCSI_OP_SCSI_DATAOUT) {
+ SPDK_ERRLOG("CmdSN(%u) ignore (ExpCmdSN=%u, MaxCmdSN=%u)\n",
+ pdu->cmd_sn, sess->ExpCmdSN, sess->MaxCmdSN);
+
+ if (sess->ErrorRecoveryLevel >= 1) {
+ SPDK_DEBUGLOG(SPDK_LOG_ISCSI, "Skip the error in ERL 1 and 2\n");
+ } else {
+ return SPDK_PDU_FATAL;
+ }
+ }
+ }
+ } else if (pdu->cmd_sn != sess->ExpCmdSN) {
+ SPDK_ERRLOG("CmdSN(%u) error ExpCmdSN=%u\n", pdu->cmd_sn, sess->ExpCmdSN);
+
+ if (sess->ErrorRecoveryLevel >= 1) {
+ SPDK_DEBUGLOG(SPDK_LOG_ISCSI, "Skip the error in ERL 1 and 2\n");
+ } else if (opcode != ISCSI_OP_NOPOUT) {
+ /*
+ * The Linux initiator does not send valid CmdSNs for
+ * nopout under heavy load, so do not close the
+ * connection in that case.
+ */
+ return SPDK_ISCSI_CONNECTION_FATAL;
+ }
+ }
+
+ ExpStatSN = from_be32(&reqh->exp_stat_sn);
+ if (spdk_sn32_gt(ExpStatSN, conn->StatSN)) {
+ SPDK_DEBUGLOG(SPDK_LOG_ISCSI, "StatSN(%u) advanced\n", ExpStatSN);
+ ExpStatSN = conn->StatSN;
+ }
+
+ if (sess->ErrorRecoveryLevel >= 1) {
+ remove_acked_pdu(conn, ExpStatSN);
+ }
+
+ if (!I_bit && opcode != ISCSI_OP_SCSI_DATAOUT) {
+ sess->ExpCmdSN++;
+ }
+
+ return 0;
+}
+
+static int
+iscsi_pdu_hdr_handle(struct spdk_iscsi_conn *conn, struct spdk_iscsi_pdu *pdu)
+{
+ int opcode;
+ int rc;
+ struct spdk_iscsi_pdu *rsp_pdu = NULL;
+
+ if (pdu == NULL) {
+ return -1;
+ }
+
+ opcode = pdu->bhs.opcode;
+
+ SPDK_DEBUGLOG(SPDK_LOG_ISCSI, "opcode %x\n", opcode);
+
+ if (opcode == ISCSI_OP_LOGIN) {
+ return iscsi_pdu_hdr_op_login(conn, pdu);
+ }
+
+ /* connection in login phase but receive non-login opcode
+ * return response code 0x020b to initiator.
+ * */
+ if (!conn->full_feature && conn->state == ISCSI_CONN_STATE_RUNNING) {
+ rsp_pdu = iscsi_get_pdu(conn);
+ if (rsp_pdu == NULL) {
+ return SPDK_ISCSI_CONNECTION_FATAL;
+ }
+ init_login_reject_response(pdu, rsp_pdu);
+ iscsi_conn_write_pdu(conn, rsp_pdu, iscsi_conn_pdu_generic_complete, NULL);
+ SPDK_ERRLOG("Received opcode %d in login phase\n", opcode);
+ return SPDK_ISCSI_LOGIN_ERROR_RESPONSE;
+ } else if (conn->state == ISCSI_CONN_STATE_INVALID) {
+ SPDK_ERRLOG("before Full Feature\n");
+ iscsi_pdu_dump(pdu);
+ return SPDK_ISCSI_CONNECTION_FATAL;
+ }
+
+ rc = iscsi_update_cmdsn(conn, pdu);
+ if (rc != 0) {
+ return rc;
+ }
+
+ switch (opcode) {
+ case ISCSI_OP_NOPOUT:
+ rc = iscsi_pdu_hdr_op_nopout(conn, pdu);
+ break;
+
+ case ISCSI_OP_SCSI:
+ rc = iscsi_pdu_hdr_op_scsi(conn, pdu);
+ break;
+ case ISCSI_OP_TASK:
+ rc = iscsi_pdu_hdr_op_task(conn, pdu);
+ break;
+
+ case ISCSI_OP_TEXT:
+ rc = iscsi_pdu_hdr_op_text(conn, pdu);
+ break;
+
+ case ISCSI_OP_LOGOUT:
+ rc = iscsi_pdu_hdr_op_logout(conn, pdu);
+ break;
+
+ case ISCSI_OP_SCSI_DATAOUT:
+ rc = iscsi_pdu_hdr_op_data(conn, pdu);
+ break;
+
+ case ISCSI_OP_SNACK:
+ rc = iscsi_pdu_hdr_op_snack(conn, pdu);
+ break;
+
+ default:
+ SPDK_ERRLOG("unsupported opcode %x\n", opcode);
+ return iscsi_reject(conn, pdu, ISCSI_REASON_PROTOCOL_ERROR);
+ }
+
+ if (rc < 0) {
+ SPDK_ERRLOG("processing PDU header (opcode=%x) failed on %s(%s)\n",
+ opcode,
+ conn->target_port != NULL ? spdk_scsi_port_get_name(conn->target_port) : "NULL",
+ conn->initiator_port != NULL ? spdk_scsi_port_get_name(conn->initiator_port) : "NULL");
+ }
+
+ return rc;
+}
+
+static int
+iscsi_pdu_payload_handle(struct spdk_iscsi_conn *conn, struct spdk_iscsi_pdu *pdu)
+{
+ int opcode;
+ int rc = 0;
+
+ opcode = pdu->bhs.opcode;
+
+ SPDK_DEBUGLOG(SPDK_LOG_ISCSI, "opcode %x\n", opcode);
+
+ switch (opcode) {
+ case ISCSI_OP_LOGIN:
+ rc = iscsi_pdu_payload_op_login(conn, pdu);
+ break;
+ case ISCSI_OP_NOPOUT:
+ rc = iscsi_pdu_payload_op_nopout(conn, pdu);
+ break;
+ case ISCSI_OP_SCSI:
+ rc = iscsi_pdu_payload_op_scsi(conn, pdu);
+ break;
+ case ISCSI_OP_TASK:
+ break;
+ case ISCSI_OP_TEXT:
+ rc = iscsi_pdu_payload_op_text(conn, pdu);
+ break;
+ case ISCSI_OP_LOGOUT:
+ break;
+ case ISCSI_OP_SCSI_DATAOUT:
+ rc = iscsi_pdu_payload_op_data(conn, pdu);
+ break;
+ case ISCSI_OP_SNACK:
+ break;
+ default:
+ SPDK_ERRLOG("unsupported opcode %x\n", opcode);
+ return iscsi_reject(conn, pdu, ISCSI_REASON_PROTOCOL_ERROR);
+ }
+
+ if (rc < 0) {
+ SPDK_ERRLOG("processing PDU payload (opcode=%x) failed on %s(%s)\n",
+ opcode,
+ conn->target_port != NULL ? spdk_scsi_port_get_name(conn->target_port) : "NULL",
+ conn->initiator_port != NULL ? spdk_scsi_port_get_name(conn->initiator_port) : "NULL");
+ }
+
+ return rc;
+}
+
+static int
+iscsi_read_pdu(struct spdk_iscsi_conn *conn)
+{
+ enum iscsi_pdu_recv_state prev_state;
+ struct spdk_iscsi_pdu *pdu;
+ struct spdk_mempool *pool;
+ uint32_t crc32c;
+ int ahs_len;
+ uint32_t data_len;
+ int rc;
+
+ do {
+ prev_state = conn->pdu_recv_state;
+ pdu = conn->pdu_in_progress;
+
+ switch (conn->pdu_recv_state) {
+ case ISCSI_PDU_RECV_STATE_AWAIT_PDU_READY:
+ assert(conn->pdu_in_progress == NULL);
+
+ conn->pdu_in_progress = iscsi_get_pdu(conn);
+ if (conn->pdu_in_progress == NULL) {
+ return SPDK_ISCSI_CONNECTION_FATAL;
+ }
+ conn->pdu_recv_state = ISCSI_PDU_RECV_STATE_AWAIT_PDU_HDR;
+ break;
+ case ISCSI_PDU_RECV_STATE_AWAIT_PDU_HDR:
+ if (pdu->bhs_valid_bytes < ISCSI_BHS_LEN) {
+ rc = iscsi_conn_read_data(conn,
+ ISCSI_BHS_LEN - pdu->bhs_valid_bytes,
+ (uint8_t *)&pdu->bhs + pdu->bhs_valid_bytes);
+ if (rc < 0) {
+ conn->pdu_recv_state = ISCSI_PDU_RECV_STATE_ERROR;
+ break;
+ }
+ pdu->bhs_valid_bytes += rc;
+ if (pdu->bhs_valid_bytes < ISCSI_BHS_LEN) {
+ return 0;
+ }
+ }
+
+ pdu->data_segment_len = ISCSI_ALIGN(DGET24(pdu->bhs.data_segment_len));
+
+ /* AHS */
+ ahs_len = pdu->bhs.total_ahs_len * 4;
+ assert(ahs_len <= ISCSI_AHS_LEN);
+ if (pdu->ahs_valid_bytes < ahs_len) {
+ rc = iscsi_conn_read_data(conn,
+ ahs_len - pdu->ahs_valid_bytes,
+ pdu->ahs + pdu->ahs_valid_bytes);
+ if (rc < 0) {
+ conn->pdu_recv_state = ISCSI_PDU_RECV_STATE_ERROR;
+ break;
+ }
+
+ pdu->ahs_valid_bytes += rc;
+ if (pdu->ahs_valid_bytes < ahs_len) {
+ return 0;
+ }
+ }
+
+ /* Header Digest */
+ if (conn->header_digest &&
+ pdu->hdigest_valid_bytes < ISCSI_DIGEST_LEN) {
+ rc = iscsi_conn_read_data(conn,
+ ISCSI_DIGEST_LEN - pdu->hdigest_valid_bytes,
+ pdu->header_digest + pdu->hdigest_valid_bytes);
+ if (rc < 0) {
+ conn->pdu_recv_state = ISCSI_PDU_RECV_STATE_ERROR;
+ break;
+ }
+
+ pdu->hdigest_valid_bytes += rc;
+ if (pdu->hdigest_valid_bytes < ISCSI_DIGEST_LEN) {
+ return 0;
+ }
+ }
+
+ if (conn->header_digest) {
+ crc32c = iscsi_pdu_calc_header_digest(pdu);
+ rc = MATCH_DIGEST_WORD(pdu->header_digest, crc32c);
+ if (rc == 0) {
+ SPDK_ERRLOG("header digest error (%s)\n", conn->initiator_name);
+ conn->pdu_recv_state = ISCSI_PDU_RECV_STATE_ERROR;
+ break;
+ }
+ }
+
+ rc = iscsi_pdu_hdr_handle(conn, pdu);
+ if (rc < 0) {
+ SPDK_ERRLOG("Critical error is detected. Close the connection\n");
+ conn->pdu_recv_state = ISCSI_PDU_RECV_STATE_ERROR;
+ break;
+ }
+
+ conn->pdu_recv_state = ISCSI_PDU_RECV_STATE_AWAIT_PDU_PAYLOAD;
+ break;
+ case ISCSI_PDU_RECV_STATE_AWAIT_PDU_PAYLOAD:
+ data_len = pdu->data_segment_len;
+
+ if (data_len != 0 && pdu->data_buf == NULL) {
+ if (data_len <= iscsi_get_max_immediate_data_size()) {
+ pool = g_iscsi.pdu_immediate_data_pool;
+ pdu->data_buf_len = SPDK_BDEV_BUF_SIZE_WITH_MD(iscsi_get_max_immediate_data_size());
+ } else if (data_len <= SPDK_ISCSI_MAX_RECV_DATA_SEGMENT_LENGTH) {
+ pool = g_iscsi.pdu_data_out_pool;
+ pdu->data_buf_len = SPDK_BDEV_BUF_SIZE_WITH_MD(SPDK_ISCSI_MAX_RECV_DATA_SEGMENT_LENGTH);
+ } else {
+ SPDK_ERRLOG("Data(%d) > MaxSegment(%d)\n",
+ data_len, SPDK_ISCSI_MAX_RECV_DATA_SEGMENT_LENGTH);
+ conn->pdu_recv_state = ISCSI_PDU_RECV_STATE_ERROR;
+ break;
+ }
+ pdu->mobj = spdk_mempool_get(pool);
+ if (pdu->mobj == NULL) {
+ return 0;
+ }
+ pdu->data_buf = pdu->mobj->buf;
+ pdu->data = pdu->mobj->buf;
+ pdu->data_from_mempool = true;
+ }
+
+ /* copy the actual data into local buffer */
+ if (pdu->data_valid_bytes < data_len) {
+ rc = iscsi_conn_read_data_segment(conn, pdu, data_len);
+ if (rc < 0) {
+ conn->pdu_recv_state = ISCSI_PDU_RECV_STATE_ERROR;
+ break;
+ }
+
+ pdu->data_valid_bytes += rc;
+ if (pdu->data_valid_bytes < data_len) {
+ return 0;
+ }
+ }
+
+ /* copy out the data digest */
+ if (conn->data_digest && data_len != 0 &&
+ pdu->ddigest_valid_bytes < ISCSI_DIGEST_LEN) {
+ rc = iscsi_conn_read_data(conn,
+ ISCSI_DIGEST_LEN - pdu->ddigest_valid_bytes,
+ pdu->data_digest + pdu->ddigest_valid_bytes);
+ if (rc < 0) {
+ conn->pdu_recv_state = ISCSI_PDU_RECV_STATE_ERROR;
+ break;
+ }
+
+ pdu->ddigest_valid_bytes += rc;
+ if (pdu->ddigest_valid_bytes < ISCSI_DIGEST_LEN) {
+ return 0;
+ }
+ }
+
+ /* All data for this PDU has now been read from the socket. */
+ spdk_trace_record(TRACE_ISCSI_READ_PDU, conn->id, pdu->data_valid_bytes,
+ (uintptr_t)pdu, pdu->bhs.opcode);
+
+ /* check data digest */
+ if (conn->data_digest && data_len != 0) {
+ crc32c = iscsi_pdu_calc_data_digest(pdu);
+ rc = MATCH_DIGEST_WORD(pdu->data_digest, crc32c);
+ if (rc == 0) {
+ SPDK_ERRLOG("data digest error (%s)\n", conn->initiator_name);
+ conn->pdu_recv_state = ISCSI_PDU_RECV_STATE_ERROR;
+ break;
+ }
+ }
+
+ if (conn->is_logged_out) {
+ SPDK_DEBUGLOG(SPDK_LOG_ISCSI, "pdu received after logout\n");
+ conn->pdu_recv_state = ISCSI_PDU_RECV_STATE_ERROR;
+ break;
+ }
+
+ if (!pdu->is_rejected) {
+ rc = iscsi_pdu_payload_handle(conn, pdu);
+ } else {
+ rc = 0;
+ }
+ if (rc == 0) {
+ spdk_trace_record(TRACE_ISCSI_TASK_EXECUTED, 0, 0, (uintptr_t)pdu, 0);
+ iscsi_put_pdu(pdu);
+ conn->pdu_in_progress = NULL;
+ conn->pdu_recv_state = ISCSI_PDU_RECV_STATE_AWAIT_PDU_READY;
+ return 1;
+ } else {
+ conn->pdu_recv_state = ISCSI_PDU_RECV_STATE_ERROR;
+ }
+ break;
+ case ISCSI_PDU_RECV_STATE_ERROR:
+ return SPDK_ISCSI_CONNECTION_FATAL;
+ default:
+ assert(false);
+ SPDK_ERRLOG("code should not come here\n");
+ break;
+ }
+ } while (prev_state != conn->pdu_recv_state);
+
+ return 0;
+}
+
+#define GET_PDU_LOOP_COUNT 16
+
+int
+iscsi_handle_incoming_pdus(struct spdk_iscsi_conn *conn)
+{
+ int i, rc;
+
+ /* Read new PDUs from network */
+ for (i = 0; i < GET_PDU_LOOP_COUNT; i++) {
+ rc = iscsi_read_pdu(conn);
+ if (rc == 0) {
+ break;
+ } else if (rc < 0) {
+ return rc;
+ }
+
+ if (conn->is_stopped) {
+ break;
+ }
+ }
+
+ return i;
+}
diff --git a/src/spdk/lib/iscsi/iscsi.h b/src/spdk/lib/iscsi/iscsi.h
new file mode 100644
index 000000000..b1747e4ab
--- /dev/null
+++ b/src/spdk/lib/iscsi/iscsi.h
@@ -0,0 +1,465 @@
+/*-
+ * BSD LICENSE
+ *
+ * Copyright (C) 2008-2012 Daisuke Aoyama <aoyama@peach.ne.jp>.
+ * Copyright (c) Intel Corporation.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ * * Neither the name of Intel Corporation nor the names of its
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef SPDK_ISCSI_H
+#define SPDK_ISCSI_H
+
+#include "spdk/stdinc.h"
+
+#include "spdk/bdev.h"
+#include "spdk/iscsi_spec.h"
+#include "spdk/thread.h"
+#include "spdk/sock.h"
+
+#include "spdk/scsi.h"
+#include "iscsi/param.h"
+
+#include "spdk/assert.h"
+#include "spdk/dif.h"
+#include "spdk/util.h"
+
+#define SPDK_ISCSI_DEFAULT_NODEBASE "iqn.2016-06.io.spdk"
+
+#define DEFAULT_MAXR2T 4
+#define MAX_INITIATOR_PORT_NAME 256
+#define MAX_INITIATOR_NAME 223
+#define MAX_TARGET_NAME 223
+
+#define MAX_PORTAL 1024
+#define MAX_INITIATOR 256
+#define MAX_NETMASK 256
+#define MAX_ISCSI_CONNECTIONS 1024
+#define MAX_PORTAL_ADDR 256
+#define MAX_PORTAL_PORT 32
+
+#define DEFAULT_PORT 3260
+#define DEFAULT_MAX_SESSIONS 128
+#define DEFAULT_MAX_CONNECTIONS_PER_SESSION 2
+#define DEFAULT_MAXOUTSTANDINGR2T 1
+#define DEFAULT_DEFAULTTIME2WAIT 2
+#define DEFAULT_DEFAULTTIME2RETAIN 20
+#define DEFAULT_INITIALR2T true
+#define DEFAULT_IMMEDIATEDATA true
+#define DEFAULT_DATAPDUINORDER true
+#define DEFAULT_DATASEQUENCEINORDER true
+#define DEFAULT_ERRORRECOVERYLEVEL 0
+#define DEFAULT_TIMEOUT 60
+#define MAX_NOPININTERVAL 60
+#define DEFAULT_NOPININTERVAL 30
+
+/*
+ * SPDK iSCSI target currently only supports 64KB as the maximum data segment length
+ * it can receive from initiators. Other values may work, but no guarantees.
+ */
+#define SPDK_ISCSI_MAX_RECV_DATA_SEGMENT_LENGTH 65536
+
+/*
+ * Defines maximum number of data out buffers each connection can have in
+ * use at any given time.
+ */
+#define MAX_DATA_OUT_PER_CONNECTION 16
+
+/*
+ * Defines maximum number of data in buffers each connection can have in
+ * use at any given time. So this limit does not affect I/O smaller than
+ * SPDK_BDEV_SMALL_BUF_MAX_SIZE.
+ */
+#define MAX_LARGE_DATAIN_PER_CONNECTION 64
+
+#define SPDK_ISCSI_MAX_BURST_LENGTH \
+ (SPDK_ISCSI_MAX_RECV_DATA_SEGMENT_LENGTH * MAX_DATA_OUT_PER_CONNECTION)
+
+/*
+ * Defines default maximum amount in bytes of unsolicited data the iSCSI
+ * initiator may send to the SPDK iSCSI target during the execution of
+ * a single SCSI command. And it is smaller than the MaxBurstLength.
+ */
+#define SPDK_ISCSI_FIRST_BURST_LENGTH 8192
+
+/*
+ * Defines minimum amount in bytes of unsolicited data the iSCSI initiator
+ * may send to the SPDK iSCSI target during the execution of a single
+ * SCSI command.
+ */
+#define SPDK_ISCSI_MIN_FIRST_BURST_LENGTH 512
+
+#define SPDK_ISCSI_MAX_FIRST_BURST_LENGTH 16777215
+
+/*
+ * Defines default maximum queue depth per connection and this can be
+ * changed by configuration file.
+ */
+#define DEFAULT_MAX_QUEUE_DEPTH 64
+
+/** Defines how long we should wait for a logout request when the target
+ * requests logout to the initiator asynchronously.
+ */
+#define ISCSI_LOGOUT_REQUEST_TIMEOUT 30 /* in seconds */
+
+/** Defines how long we should wait for a TCP close after responding to a
+ * logout request, before terminating the connection ourselves.
+ */
+#define ISCSI_LOGOUT_TIMEOUT 5 /* in seconds */
+
+/* For spdk_iscsi_login_in related function use, we need to avoid the conflict
+ * with other errors
+ * */
+#define SPDK_ISCSI_LOGIN_ERROR_RESPONSE -1000
+#define SPDK_ISCSI_LOGIN_ERROR_PARAMETER -1001
+#define SPDK_ISCSI_PARAMETER_EXCHANGE_NOT_ONCE -1002
+
+#define ISCSI_AHS_LEN 60
+
+struct spdk_mobj {
+ struct spdk_mempool *mp;
+ void *buf;
+};
+
+/*
+ * Maximum number of SGL elements, i.e.,
+ * BHS, AHS, Header Digest, Data Segment and Data Digest.
+ */
+#define SPDK_ISCSI_MAX_SGL_DESCRIPTORS (5)
+
+typedef void (*iscsi_conn_xfer_complete_cb)(void *cb_arg);
+
+struct spdk_iscsi_pdu {
+ struct iscsi_bhs bhs;
+ struct spdk_mobj *mobj;
+ bool is_rejected;
+ uint8_t *data_buf;
+ uint8_t *data;
+ uint8_t header_digest[ISCSI_DIGEST_LEN];
+ uint8_t data_digest[ISCSI_DIGEST_LEN];
+ size_t data_segment_len;
+ int bhs_valid_bytes;
+ int ahs_valid_bytes;
+ uint32_t data_valid_bytes;
+ int hdigest_valid_bytes;
+ int ddigest_valid_bytes;
+ int ref;
+ bool data_from_mempool; /* indicate whether the data buffer is allocated from mempool */
+ struct spdk_iscsi_task *task; /* data tied to a task buffer */
+ uint32_t cmd_sn;
+ uint32_t writev_offset;
+ uint32_t data_buf_len;
+ bool dif_insert_or_strip;
+ struct spdk_dif_ctx dif_ctx;
+ struct spdk_iscsi_conn *conn;
+
+ iscsi_conn_xfer_complete_cb cb_fn;
+ void *cb_arg;
+
+ /* The sock request ends with a 0 length iovec. Place the actual iovec immediately
+ * after it. There is a static assert below to check if the compiler inserted
+ * any unwanted padding */
+ int32_t mapped_length;
+ struct spdk_sock_request sock_req;
+ struct iovec iov[SPDK_ISCSI_MAX_SGL_DESCRIPTORS];
+ TAILQ_ENTRY(spdk_iscsi_pdu) tailq;
+
+
+ /*
+ * 60 bytes of AHS should suffice for now.
+ * This should always be at the end of PDU data structure.
+ * we need to not zero this out when doing memory clear.
+ */
+ uint8_t ahs[ISCSI_AHS_LEN];
+
+ struct {
+ uint16_t length; /* iSCSI SenseLength (big-endian) */
+ uint8_t data[32];
+ } sense;
+};
+SPDK_STATIC_ASSERT(offsetof(struct spdk_iscsi_pdu,
+ sock_req) + sizeof(struct spdk_sock_request) == offsetof(struct spdk_iscsi_pdu, iov),
+ "Compiler inserted padding between iov and sock_req");
+
+enum iscsi_connection_state {
+ ISCSI_CONN_STATE_INVALID = 0,
+ ISCSI_CONN_STATE_RUNNING = 1,
+ ISCSI_CONN_STATE_EXITING = 2,
+ ISCSI_CONN_STATE_EXITED = 3,
+};
+
+enum iscsi_chap_phase {
+ ISCSI_CHAP_PHASE_NONE = 0,
+ ISCSI_CHAP_PHASE_WAIT_A = 1,
+ ISCSI_CHAP_PHASE_WAIT_NR = 2,
+ ISCSI_CHAP_PHASE_END = 3,
+};
+
+enum session_type {
+ SESSION_TYPE_INVALID = 0,
+ SESSION_TYPE_NORMAL = 1,
+ SESSION_TYPE_DISCOVERY = 2,
+};
+
+#define ISCSI_CHAP_CHALLENGE_LEN 1024
+#define ISCSI_CHAP_MAX_USER_LEN 255
+#define ISCSI_CHAP_MAX_SECRET_LEN 255
+
+struct iscsi_chap_auth {
+ enum iscsi_chap_phase chap_phase;
+
+ char user[ISCSI_CHAP_MAX_USER_LEN + 1];
+ char secret[ISCSI_CHAP_MAX_SECRET_LEN + 1];
+ char muser[ISCSI_CHAP_MAX_USER_LEN + 1];
+ char msecret[ISCSI_CHAP_MAX_SECRET_LEN + 1];
+
+ uint8_t chap_id[1];
+ uint8_t chap_mid[1];
+ int chap_challenge_len;
+ uint8_t chap_challenge[ISCSI_CHAP_CHALLENGE_LEN];
+ int chap_mchallenge_len;
+ uint8_t chap_mchallenge[ISCSI_CHAP_CHALLENGE_LEN];
+};
+
+struct spdk_iscsi_auth_secret {
+ char user[ISCSI_CHAP_MAX_USER_LEN + 1];
+ char secret[ISCSI_CHAP_MAX_SECRET_LEN + 1];
+ char muser[ISCSI_CHAP_MAX_USER_LEN + 1];
+ char msecret[ISCSI_CHAP_MAX_SECRET_LEN + 1];
+ TAILQ_ENTRY(spdk_iscsi_auth_secret) tailq;
+};
+
+struct spdk_iscsi_auth_group {
+ int32_t tag;
+ TAILQ_HEAD(, spdk_iscsi_auth_secret) secret_head;
+ TAILQ_ENTRY(spdk_iscsi_auth_group) tailq;
+};
+
+struct spdk_iscsi_sess {
+ uint32_t connections;
+ struct spdk_iscsi_conn **conns;
+
+ struct spdk_scsi_port *initiator_port;
+ int tag;
+
+ uint64_t isid;
+ uint16_t tsih;
+ struct spdk_iscsi_tgt_node *target;
+ int queue_depth;
+
+ struct iscsi_param *params;
+
+ enum session_type session_type;
+ uint32_t MaxConnections;
+ uint32_t MaxOutstandingR2T;
+ uint32_t DefaultTime2Wait;
+ uint32_t DefaultTime2Retain;
+ uint32_t FirstBurstLength;
+ uint32_t MaxBurstLength;
+ bool InitialR2T;
+ bool ImmediateData;
+ bool DataPDUInOrder;
+ bool DataSequenceInOrder;
+ uint32_t ErrorRecoveryLevel;
+
+ uint32_t ExpCmdSN;
+ uint32_t MaxCmdSN;
+
+ uint32_t current_text_itt;
+};
+
+struct spdk_iscsi_poll_group {
+ struct spdk_poller *poller;
+ struct spdk_poller *nop_poller;
+ STAILQ_HEAD(connections, spdk_iscsi_conn) connections;
+ struct spdk_sock_group *sock_group;
+ TAILQ_ENTRY(spdk_iscsi_poll_group) link;
+};
+
+struct spdk_iscsi_opts {
+ char *authfile;
+ char *nodebase;
+ int32_t timeout;
+ int32_t nopininterval;
+ bool disable_chap;
+ bool require_chap;
+ bool mutual_chap;
+ int32_t chap_group;
+ uint32_t MaxSessions;
+ uint32_t MaxConnectionsPerSession;
+ uint32_t MaxConnections;
+ uint32_t MaxQueueDepth;
+ uint32_t DefaultTime2Wait;
+ uint32_t DefaultTime2Retain;
+ uint32_t FirstBurstLength;
+ bool ImmediateData;
+ uint32_t ErrorRecoveryLevel;
+ bool AllowDuplicateIsid;
+};
+
+struct spdk_iscsi_globals {
+ char *authfile;
+ char *nodebase;
+ pthread_mutex_t mutex;
+ uint32_t refcnt;
+ TAILQ_HEAD(, spdk_iscsi_portal) portal_head;
+ TAILQ_HEAD(, spdk_iscsi_portal_grp) pg_head;
+ TAILQ_HEAD(, spdk_iscsi_init_grp) ig_head;
+ TAILQ_HEAD(, spdk_iscsi_tgt_node) target_head;
+ TAILQ_HEAD(, spdk_iscsi_auth_group) auth_group_head;
+ TAILQ_HEAD(, spdk_iscsi_poll_group) poll_group_head;
+
+ int32_t timeout;
+ int32_t nopininterval;
+ bool disable_chap;
+ bool require_chap;
+ bool mutual_chap;
+ int32_t chap_group;
+
+ uint32_t MaxSessions;
+ uint32_t MaxConnectionsPerSession;
+ uint32_t MaxConnections;
+ uint32_t MaxQueueDepth;
+ uint32_t DefaultTime2Wait;
+ uint32_t DefaultTime2Retain;
+ uint32_t FirstBurstLength;
+ bool ImmediateData;
+ uint32_t ErrorRecoveryLevel;
+ bool AllowDuplicateIsid;
+
+ struct spdk_mempool *pdu_pool;
+ struct spdk_mempool *pdu_immediate_data_pool;
+ struct spdk_mempool *pdu_data_out_pool;
+ struct spdk_mempool *session_pool;
+ struct spdk_mempool *task_pool;
+
+ struct spdk_iscsi_sess **session;
+};
+
+#define ISCSI_SECURITY_NEGOTIATION_PHASE 0
+#define ISCSI_OPERATIONAL_NEGOTIATION_PHASE 1
+#define ISCSI_NSG_RESERVED_CODE 2
+#define ISCSI_FULL_FEATURE_PHASE 3
+
+/* logout reason */
+#define ISCSI_LOGOUT_REASON_CLOSE_SESSION 0
+#define ISCSI_LOGOUT_REASON_CLOSE_CONNECTION 1
+#define ISCSI_LOGOUT_REASON_REMOVE_CONN_FOR_RECOVERY 2
+
+enum spdk_error_codes {
+ SPDK_ISCSI_CONNECTION_FATAL = -1,
+ SPDK_PDU_FATAL = -2,
+};
+
+#define DGET24(B) \
+ ((( (uint32_t) *((uint8_t *)(B)+0)) << 16) \
+ | (((uint32_t) *((uint8_t *)(B)+1)) << 8) \
+ | (((uint32_t) *((uint8_t *)(B)+2)) << 0))
+
+#define DSET24(B,D) \
+ (((*((uint8_t *)(B)+0)) = (uint8_t)((uint32_t)(D) >> 16)), \
+ ((*((uint8_t *)(B)+1)) = (uint8_t)((uint32_t)(D) >> 8)), \
+ ((*((uint8_t *)(B)+2)) = (uint8_t)((uint32_t)(D) >> 0)))
+
+#define xstrdup(s) (s ? strdup(s) : (char *)NULL)
+
+extern struct spdk_iscsi_globals g_iscsi;
+extern struct spdk_iscsi_opts *g_spdk_iscsi_opts;
+
+struct spdk_iscsi_task;
+struct spdk_json_write_ctx;
+
+typedef void (*spdk_iscsi_init_cb)(void *cb_arg, int rc);
+
+void spdk_iscsi_init(spdk_iscsi_init_cb cb_fn, void *cb_arg);
+typedef void (*spdk_iscsi_fini_cb)(void *arg);
+void spdk_iscsi_fini(spdk_iscsi_fini_cb cb_fn, void *cb_arg);
+void shutdown_iscsi_conns_done(void);
+void spdk_iscsi_config_text(FILE *fp);
+void spdk_iscsi_config_json(struct spdk_json_write_ctx *w);
+
+struct spdk_iscsi_opts *iscsi_opts_alloc(void);
+void iscsi_opts_free(struct spdk_iscsi_opts *opts);
+struct spdk_iscsi_opts *iscsi_opts_copy(struct spdk_iscsi_opts *src);
+void iscsi_opts_info_json(struct spdk_json_write_ctx *w);
+int iscsi_set_discovery_auth(bool disable_chap, bool require_chap,
+ bool mutual_chap, int32_t chap_group);
+int iscsi_chap_get_authinfo(struct iscsi_chap_auth *auth, const char *authuser,
+ int ag_tag);
+int iscsi_add_auth_group(int32_t tag, struct spdk_iscsi_auth_group **_group);
+struct spdk_iscsi_auth_group *iscsi_find_auth_group_by_tag(int32_t tag);
+void iscsi_delete_auth_group(struct spdk_iscsi_auth_group *group);
+int iscsi_auth_group_add_secret(struct spdk_iscsi_auth_group *group,
+ const char *user, const char *secret,
+ const char *muser, const char *msecret);
+int iscsi_auth_group_delete_secret(struct spdk_iscsi_auth_group *group,
+ const char *user);
+void iscsi_auth_groups_info_json(struct spdk_json_write_ctx *w);
+
+void iscsi_task_response(struct spdk_iscsi_conn *conn,
+ struct spdk_iscsi_task *task);
+int iscsi_build_iovs(struct spdk_iscsi_conn *conn, struct iovec *iovs, int iovcnt,
+ struct spdk_iscsi_pdu *pdu, uint32_t *mapped_length);
+int iscsi_handle_incoming_pdus(struct spdk_iscsi_conn *conn);
+void iscsi_task_mgmt_response(struct spdk_iscsi_conn *conn,
+ struct spdk_iscsi_task *task);
+
+void iscsi_free_sess(struct spdk_iscsi_sess *sess);
+void iscsi_clear_all_transfer_task(struct spdk_iscsi_conn *conn,
+ struct spdk_scsi_lun *lun,
+ struct spdk_iscsi_pdu *pdu);
+bool iscsi_del_transfer_task(struct spdk_iscsi_conn *conn, uint32_t CmdSN);
+
+uint32_t iscsi_pdu_calc_header_digest(struct spdk_iscsi_pdu *pdu);
+uint32_t iscsi_pdu_calc_data_digest(struct spdk_iscsi_pdu *pdu);
+
+/* Memory management */
+void iscsi_put_pdu(struct spdk_iscsi_pdu *pdu);
+struct spdk_iscsi_pdu *iscsi_get_pdu(struct spdk_iscsi_conn *conn);
+void iscsi_op_abort_task_set(struct spdk_iscsi_task *task,
+ uint8_t function);
+void iscsi_queue_task(struct spdk_iscsi_conn *conn, struct spdk_iscsi_task *task);
+
+static inline uint32_t
+iscsi_get_max_immediate_data_size(void)
+{
+ /*
+ * Specify enough extra space in addition to FirstBurstLength to
+ * account for a header digest, data digest and additional header
+ * segments (AHS). These are not normally used but they do not
+ * take up much space and we need to make sure the worst-case scenario
+ * can be satisified by the size returned here.
+ */
+ return g_iscsi.FirstBurstLength +
+ ISCSI_DIGEST_LEN + /* data digest */
+ ISCSI_DIGEST_LEN + /* header digest */
+ 8 + /* bidirectional AHS */
+ 52; /* extended CDB AHS (for a 64-byte CDB) */
+}
+
+#endif /* SPDK_ISCSI_H */
diff --git a/src/spdk/lib/iscsi/iscsi_rpc.c b/src/spdk/lib/iscsi/iscsi_rpc.c
new file mode 100644
index 000000000..8ab43d31d
--- /dev/null
+++ b/src/spdk/lib/iscsi/iscsi_rpc.c
@@ -0,0 +1,1639 @@
+/*-
+ * BSD LICENSE
+ *
+ * Copyright (C) 2008-2012 Daisuke Aoyama <aoyama@peach.ne.jp>.
+ * Copyright (c) Intel Corporation.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ * * Neither the name of Intel Corporation nor the names of its
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "iscsi/iscsi.h"
+#include "iscsi/conn.h"
+#include "iscsi/tgt_node.h"
+#include "iscsi/portal_grp.h"
+#include "iscsi/init_grp.h"
+
+#include "spdk/rpc.h"
+#include "spdk/util.h"
+#include "spdk/string.h"
+#include "spdk_internal/log.h"
+
+static void
+rpc_iscsi_get_initiator_groups(struct spdk_jsonrpc_request *request,
+ const struct spdk_json_val *params)
+{
+ struct spdk_json_write_ctx *w;
+
+ if (params != NULL) {
+ spdk_jsonrpc_send_error_response(request, SPDK_JSONRPC_ERROR_INVALID_PARAMS,
+ "iscsi_get_initiator_groups requires no parameters");
+ return;
+ }
+
+ w = spdk_jsonrpc_begin_result(request);
+ spdk_json_write_array_begin(w);
+ iscsi_init_grps_info_json(w);
+ spdk_json_write_array_end(w);
+
+ spdk_jsonrpc_end_result(request, w);
+}
+SPDK_RPC_REGISTER("iscsi_get_initiator_groups", rpc_iscsi_get_initiator_groups,
+ SPDK_RPC_RUNTIME)
+SPDK_RPC_REGISTER_ALIAS_DEPRECATED(iscsi_get_initiator_groups, get_initiator_groups)
+
+struct rpc_initiator_list {
+ size_t num_initiators;
+ char *initiators[MAX_INITIATOR];
+};
+
+static int
+decode_rpc_initiator_list(const struct spdk_json_val *val, void *out)
+{
+ struct rpc_initiator_list *list = out;
+
+ return spdk_json_decode_array(val, spdk_json_decode_string, list->initiators, MAX_INITIATOR,
+ &list->num_initiators, sizeof(char *));
+}
+
+static void
+free_rpc_initiator_list(struct rpc_initiator_list *list)
+{
+ size_t i;
+
+ for (i = 0; i < list->num_initiators; i++) {
+ free(list->initiators[i]);
+ }
+}
+
+struct rpc_netmask_list {
+ size_t num_netmasks;
+ char *netmasks[MAX_NETMASK];
+};
+
+static int
+decode_rpc_netmask_list(const struct spdk_json_val *val, void *out)
+{
+ struct rpc_netmask_list *list = out;
+
+ return spdk_json_decode_array(val, spdk_json_decode_string, list->netmasks, MAX_NETMASK,
+ &list->num_netmasks, sizeof(char *));
+}
+
+static void
+free_rpc_netmask_list(struct rpc_netmask_list *list)
+{
+ size_t i;
+
+ for (i = 0; i < list->num_netmasks; i++) {
+ free(list->netmasks[i]);
+ }
+}
+
+struct rpc_initiator_group {
+ int32_t tag;
+ struct rpc_initiator_list initiator_list;
+ struct rpc_netmask_list netmask_list;
+};
+
+static void
+free_rpc_initiator_group(struct rpc_initiator_group *ig)
+{
+ free_rpc_initiator_list(&ig->initiator_list);
+ free_rpc_netmask_list(&ig->netmask_list);
+}
+
+static const struct spdk_json_object_decoder rpc_initiator_group_decoders[] = {
+ {"tag", offsetof(struct rpc_initiator_group, tag), spdk_json_decode_int32},
+ {"initiators", offsetof(struct rpc_initiator_group, initiator_list), decode_rpc_initiator_list},
+ {"netmasks", offsetof(struct rpc_initiator_group, netmask_list), decode_rpc_netmask_list},
+};
+
+static void
+rpc_iscsi_create_initiator_group(struct spdk_jsonrpc_request *request,
+ const struct spdk_json_val *params)
+{
+ struct rpc_initiator_group req = {};
+ struct spdk_json_write_ctx *w;
+
+ if (spdk_json_decode_object(params, rpc_initiator_group_decoders,
+ SPDK_COUNTOF(rpc_initiator_group_decoders), &req)) {
+ SPDK_ERRLOG("spdk_json_decode_object failed\n");
+ goto invalid;
+ }
+
+ if (req.initiator_list.num_initiators == 0 ||
+ req.netmask_list.num_netmasks == 0) {
+ goto invalid;
+ }
+
+ if (iscsi_init_grp_create_from_initiator_list(req.tag,
+ req.initiator_list.num_initiators,
+ req.initiator_list.initiators,
+ req.netmask_list.num_netmasks,
+ req.netmask_list.netmasks)) {
+ SPDK_ERRLOG("create_from_initiator_list failed\n");
+ goto invalid;
+ }
+
+ free_rpc_initiator_group(&req);
+
+ w = spdk_jsonrpc_begin_result(request);
+ spdk_json_write_bool(w, true);
+ spdk_jsonrpc_end_result(request, w);
+ return;
+
+invalid:
+ spdk_jsonrpc_send_error_response(request, SPDK_JSONRPC_ERROR_INVALID_PARAMS, "Invalid parameters");
+ free_rpc_initiator_group(&req);
+}
+SPDK_RPC_REGISTER("iscsi_create_initiator_group", rpc_iscsi_create_initiator_group,
+ SPDK_RPC_RUNTIME)
+SPDK_RPC_REGISTER_ALIAS_DEPRECATED(iscsi_create_initiator_group, add_initiator_group)
+
+static const struct spdk_json_object_decoder rpc_add_or_delete_initiators_decoders[] = {
+ {"tag", offsetof(struct rpc_initiator_group, tag), spdk_json_decode_int32},
+ {"initiators", offsetof(struct rpc_initiator_group, initiator_list), decode_rpc_initiator_list, true},
+ {"netmasks", offsetof(struct rpc_initiator_group, netmask_list), decode_rpc_netmask_list, true},
+};
+
+static void
+rpc_iscsi_initiator_group_add_initiators(struct spdk_jsonrpc_request *request,
+ const struct spdk_json_val *params)
+{
+ struct rpc_initiator_group req = {};
+ struct spdk_json_write_ctx *w;
+
+ if (spdk_json_decode_object(params, rpc_add_or_delete_initiators_decoders,
+ SPDK_COUNTOF(rpc_add_or_delete_initiators_decoders), &req)) {
+ SPDK_ERRLOG("spdk_json_decode_object failed\n");
+ goto invalid;
+ }
+
+ if (iscsi_init_grp_add_initiators_from_initiator_list(req.tag,
+ req.initiator_list.num_initiators,
+ req.initiator_list.initiators,
+ req.netmask_list.num_netmasks,
+ req.netmask_list.netmasks)) {
+ SPDK_ERRLOG("add_initiators_from_initiator_list failed\n");
+ goto invalid;
+ }
+
+ free_rpc_initiator_group(&req);
+
+ w = spdk_jsonrpc_begin_result(request);
+ spdk_json_write_bool(w, true);
+ spdk_jsonrpc_end_result(request, w);
+ return;
+
+invalid:
+ spdk_jsonrpc_send_error_response(request, SPDK_JSONRPC_ERROR_INVALID_PARAMS, "Invalid parameters");
+ free_rpc_initiator_group(&req);
+}
+SPDK_RPC_REGISTER("iscsi_initiator_group_add_initiators",
+ rpc_iscsi_initiator_group_add_initiators, SPDK_RPC_RUNTIME)
+SPDK_RPC_REGISTER_ALIAS_DEPRECATED(iscsi_initiator_group_add_initiators,
+ add_initiators_to_initiator_group)
+
+static void
+rpc_iscsi_initiator_group_remove_initiators(struct spdk_jsonrpc_request *request,
+ const struct spdk_json_val *params)
+{
+ struct rpc_initiator_group req = {};
+ struct spdk_json_write_ctx *w;
+
+ if (spdk_json_decode_object(params, rpc_add_or_delete_initiators_decoders,
+ SPDK_COUNTOF(rpc_add_or_delete_initiators_decoders), &req)) {
+ SPDK_ERRLOG("spdk_json_decode_object failed\n");
+ goto invalid;
+ }
+
+ if (iscsi_init_grp_delete_initiators_from_initiator_list(req.tag,
+ req.initiator_list.num_initiators,
+ req.initiator_list.initiators,
+ req.netmask_list.num_netmasks,
+ req.netmask_list.netmasks)) {
+ SPDK_ERRLOG("delete_initiators_from_initiator_list failed\n");
+ goto invalid;
+ }
+
+ free_rpc_initiator_group(&req);
+
+ w = spdk_jsonrpc_begin_result(request);
+ spdk_json_write_bool(w, true);
+ spdk_jsonrpc_end_result(request, w);
+ return;
+
+invalid:
+ spdk_jsonrpc_send_error_response(request, SPDK_JSONRPC_ERROR_INVALID_PARAMS, "Invalid parameters");
+ free_rpc_initiator_group(&req);
+}
+SPDK_RPC_REGISTER("iscsi_initiator_group_remove_initiators",
+ rpc_iscsi_initiator_group_remove_initiators, SPDK_RPC_RUNTIME)
+SPDK_RPC_REGISTER_ALIAS_DEPRECATED(iscsi_initiator_group_remove_initiators,
+ delete_initiators_from_initiator_group)
+
+struct rpc_iscsi_delete_initiator_group {
+ int32_t tag;
+};
+
+static const struct spdk_json_object_decoder rpc_iscsi_delete_initiator_group_decoders[] = {
+ {"tag", offsetof(struct rpc_iscsi_delete_initiator_group, tag), spdk_json_decode_int32},
+};
+
+static void
+rpc_iscsi_delete_initiator_group(struct spdk_jsonrpc_request *request,
+ const struct spdk_json_val *params)
+{
+ struct rpc_iscsi_delete_initiator_group req = {};
+ struct spdk_json_write_ctx *w;
+ struct spdk_iscsi_init_grp *ig;
+
+ if (spdk_json_decode_object(params, rpc_iscsi_delete_initiator_group_decoders,
+ SPDK_COUNTOF(rpc_iscsi_delete_initiator_group_decoders),
+ &req)) {
+ SPDK_ERRLOG("spdk_json_decode_object failed\n");
+ goto invalid;
+ }
+
+ ig = iscsi_init_grp_unregister(req.tag);
+ if (!ig) {
+ goto invalid;
+ }
+ iscsi_tgt_node_delete_map(NULL, ig);
+ iscsi_init_grp_destroy(ig);
+
+ w = spdk_jsonrpc_begin_result(request);
+ spdk_json_write_bool(w, true);
+ spdk_jsonrpc_end_result(request, w);
+ return;
+
+invalid:
+ spdk_jsonrpc_send_error_response(request, SPDK_JSONRPC_ERROR_INVALID_PARAMS, "Invalid parameters");
+}
+SPDK_RPC_REGISTER("iscsi_delete_initiator_group", rpc_iscsi_delete_initiator_group,
+ SPDK_RPC_RUNTIME)
+SPDK_RPC_REGISTER_ALIAS_DEPRECATED(iscsi_delete_initiator_group, delete_initiator_group)
+
+static void
+rpc_iscsi_get_target_nodes(struct spdk_jsonrpc_request *request,
+ const struct spdk_json_val *params)
+{
+ struct spdk_json_write_ctx *w;
+
+ if (params != NULL) {
+ spdk_jsonrpc_send_error_response(request, SPDK_JSONRPC_ERROR_INVALID_PARAMS,
+ "iscsi_get_target_nodes requires no parameters");
+ return;
+ }
+
+ w = spdk_jsonrpc_begin_result(request);
+ spdk_json_write_array_begin(w);
+ iscsi_tgt_nodes_info_json(w);
+ spdk_json_write_array_end(w);
+
+ spdk_jsonrpc_end_result(request, w);
+}
+SPDK_RPC_REGISTER("iscsi_get_target_nodes", rpc_iscsi_get_target_nodes, SPDK_RPC_RUNTIME)
+SPDK_RPC_REGISTER_ALIAS_DEPRECATED(iscsi_get_target_nodes, get_target_nodes)
+
+struct rpc_pg_ig_map {
+ int32_t pg_tag;
+ int32_t ig_tag;
+};
+
+static const struct spdk_json_object_decoder rpc_pg_ig_map_decoders[] = {
+ {"pg_tag", offsetof(struct rpc_pg_ig_map, pg_tag), spdk_json_decode_int32},
+ {"ig_tag", offsetof(struct rpc_pg_ig_map, ig_tag), spdk_json_decode_int32},
+};
+
+static int
+decode_rpc_pg_ig_map(const struct spdk_json_val *val, void *out)
+{
+ struct rpc_pg_ig_map *pg_ig_map = out;
+
+ return spdk_json_decode_object(val, rpc_pg_ig_map_decoders,
+ SPDK_COUNTOF(rpc_pg_ig_map_decoders),
+ pg_ig_map);
+}
+
+struct rpc_pg_ig_maps {
+ size_t num_maps;
+ struct rpc_pg_ig_map maps[MAX_TARGET_MAP];
+};
+
+static int
+decode_rpc_pg_ig_maps(const struct spdk_json_val *val, void *out)
+{
+ struct rpc_pg_ig_maps *pg_ig_maps = out;
+
+ return spdk_json_decode_array(val, decode_rpc_pg_ig_map, pg_ig_maps->maps,
+ MAX_TARGET_MAP, &pg_ig_maps->num_maps,
+ sizeof(struct rpc_pg_ig_map));
+}
+
+#define RPC_ISCSI_CREATE_TARGET_NODE_MAX_LUN 64
+
+struct rpc_lun {
+ char *bdev_name;
+ int32_t lun_id;
+};
+
+static const struct spdk_json_object_decoder rpc_lun_decoders[] = {
+ {"bdev_name", offsetof(struct rpc_lun, bdev_name), spdk_json_decode_string},
+ {"lun_id", offsetof(struct rpc_lun, lun_id), spdk_json_decode_int32},
+};
+
+static int
+decode_rpc_lun(const struct spdk_json_val *val, void *out)
+{
+ struct rpc_lun *lun = out;
+
+ return spdk_json_decode_object(val, rpc_lun_decoders,
+ SPDK_COUNTOF(rpc_lun_decoders), lun);
+}
+
+struct rpc_luns {
+ size_t num_luns;
+ struct rpc_lun luns[RPC_ISCSI_CREATE_TARGET_NODE_MAX_LUN];
+};
+
+static int
+decode_rpc_luns(const struct spdk_json_val *val, void *out)
+{
+ struct rpc_luns *luns = out;
+
+ return spdk_json_decode_array(val, decode_rpc_lun, luns->luns,
+ RPC_ISCSI_CREATE_TARGET_NODE_MAX_LUN,
+ &luns->num_luns, sizeof(struct rpc_lun));
+}
+
+static void
+free_rpc_luns(struct rpc_luns *p)
+{
+ size_t i;
+
+ for (i = 0; i < p->num_luns; i++) {
+ free(p->luns[i].bdev_name);
+ }
+}
+
+struct rpc_target_node {
+ char *name;
+ char *alias_name;
+
+ struct rpc_pg_ig_maps pg_ig_maps;
+ struct rpc_luns luns;
+
+ int32_t queue_depth;
+ bool disable_chap;
+ bool require_chap;
+ bool mutual_chap;
+ int32_t chap_group;
+
+ bool header_digest;
+ bool data_digest;
+};
+
+static void
+free_rpc_target_node(struct rpc_target_node *req)
+{
+ free(req->name);
+ free(req->alias_name);
+ free_rpc_luns(&req->luns);
+}
+
+static const struct spdk_json_object_decoder rpc_target_node_decoders[] = {
+ {"name", offsetof(struct rpc_target_node, name), spdk_json_decode_string},
+ {"alias_name", offsetof(struct rpc_target_node, alias_name), spdk_json_decode_string},
+ {"pg_ig_maps", offsetof(struct rpc_target_node, pg_ig_maps), decode_rpc_pg_ig_maps},
+ {"luns", offsetof(struct rpc_target_node, luns), decode_rpc_luns},
+ {"queue_depth", offsetof(struct rpc_target_node, queue_depth), spdk_json_decode_int32},
+ {"disable_chap", offsetof(struct rpc_target_node, disable_chap), spdk_json_decode_bool, true},
+ {"require_chap", offsetof(struct rpc_target_node, require_chap), spdk_json_decode_bool, true},
+ {"mutual_chap", offsetof(struct rpc_target_node, mutual_chap), spdk_json_decode_bool, true},
+ {"chap_group", offsetof(struct rpc_target_node, chap_group), spdk_json_decode_int32, true},
+ {"header_digest", offsetof(struct rpc_target_node, header_digest), spdk_json_decode_bool, true},
+ {"data_digest", offsetof(struct rpc_target_node, data_digest), spdk_json_decode_bool, true},
+};
+
+static void
+rpc_iscsi_create_target_node(struct spdk_jsonrpc_request *request,
+ const struct spdk_json_val *params)
+{
+ struct rpc_target_node req = {};
+ struct spdk_json_write_ctx *w;
+ struct spdk_iscsi_tgt_node *target;
+ int32_t pg_tags[MAX_TARGET_MAP] = {0}, ig_tags[MAX_TARGET_MAP] = {0};
+ char *bdev_names[RPC_ISCSI_CREATE_TARGET_NODE_MAX_LUN] = {0};
+ int32_t lun_ids[RPC_ISCSI_CREATE_TARGET_NODE_MAX_LUN] = {0};
+ size_t i;
+
+ if (spdk_json_decode_object(params, rpc_target_node_decoders,
+ SPDK_COUNTOF(rpc_target_node_decoders),
+ &req)) {
+ SPDK_ERRLOG("spdk_json_decode_object failed\n");
+ goto invalid;
+ }
+
+ for (i = 0; i < req.pg_ig_maps.num_maps; i++) {
+ pg_tags[i] = req.pg_ig_maps.maps[i].pg_tag;
+ ig_tags[i] = req.pg_ig_maps.maps[i].ig_tag;
+ }
+
+ for (i = 0; i < req.luns.num_luns; i++) {
+ bdev_names[i] = req.luns.luns[i].bdev_name;
+ lun_ids[i] = req.luns.luns[i].lun_id;
+ }
+
+ /*
+ * Use default parameters in a few places:
+ * index = -1 : automatically pick an index for the new target node
+ * alias = NULL
+ */
+ target = iscsi_tgt_node_construct(-1, req.name, req.alias_name,
+ pg_tags,
+ ig_tags,
+ req.pg_ig_maps.num_maps,
+ (const char **)bdev_names,
+ lun_ids,
+ req.luns.num_luns,
+ req.queue_depth,
+ req.disable_chap,
+ req.require_chap,
+ req.mutual_chap,
+ req.chap_group,
+ req.header_digest,
+ req.data_digest);
+
+ if (target == NULL) {
+ goto invalid;
+ }
+
+ free_rpc_target_node(&req);
+
+ w = spdk_jsonrpc_begin_result(request);
+ spdk_json_write_bool(w, true);
+ spdk_jsonrpc_end_result(request, w);
+ return;
+
+invalid:
+ spdk_jsonrpc_send_error_response(request, SPDK_JSONRPC_ERROR_INVALID_PARAMS, "Invalid parameters");
+ free_rpc_target_node(&req);
+}
+SPDK_RPC_REGISTER("iscsi_create_target_node", rpc_iscsi_create_target_node, SPDK_RPC_RUNTIME)
+SPDK_RPC_REGISTER_ALIAS_DEPRECATED(iscsi_create_target_node, construct_target_node)
+
+struct rpc_tgt_node_pg_ig_maps {
+ char *name;
+ struct rpc_pg_ig_maps pg_ig_maps;
+};
+
+static const struct spdk_json_object_decoder rpc_tgt_node_pg_ig_maps_decoders[] = {
+ {"name", offsetof(struct rpc_tgt_node_pg_ig_maps, name), spdk_json_decode_string},
+ {"pg_ig_maps", offsetof(struct rpc_tgt_node_pg_ig_maps, pg_ig_maps), decode_rpc_pg_ig_maps},
+};
+
+static void
+rpc_iscsi_target_node_add_pg_ig_maps(struct spdk_jsonrpc_request *request,
+ const struct spdk_json_val *params)
+{
+ struct rpc_tgt_node_pg_ig_maps req = {};
+ struct spdk_json_write_ctx *w;
+ struct spdk_iscsi_tgt_node *target;
+ int32_t pg_tags[MAX_TARGET_MAP] = {0}, ig_tags[MAX_TARGET_MAP] = {0};
+ size_t i;
+ int rc;
+
+ if (spdk_json_decode_object(params, rpc_tgt_node_pg_ig_maps_decoders,
+ SPDK_COUNTOF(rpc_tgt_node_pg_ig_maps_decoders),
+ &req)) {
+ SPDK_ERRLOG("spdk_json_decode_object failed\n");
+ goto invalid;
+ }
+
+ target = iscsi_find_tgt_node(req.name);
+ if (target == NULL) {
+ SPDK_ERRLOG("target is not found\n");
+ goto invalid;
+ }
+
+ for (i = 0; i < req.pg_ig_maps.num_maps; i++) {
+ pg_tags[i] = req.pg_ig_maps.maps[i].pg_tag;
+ ig_tags[i] = req.pg_ig_maps.maps[i].ig_tag;
+ }
+
+ rc = iscsi_target_node_add_pg_ig_maps(target, pg_tags, ig_tags,
+ req.pg_ig_maps.num_maps);
+ if (rc < 0) {
+ SPDK_ERRLOG("add pg-ig maps failed\n");
+ goto invalid;
+ }
+
+ free(req.name);
+
+ w = spdk_jsonrpc_begin_result(request);
+ spdk_json_write_bool(w, true);
+ spdk_jsonrpc_end_result(request, w);
+ return;
+
+invalid:
+ spdk_jsonrpc_send_error_response(request, SPDK_JSONRPC_ERROR_INVALID_PARAMS,
+ "Invalid parameters");
+ free(req.name);
+}
+SPDK_RPC_REGISTER("iscsi_target_node_add_pg_ig_maps",
+ rpc_iscsi_target_node_add_pg_ig_maps, SPDK_RPC_RUNTIME)
+SPDK_RPC_REGISTER_ALIAS_DEPRECATED(iscsi_target_node_add_pg_ig_maps, add_pg_ig_maps)
+
+static void
+rpc_iscsi_target_node_remove_pg_ig_maps(struct spdk_jsonrpc_request *request,
+ const struct spdk_json_val *params)
+{
+ struct rpc_tgt_node_pg_ig_maps req = {};
+ struct spdk_json_write_ctx *w;
+ struct spdk_iscsi_tgt_node *target;
+ int32_t pg_tags[MAX_TARGET_MAP] = {0}, ig_tags[MAX_TARGET_MAP] = {0};
+ size_t i;
+ int rc;
+
+ if (spdk_json_decode_object(params, rpc_tgt_node_pg_ig_maps_decoders,
+ SPDK_COUNTOF(rpc_tgt_node_pg_ig_maps_decoders),
+ &req)) {
+ SPDK_ERRLOG("spdk_json_decode_object failed\n");
+ goto invalid;
+ }
+
+ target = iscsi_find_tgt_node(req.name);
+ if (target == NULL) {
+ SPDK_ERRLOG("target is not found\n");
+ goto invalid;
+ }
+
+ for (i = 0; i < req.pg_ig_maps.num_maps; i++) {
+ pg_tags[i] = req.pg_ig_maps.maps[i].pg_tag;
+ ig_tags[i] = req.pg_ig_maps.maps[i].ig_tag;
+ }
+
+ rc = iscsi_target_node_remove_pg_ig_maps(target, pg_tags, ig_tags,
+ req.pg_ig_maps.num_maps);
+ if (rc < 0) {
+ SPDK_ERRLOG("remove pg-ig maps failed\n");
+ goto invalid;
+ }
+
+ free(req.name);
+
+ w = spdk_jsonrpc_begin_result(request);
+ spdk_json_write_bool(w, true);
+ spdk_jsonrpc_end_result(request, w);
+ return;
+
+invalid:
+ spdk_jsonrpc_send_error_response(request, SPDK_JSONRPC_ERROR_INVALID_PARAMS,
+ "Invalid parameters");
+ free(req.name);
+}
+SPDK_RPC_REGISTER("iscsi_target_node_remove_pg_ig_maps",
+ rpc_iscsi_target_node_remove_pg_ig_maps, SPDK_RPC_RUNTIME)
+SPDK_RPC_REGISTER_ALIAS_DEPRECATED(iscsi_target_node_remove_pg_ig_maps,
+ delete_pg_ig_maps)
+
+struct rpc_iscsi_delete_target_node {
+ char *name;
+};
+
+static void
+free_rpc_iscsi_delete_target_node(struct rpc_iscsi_delete_target_node *r)
+{
+ free(r->name);
+}
+
+static const struct spdk_json_object_decoder rpc_iscsi_delete_target_node_decoders[] = {
+ {"name", offsetof(struct rpc_iscsi_delete_target_node, name), spdk_json_decode_string},
+};
+
+struct rpc_iscsi_delete_target_node_ctx {
+ struct rpc_iscsi_delete_target_node req;
+ struct spdk_jsonrpc_request *request;
+};
+
+static void
+rpc_iscsi_delete_target_node_done(void *cb_arg, int rc)
+{
+ struct rpc_iscsi_delete_target_node_ctx *ctx = cb_arg;
+ struct spdk_json_write_ctx *w;
+
+ free_rpc_iscsi_delete_target_node(&ctx->req);
+
+ w = spdk_jsonrpc_begin_result(ctx->request);
+ spdk_json_write_bool(w, rc == 0);
+ spdk_jsonrpc_end_result(ctx->request, w);
+
+ free(ctx);
+}
+
+static void
+rpc_iscsi_delete_target_node(struct spdk_jsonrpc_request *request,
+ const struct spdk_json_val *params)
+{
+ struct rpc_iscsi_delete_target_node_ctx *ctx;
+
+ ctx = calloc(1, sizeof(*ctx));
+ if (!ctx) {
+ spdk_jsonrpc_send_error_response(request, SPDK_JSONRPC_ERROR_INTERNAL_ERROR,
+ spdk_strerror(ENOMEM));
+ return;
+ }
+
+ if (spdk_json_decode_object(params, rpc_iscsi_delete_target_node_decoders,
+ SPDK_COUNTOF(rpc_iscsi_delete_target_node_decoders),
+ &ctx->req)) {
+ SPDK_ERRLOG("spdk_json_decode_object failed\n");
+ goto invalid;
+ }
+
+ if (ctx->req.name == NULL) {
+ SPDK_ERRLOG("missing name param\n");
+ goto invalid;
+ }
+
+ ctx->request = request;
+
+ iscsi_shutdown_tgt_node_by_name(ctx->req.name,
+ rpc_iscsi_delete_target_node_done, ctx);
+ return;
+
+invalid:
+ spdk_jsonrpc_send_error_response(request, SPDK_JSONRPC_ERROR_INVALID_PARAMS, "Invalid parameters");
+ free_rpc_iscsi_delete_target_node(&ctx->req);
+ free(ctx);
+}
+SPDK_RPC_REGISTER("iscsi_delete_target_node", rpc_iscsi_delete_target_node, SPDK_RPC_RUNTIME)
+SPDK_RPC_REGISTER_ALIAS_DEPRECATED(iscsi_delete_target_node, delete_target_node)
+
+static void
+rpc_iscsi_get_portal_groups(struct spdk_jsonrpc_request *request,
+ const struct spdk_json_val *params)
+{
+ struct spdk_json_write_ctx *w;
+
+ if (params != NULL) {
+ spdk_jsonrpc_send_error_response(request, SPDK_JSONRPC_ERROR_INVALID_PARAMS,
+ "iscsi_get_portal_groups requires no parameters");
+ return;
+ }
+
+ w = spdk_jsonrpc_begin_result(request);
+ spdk_json_write_array_begin(w);
+ iscsi_portal_grps_info_json(w);
+ spdk_json_write_array_end(w);
+
+ spdk_jsonrpc_end_result(request, w);
+}
+SPDK_RPC_REGISTER("iscsi_get_portal_groups", rpc_iscsi_get_portal_groups, SPDK_RPC_RUNTIME)
+SPDK_RPC_REGISTER_ALIAS_DEPRECATED(iscsi_get_portal_groups, get_portal_groups)
+
+struct rpc_portal {
+ char *host;
+ char *port;
+};
+
+struct rpc_portal_list {
+ size_t num_portals;
+ struct rpc_portal portals[MAX_PORTAL];
+};
+
+struct rpc_portal_group {
+ int32_t tag;
+ struct rpc_portal_list portal_list;
+};
+
+static void
+free_rpc_portal(struct rpc_portal *portal)
+{
+ free(portal->host);
+ free(portal->port);
+}
+
+static void
+free_rpc_portal_list(struct rpc_portal_list *pl)
+{
+ size_t i;
+
+ for (i = 0; i < pl->num_portals; i++) {
+ free_rpc_portal(&pl->portals[i]);
+ }
+ pl->num_portals = 0;
+}
+
+static void
+free_rpc_portal_group(struct rpc_portal_group *pg)
+{
+ free_rpc_portal_list(&pg->portal_list);
+}
+
+static const struct spdk_json_object_decoder rpc_portal_decoders[] = {
+ {"host", offsetof(struct rpc_portal, host), spdk_json_decode_string},
+ {"port", offsetof(struct rpc_portal, port), spdk_json_decode_string},
+};
+
+static int
+decode_rpc_portal(const struct spdk_json_val *val, void *out)
+{
+ struct rpc_portal *portal = out;
+
+ return spdk_json_decode_object(val, rpc_portal_decoders,
+ SPDK_COUNTOF(rpc_portal_decoders),
+ portal);
+}
+
+static int
+decode_rpc_portal_list(const struct spdk_json_val *val, void *out)
+{
+ struct rpc_portal_list *list = out;
+
+ return spdk_json_decode_array(val, decode_rpc_portal, list->portals, MAX_PORTAL, &list->num_portals,
+ sizeof(struct rpc_portal));
+}
+
+static const struct spdk_json_object_decoder rpc_portal_group_decoders[] = {
+ {"tag", offsetof(struct rpc_portal_group, tag), spdk_json_decode_int32},
+ {"portals", offsetof(struct rpc_portal_group, portal_list), decode_rpc_portal_list},
+};
+
+static void
+rpc_iscsi_create_portal_group(struct spdk_jsonrpc_request *request,
+ const struct spdk_json_val *params)
+{
+ struct rpc_portal_group req = {};
+ struct spdk_iscsi_portal_grp *pg = NULL;
+ struct spdk_iscsi_portal *portal;
+ struct spdk_json_write_ctx *w;
+ size_t i = 0;
+ int rc = -1;
+
+ if (spdk_json_decode_object(params, rpc_portal_group_decoders,
+ SPDK_COUNTOF(rpc_portal_group_decoders),
+ &req)) {
+ SPDK_ERRLOG("spdk_json_decode_object failed\n");
+ goto out;
+ }
+
+ pg = iscsi_portal_grp_create(req.tag);
+ if (pg == NULL) {
+ SPDK_ERRLOG("portal_grp_create failed\n");
+ goto out;
+ }
+ for (i = 0; i < req.portal_list.num_portals; i++) {
+ portal = iscsi_portal_create(req.portal_list.portals[i].host,
+ req.portal_list.portals[i].port);
+ if (portal == NULL) {
+ SPDK_ERRLOG("portal_create failed\n");
+ goto out;
+ }
+ iscsi_portal_grp_add_portal(pg, portal);
+ }
+
+ rc = iscsi_portal_grp_open(pg);
+ if (rc != 0) {
+ SPDK_ERRLOG("portal_grp_open failed\n");
+ goto out;
+ }
+
+ rc = iscsi_portal_grp_register(pg);
+ if (rc != 0) {
+ SPDK_ERRLOG("portal_grp_register failed\n");
+ }
+
+out:
+ if (rc == 0) {
+ w = spdk_jsonrpc_begin_result(request);
+ spdk_json_write_bool(w, true);
+ spdk_jsonrpc_end_result(request, w);
+ } else {
+ spdk_jsonrpc_send_error_response(request, SPDK_JSONRPC_ERROR_INVALID_PARAMS, "Invalid parameters");
+
+ if (pg != NULL) {
+ iscsi_portal_grp_release(pg);
+ }
+ }
+ free_rpc_portal_group(&req);
+}
+SPDK_RPC_REGISTER("iscsi_create_portal_group", rpc_iscsi_create_portal_group, SPDK_RPC_RUNTIME)
+SPDK_RPC_REGISTER_ALIAS_DEPRECATED(iscsi_create_portal_group, add_portal_group)
+
+struct rpc_iscsi_delete_portal_group {
+ int32_t tag;
+};
+
+static const struct spdk_json_object_decoder rpc_iscsi_delete_portal_group_decoders[] = {
+ {"tag", offsetof(struct rpc_iscsi_delete_portal_group, tag), spdk_json_decode_int32},
+};
+
+static void
+rpc_iscsi_delete_portal_group(struct spdk_jsonrpc_request *request,
+ const struct spdk_json_val *params)
+{
+ struct rpc_iscsi_delete_portal_group req = {};
+ struct spdk_json_write_ctx *w;
+ struct spdk_iscsi_portal_grp *pg;
+
+ if (spdk_json_decode_object(params, rpc_iscsi_delete_portal_group_decoders,
+ SPDK_COUNTOF(rpc_iscsi_delete_portal_group_decoders),
+ &req)) {
+ SPDK_ERRLOG("spdk_json_decode_object failed\n");
+ goto invalid;
+ }
+
+ pg = iscsi_portal_grp_unregister(req.tag);
+ if (!pg) {
+ goto invalid;
+ }
+
+ iscsi_tgt_node_delete_map(pg, NULL);
+ iscsi_portal_grp_release(pg);
+
+ w = spdk_jsonrpc_begin_result(request);
+ spdk_json_write_bool(w, true);
+ spdk_jsonrpc_end_result(request, w);
+ return;
+
+invalid:
+ spdk_jsonrpc_send_error_response(request, SPDK_JSONRPC_ERROR_INVALID_PARAMS, "Invalid parameters");
+}
+SPDK_RPC_REGISTER("iscsi_delete_portal_group", rpc_iscsi_delete_portal_group, SPDK_RPC_RUNTIME)
+SPDK_RPC_REGISTER_ALIAS_DEPRECATED(iscsi_delete_portal_group, delete_portal_group)
+
+struct rpc_portal_group_auth {
+ int32_t tag;
+ bool disable_chap;
+ bool require_chap;
+ bool mutual_chap;
+ int32_t chap_group;
+};
+
+static const struct spdk_json_object_decoder rpc_portal_group_auth_decoders[] = {
+ {"tag", offsetof(struct rpc_portal_group_auth, tag), spdk_json_decode_int32},
+ {"disable_chap", offsetof(struct rpc_portal_group_auth, disable_chap), spdk_json_decode_bool, true},
+ {"require_chap", offsetof(struct rpc_portal_group_auth, require_chap), spdk_json_decode_bool, true},
+ {"mutual_chap", offsetof(struct rpc_portal_group_auth, mutual_chap), spdk_json_decode_bool, true},
+ {"chap_group", offsetof(struct rpc_portal_group_auth, chap_group), spdk_json_decode_int32, true},
+};
+
+static void
+rpc_iscsi_portal_group_set_auth(struct spdk_jsonrpc_request *request,
+ const struct spdk_json_val *params)
+{
+ struct rpc_portal_group_auth req = {};
+ struct spdk_json_write_ctx *w;
+ struct spdk_iscsi_portal_grp *pg;
+ int rc;
+
+ if (spdk_json_decode_object(params, rpc_portal_group_auth_decoders,
+ SPDK_COUNTOF(rpc_portal_group_auth_decoders), &req)) {
+ SPDK_ERRLOG("spdk_json_decode_object failed\n");
+ spdk_jsonrpc_send_error_response(request, SPDK_JSONRPC_ERROR_INVALID_PARAMS,
+ "Invalid parameters");
+ return;
+ }
+
+ pthread_mutex_lock(&g_iscsi.mutex);
+
+ pg = iscsi_portal_grp_find_by_tag(req.tag);
+ if (pg == NULL) {
+ spdk_jsonrpc_send_error_response_fmt(request, SPDK_JSONRPC_ERROR_INVALID_PARAMS,
+ "Could not find portal group %d", req.tag);
+ goto exit;
+ }
+
+ rc = iscsi_portal_grp_set_chap_params(pg, req.disable_chap, req.require_chap,
+ req.mutual_chap, req.chap_group);
+ if (rc < 0) {
+ spdk_jsonrpc_send_error_response(request, SPDK_JSONRPC_ERROR_INVALID_PARAMS,
+ "Invalid combination of auth params");
+ goto exit;
+ }
+
+ pthread_mutex_unlock(&g_iscsi.mutex);
+
+ w = spdk_jsonrpc_begin_result(request);
+ spdk_json_write_bool(w, true);
+ spdk_jsonrpc_end_result(request, w);
+
+ return;
+
+exit:
+ pthread_mutex_unlock(&g_iscsi.mutex);
+}
+SPDK_RPC_REGISTER("iscsi_portal_group_set_auth", rpc_iscsi_portal_group_set_auth,
+ SPDK_RPC_RUNTIME)
+
+struct rpc_iscsi_get_connections_ctx {
+ struct spdk_jsonrpc_request *request;
+ struct spdk_json_write_ctx *w;
+};
+
+static void
+_rpc_iscsi_get_connections_done(struct spdk_io_channel_iter *i, int status)
+{
+ struct rpc_iscsi_get_connections_ctx *ctx = spdk_io_channel_iter_get_ctx(i);
+
+ spdk_json_write_array_end(ctx->w);
+ spdk_jsonrpc_end_result(ctx->request, ctx->w);
+
+ free(ctx);
+}
+
+static void
+_rpc_iscsi_get_connections(struct spdk_io_channel_iter *i)
+{
+ struct rpc_iscsi_get_connections_ctx *ctx = spdk_io_channel_iter_get_ctx(i);
+ struct spdk_io_channel *ch = spdk_io_channel_iter_get_channel(i);
+ struct spdk_iscsi_poll_group *pg = spdk_io_channel_get_ctx(ch);
+ struct spdk_iscsi_conn *conn;
+
+ STAILQ_FOREACH(conn, &pg->connections, pg_link) {
+ iscsi_conn_info_json(ctx->w, conn);
+ }
+
+ spdk_for_each_channel_continue(i, 0);
+}
+
+static void
+rpc_iscsi_get_connections(struct spdk_jsonrpc_request *request,
+ const struct spdk_json_val *params)
+{
+ struct rpc_iscsi_get_connections_ctx *ctx;
+
+ if (params != NULL) {
+ spdk_jsonrpc_send_error_response(request, SPDK_JSONRPC_ERROR_INVALID_PARAMS,
+ "iscsi_get_connections requires no parameters");
+ return;
+ }
+
+ ctx = calloc(1, sizeof(struct rpc_iscsi_get_connections_ctx));
+ if (ctx == NULL) {
+ SPDK_ERRLOG("Failed to allocate rpc_get_iscsi_conns_ctx struct\n");
+ spdk_jsonrpc_send_error_response(request, -ENOMEM, spdk_strerror(ENOMEM));
+ return;
+ }
+
+ ctx->request = request;
+ ctx->w = spdk_jsonrpc_begin_result(request);
+
+ spdk_json_write_array_begin(ctx->w);
+
+ spdk_for_each_channel(&g_iscsi,
+ _rpc_iscsi_get_connections,
+ ctx,
+ _rpc_iscsi_get_connections_done);
+}
+SPDK_RPC_REGISTER("iscsi_get_connections", rpc_iscsi_get_connections, SPDK_RPC_RUNTIME)
+SPDK_RPC_REGISTER_ALIAS_DEPRECATED(iscsi_get_connections, get_iscsi_connections)
+
+struct rpc_target_lun {
+ char *name;
+ char *bdev_name;
+ int32_t lun_id;
+};
+
+static void
+free_rpc_target_lun(struct rpc_target_lun *req)
+{
+ free(req->name);
+ free(req->bdev_name);
+}
+
+static const struct spdk_json_object_decoder rpc_target_lun_decoders[] = {
+ {"name", offsetof(struct rpc_target_lun, name), spdk_json_decode_string},
+ {"bdev_name", offsetof(struct rpc_target_lun, bdev_name), spdk_json_decode_string},
+ {"lun_id", offsetof(struct rpc_target_lun, lun_id), spdk_json_decode_int32, true},
+};
+
+static void
+rpc_iscsi_target_node_add_lun(struct spdk_jsonrpc_request *request,
+ const struct spdk_json_val *params)
+{
+ struct rpc_target_lun req = {};
+ struct spdk_json_write_ctx *w;
+ struct spdk_iscsi_tgt_node *target;
+ int rc;
+
+ req.lun_id = -1;
+
+ if (spdk_json_decode_object(params, rpc_target_lun_decoders,
+ SPDK_COUNTOF(rpc_target_lun_decoders), &req)) {
+ SPDK_ERRLOG("spdk_json_decode_object failed\n");
+ goto invalid;
+ }
+
+ target = iscsi_find_tgt_node(req.name);
+ if (target == NULL) {
+ SPDK_ERRLOG("target is not found\n");
+ goto invalid;
+ }
+
+ rc = iscsi_tgt_node_add_lun(target, req.bdev_name, req.lun_id);
+ if (rc < 0) {
+ SPDK_ERRLOG("add lun failed\n");
+ goto invalid;
+ }
+
+ free_rpc_target_lun(&req);
+
+ w = spdk_jsonrpc_begin_result(request);
+ spdk_json_write_bool(w, true);
+ spdk_jsonrpc_end_result(request, w);
+ return;
+
+invalid:
+ spdk_jsonrpc_send_error_response(request, SPDK_JSONRPC_ERROR_INVALID_PARAMS,
+ "Invalid parameters");
+ free_rpc_target_lun(&req);
+}
+SPDK_RPC_REGISTER("iscsi_target_node_add_lun", rpc_iscsi_target_node_add_lun, SPDK_RPC_RUNTIME)
+SPDK_RPC_REGISTER_ALIAS_DEPRECATED(iscsi_target_node_add_lun, target_node_add_lun)
+
+struct rpc_target_auth {
+ char *name;
+ bool disable_chap;
+ bool require_chap;
+ bool mutual_chap;
+ int32_t chap_group;
+};
+
+static void
+free_rpc_target_auth(struct rpc_target_auth *req)
+{
+ free(req->name);
+}
+
+static const struct spdk_json_object_decoder rpc_target_auth_decoders[] = {
+ {"name", offsetof(struct rpc_target_auth, name), spdk_json_decode_string},
+ {"disable_chap", offsetof(struct rpc_target_auth, disable_chap), spdk_json_decode_bool, true},
+ {"require_chap", offsetof(struct rpc_target_auth, require_chap), spdk_json_decode_bool, true},
+ {"mutual_chap", offsetof(struct rpc_target_auth, mutual_chap), spdk_json_decode_bool, true},
+ {"chap_group", offsetof(struct rpc_target_auth, chap_group), spdk_json_decode_int32, true},
+};
+
+static void
+rpc_iscsi_target_node_set_auth(struct spdk_jsonrpc_request *request,
+ const struct spdk_json_val *params)
+{
+ struct rpc_target_auth req = {};
+ struct spdk_json_write_ctx *w;
+ struct spdk_iscsi_tgt_node *target;
+ int rc;
+
+ if (spdk_json_decode_object(params, rpc_target_auth_decoders,
+ SPDK_COUNTOF(rpc_target_auth_decoders), &req)) {
+ SPDK_ERRLOG("spdk_json_decode_object failed\n");
+ spdk_jsonrpc_send_error_response(request, SPDK_JSONRPC_ERROR_INVALID_PARAMS,
+ "Invalid parameters");
+ goto exit;
+ }
+
+ target = iscsi_find_tgt_node(req.name);
+ if (target == NULL) {
+ spdk_jsonrpc_send_error_response_fmt(request, SPDK_JSONRPC_ERROR_INVALID_PARAMS,
+ "Could not find target %s", req.name);
+ goto exit;
+ }
+
+ rc = iscsi_tgt_node_set_chap_params(target, req.disable_chap, req.require_chap,
+ req.mutual_chap, req.chap_group);
+ if (rc < 0) {
+ spdk_jsonrpc_send_error_response(request, SPDK_JSONRPC_ERROR_INVALID_PARAMS,
+ "Invalid combination of auth params");
+ goto exit;
+ }
+
+ free_rpc_target_auth(&req);
+
+ w = spdk_jsonrpc_begin_result(request);
+ spdk_json_write_bool(w, true);
+ spdk_jsonrpc_end_result(request, w);
+ return;
+
+exit:
+ free_rpc_target_auth(&req);
+}
+SPDK_RPC_REGISTER("iscsi_target_node_set_auth", rpc_iscsi_target_node_set_auth,
+ SPDK_RPC_RUNTIME)
+SPDK_RPC_REGISTER_ALIAS_DEPRECATED(iscsi_target_node_set_auth, set_iscsi_target_node_auth)
+
+static void
+rpc_iscsi_get_options(struct spdk_jsonrpc_request *request,
+ const struct spdk_json_val *params)
+{
+ struct spdk_json_write_ctx *w;
+
+ if (params != NULL) {
+ spdk_jsonrpc_send_error_response(request, SPDK_JSONRPC_ERROR_INVALID_PARAMS,
+ "iscsi_get_options requires no parameters");
+ return;
+ }
+
+ w = spdk_jsonrpc_begin_result(request);
+ iscsi_opts_info_json(w);
+
+ spdk_jsonrpc_end_result(request, w);
+}
+SPDK_RPC_REGISTER("iscsi_get_options", rpc_iscsi_get_options, SPDK_RPC_RUNTIME)
+SPDK_RPC_REGISTER_ALIAS_DEPRECATED(iscsi_get_options, get_iscsi_global_params)
+
+struct rpc_discovery_auth {
+ bool disable_chap;
+ bool require_chap;
+ bool mutual_chap;
+ int32_t chap_group;
+};
+
+static const struct spdk_json_object_decoder rpc_discovery_auth_decoders[] = {
+ {"disable_chap", offsetof(struct rpc_discovery_auth, disable_chap), spdk_json_decode_bool, true},
+ {"require_chap", offsetof(struct rpc_discovery_auth, require_chap), spdk_json_decode_bool, true},
+ {"mutual_chap", offsetof(struct rpc_discovery_auth, mutual_chap), spdk_json_decode_bool, true},
+ {"chap_group", offsetof(struct rpc_discovery_auth, chap_group), spdk_json_decode_int32, true},
+};
+
+static void
+rpc_iscsi_set_discovery_auth(struct spdk_jsonrpc_request *request,
+ const struct spdk_json_val *params)
+{
+ struct rpc_discovery_auth req = {};
+ struct spdk_json_write_ctx *w;
+ int rc;
+
+ if (spdk_json_decode_object(params, rpc_discovery_auth_decoders,
+ SPDK_COUNTOF(rpc_discovery_auth_decoders), &req)) {
+ SPDK_ERRLOG("spdk_json_decode_object failed\n");
+ spdk_jsonrpc_send_error_response(request, SPDK_JSONRPC_ERROR_INVALID_PARAMS,
+ "Invalid parameters");
+ return;
+ }
+
+ rc = iscsi_set_discovery_auth(req.disable_chap, req.require_chap,
+ req.mutual_chap, req.chap_group);
+ if (rc < 0) {
+ spdk_jsonrpc_send_error_response(request, SPDK_JSONRPC_ERROR_INVALID_PARAMS,
+ "Invalid combination of CHAP params");
+ return;
+ }
+
+ w = spdk_jsonrpc_begin_result(request);
+ spdk_json_write_bool(w, true);
+ spdk_jsonrpc_end_result(request, w);
+}
+SPDK_RPC_REGISTER("iscsi_set_discovery_auth", rpc_iscsi_set_discovery_auth, SPDK_RPC_RUNTIME)
+SPDK_RPC_REGISTER_ALIAS_DEPRECATED(iscsi_set_discovery_auth, set_iscsi_discovery_auth)
+
+#define MAX_AUTH_SECRETS 64
+
+struct rpc_auth_secret {
+ char *user;
+ char *secret;
+ char *muser;
+ char *msecret;
+};
+
+static void
+free_rpc_auth_secret(struct rpc_auth_secret *_secret)
+{
+ free(_secret->user);
+ free(_secret->secret);
+ free(_secret->muser);
+ free(_secret->msecret);
+}
+
+static const struct spdk_json_object_decoder rpc_auth_secret_decoders[] = {
+ {"user", offsetof(struct rpc_auth_secret, user), spdk_json_decode_string},
+ {"secret", offsetof(struct rpc_auth_secret, secret), spdk_json_decode_string},
+ {"muser", offsetof(struct rpc_auth_secret, muser), spdk_json_decode_string, true},
+ {"msecret", offsetof(struct rpc_auth_secret, msecret), spdk_json_decode_string, true},
+};
+
+static int
+decode_rpc_auth_secret(const struct spdk_json_val *val, void *out)
+{
+ struct rpc_auth_secret *_secret = out;
+
+ return spdk_json_decode_object(val, rpc_auth_secret_decoders,
+ SPDK_COUNTOF(rpc_auth_secret_decoders), _secret);
+}
+
+struct rpc_auth_secrets {
+ size_t num_secret;
+ struct rpc_auth_secret secrets[MAX_AUTH_SECRETS];
+};
+
+static void
+free_rpc_auth_secrets(struct rpc_auth_secrets *secrets)
+{
+ size_t i;
+
+ for (i = 0; i < secrets->num_secret; i++) {
+ free_rpc_auth_secret(&secrets->secrets[i]);
+ }
+}
+
+static int
+decode_rpc_auth_secrets(const struct spdk_json_val *val, void *out)
+{
+ struct rpc_auth_secrets *secrets = out;
+
+ return spdk_json_decode_array(val, decode_rpc_auth_secret, secrets->secrets,
+ MAX_AUTH_SECRETS, &secrets->num_secret,
+ sizeof(struct rpc_auth_secret));
+}
+
+struct rpc_auth_group {
+ int32_t tag;
+ struct rpc_auth_secrets secrets;
+};
+
+static void
+free_rpc_auth_group(struct rpc_auth_group *group)
+{
+ free_rpc_auth_secrets(&group->secrets);
+}
+
+static const struct spdk_json_object_decoder rpc_auth_group_decoders[] = {
+ {"tag", offsetof(struct rpc_auth_group, tag), spdk_json_decode_int32},
+ {"secrets", offsetof(struct rpc_auth_group, secrets), decode_rpc_auth_secrets, true},
+};
+
+static void
+rpc_iscsi_create_auth_group(struct spdk_jsonrpc_request *request,
+ const struct spdk_json_val *params)
+{
+ struct rpc_auth_group req = {};
+ struct rpc_auth_secret *_secret;
+ struct spdk_json_write_ctx *w;
+ struct spdk_iscsi_auth_group *group = NULL;
+ int rc;
+ size_t i;
+
+ if (spdk_json_decode_object(params, rpc_auth_group_decoders,
+ SPDK_COUNTOF(rpc_auth_group_decoders), &req)) {
+ SPDK_ERRLOG("spdk_json_decode_object failed\n");
+ spdk_jsonrpc_send_error_response(request, SPDK_JSONRPC_ERROR_INVALID_PARAMS,
+ "Invalid parameters");
+ free_rpc_auth_group(&req);
+ return;
+ }
+
+ pthread_mutex_lock(&g_iscsi.mutex);
+
+ rc = iscsi_add_auth_group(req.tag, &group);
+ if (rc != 0) {
+ pthread_mutex_unlock(&g_iscsi.mutex);
+
+ spdk_jsonrpc_send_error_response_fmt(request, SPDK_JSONRPC_ERROR_INVALID_PARAMS,
+ "Could not add auth group (%d), %s",
+ req.tag, spdk_strerror(-rc));
+ free_rpc_auth_group(&req);
+ return;
+ }
+
+ for (i = 0; i < req.secrets.num_secret; i++) {
+ _secret = &req.secrets.secrets[i];
+ rc = iscsi_auth_group_add_secret(group, _secret->user, _secret->secret,
+ _secret->muser, _secret->msecret);
+ if (rc != 0) {
+ iscsi_delete_auth_group(group);
+ pthread_mutex_unlock(&g_iscsi.mutex);
+
+ spdk_jsonrpc_send_error_response_fmt(request, SPDK_JSONRPC_ERROR_INVALID_PARAMS,
+ "Could not add secret to auth group (%d), %s",
+ req.tag, spdk_strerror(-rc));
+ free_rpc_auth_group(&req);
+ return;
+ }
+ }
+
+ pthread_mutex_unlock(&g_iscsi.mutex);
+
+ free_rpc_auth_group(&req);
+
+ w = spdk_jsonrpc_begin_result(request);
+ spdk_json_write_bool(w, true);
+ spdk_jsonrpc_end_result(request, w);
+}
+SPDK_RPC_REGISTER("iscsi_create_auth_group", rpc_iscsi_create_auth_group, SPDK_RPC_RUNTIME)
+SPDK_RPC_REGISTER_ALIAS_DEPRECATED(iscsi_create_auth_group, add_iscsi_auth_group)
+
+struct rpc_delete_auth_group {
+ int32_t tag;
+};
+
+static const struct spdk_json_object_decoder rpc_delete_auth_group_decoders[] = {
+ {"tag", offsetof(struct rpc_delete_auth_group, tag), spdk_json_decode_int32},
+};
+
+static void
+rpc_iscsi_delete_auth_group(struct spdk_jsonrpc_request *request,
+ const struct spdk_json_val *params)
+{
+ struct rpc_delete_auth_group req = {};
+ struct spdk_json_write_ctx *w;
+ struct spdk_iscsi_auth_group *group;
+
+ if (spdk_json_decode_object(params, rpc_delete_auth_group_decoders,
+ SPDK_COUNTOF(rpc_delete_auth_group_decoders), &req)) {
+ SPDK_ERRLOG("spdk_json_decode_object failed\n");
+ spdk_jsonrpc_send_error_response(request, SPDK_JSONRPC_ERROR_INVALID_PARAMS,
+ "Invalid parameters");
+ return;
+ }
+
+ pthread_mutex_lock(&g_iscsi.mutex);
+
+ group = iscsi_find_auth_group_by_tag(req.tag);
+ if (group == NULL) {
+ pthread_mutex_unlock(&g_iscsi.mutex);
+
+ spdk_jsonrpc_send_error_response_fmt(request, SPDK_JSONRPC_ERROR_INVALID_PARAMS,
+ "Could not find auth group (%d)", req.tag);
+ return;
+ }
+
+ iscsi_delete_auth_group(group);
+
+ pthread_mutex_unlock(&g_iscsi.mutex);
+
+ w = spdk_jsonrpc_begin_result(request);
+ spdk_json_write_bool(w, true);
+ spdk_jsonrpc_end_result(request, w);
+}
+SPDK_RPC_REGISTER("iscsi_delete_auth_group", rpc_iscsi_delete_auth_group, SPDK_RPC_RUNTIME)
+SPDK_RPC_REGISTER_ALIAS_DEPRECATED(iscsi_delete_auth_group, delete_iscsi_auth_group)
+
+struct rpc_add_auth_secret {
+ int32_t tag;
+ char *user;
+ char *secret;
+ char *muser;
+ char *msecret;
+};
+
+static void
+free_rpc_add_auth_secret(struct rpc_add_auth_secret *_secret)
+{
+ free(_secret->user);
+ free(_secret->secret);
+ free(_secret->muser);
+ free(_secret->msecret);
+}
+
+static const struct spdk_json_object_decoder rpc_add_auth_secret_decoders[] = {
+ {"tag", offsetof(struct rpc_add_auth_secret, tag), spdk_json_decode_int32},
+ {"user", offsetof(struct rpc_add_auth_secret, user), spdk_json_decode_string},
+ {"secret", offsetof(struct rpc_add_auth_secret, secret), spdk_json_decode_string},
+ {"muser", offsetof(struct rpc_add_auth_secret, muser), spdk_json_decode_string, true},
+ {"msecret", offsetof(struct rpc_add_auth_secret, msecret), spdk_json_decode_string, true},
+};
+
+static void
+rpc_iscsi_auth_group_add_secret(struct spdk_jsonrpc_request *request,
+ const struct spdk_json_val *params)
+{
+ struct rpc_add_auth_secret req = {};
+ struct spdk_json_write_ctx *w;
+ struct spdk_iscsi_auth_group *group;
+ int rc;
+
+ if (spdk_json_decode_object(params, rpc_add_auth_secret_decoders,
+ SPDK_COUNTOF(rpc_add_auth_secret_decoders), &req)) {
+ SPDK_ERRLOG("spdk_json_decode_object failed\n");
+ spdk_jsonrpc_send_error_response(request, SPDK_JSONRPC_ERROR_INVALID_PARAMS,
+ "Invalid parameters");
+ free_rpc_add_auth_secret(&req);
+ return;
+ }
+
+ pthread_mutex_lock(&g_iscsi.mutex);
+
+ group = iscsi_find_auth_group_by_tag(req.tag);
+ if (group == NULL) {
+ pthread_mutex_unlock(&g_iscsi.mutex);
+
+ spdk_jsonrpc_send_error_response_fmt(request, SPDK_JSONRPC_ERROR_INVALID_PARAMS,
+ "Could not find auth group (%d)", req.tag);
+ free_rpc_add_auth_secret(&req);
+ return;
+ }
+
+ rc = iscsi_auth_group_add_secret(group, req.user, req.secret, req.muser, req.msecret);
+ if (rc != 0) {
+ pthread_mutex_unlock(&g_iscsi.mutex);
+
+ spdk_jsonrpc_send_error_response_fmt(request, SPDK_JSONRPC_ERROR_INVALID_PARAMS,
+ "Could not add secret to auth group (%d), %s",
+ req.tag, spdk_strerror(-rc));
+ free_rpc_add_auth_secret(&req);
+ return;
+ }
+
+ pthread_mutex_unlock(&g_iscsi.mutex);
+
+ free_rpc_add_auth_secret(&req);
+
+ w = spdk_jsonrpc_begin_result(request);
+ spdk_json_write_bool(w, true);
+ spdk_jsonrpc_end_result(request, w);
+}
+SPDK_RPC_REGISTER("iscsi_auth_group_add_secret", rpc_iscsi_auth_group_add_secret,
+ SPDK_RPC_RUNTIME)
+SPDK_RPC_REGISTER_ALIAS_DEPRECATED(iscsi_auth_group_add_secret, add_secret_to_iscsi_auth_group)
+
+
+struct rpc_remove_auth_secret {
+ int32_t tag;
+ char *user;
+};
+
+static void
+free_rpc_remove_auth_secret(struct rpc_remove_auth_secret *_secret)
+{
+ free(_secret->user);
+}
+
+static const struct spdk_json_object_decoder rpc_remove_auth_secret_decoders[] = {
+ {"tag", offsetof(struct rpc_remove_auth_secret, tag), spdk_json_decode_int32},
+ {"user", offsetof(struct rpc_remove_auth_secret, user), spdk_json_decode_string},
+};
+
+static void
+rpc_iscsi_auth_group_remove_secret(struct spdk_jsonrpc_request *request,
+ const struct spdk_json_val *params)
+{
+ struct rpc_remove_auth_secret req = {};
+ struct spdk_json_write_ctx *w;
+ struct spdk_iscsi_auth_group *group;
+ int rc;
+
+ if (spdk_json_decode_object(params, rpc_remove_auth_secret_decoders,
+ SPDK_COUNTOF(rpc_remove_auth_secret_decoders), &req)) {
+ SPDK_ERRLOG("spdk_json_decode_object failed\n");
+ spdk_jsonrpc_send_error_response(request, SPDK_JSONRPC_ERROR_INVALID_PARAMS,
+ "Invalid parameters");
+ free_rpc_remove_auth_secret(&req);
+ return;
+ }
+
+ pthread_mutex_lock(&g_iscsi.mutex);
+
+ group = iscsi_find_auth_group_by_tag(req.tag);
+ if (group == NULL) {
+ pthread_mutex_unlock(&g_iscsi.mutex);
+
+ spdk_jsonrpc_send_error_response_fmt(request, SPDK_JSONRPC_ERROR_INVALID_PARAMS,
+ "Could not find auth group (%d)", req.tag);
+ free_rpc_remove_auth_secret(&req);
+ return;
+ }
+
+ rc = iscsi_auth_group_delete_secret(group, req.user);
+ if (rc != 0) {
+ pthread_mutex_unlock(&g_iscsi.mutex);
+
+ spdk_jsonrpc_send_error_response_fmt(request, SPDK_JSONRPC_ERROR_INVALID_PARAMS,
+ "Could not delete secret from CHAP group (%d), %s",
+ req.tag, spdk_strerror(-rc));
+ free_rpc_remove_auth_secret(&req);
+ return;
+ }
+
+ pthread_mutex_unlock(&g_iscsi.mutex);
+
+ free_rpc_remove_auth_secret(&req);
+
+ w = spdk_jsonrpc_begin_result(request);
+ spdk_json_write_bool(w, true);
+ spdk_jsonrpc_end_result(request, w);
+}
+SPDK_RPC_REGISTER("iscsi_auth_group_remove_secret",
+ rpc_iscsi_auth_group_remove_secret, SPDK_RPC_RUNTIME)
+SPDK_RPC_REGISTER_ALIAS_DEPRECATED(iscsi_auth_group_remove_secret,
+ delete_secret_from_iscsi_auth_group)
+
+static void
+rpc_iscsi_get_auth_groups(struct spdk_jsonrpc_request *request,
+ const struct spdk_json_val *params)
+{
+ struct spdk_json_write_ctx *w;
+
+ if (params != NULL) {
+ spdk_jsonrpc_send_error_response(request, SPDK_JSONRPC_ERROR_INVALID_PARAMS,
+ "iscsi_get_auth_groups requires no parameters");
+ return;
+ }
+
+ w = spdk_jsonrpc_begin_result(request);
+ spdk_json_write_array_begin(w);
+ iscsi_auth_groups_info_json(w);
+ spdk_json_write_array_end(w);
+
+ spdk_jsonrpc_end_result(request, w);
+}
+SPDK_RPC_REGISTER("iscsi_get_auth_groups", rpc_iscsi_get_auth_groups, SPDK_RPC_RUNTIME)
+SPDK_RPC_REGISTER_ALIAS_DEPRECATED(iscsi_get_auth_groups, get_iscsi_auth_groups)
+
+static const struct spdk_json_object_decoder rpc_set_iscsi_opts_decoders[] = {
+ {"auth_file", offsetof(struct spdk_iscsi_opts, authfile), spdk_json_decode_string, true},
+ {"node_base", offsetof(struct spdk_iscsi_opts, nodebase), spdk_json_decode_string, true},
+ {"nop_timeout", offsetof(struct spdk_iscsi_opts, timeout), spdk_json_decode_int32, true},
+ {"nop_in_interval", offsetof(struct spdk_iscsi_opts, nopininterval), spdk_json_decode_int32, true},
+ {"no_discovery_auth", offsetof(struct spdk_iscsi_opts, disable_chap), spdk_json_decode_bool, true},
+ {"req_discovery_auth", offsetof(struct spdk_iscsi_opts, require_chap), spdk_json_decode_bool, true},
+ {"req_discovery_auth_mutual", offsetof(struct spdk_iscsi_opts, mutual_chap), spdk_json_decode_bool, true},
+ {"discovery_auth_group", offsetof(struct spdk_iscsi_opts, chap_group), spdk_json_decode_int32, true},
+ {"disable_chap", offsetof(struct spdk_iscsi_opts, disable_chap), spdk_json_decode_bool, true},
+ {"require_chap", offsetof(struct spdk_iscsi_opts, require_chap), spdk_json_decode_bool, true},
+ {"mutual_chap", offsetof(struct spdk_iscsi_opts, mutual_chap), spdk_json_decode_bool, true},
+ {"chap_group", offsetof(struct spdk_iscsi_opts, chap_group), spdk_json_decode_int32, true},
+ {"max_sessions", offsetof(struct spdk_iscsi_opts, MaxSessions), spdk_json_decode_uint32, true},
+ {"max_queue_depth", offsetof(struct spdk_iscsi_opts, MaxQueueDepth), spdk_json_decode_uint32, true},
+ {"max_connections_per_session", offsetof(struct spdk_iscsi_opts, MaxConnectionsPerSession), spdk_json_decode_uint32, true},
+ {"default_time2wait", offsetof(struct spdk_iscsi_opts, DefaultTime2Wait), spdk_json_decode_uint32, true},
+ {"default_time2retain", offsetof(struct spdk_iscsi_opts, DefaultTime2Retain), spdk_json_decode_uint32, true},
+ {"first_burst_length", offsetof(struct spdk_iscsi_opts, FirstBurstLength), spdk_json_decode_uint32, true},
+ {"immediate_data", offsetof(struct spdk_iscsi_opts, ImmediateData), spdk_json_decode_bool, true},
+ {"error_recovery_level", offsetof(struct spdk_iscsi_opts, ErrorRecoveryLevel), spdk_json_decode_uint32, true},
+ {"allow_duplicated_isid", offsetof(struct spdk_iscsi_opts, AllowDuplicateIsid), spdk_json_decode_bool, true},
+};
+
+static void
+rpc_iscsi_set_options(struct spdk_jsonrpc_request *request,
+ const struct spdk_json_val *params)
+{
+ struct spdk_iscsi_opts *opts;
+ struct spdk_json_write_ctx *w;
+
+ if (g_spdk_iscsi_opts != NULL) {
+ SPDK_ERRLOG("this RPC must not be called more than once.\n");
+ spdk_jsonrpc_send_error_response(request, SPDK_JSONRPC_ERROR_INTERNAL_ERROR,
+ "Must not call more than once");
+ return;
+ }
+
+ opts = iscsi_opts_alloc();
+ if (opts == NULL) {
+ SPDK_ERRLOG("iscsi_opts_alloc() failed.\n");
+ spdk_jsonrpc_send_error_response(request, SPDK_JSONRPC_ERROR_INTERNAL_ERROR,
+ "Out of memory");
+ return;
+ }
+
+ if (params != NULL) {
+ if (spdk_json_decode_object(params, rpc_set_iscsi_opts_decoders,
+ SPDK_COUNTOF(rpc_set_iscsi_opts_decoders), opts)) {
+ SPDK_ERRLOG("spdk_json_decode_object() failed\n");
+ spdk_jsonrpc_send_error_response(request, SPDK_JSONRPC_ERROR_INVALID_PARAMS,
+ "Invalid parameters");
+ iscsi_opts_free(opts);
+ return;
+ }
+ }
+
+ g_spdk_iscsi_opts = iscsi_opts_copy(opts);
+ iscsi_opts_free(opts);
+
+ if (g_spdk_iscsi_opts == NULL) {
+ SPDK_ERRLOG("iscsi_opts_copy() failed\n");
+ spdk_jsonrpc_send_error_response(request, SPDK_JSONRPC_ERROR_INTERNAL_ERROR,
+ "Out of memory");
+ return;
+ }
+
+ w = spdk_jsonrpc_begin_result(request);
+ spdk_json_write_bool(w, true);
+ spdk_jsonrpc_end_result(request, w);
+}
+SPDK_RPC_REGISTER("iscsi_set_options", rpc_iscsi_set_options, SPDK_RPC_STARTUP)
+SPDK_RPC_REGISTER_ALIAS_DEPRECATED(iscsi_set_options, set_iscsi_options)
diff --git a/src/spdk/lib/iscsi/iscsi_subsystem.c b/src/spdk/lib/iscsi/iscsi_subsystem.c
new file mode 100644
index 000000000..1eb766233
--- /dev/null
+++ b/src/spdk/lib/iscsi/iscsi_subsystem.c
@@ -0,0 +1,1577 @@
+/*-
+ * BSD LICENSE
+ *
+ * Copyright (C) 2008-2012 Daisuke Aoyama <aoyama@peach.ne.jp>.
+ * Copyright (c) Intel Corporation.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ * * Neither the name of Intel Corporation nor the names of its
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "spdk/stdinc.h"
+#include "spdk/env.h"
+#include "spdk/string.h"
+#include "spdk/sock.h"
+#include "spdk/likely.h"
+
+#include "iscsi/iscsi.h"
+#include "iscsi/init_grp.h"
+#include "iscsi/portal_grp.h"
+#include "iscsi/conn.h"
+#include "iscsi/task.h"
+#include "iscsi/tgt_node.h"
+
+#include "spdk_internal/event.h"
+#include "spdk_internal/log.h"
+
+struct spdk_iscsi_opts *g_spdk_iscsi_opts = NULL;
+
+static struct spdk_thread *g_init_thread = NULL;
+static spdk_iscsi_init_cb g_init_cb_fn = NULL;
+static void *g_init_cb_arg = NULL;
+
+static spdk_iscsi_fini_cb g_fini_cb_fn;
+static void *g_fini_cb_arg;
+
+#define ISCSI_CONFIG_TMPL \
+"[iSCSI]\n" \
+" # node name (not include optional part)\n" \
+" # Users can optionally change this to fit their environment.\n" \
+" NodeBase \"%s\"\n" \
+"\n" \
+" # files\n" \
+" %s %s\n" \
+"\n" \
+" # socket I/O timeout sec. (polling is infinity)\n" \
+" Timeout %d\n" \
+"\n" \
+" # authentication information for discovery session\n" \
+" DiscoveryAuthMethod %s\n" \
+" DiscoveryAuthGroup %s\n" \
+"\n" \
+" MaxSessions %d\n" \
+" MaxConnectionsPerSession %d\n" \
+" MaxConnections %d\n" \
+" MaxQueueDepth %d\n" \
+"\n" \
+" # iSCSI initial parameters negotiate with initiators\n" \
+" # NOTE: incorrect values might crash\n" \
+" DefaultTime2Wait %d\n" \
+" DefaultTime2Retain %d\n" \
+"\n" \
+" FirstBurstLength %d\n" \
+" ImmediateData %s\n" \
+" ErrorRecoveryLevel %d\n" \
+"\n"
+
+static void
+iscsi_globals_config_text(FILE *fp)
+{
+ const char *authmethod = "None";
+ char authgroup[32] = "None";
+
+ if (NULL == fp) {
+ return;
+ }
+
+ if (g_iscsi.require_chap) {
+ authmethod = "CHAP";
+ } else if (g_iscsi.mutual_chap) {
+ authmethod = "CHAP Mutual";
+ } else if (!g_iscsi.disable_chap) {
+ authmethod = "Auto";
+ }
+
+ if (g_iscsi.chap_group) {
+ snprintf(authgroup, sizeof(authgroup), "AuthGroup%d", g_iscsi.chap_group);
+ }
+
+ fprintf(fp, ISCSI_CONFIG_TMPL,
+ g_iscsi.nodebase,
+ g_iscsi.authfile ? "AuthFile" : "",
+ g_iscsi.authfile ? g_iscsi.authfile : "",
+ g_iscsi.timeout, authmethod, authgroup,
+ g_iscsi.MaxSessions, g_iscsi.MaxConnectionsPerSession,
+ g_iscsi.MaxConnections,
+ g_iscsi.MaxQueueDepth,
+ g_iscsi.DefaultTime2Wait, g_iscsi.DefaultTime2Retain,
+ g_iscsi.FirstBurstLength,
+ (g_iscsi.ImmediateData) ? "Yes" : "No",
+ g_iscsi.ErrorRecoveryLevel);
+}
+
+#define ISCSI_DATA_BUFFER_ALIGNMENT (0x1000)
+#define ISCSI_DATA_BUFFER_MASK (ISCSI_DATA_BUFFER_ALIGNMENT - 1)
+
+static void
+mobj_ctor(struct spdk_mempool *mp, __attribute__((unused)) void *arg,
+ void *_m, __attribute__((unused)) unsigned i)
+{
+ struct spdk_mobj *m = _m;
+
+ m->mp = mp;
+ m->buf = (uint8_t *)m + sizeof(struct spdk_mobj);
+ m->buf = (void *)((unsigned long)((uint8_t *)m->buf + ISCSI_DATA_BUFFER_ALIGNMENT) &
+ ~ISCSI_DATA_BUFFER_MASK);
+}
+
+#define NUM_PDU_PER_CONNECTION(iscsi) (2 * (iscsi->MaxQueueDepth + MAX_LARGE_DATAIN_PER_CONNECTION + 8))
+#define PDU_POOL_SIZE(iscsi) (iscsi->MaxConnections * NUM_PDU_PER_CONNECTION(iscsi))
+#define IMMEDIATE_DATA_POOL_SIZE(iscsi) (iscsi->MaxConnections * 128)
+#define DATA_OUT_POOL_SIZE(iscsi) (iscsi->MaxConnections * MAX_DATA_OUT_PER_CONNECTION)
+
+static int
+iscsi_initialize_pdu_pool(void)
+{
+ struct spdk_iscsi_globals *iscsi = &g_iscsi;
+ int imm_mobj_size = SPDK_BDEV_BUF_SIZE_WITH_MD(iscsi_get_max_immediate_data_size()) +
+ sizeof(struct spdk_mobj) + ISCSI_DATA_BUFFER_ALIGNMENT;
+ int dout_mobj_size = SPDK_BDEV_BUF_SIZE_WITH_MD(SPDK_ISCSI_MAX_RECV_DATA_SEGMENT_LENGTH) +
+ sizeof(struct spdk_mobj) + ISCSI_DATA_BUFFER_ALIGNMENT;
+
+ /* create PDU pool */
+ iscsi->pdu_pool = spdk_mempool_create("PDU_Pool",
+ PDU_POOL_SIZE(iscsi),
+ sizeof(struct spdk_iscsi_pdu),
+ 256, SPDK_ENV_SOCKET_ID_ANY);
+ if (!iscsi->pdu_pool) {
+ SPDK_ERRLOG("create PDU pool failed\n");
+ return -1;
+ }
+
+ iscsi->pdu_immediate_data_pool = spdk_mempool_create_ctor("PDU_immediate_data_Pool",
+ IMMEDIATE_DATA_POOL_SIZE(iscsi),
+ imm_mobj_size, 256,
+ SPDK_ENV_SOCKET_ID_ANY,
+ mobj_ctor, NULL);
+ if (!iscsi->pdu_immediate_data_pool) {
+ SPDK_ERRLOG("create PDU immediate data pool failed\n");
+ return -1;
+ }
+
+ iscsi->pdu_data_out_pool = spdk_mempool_create_ctor("PDU_data_out_Pool",
+ DATA_OUT_POOL_SIZE(iscsi),
+ dout_mobj_size, 256,
+ SPDK_ENV_SOCKET_ID_ANY,
+ mobj_ctor, NULL);
+ if (!iscsi->pdu_data_out_pool) {
+ SPDK_ERRLOG("create PDU data out pool failed\n");
+ return -1;
+ }
+
+ return 0;
+}
+
+static void
+iscsi_sess_ctor(struct spdk_mempool *pool, void *arg, void *session_buf,
+ unsigned index)
+{
+ struct spdk_iscsi_globals *iscsi = arg;
+ struct spdk_iscsi_sess *sess = session_buf;
+
+ iscsi->session[index] = sess;
+
+ /* tsih 0 is reserved, so start tsih values at 1. */
+ sess->tsih = index + 1;
+}
+
+#define DEFAULT_TASK_POOL_SIZE 32768
+
+static int
+iscsi_initialize_task_pool(void)
+{
+ struct spdk_iscsi_globals *iscsi = &g_iscsi;
+
+ /* create scsi_task pool */
+ iscsi->task_pool = spdk_mempool_create("SCSI_TASK_Pool",
+ DEFAULT_TASK_POOL_SIZE,
+ sizeof(struct spdk_iscsi_task),
+ 128, SPDK_ENV_SOCKET_ID_ANY);
+ if (!iscsi->task_pool) {
+ SPDK_ERRLOG("create task pool failed\n");
+ return -1;
+ }
+
+ return 0;
+}
+
+#define SESSION_POOL_SIZE(iscsi) (iscsi->MaxSessions)
+static int
+iscsi_initialize_session_pool(void)
+{
+ struct spdk_iscsi_globals *iscsi = &g_iscsi;
+
+ iscsi->session_pool = spdk_mempool_create_ctor("Session_Pool",
+ SESSION_POOL_SIZE(iscsi),
+ sizeof(struct spdk_iscsi_sess), 0,
+ SPDK_ENV_SOCKET_ID_ANY,
+ iscsi_sess_ctor, iscsi);
+ if (!iscsi->session_pool) {
+ SPDK_ERRLOG("create session pool failed\n");
+ return -1;
+ }
+
+ return 0;
+}
+
+static int
+iscsi_initialize_all_pools(void)
+{
+ if (iscsi_initialize_pdu_pool() != 0) {
+ return -1;
+ }
+
+ if (iscsi_initialize_session_pool() != 0) {
+ return -1;
+ }
+
+ if (iscsi_initialize_task_pool() != 0) {
+ return -1;
+ }
+
+ return 0;
+}
+
+static void
+iscsi_check_pool(struct spdk_mempool *pool, size_t count)
+{
+ if (pool && spdk_mempool_count(pool) != count) {
+ SPDK_ERRLOG("spdk_mempool_count(%s) == %zu, should be %zu\n",
+ spdk_mempool_get_name(pool), spdk_mempool_count(pool), count);
+ }
+}
+
+static void
+iscsi_check_pools(void)
+{
+ struct spdk_iscsi_globals *iscsi = &g_iscsi;
+
+ iscsi_check_pool(iscsi->pdu_pool, PDU_POOL_SIZE(iscsi));
+ iscsi_check_pool(iscsi->session_pool, SESSION_POOL_SIZE(iscsi));
+ iscsi_check_pool(iscsi->pdu_immediate_data_pool, IMMEDIATE_DATA_POOL_SIZE(iscsi));
+ iscsi_check_pool(iscsi->pdu_data_out_pool, DATA_OUT_POOL_SIZE(iscsi));
+ iscsi_check_pool(iscsi->task_pool, DEFAULT_TASK_POOL_SIZE);
+}
+
+static void
+iscsi_free_pools(void)
+{
+ struct spdk_iscsi_globals *iscsi = &g_iscsi;
+
+ spdk_mempool_free(iscsi->pdu_pool);
+ spdk_mempool_free(iscsi->session_pool);
+ spdk_mempool_free(iscsi->pdu_immediate_data_pool);
+ spdk_mempool_free(iscsi->pdu_data_out_pool);
+ spdk_mempool_free(iscsi->task_pool);
+}
+
+void iscsi_put_pdu(struct spdk_iscsi_pdu *pdu)
+{
+ if (!pdu) {
+ return;
+ }
+
+ assert(pdu->ref > 0);
+ pdu->ref--;
+
+ if (pdu->ref == 0) {
+ if (pdu->mobj) {
+ spdk_mempool_put(pdu->mobj->mp, (void *)pdu->mobj);
+ }
+
+ if (pdu->data && !pdu->data_from_mempool) {
+ free(pdu->data);
+ }
+
+ spdk_mempool_put(g_iscsi.pdu_pool, (void *)pdu);
+ }
+}
+
+struct spdk_iscsi_pdu *iscsi_get_pdu(struct spdk_iscsi_conn *conn)
+{
+ struct spdk_iscsi_pdu *pdu;
+
+ assert(conn != NULL);
+ pdu = spdk_mempool_get(g_iscsi.pdu_pool);
+ if (!pdu) {
+ SPDK_ERRLOG("Unable to get PDU\n");
+ abort();
+ }
+
+ /* we do not want to zero out the last part of the structure reserved for AHS and sense data */
+ memset(pdu, 0, offsetof(struct spdk_iscsi_pdu, ahs));
+ pdu->ref = 1;
+ pdu->conn = conn;
+
+ return pdu;
+}
+
+static void
+iscsi_log_globals(void)
+{
+ SPDK_DEBUGLOG(SPDK_LOG_ISCSI, "AuthFile %s\n",
+ g_iscsi.authfile ? g_iscsi.authfile : "(none)");
+ SPDK_DEBUGLOG(SPDK_LOG_ISCSI, "NodeBase %s\n", g_iscsi.nodebase);
+ SPDK_DEBUGLOG(SPDK_LOG_ISCSI, "MaxSessions %d\n", g_iscsi.MaxSessions);
+ SPDK_DEBUGLOG(SPDK_LOG_ISCSI, "MaxConnectionsPerSession %d\n",
+ g_iscsi.MaxConnectionsPerSession);
+ SPDK_DEBUGLOG(SPDK_LOG_ISCSI, "MaxQueueDepth %d\n", g_iscsi.MaxQueueDepth);
+ SPDK_DEBUGLOG(SPDK_LOG_ISCSI, "DefaultTime2Wait %d\n",
+ g_iscsi.DefaultTime2Wait);
+ SPDK_DEBUGLOG(SPDK_LOG_ISCSI, "DefaultTime2Retain %d\n",
+ g_iscsi.DefaultTime2Retain);
+ SPDK_DEBUGLOG(SPDK_LOG_ISCSI, "FirstBurstLength %d\n",
+ g_iscsi.FirstBurstLength);
+ SPDK_DEBUGLOG(SPDK_LOG_ISCSI, "ImmediateData %s\n",
+ g_iscsi.ImmediateData ? "Yes" : "No");
+ SPDK_DEBUGLOG(SPDK_LOG_ISCSI, "AllowDuplicateIsid %s\n",
+ g_iscsi.AllowDuplicateIsid ? "Yes" : "No");
+ SPDK_DEBUGLOG(SPDK_LOG_ISCSI, "ErrorRecoveryLevel %d\n",
+ g_iscsi.ErrorRecoveryLevel);
+ SPDK_DEBUGLOG(SPDK_LOG_ISCSI, "Timeout %d\n", g_iscsi.timeout);
+ SPDK_DEBUGLOG(SPDK_LOG_ISCSI, "NopInInterval %d\n",
+ g_iscsi.nopininterval);
+ if (g_iscsi.disable_chap) {
+ SPDK_DEBUGLOG(SPDK_LOG_ISCSI,
+ "DiscoveryAuthMethod None\n");
+ } else if (!g_iscsi.require_chap) {
+ SPDK_DEBUGLOG(SPDK_LOG_ISCSI,
+ "DiscoveryAuthMethod Auto\n");
+ } else {
+ SPDK_DEBUGLOG(SPDK_LOG_ISCSI,
+ "DiscoveryAuthMethod %s %s\n",
+ g_iscsi.require_chap ? "CHAP" : "",
+ g_iscsi.mutual_chap ? "Mutual" : "");
+ }
+
+ if (g_iscsi.chap_group == 0) {
+ SPDK_DEBUGLOG(SPDK_LOG_ISCSI,
+ "DiscoveryAuthGroup None\n");
+ } else {
+ SPDK_DEBUGLOG(SPDK_LOG_ISCSI,
+ "DiscoveryAuthGroup AuthGroup%d\n",
+ g_iscsi.chap_group);
+ }
+}
+
+static void
+iscsi_opts_init(struct spdk_iscsi_opts *opts)
+{
+ opts->MaxSessions = DEFAULT_MAX_SESSIONS;
+ opts->MaxConnectionsPerSession = DEFAULT_MAX_CONNECTIONS_PER_SESSION;
+ opts->MaxQueueDepth = DEFAULT_MAX_QUEUE_DEPTH;
+ opts->DefaultTime2Wait = DEFAULT_DEFAULTTIME2WAIT;
+ opts->DefaultTime2Retain = DEFAULT_DEFAULTTIME2RETAIN;
+ opts->FirstBurstLength = SPDK_ISCSI_FIRST_BURST_LENGTH;
+ opts->ImmediateData = DEFAULT_IMMEDIATEDATA;
+ opts->AllowDuplicateIsid = false;
+ opts->ErrorRecoveryLevel = DEFAULT_ERRORRECOVERYLEVEL;
+ opts->timeout = DEFAULT_TIMEOUT;
+ opts->nopininterval = DEFAULT_NOPININTERVAL;
+ opts->disable_chap = false;
+ opts->require_chap = false;
+ opts->mutual_chap = false;
+ opts->chap_group = 0;
+ opts->authfile = NULL;
+ opts->nodebase = NULL;
+}
+
+struct spdk_iscsi_opts *
+iscsi_opts_alloc(void)
+{
+ struct spdk_iscsi_opts *opts;
+
+ opts = calloc(1, sizeof(*opts));
+ if (!opts) {
+ SPDK_ERRLOG("calloc() failed for iscsi options\n");
+ return NULL;
+ }
+
+ iscsi_opts_init(opts);
+
+ return opts;
+}
+
+void
+iscsi_opts_free(struct spdk_iscsi_opts *opts)
+{
+ free(opts->authfile);
+ free(opts->nodebase);
+ free(opts);
+}
+
+/* Deep copy of spdk_iscsi_opts */
+struct spdk_iscsi_opts *
+iscsi_opts_copy(struct spdk_iscsi_opts *src)
+{
+ struct spdk_iscsi_opts *dst;
+
+ dst = calloc(1, sizeof(*dst));
+ if (!dst) {
+ SPDK_ERRLOG("calloc() failed for iscsi options\n");
+ return NULL;
+ }
+
+ if (src->authfile) {
+ dst->authfile = strdup(src->authfile);
+ if (!dst->authfile) {
+ free(dst);
+ SPDK_ERRLOG("failed to strdup for auth file %s\n", src->authfile);
+ return NULL;
+ }
+ }
+
+ if (src->nodebase) {
+ dst->nodebase = strdup(src->nodebase);
+ if (!dst->nodebase) {
+ free(dst->authfile);
+ free(dst);
+ SPDK_ERRLOG("failed to strdup for nodebase %s\n", src->nodebase);
+ return NULL;
+ }
+ }
+
+ dst->MaxSessions = src->MaxSessions;
+ dst->MaxConnectionsPerSession = src->MaxConnectionsPerSession;
+ dst->MaxQueueDepth = src->MaxQueueDepth;
+ dst->DefaultTime2Wait = src->DefaultTime2Wait;
+ dst->DefaultTime2Retain = src->DefaultTime2Retain;
+ dst->FirstBurstLength = src->FirstBurstLength;
+ dst->ImmediateData = src->ImmediateData;
+ dst->AllowDuplicateIsid = src->AllowDuplicateIsid;
+ dst->ErrorRecoveryLevel = src->ErrorRecoveryLevel;
+ dst->timeout = src->timeout;
+ dst->nopininterval = src->nopininterval;
+ dst->disable_chap = src->disable_chap;
+ dst->require_chap = src->require_chap;
+ dst->mutual_chap = src->mutual_chap;
+ dst->chap_group = src->chap_group;
+
+ return dst;
+}
+
+static int
+iscsi_read_config_file_params(struct spdk_conf_section *sp,
+ struct spdk_iscsi_opts *opts)
+{
+ const char *val;
+ int MaxSessions;
+ int MaxConnectionsPerSession;
+ int MaxQueueDepth;
+ int DefaultTime2Wait;
+ int DefaultTime2Retain;
+ int FirstBurstLength;
+ int ErrorRecoveryLevel;
+ int timeout;
+ int nopininterval;
+ const char *ag_tag;
+ int ag_tag_i;
+ int i;
+
+ val = spdk_conf_section_get_val(sp, "Comment");
+ if (val != NULL) {
+ SPDK_DEBUGLOG(SPDK_LOG_ISCSI, "Comment %s\n", val);
+ }
+
+ val = spdk_conf_section_get_val(sp, "AuthFile");
+ if (val != NULL) {
+ opts->authfile = strdup(val);
+ if (!opts->authfile) {
+ SPDK_ERRLOG("strdup() failed for AuthFile\n");
+ return -ENOMEM;
+ }
+ }
+
+ val = spdk_conf_section_get_val(sp, "NodeBase");
+ if (val != NULL) {
+ opts->nodebase = strdup(val);
+ if (!opts->nodebase) {
+ free(opts->authfile);
+ SPDK_ERRLOG("strdup() failed for NodeBase\n");
+ return -ENOMEM;
+ }
+ }
+
+ MaxSessions = spdk_conf_section_get_intval(sp, "MaxSessions");
+ if (MaxSessions >= 0) {
+ opts->MaxSessions = MaxSessions;
+ }
+
+ MaxConnectionsPerSession = spdk_conf_section_get_intval(sp, "MaxConnectionsPerSession");
+ if (MaxConnectionsPerSession >= 0) {
+ opts->MaxConnectionsPerSession = MaxConnectionsPerSession;
+ }
+
+ MaxQueueDepth = spdk_conf_section_get_intval(sp, "MaxQueueDepth");
+ if (MaxQueueDepth >= 0) {
+ opts->MaxQueueDepth = MaxQueueDepth;
+ }
+
+ DefaultTime2Wait = spdk_conf_section_get_intval(sp, "DefaultTime2Wait");
+ if (DefaultTime2Wait >= 0) {
+ opts->DefaultTime2Wait = DefaultTime2Wait;
+ }
+
+ DefaultTime2Retain = spdk_conf_section_get_intval(sp, "DefaultTime2Retain");
+ if (DefaultTime2Retain >= 0) {
+ opts->DefaultTime2Retain = DefaultTime2Retain;
+ }
+
+ FirstBurstLength = spdk_conf_section_get_intval(sp, "FirstBurstLength");
+ if (FirstBurstLength >= 0) {
+ opts->FirstBurstLength = FirstBurstLength;
+ }
+
+ opts->ImmediateData = spdk_conf_section_get_boolval(sp, "ImmediateData",
+ opts->ImmediateData);
+
+ /* This option is only for test.
+ * If AllowDuplicateIsid is enabled, it allows different connections carrying
+ * TSIH=0 login the target within the same session.
+ */
+ opts->AllowDuplicateIsid = spdk_conf_section_get_boolval(sp, "AllowDuplicateIsid",
+ opts->AllowDuplicateIsid);
+
+ ErrorRecoveryLevel = spdk_conf_section_get_intval(sp, "ErrorRecoveryLevel");
+ if (ErrorRecoveryLevel >= 0) {
+ opts->ErrorRecoveryLevel = ErrorRecoveryLevel;
+ }
+ timeout = spdk_conf_section_get_intval(sp, "Timeout");
+ if (timeout >= 0) {
+ opts->timeout = timeout;
+ }
+ nopininterval = spdk_conf_section_get_intval(sp, "NopInInterval");
+ if (nopininterval >= 0) {
+ opts->nopininterval = nopininterval;
+ }
+ val = spdk_conf_section_get_val(sp, "DiscoveryAuthMethod");
+ if (val != NULL) {
+ for (i = 0; ; i++) {
+ val = spdk_conf_section_get_nmval(sp, "DiscoveryAuthMethod", 0, i);
+ if (val == NULL) {
+ break;
+ }
+ if (strcasecmp(val, "CHAP") == 0) {
+ opts->require_chap = true;
+ } else if (strcasecmp(val, "Mutual") == 0) {
+ opts->require_chap = true;
+ opts->mutual_chap = true;
+ } else if (strcasecmp(val, "Auto") == 0) {
+ opts->disable_chap = false;
+ opts->require_chap = false;
+ opts->mutual_chap = false;
+ } else if (strcasecmp(val, "None") == 0) {
+ opts->disable_chap = true;
+ opts->require_chap = false;
+ opts->mutual_chap = false;
+ } else {
+ SPDK_ERRLOG("unknown CHAP mode %s\n", val);
+ }
+ }
+ if (opts->mutual_chap && !opts->require_chap) {
+ free(opts->authfile);
+ free(opts->nodebase);
+ SPDK_ERRLOG("CHAP must set to be required when using mutual CHAP.\n");
+ return -EINVAL;
+ }
+ }
+ val = spdk_conf_section_get_val(sp, "DiscoveryAuthGroup");
+ if (val != NULL) {
+ ag_tag = val;
+ if (strcasecmp(ag_tag, "None") == 0) {
+ opts->chap_group = 0;
+ } else {
+ if (strncasecmp(ag_tag, "AuthGroup",
+ strlen("AuthGroup")) != 0
+ || sscanf(ag_tag, "%*[^0-9]%d", &ag_tag_i) != 1
+ || ag_tag_i == 0) {
+ SPDK_ERRLOG("invalid auth group %s, ignoring\n", ag_tag);
+ } else {
+ opts->chap_group = ag_tag_i;
+ }
+ }
+ }
+
+ return 0;
+}
+
+static int
+iscsi_opts_verify(struct spdk_iscsi_opts *opts)
+{
+ if (!opts->nodebase) {
+ opts->nodebase = strdup(SPDK_ISCSI_DEFAULT_NODEBASE);
+ if (opts->nodebase == NULL) {
+ SPDK_ERRLOG("strdup() failed for default nodebase\n");
+ return -ENOMEM;
+ }
+ }
+
+ if (opts->MaxSessions == 0 || opts->MaxSessions > 65535) {
+ SPDK_ERRLOG("%d is invalid. MaxSessions must be more than 0 and no more than 65535\n",
+ opts->MaxSessions);
+ return -EINVAL;
+ }
+
+ if (opts->MaxConnectionsPerSession == 0 || opts->MaxConnectionsPerSession > 65535) {
+ SPDK_ERRLOG("%d is invalid. MaxConnectionsPerSession must be more than 0 and no more than 65535\n",
+ opts->MaxConnectionsPerSession);
+ return -EINVAL;
+ }
+
+ if (opts->MaxQueueDepth == 0 || opts->MaxQueueDepth > 256) {
+ SPDK_ERRLOG("%d is invalid. MaxQueueDepth must be more than 0 and no more than 256\n",
+ opts->MaxQueueDepth);
+ return -EINVAL;
+ }
+
+ if (opts->DefaultTime2Wait > 3600) {
+ SPDK_ERRLOG("%d is invalid. DefaultTime2Wait must be no more than 3600\n",
+ opts->DefaultTime2Wait);
+ return -EINVAL;
+ }
+
+ if (opts->DefaultTime2Retain > 3600) {
+ SPDK_ERRLOG("%d is invalid. DefaultTime2Retain must be no more than 3600\n",
+ opts->DefaultTime2Retain);
+ return -EINVAL;
+ }
+
+ if (opts->FirstBurstLength >= SPDK_ISCSI_MIN_FIRST_BURST_LENGTH) {
+ if (opts->FirstBurstLength > SPDK_ISCSI_MAX_BURST_LENGTH) {
+ SPDK_ERRLOG("FirstBurstLength %d shall not exceed MaxBurstLength %d\n",
+ opts->FirstBurstLength, SPDK_ISCSI_MAX_BURST_LENGTH);
+ return -EINVAL;
+ }
+ } else {
+ SPDK_ERRLOG("FirstBurstLength %d shall be no less than %d\n",
+ opts->FirstBurstLength, SPDK_ISCSI_MIN_FIRST_BURST_LENGTH);
+ return -EINVAL;
+ }
+
+ if (opts->ErrorRecoveryLevel > 2) {
+ SPDK_ERRLOG("ErrorRecoveryLevel %d is not supported.\n", opts->ErrorRecoveryLevel);
+ return -EINVAL;
+ }
+
+ if (opts->timeout < 0) {
+ SPDK_ERRLOG("%d is invalid. timeout must not be less than 0\n", opts->timeout);
+ return -EINVAL;
+ }
+
+ if (opts->nopininterval < 0 || opts->nopininterval > MAX_NOPININTERVAL) {
+ SPDK_ERRLOG("%d is invalid. nopinterval must be between 0 and %d\n",
+ opts->nopininterval, MAX_NOPININTERVAL);
+ return -EINVAL;
+ }
+
+ if (!iscsi_check_chap_params(opts->disable_chap, opts->require_chap,
+ opts->mutual_chap, opts->chap_group)) {
+ SPDK_ERRLOG("CHAP params in opts are illegal combination\n");
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int
+iscsi_parse_options(struct spdk_iscsi_opts **popts)
+{
+ struct spdk_iscsi_opts *opts;
+ struct spdk_conf_section *sp;
+ int rc;
+
+ opts = iscsi_opts_alloc();
+ if (!opts) {
+ SPDK_ERRLOG("iscsi_opts_alloc_failed() failed\n");
+ return -ENOMEM;
+ }
+
+ /* Process parameters */
+ SPDK_DEBUGLOG(SPDK_LOG_ISCSI, "iscsi_read_config_file_parmas\n");
+ sp = spdk_conf_find_section(NULL, "iSCSI");
+ if (sp != NULL) {
+ rc = iscsi_read_config_file_params(sp, opts);
+ if (rc != 0) {
+ free(opts);
+ SPDK_ERRLOG("iscsi_read_config_file_params() failed\n");
+ return rc;
+ }
+ }
+
+ *popts = opts;
+
+ return 0;
+}
+
+static int
+iscsi_set_global_params(struct spdk_iscsi_opts *opts)
+{
+ int rc;
+
+ rc = iscsi_opts_verify(opts);
+ if (rc != 0) {
+ SPDK_ERRLOG("spdk_iscsi_opts_verify() failed\n");
+ return rc;
+ }
+
+ if (opts->authfile != NULL) {
+ g_iscsi.authfile = strdup(opts->authfile);
+ if (!g_iscsi.authfile) {
+ SPDK_ERRLOG("failed to strdup for auth file %s\n", opts->authfile);
+ return -ENOMEM;
+ }
+ }
+
+ g_iscsi.nodebase = strdup(opts->nodebase);
+ if (!g_iscsi.nodebase) {
+ SPDK_ERRLOG("failed to strdup for nodebase %s\n", opts->nodebase);
+ return -ENOMEM;
+ }
+
+ g_iscsi.MaxSessions = opts->MaxSessions;
+ g_iscsi.MaxConnectionsPerSession = opts->MaxConnectionsPerSession;
+ g_iscsi.MaxQueueDepth = opts->MaxQueueDepth;
+ g_iscsi.DefaultTime2Wait = opts->DefaultTime2Wait;
+ g_iscsi.DefaultTime2Retain = opts->DefaultTime2Retain;
+ g_iscsi.FirstBurstLength = opts->FirstBurstLength;
+ g_iscsi.ImmediateData = opts->ImmediateData;
+ g_iscsi.AllowDuplicateIsid = opts->AllowDuplicateIsid;
+ g_iscsi.ErrorRecoveryLevel = opts->ErrorRecoveryLevel;
+ g_iscsi.timeout = opts->timeout;
+ g_iscsi.nopininterval = opts->nopininterval;
+ g_iscsi.disable_chap = opts->disable_chap;
+ g_iscsi.require_chap = opts->require_chap;
+ g_iscsi.mutual_chap = opts->mutual_chap;
+ g_iscsi.chap_group = opts->chap_group;
+
+ iscsi_log_globals();
+
+ return 0;
+}
+
+int
+iscsi_set_discovery_auth(bool disable_chap, bool require_chap, bool mutual_chap,
+ int32_t chap_group)
+{
+ if (!iscsi_check_chap_params(disable_chap, require_chap, mutual_chap,
+ chap_group)) {
+ SPDK_ERRLOG("CHAP params are illegal combination\n");
+ return -EINVAL;
+ }
+
+ pthread_mutex_lock(&g_iscsi.mutex);
+ g_iscsi.disable_chap = disable_chap;
+ g_iscsi.require_chap = require_chap;
+ g_iscsi.mutual_chap = mutual_chap;
+ g_iscsi.chap_group = chap_group;
+ pthread_mutex_unlock(&g_iscsi.mutex);
+
+ return 0;
+}
+
+int
+iscsi_auth_group_add_secret(struct spdk_iscsi_auth_group *group,
+ const char *user, const char *secret,
+ const char *muser, const char *msecret)
+{
+ struct spdk_iscsi_auth_secret *_secret;
+ size_t len;
+
+ if (user == NULL || secret == NULL) {
+ SPDK_ERRLOG("user and secret must be specified\n");
+ return -EINVAL;
+ }
+
+ if (muser != NULL && msecret == NULL) {
+ SPDK_ERRLOG("msecret must be specified with muser\n");
+ return -EINVAL;
+ }
+
+ TAILQ_FOREACH(_secret, &group->secret_head, tailq) {
+ if (strcmp(_secret->user, user) == 0) {
+ SPDK_ERRLOG("user for secret is duplicated\n");
+ return -EEXIST;
+ }
+ }
+
+ _secret = calloc(1, sizeof(*_secret));
+ if (_secret == NULL) {
+ SPDK_ERRLOG("calloc() failed for CHAP secret\n");
+ return -ENOMEM;
+ }
+
+ len = strnlen(user, sizeof(_secret->user));
+ if (len > sizeof(_secret->user) - 1) {
+ SPDK_ERRLOG("CHAP user longer than %zu characters: %s\n",
+ sizeof(_secret->user) - 1, user);
+ free(_secret);
+ return -EINVAL;
+ }
+ memcpy(_secret->user, user, len);
+
+ len = strnlen(secret, sizeof(_secret->secret));
+ if (len > sizeof(_secret->secret) - 1) {
+ SPDK_ERRLOG("CHAP secret longer than %zu characters: %s\n",
+ sizeof(_secret->secret) - 1, secret);
+ free(_secret);
+ return -EINVAL;
+ }
+ memcpy(_secret->secret, secret, len);
+
+ if (muser != NULL) {
+ len = strnlen(muser, sizeof(_secret->muser));
+ if (len > sizeof(_secret->muser) - 1) {
+ SPDK_ERRLOG("Mutual CHAP user longer than %zu characters: %s\n",
+ sizeof(_secret->muser) - 1, muser);
+ free(_secret);
+ return -EINVAL;
+ }
+ memcpy(_secret->muser, muser, len);
+
+ len = strnlen(msecret, sizeof(_secret->msecret));
+ if (len > sizeof(_secret->msecret) - 1) {
+ SPDK_ERRLOG("Mutual CHAP secret longer than %zu characters: %s\n",
+ sizeof(_secret->msecret) - 1, msecret);
+ free(_secret);
+ return -EINVAL;
+ }
+ memcpy(_secret->msecret, msecret, len);
+ }
+
+ TAILQ_INSERT_TAIL(&group->secret_head, _secret, tailq);
+ return 0;
+}
+
+int
+iscsi_auth_group_delete_secret(struct spdk_iscsi_auth_group *group,
+ const char *user)
+{
+ struct spdk_iscsi_auth_secret *_secret;
+
+ if (user == NULL) {
+ SPDK_ERRLOG("user must be specified\n");
+ return -EINVAL;
+ }
+
+ TAILQ_FOREACH(_secret, &group->secret_head, tailq) {
+ if (strcmp(_secret->user, user) == 0) {
+ break;
+ }
+ }
+
+ if (_secret == NULL) {
+ SPDK_ERRLOG("secret is not found\n");
+ return -ENODEV;
+ }
+
+ TAILQ_REMOVE(&group->secret_head, _secret, tailq);
+ free(_secret);
+
+ return 0;
+}
+
+int
+iscsi_add_auth_group(int32_t tag, struct spdk_iscsi_auth_group **_group)
+{
+ struct spdk_iscsi_auth_group *group;
+
+ TAILQ_FOREACH(group, &g_iscsi.auth_group_head, tailq) {
+ if (group->tag == tag) {
+ SPDK_ERRLOG("Auth group (%d) already exists\n", tag);
+ return -EEXIST;
+ }
+ }
+
+ group = calloc(1, sizeof(*group));
+ if (group == NULL) {
+ SPDK_ERRLOG("calloc() failed for auth group\n");
+ return -ENOMEM;
+ }
+
+ TAILQ_INIT(&group->secret_head);
+ group->tag = tag;
+
+ TAILQ_INSERT_TAIL(&g_iscsi.auth_group_head, group, tailq);
+
+ *_group = group;
+ return 0;
+}
+
+void
+iscsi_delete_auth_group(struct spdk_iscsi_auth_group *group)
+{
+ struct spdk_iscsi_auth_secret *_secret, *tmp;
+
+ TAILQ_REMOVE(&g_iscsi.auth_group_head, group, tailq);
+
+ TAILQ_FOREACH_SAFE(_secret, &group->secret_head, tailq, tmp) {
+ TAILQ_REMOVE(&group->secret_head, _secret, tailq);
+ free(_secret);
+ }
+ free(group);
+}
+
+struct spdk_iscsi_auth_group *
+iscsi_find_auth_group_by_tag(int32_t tag)
+{
+ struct spdk_iscsi_auth_group *group;
+
+ TAILQ_FOREACH(group, &g_iscsi.auth_group_head, tailq) {
+ if (group->tag == tag) {
+ return group;
+ }
+ }
+
+ return NULL;
+}
+
+static void
+iscsi_auth_groups_destroy(void)
+{
+ struct spdk_iscsi_auth_group *group, *tmp;
+
+ TAILQ_FOREACH_SAFE(group, &g_iscsi.auth_group_head, tailq, tmp) {
+ iscsi_delete_auth_group(group);
+ }
+}
+
+static int
+iscsi_parse_auth_group(struct spdk_conf_section *sp)
+{
+ int rc;
+ int i;
+ int tag;
+ const char *val, *user, *secret, *muser, *msecret;
+ struct spdk_iscsi_auth_group *group = NULL;
+
+ val = spdk_conf_section_get_val(sp, "Comment");
+ if (val != NULL) {
+ SPDK_DEBUGLOG(SPDK_LOG_ISCSI, "Comment %s\n", val);
+ }
+
+ tag = spdk_conf_section_get_num(sp);
+
+ rc = iscsi_add_auth_group(tag, &group);
+ if (rc != 0) {
+ SPDK_ERRLOG("Failed to add auth group\n");
+ return rc;
+ }
+
+ for (i = 0; ; i++) {
+ val = spdk_conf_section_get_nval(sp, "Auth", i);
+ if (val == NULL) {
+ break;
+ }
+
+ user = spdk_conf_section_get_nmval(sp, "Auth", i, 0);
+ secret = spdk_conf_section_get_nmval(sp, "Auth", i, 1);
+ muser = spdk_conf_section_get_nmval(sp, "Auth", i, 2);
+ msecret = spdk_conf_section_get_nmval(sp, "Auth", i, 3);
+
+ rc = iscsi_auth_group_add_secret(group, user, secret, muser, msecret);
+ if (rc != 0) {
+ SPDK_ERRLOG("Failed to add secret to auth group\n");
+ iscsi_delete_auth_group(group);
+ return rc;
+ }
+ }
+
+ return 0;
+}
+
+static int
+iscsi_parse_auth_info(void)
+{
+ struct spdk_conf *config;
+ struct spdk_conf_section *sp;
+ int rc;
+
+ config = spdk_conf_allocate();
+ if (!config) {
+ SPDK_ERRLOG("Failed to allocate config file\n");
+ return -ENOMEM;
+ }
+
+ rc = spdk_conf_read(config, g_iscsi.authfile);
+ if (rc != 0) {
+ SPDK_INFOLOG(SPDK_LOG_ISCSI, "Failed to load auth file\n");
+ spdk_conf_free(config);
+ return rc;
+ }
+
+ sp = spdk_conf_first_section(config);
+ while (sp != NULL) {
+ if (spdk_conf_section_match_prefix(sp, "AuthGroup")) {
+ if (spdk_conf_section_get_num(sp) == 0) {
+ SPDK_ERRLOG("Group 0 is invalid\n");
+ iscsi_auth_groups_destroy();
+ spdk_conf_free(config);
+ return -EINVAL;
+ }
+
+ rc = iscsi_parse_auth_group(sp);
+ if (rc != 0) {
+ SPDK_ERRLOG("parse_auth_group() failed\n");
+ iscsi_auth_groups_destroy();
+ spdk_conf_free(config);
+ return rc;
+ }
+ }
+ sp = spdk_conf_next_section(sp);
+ }
+
+ spdk_conf_free(config);
+ return 0;
+}
+
+static struct spdk_iscsi_auth_secret *
+iscsi_find_auth_secret(const char *authuser, int ag_tag)
+{
+ struct spdk_iscsi_auth_group *group;
+ struct spdk_iscsi_auth_secret *_secret;
+
+ TAILQ_FOREACH(group, &g_iscsi.auth_group_head, tailq) {
+ if (group->tag == ag_tag) {
+ TAILQ_FOREACH(_secret, &group->secret_head, tailq) {
+ if (strcmp(_secret->user, authuser) == 0) {
+ return _secret;
+ }
+ }
+ }
+ }
+
+ return NULL;
+}
+
+int
+iscsi_chap_get_authinfo(struct iscsi_chap_auth *auth, const char *authuser,
+ int ag_tag)
+{
+ struct spdk_iscsi_auth_secret *_secret;
+
+ if (authuser == NULL) {
+ return -EINVAL;
+ }
+
+ if (auth->user[0] != '\0') {
+ memset(auth->user, 0, sizeof(auth->user));
+ memset(auth->secret, 0, sizeof(auth->secret));
+ memset(auth->muser, 0, sizeof(auth->muser));
+ memset(auth->msecret, 0, sizeof(auth->msecret));
+ }
+
+ pthread_mutex_lock(&g_iscsi.mutex);
+
+ _secret = iscsi_find_auth_secret(authuser, ag_tag);
+ if (_secret == NULL) {
+ pthread_mutex_unlock(&g_iscsi.mutex);
+
+ SPDK_ERRLOG("CHAP secret is not found: user:%s, tag:%d\n",
+ authuser, ag_tag);
+ return -ENOENT;
+ }
+
+ memcpy(auth->user, _secret->user, sizeof(auth->user));
+ memcpy(auth->secret, _secret->secret, sizeof(auth->secret));
+
+ if (_secret->muser[0] != '\0') {
+ memcpy(auth->muser, _secret->muser, sizeof(auth->muser));
+ memcpy(auth->msecret, _secret->msecret, sizeof(auth->msecret));
+ }
+
+ pthread_mutex_unlock(&g_iscsi.mutex);
+ return 0;
+}
+
+static int
+iscsi_initialize_global_params(void)
+{
+ int rc;
+
+ if (!g_spdk_iscsi_opts) {
+ rc = iscsi_parse_options(&g_spdk_iscsi_opts);
+ if (rc != 0) {
+ SPDK_ERRLOG("iscsi_parse_options() failed\n");
+ return rc;
+ }
+ }
+
+ rc = iscsi_set_global_params(g_spdk_iscsi_opts);
+ if (rc != 0) {
+ SPDK_ERRLOG("iscsi_set_global_params() failed\n");
+ }
+
+ iscsi_opts_free(g_spdk_iscsi_opts);
+ g_spdk_iscsi_opts = NULL;
+
+ return rc;
+}
+
+static void
+iscsi_init_complete(int rc)
+{
+ spdk_iscsi_init_cb cb_fn = g_init_cb_fn;
+ void *cb_arg = g_init_cb_arg;
+
+ g_init_cb_fn = NULL;
+ g_init_cb_arg = NULL;
+
+ cb_fn(cb_arg, rc);
+}
+
+static void
+iscsi_parse_configuration(void)
+{
+ int rc;
+
+ rc = iscsi_parse_portal_grps();
+ if (rc < 0) {
+ SPDK_ERRLOG("iscsi_parse_portal_grps() failed\n");
+ goto end;
+ }
+
+ rc = iscsi_parse_init_grps();
+ if (rc < 0) {
+ SPDK_ERRLOG("iscsi_parse_init_grps() failed\n");
+ goto end;
+ }
+
+ rc = iscsi_parse_tgt_nodes();
+ if (rc < 0) {
+ SPDK_ERRLOG("iscsi_parse_tgt_nodes() failed\n");
+ }
+
+ if (g_iscsi.authfile != NULL) {
+ if (access(g_iscsi.authfile, R_OK) == 0) {
+ rc = iscsi_parse_auth_info();
+ if (rc < 0) {
+ SPDK_ERRLOG("iscsi_parse_auth_info() failed\n");
+ }
+ } else {
+ SPDK_INFOLOG(SPDK_LOG_ISCSI, "CHAP secret file is not found in the path %s\n",
+ g_iscsi.authfile);
+ }
+ }
+
+end:
+ iscsi_init_complete(rc);
+}
+
+static int
+iscsi_poll_group_poll(void *ctx)
+{
+ struct spdk_iscsi_poll_group *group = ctx;
+ struct spdk_iscsi_conn *conn, *tmp;
+ int rc;
+
+ if (spdk_unlikely(STAILQ_EMPTY(&group->connections))) {
+ return SPDK_POLLER_IDLE;
+ }
+
+ rc = spdk_sock_group_poll(group->sock_group);
+ if (rc < 0) {
+ SPDK_ERRLOG("Failed to poll sock_group=%p\n", group->sock_group);
+ }
+
+ STAILQ_FOREACH_SAFE(conn, &group->connections, pg_link, tmp) {
+ if (conn->state == ISCSI_CONN_STATE_EXITING) {
+ iscsi_conn_destruct(conn);
+ }
+ }
+
+ return rc != 0 ? SPDK_POLLER_BUSY : SPDK_POLLER_IDLE;
+}
+
+static int
+iscsi_poll_group_handle_nop(void *ctx)
+{
+ struct spdk_iscsi_poll_group *group = ctx;
+ struct spdk_iscsi_conn *conn, *tmp;
+
+ STAILQ_FOREACH_SAFE(conn, &group->connections, pg_link, tmp) {
+ iscsi_conn_handle_nop(conn);
+ }
+
+ return SPDK_POLLER_BUSY;
+}
+
+static int
+iscsi_poll_group_create(void *io_device, void *ctx_buf)
+{
+ struct spdk_iscsi_poll_group *pg = ctx_buf;
+
+ STAILQ_INIT(&pg->connections);
+ pg->sock_group = spdk_sock_group_create(NULL);
+ assert(pg->sock_group != NULL);
+
+ pg->poller = SPDK_POLLER_REGISTER(iscsi_poll_group_poll, pg, 0);
+ /* set the period to 1 sec */
+ pg->nop_poller = SPDK_POLLER_REGISTER(iscsi_poll_group_handle_nop, pg, 1000000);
+
+ return 0;
+}
+
+static void
+iscsi_poll_group_destroy(void *io_device, void *ctx_buf)
+{
+ struct spdk_iscsi_poll_group *pg = ctx_buf;
+ struct spdk_io_channel *ch;
+ struct spdk_thread *thread;
+
+ assert(pg->poller != NULL);
+ assert(pg->sock_group != NULL);
+
+ spdk_sock_group_close(&pg->sock_group);
+ spdk_poller_unregister(&pg->poller);
+ spdk_poller_unregister(&pg->nop_poller);
+
+ ch = spdk_io_channel_from_ctx(pg);
+ thread = spdk_io_channel_get_thread(ch);
+
+ assert(thread == spdk_get_thread());
+
+ spdk_thread_exit(thread);
+}
+
+static void
+_iscsi_init_thread_done(void *ctx)
+{
+ struct spdk_iscsi_poll_group *pg = ctx;
+
+ TAILQ_INSERT_TAIL(&g_iscsi.poll_group_head, pg, link);
+ if (--g_iscsi.refcnt == 0) {
+ iscsi_parse_configuration();
+ }
+}
+
+static void
+_iscsi_init_thread(void *ctx)
+{
+ struct spdk_io_channel *ch;
+ struct spdk_iscsi_poll_group *pg;
+
+ ch = spdk_get_io_channel(&g_iscsi);
+ pg = spdk_io_channel_get_ctx(ch);
+
+ spdk_thread_send_msg(g_init_thread, _iscsi_init_thread_done, pg);
+}
+
+static void
+initialize_iscsi_poll_group(void)
+{
+ struct spdk_cpuset tmp_cpumask = {};
+ uint32_t i;
+ char thread_name[32];
+ struct spdk_thread *thread;
+
+ spdk_io_device_register(&g_iscsi, iscsi_poll_group_create, iscsi_poll_group_destroy,
+ sizeof(struct spdk_iscsi_poll_group), "iscsi_tgt");
+
+ /* Create threads for CPU cores active for this application, and send a
+ * message to each thread to create a poll group on it.
+ */
+ g_init_thread = spdk_get_thread();
+ assert(g_init_thread != NULL);
+ assert(g_iscsi.refcnt == 0);
+
+ SPDK_ENV_FOREACH_CORE(i) {
+ spdk_cpuset_zero(&tmp_cpumask);
+ spdk_cpuset_set_cpu(&tmp_cpumask, i, true);
+ snprintf(thread_name, sizeof(thread_name), "iscsi_poll_group_%u", i);
+
+ thread = spdk_thread_create(thread_name, &tmp_cpumask);
+ assert(thread != NULL);
+
+ g_iscsi.refcnt++;
+ spdk_thread_send_msg(thread, _iscsi_init_thread, NULL);
+ }
+}
+
+static int
+iscsi_parse_globals(void)
+{
+ int rc;
+
+ rc = iscsi_initialize_global_params();
+ if (rc != 0) {
+ SPDK_ERRLOG("iscsi_initialize_iscsi_global_params() failed\n");
+ return rc;
+ }
+
+ g_iscsi.session = calloc(1, sizeof(struct spdk_iscsi_sess *) * g_iscsi.MaxSessions);
+ if (!g_iscsi.session) {
+ SPDK_ERRLOG("calloc() failed for session array\n");
+ return -1;
+ }
+
+ /*
+ * For now, just support same number of total connections, rather
+ * than MaxSessions * MaxConnectionsPerSession. After we add better
+ * handling for low resource conditions from our various buffer
+ * pools, we can bump this up to support more connections.
+ */
+ g_iscsi.MaxConnections = g_iscsi.MaxSessions;
+
+ rc = iscsi_initialize_all_pools();
+ if (rc != 0) {
+ SPDK_ERRLOG("initialize_all_pools() failed\n");
+ free(g_iscsi.session);
+ g_iscsi.session = NULL;
+ return -1;
+ }
+
+ rc = initialize_iscsi_conns();
+ if (rc < 0) {
+ SPDK_ERRLOG("initialize_iscsi_conns() failed\n");
+ free(g_iscsi.session);
+ g_iscsi.session = NULL;
+ return rc;
+ }
+
+ initialize_iscsi_poll_group();
+ return 0;
+}
+
+void
+spdk_iscsi_init(spdk_iscsi_init_cb cb_fn, void *cb_arg)
+{
+ int rc;
+
+ assert(cb_fn != NULL);
+ g_init_cb_fn = cb_fn;
+ g_init_cb_arg = cb_arg;
+
+ rc = iscsi_parse_globals();
+ if (rc < 0) {
+ SPDK_ERRLOG("iscsi_parse_globals() failed\n");
+ iscsi_init_complete(-1);
+ }
+
+ /*
+ * iscsi_parse_configuration() will be called as the callback to
+ * spdk_initialize_iscsi_poll_group() and will complete iSCSI
+ * subsystem initialization.
+ */
+}
+
+void
+spdk_iscsi_fini(spdk_iscsi_fini_cb cb_fn, void *cb_arg)
+{
+ g_fini_cb_fn = cb_fn;
+ g_fini_cb_arg = cb_arg;
+
+ iscsi_portal_grp_close_all();
+ shutdown_iscsi_conns();
+}
+
+static void
+iscsi_fini_done(void *io_device)
+{
+ free(g_iscsi.authfile);
+ free(g_iscsi.nodebase);
+
+ pthread_mutex_destroy(&g_iscsi.mutex);
+ g_fini_cb_fn(g_fini_cb_arg);
+}
+
+static void
+_iscsi_fini_dev_unreg(struct spdk_io_channel_iter *i, int status)
+{
+ iscsi_check_pools();
+ iscsi_free_pools();
+ free(g_iscsi.session);
+
+ assert(TAILQ_EMPTY(&g_iscsi.poll_group_head));
+
+ iscsi_shutdown_tgt_nodes();
+ iscsi_init_grps_destroy();
+ iscsi_portal_grps_destroy();
+ iscsi_auth_groups_destroy();
+
+ spdk_io_device_unregister(&g_iscsi, iscsi_fini_done);
+}
+
+static void
+_iscsi_fini_thread(struct spdk_io_channel_iter *i)
+{
+ struct spdk_io_channel *ch;
+ struct spdk_iscsi_poll_group *pg;
+
+ ch = spdk_io_channel_iter_get_channel(i);
+ pg = spdk_io_channel_get_ctx(ch);
+
+ pthread_mutex_lock(&g_iscsi.mutex);
+ TAILQ_REMOVE(&g_iscsi.poll_group_head, pg, link);
+ pthread_mutex_unlock(&g_iscsi.mutex);
+
+ spdk_put_io_channel(ch);
+
+ spdk_for_each_channel_continue(i, 0);
+}
+
+void
+shutdown_iscsi_conns_done(void)
+{
+ spdk_for_each_channel(&g_iscsi, _iscsi_fini_thread, NULL, _iscsi_fini_dev_unreg);
+}
+
+void
+spdk_iscsi_config_text(FILE *fp)
+{
+ iscsi_globals_config_text(fp);
+ iscsi_portal_grps_config_text(fp);
+ iscsi_init_grps_config_text(fp);
+ iscsi_tgt_nodes_config_text(fp);
+}
+
+void
+iscsi_opts_info_json(struct spdk_json_write_ctx *w)
+{
+ spdk_json_write_object_begin(w);
+
+ if (g_iscsi.authfile != NULL) {
+ spdk_json_write_named_string(w, "auth_file", g_iscsi.authfile);
+ }
+ spdk_json_write_named_string(w, "node_base", g_iscsi.nodebase);
+
+ spdk_json_write_named_uint32(w, "max_sessions", g_iscsi.MaxSessions);
+ spdk_json_write_named_uint32(w, "max_connections_per_session",
+ g_iscsi.MaxConnectionsPerSession);
+
+ spdk_json_write_named_uint32(w, "max_queue_depth", g_iscsi.MaxQueueDepth);
+
+ spdk_json_write_named_uint32(w, "default_time2wait", g_iscsi.DefaultTime2Wait);
+ spdk_json_write_named_uint32(w, "default_time2retain", g_iscsi.DefaultTime2Retain);
+
+ spdk_json_write_named_uint32(w, "first_burst_length", g_iscsi.FirstBurstLength);
+
+ spdk_json_write_named_bool(w, "immediate_data", g_iscsi.ImmediateData);
+
+ spdk_json_write_named_bool(w, "allow_duplicated_isid", g_iscsi.AllowDuplicateIsid);
+
+ spdk_json_write_named_uint32(w, "error_recovery_level", g_iscsi.ErrorRecoveryLevel);
+
+ spdk_json_write_named_int32(w, "nop_timeout", g_iscsi.timeout);
+ spdk_json_write_named_int32(w, "nop_in_interval", g_iscsi.nopininterval);
+
+ spdk_json_write_named_bool(w, "disable_chap", g_iscsi.disable_chap);
+ spdk_json_write_named_bool(w, "require_chap", g_iscsi.require_chap);
+ spdk_json_write_named_bool(w, "mutual_chap", g_iscsi.mutual_chap);
+ spdk_json_write_named_int32(w, "chap_group", g_iscsi.chap_group);
+
+ spdk_json_write_object_end(w);
+}
+
+static void
+iscsi_auth_group_info_json(struct spdk_iscsi_auth_group *group,
+ struct spdk_json_write_ctx *w)
+{
+ struct spdk_iscsi_auth_secret *_secret;
+
+ spdk_json_write_object_begin(w);
+
+ spdk_json_write_named_int32(w, "tag", group->tag);
+
+ spdk_json_write_named_array_begin(w, "secrets");
+ TAILQ_FOREACH(_secret, &group->secret_head, tailq) {
+ spdk_json_write_object_begin(w);
+
+ spdk_json_write_named_string(w, "user", _secret->user);
+ spdk_json_write_named_string(w, "secret", _secret->secret);
+
+ if (_secret->muser[0] != '\0') {
+ spdk_json_write_named_string(w, "muser", _secret->muser);
+ spdk_json_write_named_string(w, "msecret", _secret->msecret);
+ }
+
+ spdk_json_write_object_end(w);
+ }
+ spdk_json_write_array_end(w);
+
+ spdk_json_write_object_end(w);
+}
+
+static void
+iscsi_auth_group_config_json(struct spdk_iscsi_auth_group *group,
+ struct spdk_json_write_ctx *w)
+{
+ spdk_json_write_object_begin(w);
+
+ spdk_json_write_named_string(w, "method", "iscsi_create_auth_group");
+
+ spdk_json_write_name(w, "params");
+ iscsi_auth_group_info_json(group, w);
+
+ spdk_json_write_object_end(w);
+}
+
+void
+iscsi_auth_groups_info_json(struct spdk_json_write_ctx *w)
+{
+ struct spdk_iscsi_auth_group *group;
+
+ TAILQ_FOREACH(group, &g_iscsi.auth_group_head, tailq) {
+ iscsi_auth_group_info_json(group, w);
+ }
+}
+
+static void
+iscsi_auth_groups_config_json(struct spdk_json_write_ctx *w)
+{
+ struct spdk_iscsi_auth_group *group;
+
+ TAILQ_FOREACH(group, &g_iscsi.auth_group_head, tailq) {
+ iscsi_auth_group_config_json(group, w);
+ }
+}
+
+static void
+iscsi_opts_config_json(struct spdk_json_write_ctx *w)
+{
+ spdk_json_write_object_begin(w);
+
+ spdk_json_write_named_string(w, "method", "iscsi_set_options");
+
+ spdk_json_write_name(w, "params");
+ iscsi_opts_info_json(w);
+
+ spdk_json_write_object_end(w);
+}
+
+void
+spdk_iscsi_config_json(struct spdk_json_write_ctx *w)
+{
+ spdk_json_write_array_begin(w);
+ iscsi_opts_config_json(w);
+ iscsi_portal_grps_config_json(w);
+ iscsi_init_grps_config_json(w);
+ iscsi_tgt_nodes_config_json(w);
+ iscsi_auth_groups_config_json(w);
+ spdk_json_write_array_end(w);
+}
+
+SPDK_LOG_REGISTER_COMPONENT("iscsi", SPDK_LOG_ISCSI)
diff --git a/src/spdk/lib/iscsi/md5.c b/src/spdk/lib/iscsi/md5.c
new file mode 100644
index 000000000..c316ac354
--- /dev/null
+++ b/src/spdk/lib/iscsi/md5.c
@@ -0,0 +1,75 @@
+/*-
+ * BSD LICENSE
+ *
+ * Copyright (C) 2008-2012 Daisuke Aoyama <aoyama@peach.ne.jp>.
+ * Copyright (c) Intel Corporation.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ * * Neither the name of Intel Corporation nor the names of its
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "spdk/stdinc.h"
+
+#include <openssl/md5.h>
+
+#include "iscsi/md5.h"
+
+int md5init(struct spdk_md5ctx *md5ctx)
+{
+ int rc;
+
+ if (md5ctx == NULL) {
+ return -1;
+ }
+ rc = MD5_Init(&md5ctx->md5ctx);
+ return rc;
+}
+
+int md5final(void *md5, struct spdk_md5ctx *md5ctx)
+{
+ int rc;
+
+ if (md5ctx == NULL || md5 == NULL) {
+ return -1;
+ }
+ rc = MD5_Final(md5, &md5ctx->md5ctx);
+ return rc;
+}
+
+int md5update(struct spdk_md5ctx *md5ctx, const void *data, size_t len)
+{
+ int rc;
+
+ if (md5ctx == NULL) {
+ return -1;
+ }
+ if (data == NULL || len == 0) {
+ return 0;
+ }
+ rc = MD5_Update(&md5ctx->md5ctx, data, len);
+ return rc;
+}
diff --git a/src/spdk/lib/iscsi/md5.h b/src/spdk/lib/iscsi/md5.h
new file mode 100644
index 000000000..d6fc4c1ff
--- /dev/null
+++ b/src/spdk/lib/iscsi/md5.h
@@ -0,0 +1,52 @@
+/*-
+ * BSD LICENSE
+ *
+ * Copyright (C) 2008-2012 Daisuke Aoyama <aoyama@peach.ne.jp>.
+ * Copyright (c) Intel Corporation.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ * * Neither the name of Intel Corporation nor the names of its
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef SPDK_MD5_H
+#define SPDK_MD5_H
+
+#include "spdk/stdinc.h"
+
+#include <openssl/md5.h>
+
+#define SPDK_MD5DIGEST_LEN MD5_DIGEST_LENGTH
+
+struct spdk_md5ctx {
+ MD5_CTX md5ctx;
+};
+
+int md5init(struct spdk_md5ctx *md5ctx);
+int md5final(void *md5, struct spdk_md5ctx *md5ctx);
+int md5update(struct spdk_md5ctx *md5ctx, const void *data, size_t len);
+
+#endif /* SPDK_MD5_H */
diff --git a/src/spdk/lib/iscsi/param.c b/src/spdk/lib/iscsi/param.c
new file mode 100644
index 000000000..18f579359
--- /dev/null
+++ b/src/spdk/lib/iscsi/param.c
@@ -0,0 +1,1216 @@
+/*-
+ * BSD LICENSE
+ *
+ * Copyright (C) 2008-2012 Daisuke Aoyama <aoyama@peach.ne.jp>.
+ * Copyright (c) Intel Corporation.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ * * Neither the name of Intel Corporation nor the names of its
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "spdk/stdinc.h"
+
+#include "spdk/string.h"
+#include "iscsi/iscsi.h"
+#include "iscsi/param.h"
+#include "iscsi/conn.h"
+#include "spdk/string.h"
+
+#include "spdk_internal/log.h"
+
+#define MAX_TMPBUF 1024
+
+/* whose value may be bigger than 255 */
+static const char *non_simple_value_params[] = {
+ "CHAP_C",
+ "CHAP_R",
+ NULL,
+};
+
+void
+iscsi_param_free(struct iscsi_param *params)
+{
+ struct iscsi_param *param, *next_param;
+
+ if (params == NULL) {
+ return;
+ }
+ for (param = params; param != NULL; param = next_param) {
+ next_param = param->next;
+ if (param->list) {
+ free(param->list);
+ }
+ free(param->val);
+ free(param->key);
+ free(param);
+ }
+}
+
+static int
+iscsi_find_key_in_array(const char *key, const char *array[])
+{
+ int i;
+
+ for (i = 0; array[i] != NULL; i++) {
+ if (strcasecmp(key, array[i]) == 0) {
+ return 1;
+ }
+ }
+ return 0;
+}
+
+struct iscsi_param *
+iscsi_param_find(struct iscsi_param *params, const char *key)
+{
+ struct iscsi_param *param;
+
+ if (params == NULL || key == NULL) {
+ return NULL;
+ }
+ for (param = params; param != NULL; param = param->next) {
+ if (param->key != NULL && param->key[0] == key[0]
+ && strcasecmp(param->key, key) == 0) {
+ return param;
+ }
+ }
+ return NULL;
+}
+
+int
+iscsi_param_del(struct iscsi_param **params, const char *key)
+{
+ struct iscsi_param *param, *prev_param = NULL;
+
+ SPDK_DEBUGLOG(SPDK_LOG_ISCSI, "del %s\n", key);
+ if (params == NULL || key == NULL) {
+ return 0;
+ }
+ for (param = *params; param != NULL; param = param->next) {
+ if (param->key != NULL && param->key[0] == key[0]
+ && strcasecmp(param->key, key) == 0) {
+ if (prev_param != NULL) {
+ prev_param->next = param->next;
+ } else {
+ *params = param->next;
+ }
+ param->next = NULL;
+ iscsi_param_free(param);
+ return 0;
+ }
+ prev_param = param;
+ }
+ return -1;
+}
+
+int
+iscsi_param_add(struct iscsi_param **params, const char *key,
+ const char *val, const char *list, int type)
+{
+ struct iscsi_param *param, *last_param;
+
+ SPDK_DEBUGLOG(SPDK_LOG_ISCSI, "add %s=%s, list=[%s], type=%d\n",
+ key, val, list, type);
+ if (key == NULL) {
+ return -1;
+ }
+
+ param = iscsi_param_find(*params, key);
+ if (param != NULL) {
+ iscsi_param_del(params, key);
+ }
+
+ param = calloc(1, sizeof(*param));
+ if (!param) {
+ SPDK_ERRLOG("calloc() failed for parameter\n");
+ return -ENOMEM;
+ }
+
+ param->next = NULL;
+ param->key = xstrdup(key);
+ param->val = xstrdup(val);
+ param->list = xstrdup(list);
+ param->type = type;
+
+ last_param = *params;
+ if (last_param != NULL) {
+ while (last_param->next != NULL) {
+ last_param = last_param->next;
+ }
+ last_param->next = param;
+ } else {
+ *params = param;
+ }
+
+ return 0;
+}
+
+int
+iscsi_param_set(struct iscsi_param *params, const char *key,
+ const char *val)
+{
+ struct iscsi_param *param;
+
+ SPDK_DEBUGLOG(SPDK_LOG_ISCSI, "set %s=%s\n", key, val);
+ param = iscsi_param_find(params, key);
+ if (param == NULL) {
+ SPDK_ERRLOG("no key %s\n", key);
+ return -1;
+ }
+
+ free(param->val);
+
+ param->val = xstrdup(val);
+
+ return 0;
+}
+
+int
+iscsi_param_set_int(struct iscsi_param *params, const char *key, uint32_t val)
+{
+ char buf[MAX_TMPBUF];
+ struct iscsi_param *param;
+
+ SPDK_DEBUGLOG(SPDK_LOG_ISCSI, "set %s=%d\n", key, val);
+ param = iscsi_param_find(params, key);
+ if (param == NULL) {
+ SPDK_ERRLOG("no key %s\n", key);
+ return -1;
+ }
+
+ free(param->val);
+ snprintf(buf, sizeof buf, "%d", val);
+
+ param->val = strdup(buf);
+
+ return 0;
+}
+
+/**
+ * Parse a single KEY=VAL pair
+ *
+ * data = "KEY=VAL<NUL>"
+ */
+static int
+iscsi_parse_param(struct iscsi_param **params, const uint8_t *data, uint32_t data_len)
+{
+ int rc;
+ uint8_t *key_copy, *val_copy;
+ const uint8_t *key_end;
+ int key_len, val_len;
+ int max_len;
+
+ data_len = strnlen(data, data_len);
+ /* No such thing as strnchr so use memchr instead. */
+ key_end = memchr(data, '=', data_len);
+ if (!key_end) {
+ SPDK_ERRLOG("'=' not found\n");
+ return -1;
+ }
+
+ key_len = key_end - data;
+ if (key_len == 0) {
+ SPDK_ERRLOG("Empty key\n");
+ return -1;
+ }
+ /*
+ * RFC 7143 6.1
+ */
+ if (key_len > ISCSI_TEXT_MAX_KEY_LEN) {
+ SPDK_ERRLOG("Key name length is bigger than 63\n");
+ return -1;
+ }
+
+ key_copy = malloc(key_len + 1);
+ if (!key_copy) {
+ SPDK_ERRLOG("malloc() failed for key_copy\n");
+ return -ENOMEM;
+ }
+
+ memcpy(key_copy, data, key_len);
+ key_copy[key_len] = '\0';
+ /* check whether this key is duplicated */
+ if (NULL != iscsi_param_find(*params, key_copy)) {
+ SPDK_ERRLOG("Duplicated Key %s\n", key_copy);
+ free(key_copy);
+ return -1;
+ }
+
+ val_len = strnlen(key_end + 1, data_len - key_len - 1);
+ /*
+ * RFC 3720 5.1
+ * If not otherwise specified, the maximum length of a simple-value
+ * (not its encoded representation) is 255 bytes, not including the delimiter
+ * (comma or zero byte).
+ */
+ /*
+ * comma or zero is counted in, otherwise we need to iterate each parameter
+ * value
+ */
+ max_len = iscsi_find_key_in_array(key_copy, non_simple_value_params) ?
+ ISCSI_TEXT_MAX_VAL_LEN : ISCSI_TEXT_MAX_SIMPLE_VAL_LEN;
+ if (val_len > max_len) {
+ SPDK_ERRLOG("Overflow Val %d\n", val_len);
+ free(key_copy);
+ return -1;
+ }
+
+ val_copy = calloc(1, val_len + 1);
+ if (val_copy == NULL) {
+ SPDK_ERRLOG("Could not allocate value string\n");
+ free(key_copy);
+ return -1;
+ }
+
+ memcpy(val_copy, key_end + 1, val_len);
+
+ rc = iscsi_param_add(params, key_copy, val_copy, NULL, 0);
+ free(val_copy);
+ free(key_copy);
+ if (rc < 0) {
+ SPDK_ERRLOG("iscsi_param_add() failed\n");
+ return -1;
+ }
+
+ /* return number of bytes consumed
+ * +1 for '=' and +1 for NUL
+ */
+ return key_len + 1 + val_len + 1;
+}
+
+/**
+ * Parse a sequence of KEY=VAL pairs.
+ *
+ * \param data "KEY=VAL<NUL>KEY=VAL<NUL>..."
+ * \param len length of data in bytes
+ */
+int
+iscsi_parse_params(struct iscsi_param **params, const uint8_t *data,
+ int len, bool cbit_enabled, char **partial_parameter)
+{
+ int rc, offset = 0;
+ char *p;
+ int i;
+
+ /* strip the partial text parameters if previous PDU have C enabled */
+ if (partial_parameter && *partial_parameter) {
+ for (i = 0; i < len && data[i] != '\0'; i++) {
+ ;
+ }
+ p = spdk_sprintf_alloc("%s%s", *partial_parameter, (const char *)data);
+ if (!p) {
+ return -1;
+ }
+ rc = iscsi_parse_param(params, p, i + strlen(*partial_parameter));
+ free(p);
+ if (rc < 0) {
+ return -1;
+ }
+ free(*partial_parameter);
+ *partial_parameter = NULL;
+
+ data = data + i + 1;
+ len = len - (i + 1);
+ }
+
+ /* strip the partial text parameters if C bit is enabled */
+ if (cbit_enabled) {
+ if (partial_parameter == NULL) {
+ SPDK_ERRLOG("C bit set but no partial parameters provided\n");
+ return -1;
+ }
+
+ /*
+ * reverse iterate the string from the tail not including '\0'
+ */
+ for (i = len - 1; data[i] != '\0' && i > 0; i--) {
+ ;
+ }
+ if (i != 0) {
+ /* We found a NULL character - don't copy it into the
+ * partial parameter.
+ */
+ i++;
+ }
+
+ *partial_parameter = calloc(1, len - i + 1);
+ if (*partial_parameter == NULL) {
+ SPDK_ERRLOG("could not allocate partial parameter\n");
+ return -1;
+ }
+ memcpy(*partial_parameter, &data[i], len - i);
+ if (i == 0) {
+ /* No full parameters to parse - so return now. */
+ return 0;
+ } else {
+ len = i - 1;
+ }
+ }
+
+ while (offset < len && data[offset] != '\0') {
+ rc = iscsi_parse_param(params, data + offset, len - offset);
+ if (rc < 0) {
+ return -1;
+ }
+ offset += rc;
+ }
+ return 0;
+}
+
+char *
+iscsi_param_get_val(struct iscsi_param *params, const char *key)
+{
+ struct iscsi_param *param;
+
+ param = iscsi_param_find(params, key);
+ if (param == NULL) {
+ return NULL;
+ }
+ return param->val;
+}
+
+int
+iscsi_param_eq_val(struct iscsi_param *params, const char *key,
+ const char *val)
+{
+ struct iscsi_param *param;
+
+ param = iscsi_param_find(params, key);
+ if (param == NULL) {
+ return 0;
+ }
+ if (strcasecmp(param->val, val) == 0) {
+ return 1;
+ }
+ return 0;
+}
+
+struct iscsi_param_table {
+ const char *key;
+ const char *val;
+ const char *list;
+ int type;
+};
+
+static const struct iscsi_param_table conn_param_table[] = {
+ { "HeaderDigest", "None", "CRC32C,None", ISPT_LIST },
+ { "DataDigest", "None", "CRC32C,None", ISPT_LIST },
+ { "MaxRecvDataSegmentLength", "8192", "512,16777215", ISPT_NUMERICAL_DECLARATIVE },
+ { "OFMarker", "No", "Yes,No", ISPT_BOOLEAN_AND },
+ { "IFMarker", "No", "Yes,No", ISPT_BOOLEAN_AND },
+ { "OFMarkInt", "1", "1,65535", ISPT_NUMERICAL_MIN },
+ { "IFMarkInt", "1", "1,65535", ISPT_NUMERICAL_MIN },
+ { "AuthMethod", "None", "CHAP,None", ISPT_LIST },
+ { "CHAP_A", "5", "5", ISPT_LIST },
+ { "CHAP_N", "", "", ISPT_DECLARATIVE },
+ { "CHAP_R", "", "", ISPT_DECLARATIVE },
+ { "CHAP_I", "", "", ISPT_DECLARATIVE },
+ { "CHAP_C", "", "", ISPT_DECLARATIVE },
+ { NULL, NULL, NULL, ISPT_INVALID },
+};
+
+static const struct iscsi_param_table sess_param_table[] = {
+ { "MaxConnections", "1", "1,65535", ISPT_NUMERICAL_MIN },
+#if 0
+ /* need special handling */
+ { "SendTargets", "", "", ISPT_DECLARATIVE },
+#endif
+ { "TargetName", "", "", ISPT_DECLARATIVE },
+ { "InitiatorName", "", "", ISPT_DECLARATIVE },
+ { "TargetAlias", "", "", ISPT_DECLARATIVE },
+ { "InitiatorAlias", "", "", ISPT_DECLARATIVE },
+ { "TargetAddress", "", "", ISPT_DECLARATIVE },
+ { "TargetPortalGroupTag", "1", "1,65535", ISPT_NUMERICAL_DECLARATIVE },
+ { "InitialR2T", "Yes", "Yes,No", ISPT_BOOLEAN_OR },
+ { "ImmediateData", "Yes", "Yes,No", ISPT_BOOLEAN_AND },
+ { "MaxBurstLength", "262144", "512,16777215", ISPT_NUMERICAL_MIN },
+ { "FirstBurstLength", "65536", "512,16777215", ISPT_NUMERICAL_MIN },
+ { "DefaultTime2Wait", "2", "0,3600", ISPT_NUMERICAL_MAX },
+ { "DefaultTime2Retain", "20", "0,3600", ISPT_NUMERICAL_MIN },
+ { "MaxOutstandingR2T", "1", "1,65536", ISPT_NUMERICAL_MIN },
+ { "DataPDUInOrder", "Yes", "Yes,No", ISPT_BOOLEAN_OR },
+ { "DataSequenceInOrder", "Yes", "Yes,No", ISPT_BOOLEAN_OR },
+ { "ErrorRecoveryLevel", "0", "0,2", ISPT_NUMERICAL_MIN },
+ { "SessionType", "Normal", "Normal,Discovery", ISPT_DECLARATIVE },
+ { NULL, NULL, NULL, ISPT_INVALID },
+};
+
+static int
+iscsi_params_init_internal(struct iscsi_param **params,
+ const struct iscsi_param_table *table)
+{
+ int rc;
+ int i;
+ struct iscsi_param *param;
+
+ for (i = 0; table[i].key != NULL; i++) {
+ rc = iscsi_param_add(params, table[i].key, table[i].val,
+ table[i].list, table[i].type);
+ if (rc < 0) {
+ SPDK_ERRLOG("iscsi_param_add() failed\n");
+ return -1;
+ }
+ param = iscsi_param_find(*params, table[i].key);
+ if (param != NULL) {
+ param->state_index = i;
+ } else {
+ SPDK_ERRLOG("iscsi_param_find() failed\n");
+ return -1;
+ }
+ }
+
+ return 0;
+}
+
+int
+iscsi_conn_params_init(struct iscsi_param **params)
+{
+ return iscsi_params_init_internal(params, &conn_param_table[0]);
+}
+
+int
+iscsi_sess_params_init(struct iscsi_param **params)
+{
+ return iscsi_params_init_internal(params, &sess_param_table[0]);
+}
+
+static const char *chap_type[] = {
+ "CHAP_A",
+ "CHAP_N",
+ "CHAP_R",
+ "CHAP_I",
+ "CHAP_C",
+ NULL,
+};
+
+static const char *discovery_ignored_param[] = {
+ "MaxConnections",
+ "InitialR2T",
+ "ImmediateData",
+ "MaxBurstLength",
+ "FirstBurstLength"
+ "MaxOutstandingR2T",
+ "DataPDUInOrder",
+ "DataSequenceInOrder",
+ NULL,
+};
+
+static const char *multi_negot_conn_params[] = {
+ "MaxRecvDataSegmentLength",
+ NULL,
+};
+
+/* The following params should be declared by target */
+static const char *target_declarative_params[] = {
+ "TargetAlias",
+ "TargetAddress",
+ "TargetPortalGroupTag",
+ NULL,
+};
+
+/* This function is used to construct the data from the special param (e.g.,
+ * MaxRecvDataSegmentLength)
+ * return:
+ * normal: the total len of the data
+ * error: -1
+ */
+static int
+iscsi_special_param_construction(struct spdk_iscsi_conn *conn,
+ struct iscsi_param *param,
+ bool FirstBurstLength_flag, char *data,
+ int alloc_len, int total)
+{
+ int len;
+ struct iscsi_param *param_first;
+ struct iscsi_param *param_max;
+ uint32_t FirstBurstLength;
+ uint32_t MaxBurstLength;
+ char *val;
+
+ val = malloc(ISCSI_TEXT_MAX_VAL_LEN + 1);
+ if (!val) {
+ SPDK_ERRLOG("malloc() failed for temporary buffer\n");
+ return -ENOMEM;
+ }
+
+ if (strcasecmp(param->key, "MaxRecvDataSegmentLength") == 0) {
+ /*
+ * MaxRecvDataSegmentLength is sent by both
+ * initiator and target, but is declarative - meaning
+ * each direction can have different values.
+ * So when MaxRecvDataSegmentLength is found in the
+ * the parameter set sent from the initiator, add SPDK
+ * iscsi target's MaxRecvDataSegmentLength value to
+ * the returned parameter list.
+ */
+ if (alloc_len - total < 1) {
+ SPDK_ERRLOG("data space small %d\n", alloc_len);
+ free(val);
+ return -1;
+ }
+
+ SPDK_DEBUGLOG(SPDK_LOG_ISCSI,
+ "returning MaxRecvDataSegmentLength=%d\n",
+ SPDK_ISCSI_MAX_RECV_DATA_SEGMENT_LENGTH);
+ len = snprintf((char *)data + total, alloc_len - total,
+ "MaxRecvDataSegmentLength=%d",
+ SPDK_ISCSI_MAX_RECV_DATA_SEGMENT_LENGTH);
+ total += len + 1;
+ }
+
+ if (strcasecmp(param->key, "MaxBurstLength") == 0 &&
+ !FirstBurstLength_flag) {
+ if (alloc_len - total < 1) {
+ SPDK_ERRLOG("data space small %d\n", alloc_len);
+ free(val);
+ return -1;
+ }
+
+ param_first = iscsi_param_find(conn->sess->params,
+ "FirstBurstLength");
+ if (param_first != NULL) {
+ FirstBurstLength = (uint32_t)strtol(param_first->val, NULL, 10);
+ } else {
+ FirstBurstLength = SPDK_ISCSI_FIRST_BURST_LENGTH;
+ }
+ param_max = iscsi_param_find(conn->sess->params,
+ "MaxBurstLength");
+ if (param_max != NULL) {
+ MaxBurstLength = (uint32_t)strtol(param_max->val, NULL, 10);
+ } else {
+ MaxBurstLength = SPDK_ISCSI_MAX_BURST_LENGTH;
+ }
+
+ if (FirstBurstLength > MaxBurstLength) {
+ FirstBurstLength = MaxBurstLength;
+ if (param_first != NULL) {
+ free(param_first->val);
+ snprintf(val, ISCSI_TEXT_MAX_VAL_LEN, "%d",
+ FirstBurstLength);
+ param_first->val = xstrdup(val);
+ }
+ }
+ len = snprintf((char *)data + total, alloc_len - total,
+ "FirstBurstLength=%d", FirstBurstLength);
+ total += len + 1;
+ }
+
+ free(val);
+ return total;
+
+}
+
+/**
+ * iscsi_construct_data_from_param:
+ * To construct the data which will be returned to the initiator
+ * return: length of the negotiated data, -1 indicates error;
+ */
+static int
+iscsi_construct_data_from_param(struct iscsi_param *param, char *new_val,
+ char *data, int alloc_len, int total)
+{
+ int len;
+
+ if (param->type != ISPT_DECLARATIVE &&
+ param->type != ISPT_NUMERICAL_DECLARATIVE) {
+ if (alloc_len - total < 1) {
+ SPDK_ERRLOG("data space small %d\n", alloc_len);
+ return -1;
+ }
+
+ SPDK_DEBUGLOG(SPDK_LOG_ISCSI, "negotiated %s=%s\n",
+ param->key, new_val);
+ len = snprintf((char *)data + total, alloc_len - total, "%s=%s",
+ param->key, new_val);
+ total += len + 1;
+ }
+ return total;
+}
+
+/**
+ * To negotiate param with
+ * type = ISPT_LIST
+ * return: the negotiated value of the key
+ */
+static char *
+iscsi_negotiate_param_list(int *add_param_value,
+ struct iscsi_param *param,
+ char *valid_list, char *in_val,
+ char *cur_val)
+{
+ char *val_start, *val_end;
+ char *in_start, *in_end;
+ int flag = 0;
+
+ if (add_param_value == NULL) {
+ return NULL;
+ }
+
+ in_start = in_val;
+ do {
+ if ((in_end = strchr(in_start, (int)',')) != NULL) {
+ *in_end = '\0';
+ }
+ val_start = valid_list;
+ do {
+ if ((val_end = strchr(val_start, (int)',')) != NULL) {
+ *val_end = '\0';
+ }
+ if (strcasecmp(in_start, val_start) == 0) {
+ SPDK_DEBUGLOG(SPDK_LOG_ISCSI, "match %s\n",
+ val_start);
+ flag = 1;
+ break;
+ }
+ if (val_end) {
+ *val_end = ',';
+ val_start = val_end + 1;
+ }
+ } while (val_end);
+ if (flag) {
+ break;
+ }
+ if (in_end) {
+ *in_end = ',';
+ in_start = in_end + 1;
+ }
+ } while (in_end);
+
+ return flag ? val_start : NULL;
+}
+
+/**
+ * To negotiate param with
+ * type = ISPT_NUMERICAL_MIN/MAX, ISPT_NUMERICAL_DECLARATIVE
+ * return: the negotiated value of the key
+ */
+static char *
+iscsi_negotiate_param_numerical(int *add_param_value,
+ struct iscsi_param *param,
+ char *valid_list, char *in_val,
+ char *cur_val)
+{
+ char *valid_next;
+ char *new_val = NULL;
+ char *min_val, *max_val;
+ int val_i, cur_val_i;
+ int min_i, max_i;
+
+ if (add_param_value == NULL) {
+ return NULL;
+ }
+
+ val_i = (int)strtol(param->val, NULL, 10);
+ /* check whether the key is FirstBurstLength, if that we use in_val */
+ if (strcasecmp(param->key, "FirstBurstLength") == 0) {
+ val_i = (int)strtol(in_val, NULL, 10);
+ }
+
+ cur_val_i = (int)strtol(cur_val, NULL, 10);
+ valid_next = valid_list;
+ min_val = spdk_strsepq(&valid_next, ",");
+ max_val = spdk_strsepq(&valid_next, ",");
+ min_i = (min_val != NULL) ? (int)strtol(min_val, NULL, 10) : 0;
+ max_i = (max_val != NULL) ? (int)strtol(max_val, NULL, 10) : 0;
+ if (val_i < min_i || val_i > max_i) {
+ SPDK_DEBUGLOG(SPDK_LOG_ISCSI, "key %.64s reject\n", param->key);
+ new_val = NULL;
+ } else {
+ switch (param->type) {
+ case ISPT_NUMERICAL_MIN:
+ if (val_i > cur_val_i) {
+ val_i = cur_val_i;
+ }
+ break;
+ case ISPT_NUMERICAL_MAX:
+ if (val_i < cur_val_i) {
+ val_i = cur_val_i;
+ }
+ break;
+ default:
+ break;
+ }
+ snprintf(in_val, ISCSI_TEXT_MAX_VAL_LEN, "%d", val_i);
+ new_val = in_val;
+ }
+
+ return new_val;
+}
+
+/**
+ * To negotiate param with
+ * type = ISPT_BOOLEAN_OR, ISPT_BOOLEAN_AND
+ * return: the negotiated value of the key
+ */
+static char *
+iscsi_negotiate_param_boolean(int *add_param_value,
+ struct iscsi_param *param,
+ char *in_val, char *cur_val,
+ const char *value)
+{
+ char *new_val = NULL;
+
+ if (add_param_value == NULL) {
+ return NULL;
+ }
+
+ /* Make sure the val is Yes or No */
+ if (!((strcasecmp(in_val, "Yes") == 0) ||
+ (strcasecmp(in_val, "No") == 0))) {
+ /* unknown value */
+ snprintf(in_val, ISCSI_TEXT_MAX_VAL_LEN + 1, "%s", "Reject");
+ new_val = in_val;
+ *add_param_value = 1;
+ return new_val;
+ }
+
+ if (strcasecmp(cur_val, value) == 0) {
+ snprintf(in_val, ISCSI_TEXT_MAX_VAL_LEN + 1, "%s", value);
+ new_val = in_val;
+ } else {
+ new_val = param->val;
+ }
+
+ return new_val;
+}
+
+/**
+ * The entry function to handle each type of the param
+ * return value: the new negotiated value
+ */
+static char *
+iscsi_negotiate_param_all(int *add_param_value, struct iscsi_param *param,
+ char *valid_list, char *in_val, char *cur_val)
+{
+ char *new_val;
+ switch (param->type) {
+ case ISPT_LIST:
+ new_val = iscsi_negotiate_param_list(add_param_value,
+ param,
+ valid_list,
+ in_val,
+ cur_val);
+ break;
+
+ case ISPT_NUMERICAL_MIN:
+ case ISPT_NUMERICAL_MAX:
+ case ISPT_NUMERICAL_DECLARATIVE:
+ new_val = iscsi_negotiate_param_numerical(add_param_value,
+ param,
+ valid_list,
+ in_val,
+ cur_val);
+ break;
+
+ case ISPT_BOOLEAN_OR:
+ new_val = iscsi_negotiate_param_boolean(add_param_value,
+ param,
+ in_val,
+ cur_val,
+ "Yes");
+ break;
+ case ISPT_BOOLEAN_AND:
+ new_val = iscsi_negotiate_param_boolean(add_param_value,
+ param,
+ in_val,
+ cur_val,
+ "No");
+ break;
+
+ default:
+ snprintf(in_val, ISCSI_TEXT_MAX_VAL_LEN + 1, "%s", param->val);
+ new_val = in_val;
+ break;
+ }
+
+ return new_val;
+}
+
+/**
+ * This function is used to judge whether the param is in session's params or
+ * connection's params
+ */
+static int
+iscsi_negotiate_param_init(struct spdk_iscsi_conn *conn,
+ struct iscsi_param **cur_param_p,
+ struct iscsi_param **params_dst_p,
+ struct iscsi_param *param)
+{
+ int index;
+
+ *cur_param_p = iscsi_param_find(*params_dst_p, param->key);
+ if (*cur_param_p == NULL) {
+ *params_dst_p = conn->sess->params;
+ *cur_param_p = iscsi_param_find(*params_dst_p, param->key);
+ if (*cur_param_p == NULL) {
+ if ((strncasecmp(param->key, "X-", 2) == 0) ||
+ (strncasecmp(param->key, "X#", 2) == 0)) {
+ /* Extension Key */
+ SPDK_DEBUGLOG(SPDK_LOG_ISCSI,
+ "extension key %.64s\n",
+ param->key);
+ } else {
+ SPDK_ERRLOG("unknown key %.64s\n", param->key);
+ }
+ return 1;
+ } else {
+ index = (*cur_param_p)->state_index;
+ if (conn->sess_param_state_negotiated[index] &&
+ !iscsi_find_key_in_array(param->key,
+ target_declarative_params)) {
+ return SPDK_ISCSI_PARAMETER_EXCHANGE_NOT_ONCE;
+ }
+ conn->sess_param_state_negotiated[index] = true;
+ }
+ } else {
+ index = (*cur_param_p)->state_index;
+ if (conn->conn_param_state_negotiated[index] &&
+ !iscsi_find_key_in_array(param->key,
+ multi_negot_conn_params)) {
+ return SPDK_ISCSI_PARAMETER_EXCHANGE_NOT_ONCE;
+ }
+ conn->conn_param_state_negotiated[index] = true;
+ }
+
+ return 0;
+}
+
+int
+iscsi_negotiate_params(struct spdk_iscsi_conn *conn,
+ struct iscsi_param **params, uint8_t *data, int alloc_len,
+ int data_len)
+{
+ struct iscsi_param *param;
+ struct iscsi_param *cur_param;
+ char *valid_list, *in_val;
+ char *cur_val;
+ char *new_val;
+ int discovery;
+ int total;
+ int rc;
+ uint32_t FirstBurstLength;
+ uint32_t MaxBurstLength;
+ bool FirstBurstLength_flag = false;
+ int type;
+
+ total = data_len;
+ if (data_len < 0) {
+ assert(false);
+ return -EINVAL;
+ }
+ if (alloc_len < 1) {
+ return 0;
+ }
+ if (total > alloc_len) {
+ total = alloc_len;
+ data[total - 1] = '\0';
+ return total;
+ }
+
+ if (*params == NULL) {
+ /* no input */
+ return total;
+ }
+
+ /* discovery? */
+ discovery = 0;
+ cur_param = iscsi_param_find(*params, "SessionType");
+ if (cur_param == NULL) {
+ cur_param = iscsi_param_find(conn->sess->params, "SessionType");
+ if (cur_param == NULL) {
+ /* no session type */
+ } else {
+ if (strcasecmp(cur_param->val, "Discovery") == 0) {
+ discovery = 1;
+ }
+ }
+ } else {
+ if (strcasecmp(cur_param->val, "Discovery") == 0) {
+ discovery = 1;
+ }
+ }
+
+ /* for temporary store */
+ valid_list = malloc(ISCSI_TEXT_MAX_VAL_LEN + 1);
+ if (!valid_list) {
+ SPDK_ERRLOG("malloc() failed for valid_list\n");
+ return -ENOMEM;
+ }
+
+ in_val = malloc(ISCSI_TEXT_MAX_VAL_LEN + 1);
+ if (!in_val) {
+ SPDK_ERRLOG("malloc() failed for in_val\n");
+ free(valid_list);
+ return -ENOMEM;
+ }
+
+ cur_val = malloc(ISCSI_TEXT_MAX_VAL_LEN + 1);
+ if (!cur_val) {
+ SPDK_ERRLOG("malloc() failed for cur_val\n");
+ free(valid_list);
+ free(in_val);
+ return -ENOMEM;
+ }
+
+ /* To adjust the location of FirstBurstLength location and put it to
+ * the end, then we can always firstly determine the MaxBurstLength
+ */
+ param = iscsi_param_find(*params, "MaxBurstLength");
+ if (param != NULL) {
+ param = iscsi_param_find(*params, "FirstBurstLength");
+
+ /* check the existence of FirstBurstLength */
+ if (param != NULL) {
+ FirstBurstLength_flag = true;
+ if (param->next != NULL) {
+ snprintf(in_val, ISCSI_TEXT_MAX_VAL_LEN + 1, "%s", param->val);
+ type = param->type;
+ iscsi_param_add(params, "FirstBurstLength",
+ in_val, NULL, type);
+ }
+ }
+ }
+
+ for (param = *params; param != NULL; param = param->next) {
+ struct iscsi_param *params_dst = conn->params;
+ int add_param_value = 0;
+ new_val = NULL;
+ param->type = ISPT_INVALID;
+
+ /* sendtargets is special */
+ if (strcasecmp(param->key, "SendTargets") == 0) {
+ continue;
+ }
+ /* CHAP keys */
+ if (iscsi_find_key_in_array(param->key, chap_type)) {
+ continue;
+ }
+
+ /* 12.2, 12.10, 12.11, 12.13, 12.14, 12.17, 12.18, 12.19 */
+ if (discovery &&
+ iscsi_find_key_in_array(param->key, discovery_ignored_param)) {
+ snprintf(in_val, ISCSI_TEXT_MAX_VAL_LEN + 1, "%s", "Irrelevant");
+ new_val = in_val;
+ add_param_value = 1;
+ } else {
+ rc = iscsi_negotiate_param_init(conn,
+ &cur_param,
+ &params_dst,
+ param);
+ if (rc < 0) {
+ free(valid_list);
+ free(in_val);
+ free(cur_val);
+ return rc;
+ } else if (rc > 0) {
+ snprintf(in_val, ISCSI_TEXT_MAX_VAL_LEN + 1, "%s", "NotUnderstood");
+ new_val = in_val;
+ add_param_value = 1;
+ } else {
+ snprintf(valid_list, ISCSI_TEXT_MAX_VAL_LEN + 1, "%s", cur_param->list);
+ snprintf(cur_val, ISCSI_TEXT_MAX_VAL_LEN + 1, "%s", cur_param->val);
+ param->type = cur_param->type;
+ }
+ }
+
+ if (param->type > 0) {
+ snprintf(in_val, ISCSI_TEXT_MAX_VAL_LEN + 1, "%s", param->val);
+
+ /* "NotUnderstood" value shouldn't be assigned to "Understood" key */
+ if (strcasecmp(in_val, "NotUnderstood") == 0) {
+ free(in_val);
+ free(valid_list);
+ free(cur_val);
+ return SPDK_ISCSI_LOGIN_ERROR_PARAMETER;
+ }
+
+ if (strcasecmp(param->key, "FirstBurstLength") == 0) {
+ FirstBurstLength = (uint32_t)strtol(param->val, NULL,
+ 10);
+ new_val = iscsi_param_get_val(conn->sess->params,
+ "MaxBurstLength");
+ if (new_val != NULL) {
+ MaxBurstLength = (uint32_t) strtol(new_val, NULL,
+ 10);
+ } else {
+ MaxBurstLength = SPDK_ISCSI_MAX_BURST_LENGTH;
+ }
+ if (FirstBurstLength < SPDK_ISCSI_MAX_FIRST_BURST_LENGTH &&
+ FirstBurstLength > MaxBurstLength) {
+ FirstBurstLength = MaxBurstLength;
+ snprintf(in_val, ISCSI_TEXT_MAX_VAL_LEN, "%d",
+ FirstBurstLength);
+ }
+ }
+
+ /* prevent target's declarative params from being changed by initiator */
+ if (iscsi_find_key_in_array(param->key, target_declarative_params)) {
+ add_param_value = 1;
+ }
+
+ new_val = iscsi_negotiate_param_all(&add_param_value,
+ param,
+ valid_list,
+ in_val,
+ cur_val);
+ }
+
+ /* check the negotiated value of the key */
+ if (new_val != NULL) {
+ /* add_param_value = 0 means updating the value of
+ * existed key in the connection's parameters
+ */
+ if (add_param_value == 0) {
+ iscsi_param_set(params_dst, param->key, new_val);
+ }
+ total = iscsi_construct_data_from_param(param,
+ new_val,
+ data,
+ alloc_len,
+ total);
+ if (total < 0) {
+ goto final_return;
+ }
+
+ total = iscsi_special_param_construction(conn,
+ param,
+ FirstBurstLength_flag,
+ data,
+ alloc_len,
+ total);
+ if (total < 0) {
+ goto final_return;
+ }
+ } else {
+ total = -1;
+ break;
+ }
+ }
+
+final_return:
+ free(valid_list);
+ free(in_val);
+ free(cur_val);
+
+ return total;
+}
+
+int
+iscsi_copy_param2var(struct spdk_iscsi_conn *conn)
+{
+ const char *val;
+
+ val = iscsi_param_get_val(conn->params, "MaxRecvDataSegmentLength");
+ if (val == NULL) {
+ SPDK_ERRLOG("Getval MaxRecvDataSegmentLength failed\n");
+ return -1;
+ }
+ SPDK_DEBUGLOG(SPDK_LOG_ISCSI,
+ "copy MaxRecvDataSegmentLength=%s\n", val);
+ conn->MaxRecvDataSegmentLength = (int)strtol(val, NULL, 10);
+ if (conn->MaxRecvDataSegmentLength > SPDK_BDEV_LARGE_BUF_MAX_SIZE) {
+ conn->MaxRecvDataSegmentLength = SPDK_BDEV_LARGE_BUF_MAX_SIZE;
+ }
+
+ val = iscsi_param_get_val(conn->params, "HeaderDigest");
+ if (val == NULL) {
+ SPDK_ERRLOG("Getval HeaderDigest failed\n");
+ return -1;
+ }
+ if (strcasecmp(val, "CRC32C") == 0) {
+ SPDK_DEBUGLOG(SPDK_LOG_ISCSI, "set HeaderDigest=1\n");
+ conn->header_digest = 1;
+ } else {
+ SPDK_DEBUGLOG(SPDK_LOG_ISCSI, "set HeaderDigest=0\n");
+ conn->header_digest = 0;
+ }
+ val = iscsi_param_get_val(conn->params, "DataDigest");
+ if (val == NULL) {
+ SPDK_ERRLOG("Getval DataDigest failed\n");
+ return -1;
+ }
+ if (strcasecmp(val, "CRC32C") == 0) {
+ SPDK_DEBUGLOG(SPDK_LOG_ISCSI, "set DataDigest=1\n");
+ conn->data_digest = 1;
+ } else {
+ SPDK_DEBUGLOG(SPDK_LOG_ISCSI, "set DataDigest=0\n");
+ conn->data_digest = 0;
+ }
+
+ val = iscsi_param_get_val(conn->sess->params, "MaxConnections");
+ if (val == NULL) {
+ SPDK_ERRLOG("Getval MaxConnections failed\n");
+ return -1;
+ }
+ SPDK_DEBUGLOG(SPDK_LOG_ISCSI, "copy MaxConnections=%s\n", val);
+ conn->sess->MaxConnections = (uint32_t) strtol(val, NULL, 10);
+ val = iscsi_param_get_val(conn->sess->params, "MaxOutstandingR2T");
+ if (val == NULL) {
+ SPDK_ERRLOG("Getval MaxOutstandingR2T failed\n");
+ return -1;
+ }
+ SPDK_DEBUGLOG(SPDK_LOG_ISCSI, "copy MaxOutstandingR2T=%s\n", val);
+ conn->sess->MaxOutstandingR2T = (uint32_t) strtol(val, NULL, 10);
+ val = iscsi_param_get_val(conn->sess->params, "FirstBurstLength");
+ if (val == NULL) {
+ SPDK_ERRLOG("Getval FirstBurstLength failed\n");
+ return -1;
+ }
+ SPDK_DEBUGLOG(SPDK_LOG_ISCSI, "copy FirstBurstLength=%s\n", val);
+ conn->sess->FirstBurstLength = (uint32_t) strtol(val, NULL, 10);
+ val = iscsi_param_get_val(conn->sess->params, "MaxBurstLength");
+ if (val == NULL) {
+ SPDK_ERRLOG("Getval MaxBurstLength failed\n");
+ return -1;
+ }
+ SPDK_DEBUGLOG(SPDK_LOG_ISCSI, "copy MaxBurstLength=%s\n", val);
+ conn->sess->MaxBurstLength = (uint32_t) strtol(val, NULL, 10);
+ val = iscsi_param_get_val(conn->sess->params, "InitialR2T");
+ if (val == NULL) {
+ SPDK_ERRLOG("Getval InitialR2T failed\n");
+ return -1;
+ }
+ if (strcasecmp(val, "Yes") == 0) {
+ SPDK_DEBUGLOG(SPDK_LOG_ISCSI, "set InitialR2T=1\n");
+ conn->sess->InitialR2T = true;
+ } else {
+ SPDK_DEBUGLOG(SPDK_LOG_ISCSI, "set InitialR2T=0\n");
+ conn->sess->InitialR2T = false;
+ }
+ val = iscsi_param_get_val(conn->sess->params, "ImmediateData");
+ if (val == NULL) {
+ SPDK_ERRLOG("Getval ImmediateData failed\n");
+ return -1;
+ }
+ if (strcasecmp(val, "Yes") == 0) {
+ SPDK_DEBUGLOG(SPDK_LOG_ISCSI, "set ImmediateData=1\n");
+ conn->sess->ImmediateData = true;
+ } else {
+ SPDK_DEBUGLOG(SPDK_LOG_ISCSI, "set ImmediateData=0\n");
+ conn->sess->ImmediateData = false;
+ }
+ return 0;
+}
diff --git a/src/spdk/lib/iscsi/param.h b/src/spdk/lib/iscsi/param.h
new file mode 100644
index 000000000..ce194c514
--- /dev/null
+++ b/src/spdk/lib/iscsi/param.h
@@ -0,0 +1,94 @@
+/*-
+ * BSD LICENSE
+ *
+ * Copyright (C) 2008-2012 Daisuke Aoyama <aoyama@peach.ne.jp>.
+ * Copyright (c) Intel Corporation.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ * * Neither the name of Intel Corporation nor the names of its
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef SPDK_ISCSI_PARAM_H
+#define SPDK_ISCSI_PARAM_H
+
+#include "spdk/stdinc.h"
+
+struct spdk_iscsi_conn;
+
+enum iscsi_param_type {
+ ISPT_INVALID = -1,
+ ISPT_NOTSPECIFIED = 0,
+ ISPT_LIST,
+ ISPT_NUMERICAL_MIN,
+ ISPT_NUMERICAL_MAX,
+ ISPT_NUMERICAL_DECLARATIVE,
+ ISPT_DECLARATIVE,
+ ISPT_BOOLEAN_OR,
+ ISPT_BOOLEAN_AND,
+};
+
+struct iscsi_param {
+ struct iscsi_param *next;
+ char *key;
+ char *val;
+ char *list;
+ int type;
+ int state_index;
+};
+
+void
+iscsi_param_free(struct iscsi_param *params);
+struct iscsi_param *
+iscsi_param_find(struct iscsi_param *params, const char *key);
+int
+iscsi_param_del(struct iscsi_param **params, const char *key);
+int
+iscsi_param_add(struct iscsi_param **params, const char *key,
+ const char *val, const char *list, int type);
+int
+iscsi_param_set(struct iscsi_param *params, const char *key,
+ const char *val);
+int
+iscsi_param_set_int(struct iscsi_param *params, const char *key, uint32_t val);
+int
+iscsi_parse_params(struct iscsi_param **params, const uint8_t *data,
+ int len, bool cbit_enabled, char **partial_parameter);
+char *
+iscsi_param_get_val(struct iscsi_param *params, const char *key);
+int
+iscsi_param_eq_val(struct iscsi_param *params, const char *key,
+ const char *val);
+
+int iscsi_negotiate_params(struct spdk_iscsi_conn *conn,
+ struct iscsi_param **params_p, uint8_t *data,
+ int alloc_len, int data_len);
+int iscsi_copy_param2var(struct spdk_iscsi_conn *conn);
+
+int iscsi_conn_params_init(struct iscsi_param **params);
+int iscsi_sess_params_init(struct iscsi_param **params);
+
+#endif /* SPDK_ISCSI_PARAM_H */
diff --git a/src/spdk/lib/iscsi/portal_grp.c b/src/spdk/lib/iscsi/portal_grp.c
new file mode 100644
index 000000000..986562ad7
--- /dev/null
+++ b/src/spdk/lib/iscsi/portal_grp.c
@@ -0,0 +1,655 @@
+/*-
+ * BSD LICENSE
+ *
+ * Copyright (C) 2008-2012 Daisuke Aoyama <aoyama@peach.ne.jp>.
+ * Copyright (c) Intel Corporation.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ * * Neither the name of Intel Corporation nor the names of its
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "spdk/stdinc.h"
+
+#include "spdk/conf.h"
+#include "spdk/sock.h"
+#include "spdk/string.h"
+
+#include "spdk_internal/log.h"
+
+#include "iscsi/iscsi.h"
+#include "iscsi/conn.h"
+#include "iscsi/portal_grp.h"
+#include "iscsi/tgt_node.h"
+
+#define PORTNUMSTRLEN 32
+#define ACCEPT_TIMEOUT_US 1000 /* 1ms */
+
+static int
+iscsi_portal_accept(void *arg)
+{
+ struct spdk_iscsi_portal *portal = arg;
+ struct spdk_sock *sock;
+ int rc;
+ int count = 0;
+
+ if (portal->sock == NULL) {
+ return -1;
+ }
+
+ while (1) {
+ sock = spdk_sock_accept(portal->sock);
+ if (sock != NULL) {
+ rc = iscsi_conn_construct(portal, sock);
+ if (rc < 0) {
+ spdk_sock_close(&sock);
+ SPDK_ERRLOG("spdk_iscsi_connection_construct() failed\n");
+ break;
+ }
+ count++;
+ } else {
+ if (errno != EAGAIN && errno != EWOULDBLOCK) {
+ SPDK_ERRLOG("accept error(%d): %s\n", errno, spdk_strerror(errno));
+ }
+ break;
+ }
+ }
+
+ return count;
+}
+
+static struct spdk_iscsi_portal *
+iscsi_portal_find_by_addr(const char *host, const char *port)
+{
+ struct spdk_iscsi_portal *p;
+
+ TAILQ_FOREACH(p, &g_iscsi.portal_head, g_tailq) {
+ if (!strcmp(p->host, host) && !strcmp(p->port, port)) {
+ return p;
+ }
+ }
+
+ return NULL;
+}
+
+/* Assumes caller allocated host and port strings on the heap */
+struct spdk_iscsi_portal *
+iscsi_portal_create(const char *host, const char *port)
+{
+ struct spdk_iscsi_portal *p = NULL, *tmp;
+
+ assert(host != NULL);
+ assert(port != NULL);
+
+ if (strlen(host) > MAX_PORTAL_ADDR || strlen(port) > MAX_PORTAL_PORT) {
+ return NULL;
+ }
+
+ p = calloc(1, sizeof(*p));
+ if (!p) {
+ SPDK_ERRLOG("calloc() failed for portal\n");
+ return NULL;
+ }
+
+ /* check and overwrite abbreviation of wildcard */
+ if (strcasecmp(host, "[*]") == 0) {
+ SPDK_WARNLOG("Please use \"[::]\" as IPv6 wildcard\n");
+ SPDK_WARNLOG("Convert \"[*]\" to \"[::]\" automatically\n");
+ SPDK_WARNLOG("(Use of \"[*]\" will be deprecated in a future release)");
+ snprintf(p->host, sizeof(p->host), "[::]");
+ } else if (strcasecmp(host, "*") == 0) {
+ SPDK_WARNLOG("Please use \"0.0.0.0\" as IPv4 wildcard\n");
+ SPDK_WARNLOG("Convert \"*\" to \"0.0.0.0\" automatically\n");
+ SPDK_WARNLOG("(Use of \"[*]\" will be deprecated in a future release)");
+ snprintf(p->host, sizeof(p->host), "0.0.0.0");
+ } else {
+ memcpy(p->host, host, strlen(host));
+ }
+
+ memcpy(p->port, port, strlen(port));
+
+ p->sock = NULL;
+ p->group = NULL; /* set at a later time by caller */
+ p->acceptor_poller = NULL;
+
+ pthread_mutex_lock(&g_iscsi.mutex);
+ tmp = iscsi_portal_find_by_addr(host, port);
+ if (tmp != NULL) {
+ pthread_mutex_unlock(&g_iscsi.mutex);
+ SPDK_ERRLOG("portal (%s, %s) already exists\n", host, port);
+ goto error_out;
+ }
+
+ TAILQ_INSERT_TAIL(&g_iscsi.portal_head, p, g_tailq);
+ pthread_mutex_unlock(&g_iscsi.mutex);
+
+ return p;
+
+error_out:
+ free(p);
+
+ return NULL;
+}
+
+void
+iscsi_portal_destroy(struct spdk_iscsi_portal *p)
+{
+ assert(p != NULL);
+
+ SPDK_DEBUGLOG(SPDK_LOG_ISCSI, "iscsi_portal_destroy\n");
+
+ pthread_mutex_lock(&g_iscsi.mutex);
+ TAILQ_REMOVE(&g_iscsi.portal_head, p, g_tailq);
+ pthread_mutex_unlock(&g_iscsi.mutex);
+
+ free(p);
+
+}
+
+static int
+iscsi_portal_open(struct spdk_iscsi_portal *p)
+{
+ struct spdk_sock *sock;
+ int port;
+
+ if (p->sock != NULL) {
+ SPDK_ERRLOG("portal (%s, %s) is already opened\n",
+ p->host, p->port);
+ return -1;
+ }
+
+ port = (int)strtol(p->port, NULL, 0);
+ sock = spdk_sock_listen(p->host, port, NULL);
+ if (sock == NULL) {
+ SPDK_ERRLOG("listen error %.64s.%d\n", p->host, port);
+ return -1;
+ }
+
+ p->sock = sock;
+
+ /*
+ * When the portal is created by config file, incoming connection
+ * requests for the socket are pended to accept until reactors start.
+ * However the gap between listen() and accept() will be slight and
+ * the requests will be queued by the nonzero backlog of the socket
+ * or resend by TCP.
+ */
+ p->acceptor_poller = SPDK_POLLER_REGISTER(iscsi_portal_accept, p, ACCEPT_TIMEOUT_US);
+
+ return 0;
+}
+
+static void
+iscsi_portal_close(struct spdk_iscsi_portal *p)
+{
+ if (p->sock) {
+ SPDK_DEBUGLOG(SPDK_LOG_ISCSI, "close portal (%s, %s)\n",
+ p->host, p->port);
+ spdk_poller_unregister(&p->acceptor_poller);
+ spdk_sock_close(&p->sock);
+ }
+}
+
+static int
+iscsi_parse_portal(const char *portalstring, struct spdk_iscsi_portal **ip)
+{
+ char *host = NULL, *port = NULL;
+ int len, rc = -1;
+ const char *p;
+
+ if (portalstring == NULL) {
+ SPDK_ERRLOG("portal error\n");
+ goto error_out;
+ }
+
+ /* IP address */
+ if (portalstring[0] == '[') {
+ /* IPv6 */
+ p = strchr(portalstring + 1, ']');
+ if (p == NULL) {
+ SPDK_ERRLOG("portal error\n");
+ goto error_out;
+ }
+ p++;
+ } else {
+ /* IPv4 */
+ p = strchr(portalstring, ':');
+ if (p == NULL) {
+ p = portalstring + strlen(portalstring);
+ }
+ }
+
+ len = p - portalstring;
+ host = malloc(len + 1);
+ if (host == NULL) {
+ SPDK_ERRLOG("malloc() failed for host\n");
+ goto error_out;
+ }
+ memcpy(host, portalstring, len);
+ host[len] = '\0';
+
+ /* Port number (IPv4 and IPv6 are the same) */
+ if (p[0] == '\0') {
+ port = malloc(PORTNUMSTRLEN);
+ if (!port) {
+ SPDK_ERRLOG("malloc() failed for port\n");
+ goto error_out;
+ }
+ snprintf(port, PORTNUMSTRLEN, "%d", DEFAULT_PORT);
+ } else {
+ p++;
+ len = strlen(p);
+ port = malloc(len + 1);
+ if (port == NULL) {
+ SPDK_ERRLOG("malloc() failed for port\n");
+ goto error_out;
+ }
+ memcpy(port, p, len);
+ port[len] = '\0';
+ }
+
+ *ip = iscsi_portal_create(host, port);
+ if (!*ip) {
+ goto error_out;
+ }
+
+ rc = 0;
+error_out:
+ free(host);
+ free(port);
+
+ return rc;
+}
+
+struct spdk_iscsi_portal_grp *
+iscsi_portal_grp_create(int tag)
+{
+ struct spdk_iscsi_portal_grp *pg = malloc(sizeof(*pg));
+
+ if (!pg) {
+ SPDK_ERRLOG("malloc() failed for portal group\n");
+ return NULL;
+ }
+
+ pg->ref = 0;
+ pg->tag = tag;
+
+ pthread_mutex_lock(&g_iscsi.mutex);
+ pg->disable_chap = g_iscsi.disable_chap;
+ pg->require_chap = g_iscsi.require_chap;
+ pg->mutual_chap = g_iscsi.mutual_chap;
+ pg->chap_group = g_iscsi.chap_group;
+ pthread_mutex_unlock(&g_iscsi.mutex);
+
+ TAILQ_INIT(&pg->head);
+
+ return pg;
+}
+
+void
+iscsi_portal_grp_destroy(struct spdk_iscsi_portal_grp *pg)
+{
+ struct spdk_iscsi_portal *p;
+
+ assert(pg != NULL);
+
+ SPDK_DEBUGLOG(SPDK_LOG_ISCSI, "iscsi_portal_grp_destroy\n");
+ while (!TAILQ_EMPTY(&pg->head)) {
+ p = TAILQ_FIRST(&pg->head);
+ TAILQ_REMOVE(&pg->head, p, per_pg_tailq);
+ iscsi_portal_destroy(p);
+ }
+ free(pg);
+}
+
+int
+iscsi_portal_grp_register(struct spdk_iscsi_portal_grp *pg)
+{
+ int rc = -1;
+ struct spdk_iscsi_portal_grp *tmp;
+
+ assert(pg != NULL);
+
+ pthread_mutex_lock(&g_iscsi.mutex);
+ tmp = iscsi_portal_grp_find_by_tag(pg->tag);
+ if (tmp == NULL) {
+ TAILQ_INSERT_TAIL(&g_iscsi.pg_head, pg, tailq);
+ rc = 0;
+ }
+ pthread_mutex_unlock(&g_iscsi.mutex);
+ return rc;
+}
+
+void
+iscsi_portal_grp_add_portal(struct spdk_iscsi_portal_grp *pg,
+ struct spdk_iscsi_portal *p)
+{
+ assert(pg != NULL);
+ assert(p != NULL);
+
+ p->group = pg;
+ TAILQ_INSERT_TAIL(&pg->head, p, per_pg_tailq);
+}
+
+int
+iscsi_portal_grp_set_chap_params(struct spdk_iscsi_portal_grp *pg,
+ bool disable_chap, bool require_chap,
+ bool mutual_chap, int32_t chap_group)
+{
+ if (!iscsi_check_chap_params(disable_chap, require_chap,
+ mutual_chap, chap_group)) {
+ return -EINVAL;
+ }
+
+ pg->disable_chap = disable_chap;
+ pg->require_chap = require_chap;
+ pg->mutual_chap = mutual_chap;
+ pg->chap_group = chap_group;
+
+ return 0;
+}
+
+static int
+iscsi_parse_portal_grp(struct spdk_conf_section *sp)
+{
+ struct spdk_iscsi_portal_grp *pg;
+ struct spdk_iscsi_portal *p;
+ const char *val;
+ char *label, *portal;
+ int i = 0, rc = 0;
+
+ SPDK_DEBUGLOG(SPDK_LOG_ISCSI, "add portal group (from config file) %d\n",
+ spdk_conf_section_get_num(sp));
+
+ val = spdk_conf_section_get_val(sp, "Comment");
+ if (val != NULL) {
+ SPDK_DEBUGLOG(SPDK_LOG_ISCSI, "Comment %s\n", val);
+ }
+
+ pg = iscsi_portal_grp_create(spdk_conf_section_get_num(sp));
+ if (!pg) {
+ SPDK_ERRLOG("portal group malloc error (%s)\n", spdk_conf_section_get_name(sp));
+ return -1;
+ }
+
+ for (i = 0; ; i++) {
+ label = spdk_conf_section_get_nmval(sp, "Portal", i, 0);
+ portal = spdk_conf_section_get_nmval(sp, "Portal", i, 1);
+ if (label == NULL || portal == NULL) {
+ break;
+ }
+
+ rc = iscsi_parse_portal(portal, &p);
+ if (rc < 0) {
+ SPDK_ERRLOG("parse portal error (%s)\n", portal);
+ goto error;
+ }
+
+ SPDK_DEBUGLOG(SPDK_LOG_ISCSI,
+ "RIndex=%d, Host=%s, Port=%s, Tag=%d\n",
+ i, p->host, p->port, spdk_conf_section_get_num(sp));
+
+ iscsi_portal_grp_add_portal(pg, p);
+ }
+
+ rc = iscsi_portal_grp_open(pg);
+ if (rc != 0) {
+ SPDK_ERRLOG("portal_grp_open failed\n");
+ goto error;
+ }
+
+ /* Add portal group to the end of the pg list */
+ rc = iscsi_portal_grp_register(pg);
+ if (rc != 0) {
+ SPDK_ERRLOG("register portal failed\n");
+ goto error;
+ }
+
+ return 0;
+
+error:
+ iscsi_portal_grp_release(pg);
+ return -1;
+}
+
+struct spdk_iscsi_portal_grp *
+iscsi_portal_grp_find_by_tag(int tag)
+{
+ struct spdk_iscsi_portal_grp *pg;
+
+ TAILQ_FOREACH(pg, &g_iscsi.pg_head, tailq) {
+ if (pg->tag == tag) {
+ return pg;
+ }
+ }
+
+ return NULL;
+}
+
+int
+iscsi_parse_portal_grps(void)
+{
+ int rc = 0;
+ struct spdk_conf_section *sp;
+
+ sp = spdk_conf_first_section(NULL);
+ while (sp != NULL) {
+ if (spdk_conf_section_match_prefix(sp, "PortalGroup")) {
+ if (spdk_conf_section_get_num(sp) == 0) {
+ SPDK_ERRLOG("Group 0 is invalid\n");
+ return -1;
+ }
+
+ /* Build portal group from cfg section PortalGroup */
+ rc = iscsi_parse_portal_grp(sp);
+ if (rc < 0) {
+ SPDK_ERRLOG("parse_portal_group() failed\n");
+ return -1;
+ }
+ }
+ sp = spdk_conf_next_section(sp);
+ }
+ return 0;
+}
+
+void
+iscsi_portal_grps_destroy(void)
+{
+ struct spdk_iscsi_portal_grp *pg;
+
+ SPDK_DEBUGLOG(SPDK_LOG_ISCSI, "iscsi_portal_grps_destroy\n");
+ pthread_mutex_lock(&g_iscsi.mutex);
+ while (!TAILQ_EMPTY(&g_iscsi.pg_head)) {
+ pg = TAILQ_FIRST(&g_iscsi.pg_head);
+ TAILQ_REMOVE(&g_iscsi.pg_head, pg, tailq);
+ pthread_mutex_unlock(&g_iscsi.mutex);
+ iscsi_portal_grp_destroy(pg);
+ pthread_mutex_lock(&g_iscsi.mutex);
+ }
+ pthread_mutex_unlock(&g_iscsi.mutex);
+}
+
+int
+iscsi_portal_grp_open(struct spdk_iscsi_portal_grp *pg)
+{
+ struct spdk_iscsi_portal *p;
+ int rc;
+
+ TAILQ_FOREACH(p, &pg->head, per_pg_tailq) {
+ rc = iscsi_portal_open(p);
+ if (rc < 0) {
+ return rc;
+ }
+ }
+ return 0;
+}
+
+static void
+iscsi_portal_grp_close(struct spdk_iscsi_portal_grp *pg)
+{
+ struct spdk_iscsi_portal *p;
+
+ TAILQ_FOREACH(p, &pg->head, per_pg_tailq) {
+ iscsi_portal_close(p);
+ }
+}
+
+void
+iscsi_portal_grp_close_all(void)
+{
+ struct spdk_iscsi_portal_grp *pg;
+
+ SPDK_DEBUGLOG(SPDK_LOG_ISCSI, "iscsi_portal_grp_close_all\n");
+ pthread_mutex_lock(&g_iscsi.mutex);
+ TAILQ_FOREACH(pg, &g_iscsi.pg_head, tailq) {
+ iscsi_portal_grp_close(pg);
+ }
+ pthread_mutex_unlock(&g_iscsi.mutex);
+}
+
+struct spdk_iscsi_portal_grp *
+iscsi_portal_grp_unregister(int tag)
+{
+ struct spdk_iscsi_portal_grp *pg;
+
+ pthread_mutex_lock(&g_iscsi.mutex);
+ TAILQ_FOREACH(pg, &g_iscsi.pg_head, tailq) {
+ if (pg->tag == tag) {
+ TAILQ_REMOVE(&g_iscsi.pg_head, pg, tailq);
+ pthread_mutex_unlock(&g_iscsi.mutex);
+ return pg;
+ }
+ }
+ pthread_mutex_unlock(&g_iscsi.mutex);
+ return NULL;
+}
+
+void
+iscsi_portal_grp_release(struct spdk_iscsi_portal_grp *pg)
+{
+ iscsi_portal_grp_close(pg);
+ iscsi_portal_grp_destroy(pg);
+}
+
+static const char *portal_group_section = \
+ "\n"
+ "# Users must change the PortalGroup section(s) to match the IP addresses\n"
+ "# for their environment.\n"
+ "# PortalGroup sections define which network portals the iSCSI target\n"
+ "# will use to listen for incoming connections. These are also used to\n"
+ "# determine which targets are accessible over each portal group.\n"
+ "# Up to 1024 Portal directives are allowed. These define the network\n"
+ "# portals of the portal group. The user must specify a IP address\n"
+ "# for each network portal, and may optionally specify a port.\n"
+ "# If the port is omitted, 3260 will be used\n"
+ "# Syntax:\n"
+ "# Portal <Name> <IP address>[:<port>]\n";
+
+#define PORTAL_GROUP_TMPL \
+"[PortalGroup%d]\n" \
+" Comment \"Portal%d\"\n"
+
+#define PORTAL_TMPL \
+" Portal DA1 %s:%s\n"
+
+void
+iscsi_portal_grps_config_text(FILE *fp)
+{
+ struct spdk_iscsi_portal *p = NULL;
+ struct spdk_iscsi_portal_grp *pg = NULL;
+
+ /* Create portal group section */
+ fprintf(fp, "%s", portal_group_section);
+
+ /* Dump portal groups */
+ TAILQ_FOREACH(pg, &g_iscsi.pg_head, tailq) {
+ if (NULL == pg) { continue; }
+ fprintf(fp, PORTAL_GROUP_TMPL, pg->tag, pg->tag);
+ /* Dump portals */
+ TAILQ_FOREACH(p, &pg->head, per_pg_tailq) {
+ if (NULL == p) { continue; }
+ fprintf(fp, PORTAL_TMPL, p->host, p->port);
+ }
+ }
+}
+
+static void
+iscsi_portal_grp_info_json(struct spdk_iscsi_portal_grp *pg,
+ struct spdk_json_write_ctx *w)
+{
+ struct spdk_iscsi_portal *portal;
+
+ spdk_json_write_object_begin(w);
+
+ spdk_json_write_named_int32(w, "tag", pg->tag);
+
+ spdk_json_write_named_array_begin(w, "portals");
+ TAILQ_FOREACH(portal, &pg->head, per_pg_tailq) {
+ spdk_json_write_object_begin(w);
+
+ spdk_json_write_named_string(w, "host", portal->host);
+ spdk_json_write_named_string(w, "port", portal->port);
+
+ spdk_json_write_object_end(w);
+ }
+ spdk_json_write_array_end(w);
+
+ spdk_json_write_object_end(w);
+}
+
+static void
+iscsi_portal_grp_config_json(struct spdk_iscsi_portal_grp *pg,
+ struct spdk_json_write_ctx *w)
+{
+ spdk_json_write_object_begin(w);
+
+ spdk_json_write_named_string(w, "method", "iscsi_create_portal_group");
+
+ spdk_json_write_name(w, "params");
+ iscsi_portal_grp_info_json(pg, w);
+
+ spdk_json_write_object_end(w);
+}
+
+void
+iscsi_portal_grps_info_json(struct spdk_json_write_ctx *w)
+{
+ struct spdk_iscsi_portal_grp *pg;
+
+ TAILQ_FOREACH(pg, &g_iscsi.pg_head, tailq) {
+ iscsi_portal_grp_info_json(pg, w);
+ }
+}
+
+void
+iscsi_portal_grps_config_json(struct spdk_json_write_ctx *w)
+{
+ struct spdk_iscsi_portal_grp *pg;
+
+ TAILQ_FOREACH(pg, &g_iscsi.pg_head, tailq) {
+ iscsi_portal_grp_config_json(pg, w);
+ }
+}
diff --git a/src/spdk/lib/iscsi/portal_grp.h b/src/spdk/lib/iscsi/portal_grp.h
new file mode 100644
index 000000000..7ac72e36c
--- /dev/null
+++ b/src/spdk/lib/iscsi/portal_grp.h
@@ -0,0 +1,90 @@
+/*-
+ * BSD LICENSE
+ *
+ * Copyright (C) 2008-2012 Daisuke Aoyama <aoyama@peach.ne.jp>.
+ * Copyright (c) Intel Corporation.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ * * Neither the name of Intel Corporation nor the names of its
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef SPDK_PORTAL_GRP_H
+#define SPDK_PORTAL_GRP_H
+
+#include "spdk/conf.h"
+#include "spdk/cpuset.h"
+#include "iscsi/iscsi.h"
+
+struct spdk_json_write_ctx;
+
+struct spdk_iscsi_portal {
+ struct spdk_iscsi_portal_grp *group;
+ char host[MAX_PORTAL_ADDR + 1];
+ char port[MAX_PORTAL_PORT + 1];
+ struct spdk_sock *sock;
+ struct spdk_poller *acceptor_poller;
+ TAILQ_ENTRY(spdk_iscsi_portal) per_pg_tailq;
+ TAILQ_ENTRY(spdk_iscsi_portal) g_tailq;
+};
+
+struct spdk_iscsi_portal_grp {
+ int ref;
+ int tag;
+ bool disable_chap;
+ bool require_chap;
+ bool mutual_chap;
+ int32_t chap_group;
+ TAILQ_ENTRY(spdk_iscsi_portal_grp) tailq;
+ TAILQ_HEAD(, spdk_iscsi_portal) head;
+};
+
+/* SPDK iSCSI Portal Group management API */
+
+struct spdk_iscsi_portal *iscsi_portal_create(const char *host, const char *port);
+void iscsi_portal_destroy(struct spdk_iscsi_portal *p);
+
+struct spdk_iscsi_portal_grp *iscsi_portal_grp_create(int tag);
+void iscsi_portal_grp_add_portal(struct spdk_iscsi_portal_grp *pg,
+ struct spdk_iscsi_portal *p);
+void iscsi_portal_grp_destroy(struct spdk_iscsi_portal_grp *pg);
+void iscsi_portal_grp_release(struct spdk_iscsi_portal_grp *pg);
+int iscsi_parse_portal_grps(void);
+void iscsi_portal_grps_destroy(void);
+int iscsi_portal_grp_register(struct spdk_iscsi_portal_grp *pg);
+struct spdk_iscsi_portal_grp *iscsi_portal_grp_unregister(int tag);
+struct spdk_iscsi_portal_grp *iscsi_portal_grp_find_by_tag(int tag);
+int iscsi_portal_grp_open(struct spdk_iscsi_portal_grp *pg);
+int iscsi_portal_grp_set_chap_params(struct spdk_iscsi_portal_grp *pg,
+ bool disable_chap, bool require_chap,
+ bool mutual_chap, int32_t chap_group);
+
+void iscsi_portal_grp_close_all(void);
+void iscsi_portal_grps_config_text(FILE *fp);
+void iscsi_portal_grps_info_json(struct spdk_json_write_ctx *w);
+void iscsi_portal_grps_config_json(struct spdk_json_write_ctx *w);
+
+#endif /* SPDK_PORTAL_GRP_H */
diff --git a/src/spdk/lib/iscsi/spdk_iscsi.map b/src/spdk/lib/iscsi/spdk_iscsi.map
new file mode 100644
index 000000000..0475a800d
--- /dev/null
+++ b/src/spdk/lib/iscsi/spdk_iscsi.map
@@ -0,0 +1,11 @@
+{
+ global:
+
+ # Functions used by other SPDK libraries
+ spdk_iscsi_init;
+ spdk_iscsi_fini;
+ spdk_iscsi_config_text;
+ spdk_iscsi_config_json;
+
+ local: *;
+};
diff --git a/src/spdk/lib/iscsi/task.c b/src/spdk/lib/iscsi/task.c
new file mode 100644
index 000000000..964621178
--- /dev/null
+++ b/src/spdk/lib/iscsi/task.c
@@ -0,0 +1,98 @@
+/*-
+ * BSD LICENSE
+ *
+ * Copyright (C) 2008-2012 Daisuke Aoyama <aoyama@peach.ne.jp>.
+ * Copyright (c) Intel Corporation.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ * * Neither the name of Intel Corporation nor the names of its
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "spdk/env.h"
+#include "spdk/log.h"
+#include "iscsi/conn.h"
+#include "iscsi/task.h"
+
+static void
+iscsi_task_free(struct spdk_scsi_task *scsi_task)
+{
+ struct spdk_iscsi_task *task = iscsi_task_from_scsi_task(scsi_task);
+
+ if (task->parent) {
+ if (task->scsi.dxfer_dir == SPDK_SCSI_DIR_FROM_DEV) {
+ assert(task->conn->data_in_cnt > 0);
+ task->conn->data_in_cnt--;
+ }
+
+ spdk_scsi_task_put(&task->parent->scsi);
+ task->parent = NULL;
+ }
+
+ iscsi_task_disassociate_pdu(task);
+ assert(task->conn->pending_task_cnt > 0);
+ task->conn->pending_task_cnt--;
+ spdk_mempool_put(g_iscsi.task_pool, (void *)task);
+}
+
+struct spdk_iscsi_task *
+iscsi_task_get(struct spdk_iscsi_conn *conn, struct spdk_iscsi_task *parent,
+ spdk_scsi_task_cpl cpl_fn)
+{
+ struct spdk_iscsi_task *task;
+
+ task = spdk_mempool_get(g_iscsi.task_pool);
+ if (!task) {
+ SPDK_ERRLOG("Unable to get task\n");
+ abort();
+ }
+
+ assert(conn != NULL);
+ memset(task, 0, sizeof(*task));
+ task->conn = conn;
+ assert(conn->pending_task_cnt < UINT32_MAX);
+ conn->pending_task_cnt++;
+ spdk_scsi_task_construct(&task->scsi,
+ cpl_fn,
+ iscsi_task_free);
+ if (parent) {
+ parent->scsi.ref++;
+ task->parent = parent;
+ task->tag = parent->tag;
+ task->lun_id = parent->lun_id;
+ task->scsi.dxfer_dir = parent->scsi.dxfer_dir;
+ task->scsi.transfer_len = parent->scsi.transfer_len;
+ task->scsi.lun = parent->scsi.lun;
+ task->scsi.cdb = parent->scsi.cdb;
+ task->scsi.target_port = parent->scsi.target_port;
+ task->scsi.initiator_port = parent->scsi.initiator_port;
+ if (task->scsi.dxfer_dir == SPDK_SCSI_DIR_FROM_DEV) {
+ conn->data_in_cnt++;
+ }
+ }
+
+ return task;
+}
diff --git a/src/spdk/lib/iscsi/task.h b/src/spdk/lib/iscsi/task.h
new file mode 100644
index 000000000..0ef48599a
--- /dev/null
+++ b/src/spdk/lib/iscsi/task.h
@@ -0,0 +1,188 @@
+/*-
+ * BSD LICENSE
+ *
+ * Copyright (C) 2008-2012 Daisuke Aoyama <aoyama@peach.ne.jp>.
+ * Copyright (c) Intel Corporation.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ * * Neither the name of Intel Corporation nor the names of its
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef SPDK_ISCSI_TASK_H
+#define SPDK_ISCSI_TASK_H
+
+#include "iscsi/iscsi.h"
+#include "spdk/scsi.h"
+#include "spdk/util.h"
+
+struct spdk_iscsi_task {
+ struct spdk_scsi_task scsi;
+
+ struct spdk_iscsi_task *parent;
+
+ uint8_t rsp_scsi_status;
+ uint8_t rsp_sense_data[32];
+ size_t rsp_sense_data_len;
+
+ struct spdk_iscsi_conn *conn;
+ struct spdk_iscsi_pdu *pdu;
+ uint32_t outstanding_r2t;
+
+ uint32_t desired_data_transfer_length;
+
+ /* Only valid for Read/Write */
+ uint32_t bytes_completed;
+
+ uint32_t data_out_cnt;
+
+ /*
+ * Tracks the current offset of large read io.
+ */
+ uint32_t current_datain_offset;
+
+ /*
+ * next_expected_r2t_offset is used when we receive
+ * the DataOUT PDU.
+ */
+ uint32_t next_expected_r2t_offset;
+
+ /*
+ * Tracks the length of the R2T that is in progress.
+ * Used to check that an R2T burst does not exceed
+ * MaxBurstLength.
+ */
+ uint32_t current_r2t_length;
+
+ /*
+ * next_r2t_offset is used when we are sending the
+ * R2T packet to keep track of next offset of r2t.
+ */
+ uint32_t next_r2t_offset;
+ uint32_t R2TSN;
+ uint32_t r2t_datasn; /* record next datasn for a r2tsn */
+ uint32_t acked_r2tsn; /* next r2tsn to be acked */
+ uint32_t datain_datasn;
+ uint32_t acked_data_sn; /* next expected datain datasn */
+ uint32_t ttt;
+ bool is_r2t_active;
+
+ uint32_t tag;
+
+ /**
+ * Record the lun id just in case the lun is invalid,
+ * which will happen when hot removing the lun.
+ */
+ int lun_id;
+
+ struct spdk_poller *mgmt_poller;
+
+ TAILQ_ENTRY(spdk_iscsi_task) link;
+
+ TAILQ_HEAD(subtask_list, spdk_iscsi_task) subtask_list;
+ TAILQ_ENTRY(spdk_iscsi_task) subtask_link;
+ bool is_queued; /* is queued in scsi layer for handling */
+};
+
+static inline void
+iscsi_task_put(struct spdk_iscsi_task *task)
+{
+ spdk_scsi_task_put(&task->scsi);
+}
+
+static inline struct spdk_iscsi_pdu *
+iscsi_task_get_pdu(struct spdk_iscsi_task *task)
+{
+ return task->pdu;
+}
+
+static inline void
+iscsi_task_set_pdu(struct spdk_iscsi_task *task, struct spdk_iscsi_pdu *pdu)
+{
+ task->pdu = pdu;
+}
+
+static inline struct iscsi_bhs *
+iscsi_task_get_bhs(struct spdk_iscsi_task *task)
+{
+ return &iscsi_task_get_pdu(task)->bhs;
+}
+
+static inline void
+iscsi_task_associate_pdu(struct spdk_iscsi_task *task, struct spdk_iscsi_pdu *pdu)
+{
+ iscsi_task_set_pdu(task, pdu);
+ pdu->ref++;
+}
+
+static inline void
+iscsi_task_disassociate_pdu(struct spdk_iscsi_task *task)
+{
+ if (iscsi_task_get_pdu(task)) {
+ iscsi_put_pdu(iscsi_task_get_pdu(task));
+ iscsi_task_set_pdu(task, NULL);
+ }
+}
+
+static inline int
+iscsi_task_is_immediate(struct spdk_iscsi_task *task)
+{
+ struct iscsi_bhs_scsi_req *scsi_req;
+
+ scsi_req = (struct iscsi_bhs_scsi_req *)iscsi_task_get_bhs(task);
+ return (scsi_req->immediate == 1);
+}
+
+static inline int
+iscsi_task_is_read(struct spdk_iscsi_task *task)
+{
+ struct iscsi_bhs_scsi_req *scsi_req;
+
+ scsi_req = (struct iscsi_bhs_scsi_req *)iscsi_task_get_bhs(task);
+ return (scsi_req->read_bit == 1);
+}
+
+struct spdk_iscsi_task *iscsi_task_get(struct spdk_iscsi_conn *conn,
+ struct spdk_iscsi_task *parent,
+ spdk_scsi_task_cpl cpl_fn);
+
+static inline struct spdk_iscsi_task *
+iscsi_task_from_scsi_task(struct spdk_scsi_task *task)
+{
+ return SPDK_CONTAINEROF(task, struct spdk_iscsi_task, scsi);
+}
+
+static inline struct spdk_iscsi_task *
+iscsi_task_get_primary(struct spdk_iscsi_task *task)
+{
+ if (task->parent) {
+ return task->parent;
+ } else {
+ return task;
+ }
+}
+
+#endif /* SPDK_ISCSI_TASK_H */
diff --git a/src/spdk/lib/iscsi/tgt_node.c b/src/spdk/lib/iscsi/tgt_node.c
new file mode 100644
index 000000000..0807a3384
--- /dev/null
+++ b/src/spdk/lib/iscsi/tgt_node.c
@@ -0,0 +1,1607 @@
+/*-
+ * BSD LICENSE
+ *
+ * Copyright (C) 2008-2012 Daisuke Aoyama <aoyama@peach.ne.jp>.
+ * Copyright (c) Intel Corporation.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ * * Neither the name of Intel Corporation nor the names of its
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "spdk/stdinc.h"
+
+#include "spdk/conf.h"
+#include "spdk/sock.h"
+#include "spdk/scsi.h"
+
+#include "spdk_internal/log.h"
+
+#include "iscsi/iscsi.h"
+#include "iscsi/conn.h"
+#include "iscsi/tgt_node.h"
+#include "iscsi/portal_grp.h"
+#include "iscsi/init_grp.h"
+#include "iscsi/task.h"
+
+#define MAX_TMPBUF 4096
+#define MAX_MASKBUF 128
+
+static bool
+iscsi_ipv6_netmask_allow_addr(const char *netmask, const char *addr)
+{
+ struct in6_addr in6_mask;
+ struct in6_addr in6_addr;
+ char mask[MAX_MASKBUF];
+ const char *p;
+ size_t n;
+ int bits, bmask;
+ int i;
+
+ if (netmask[0] != '[') {
+ return false;
+ }
+ p = strchr(netmask, ']');
+ if (p == NULL) {
+ return false;
+ }
+ n = p - (netmask + 1);
+ if (n + 1 > sizeof mask) {
+ return false;
+ }
+
+ memcpy(mask, netmask + 1, n);
+ mask[n] = '\0';
+ p++;
+
+ if (p[0] == '/') {
+ bits = (int) strtol(p + 1, NULL, 10);
+ if (bits <= 0 || bits > 128) {
+ return false;
+ }
+ } else {
+ bits = 128;
+ }
+
+#if 0
+ SPDK_DEBUGLOG(SPDK_LOG_ISCSI, "input %s\n", addr);
+ SPDK_DEBUGLOG(SPDK_LOG_ISCSI, "mask %s / %d\n", mask, bits);
+#endif
+
+ /* presentation to network order binary */
+ if (inet_pton(AF_INET6, mask, &in6_mask) <= 0
+ || inet_pton(AF_INET6, addr, &in6_addr) <= 0) {
+ return false;
+ }
+
+ /* check 128bits */
+ for (i = 0; i < (bits / 8); i++) {
+ if (in6_mask.s6_addr[i] != in6_addr.s6_addr[i]) {
+ return false;
+ }
+ }
+ if (bits % 8) {
+ bmask = (0xffU << (8 - (bits % 8))) & 0xffU;
+ if ((in6_mask.s6_addr[i] & bmask) != (in6_addr.s6_addr[i] & bmask)) {
+ return false;
+ }
+ }
+
+ /* match */
+ return true;
+}
+
+static bool
+iscsi_ipv4_netmask_allow_addr(const char *netmask, const char *addr)
+{
+ struct in_addr in4_mask;
+ struct in_addr in4_addr;
+ char mask[MAX_MASKBUF];
+ const char *p;
+ uint32_t bmask;
+ size_t n;
+ int bits;
+
+ p = strchr(netmask, '/');
+ if (p == NULL) {
+ p = netmask + strlen(netmask);
+ }
+ n = p - netmask;
+ if (n + 1 > sizeof mask) {
+ return false;
+ }
+
+ memcpy(mask, netmask, n);
+ mask[n] = '\0';
+
+ if (p[0] == '/') {
+ bits = (int) strtol(p + 1, NULL, 10);
+ if (bits <= 0 || bits > 32) {
+ return false;
+ }
+ } else {
+ bits = 32;
+ }
+
+ /* presentation to network order binary */
+ if (inet_pton(AF_INET, mask, &in4_mask) <= 0
+ || inet_pton(AF_INET, addr, &in4_addr) <= 0) {
+ return false;
+ }
+
+ /* check 32bits */
+ bmask = (0xffffffffU << (32 - bits)) & 0xffffffffU;
+ if ((ntohl(in4_mask.s_addr) & bmask) != (ntohl(in4_addr.s_addr) & bmask)) {
+ return false;
+ }
+
+ /* match */
+ return true;
+}
+
+static bool
+iscsi_netmask_allow_addr(const char *netmask, const char *addr)
+{
+ if (netmask == NULL || addr == NULL) {
+ return false;
+ }
+ if (strcasecmp(netmask, "ANY") == 0) {
+ return true;
+ }
+ if (netmask[0] == '[') {
+ /* IPv6 */
+ if (iscsi_ipv6_netmask_allow_addr(netmask, addr)) {
+ return true;
+ }
+ } else {
+ /* IPv4 */
+ if (iscsi_ipv4_netmask_allow_addr(netmask, addr)) {
+ return true;
+ }
+ }
+ return false;
+}
+
+static bool
+iscsi_init_grp_allow_addr(struct spdk_iscsi_init_grp *igp,
+ const char *addr)
+{
+ struct spdk_iscsi_initiator_netmask *imask;
+
+ TAILQ_FOREACH(imask, &igp->netmask_head, tailq) {
+ SPDK_DEBUGLOG(SPDK_LOG_ISCSI, "netmask=%s, addr=%s\n",
+ imask->mask, addr);
+ if (iscsi_netmask_allow_addr(imask->mask, addr)) {
+ return true;
+ }
+ }
+ return false;
+}
+
+static int
+iscsi_init_grp_allow_iscsi_name(struct spdk_iscsi_init_grp *igp,
+ const char *iqn, bool *result)
+{
+ struct spdk_iscsi_initiator_name *iname;
+
+ TAILQ_FOREACH(iname, &igp->initiator_head, tailq) {
+ /* denied if iqn is matched */
+ if ((iname->name[0] == '!')
+ && (strcasecmp(&iname->name[1], "ANY") == 0
+ || strcasecmp(&iname->name[1], iqn) == 0)) {
+ *result = false;
+ return 0;
+ }
+ /* allowed if iqn is matched */
+ if (strcasecmp(iname->name, "ANY") == 0
+ || strcasecmp(iname->name, iqn) == 0) {
+ *result = true;
+ return 0;
+ }
+ }
+ return -1;
+}
+
+static struct spdk_iscsi_pg_map *
+iscsi_tgt_node_find_pg_map(struct spdk_iscsi_tgt_node *target,
+ struct spdk_iscsi_portal_grp *pg);
+
+bool
+iscsi_tgt_node_access(struct spdk_iscsi_conn *conn,
+ struct spdk_iscsi_tgt_node *target, const char *iqn, const char *addr)
+{
+ struct spdk_iscsi_portal_grp *pg;
+ struct spdk_iscsi_pg_map *pg_map;
+ struct spdk_iscsi_ig_map *ig_map;
+ int rc;
+ bool allowed = false;
+
+ if (conn == NULL || target == NULL || iqn == NULL || addr == NULL) {
+ return false;
+ }
+ pg = conn->portal->group;
+
+ SPDK_DEBUGLOG(SPDK_LOG_ISCSI, "pg=%d, iqn=%s, addr=%s\n",
+ pg->tag, iqn, addr);
+ pg_map = iscsi_tgt_node_find_pg_map(target, pg);
+ if (pg_map == NULL) {
+ return false;
+ }
+ TAILQ_FOREACH(ig_map, &pg_map->ig_map_head, tailq) {
+ rc = iscsi_init_grp_allow_iscsi_name(ig_map->ig, iqn, &allowed);
+ if (rc == 0) {
+ if (allowed == false) {
+ goto denied;
+ } else {
+ if (iscsi_init_grp_allow_addr(ig_map->ig, addr)) {
+ return true;
+ }
+ }
+ } else {
+ /* netmask is denied in this initiator group */
+ }
+ }
+
+denied:
+ SPDK_DEBUGLOG(SPDK_LOG_ISCSI, "access denied from %s (%s) to %s (%s:%s,%d)\n",
+ iqn, addr, target->name, conn->portal_host,
+ conn->portal_port, conn->pg_tag);
+ return false;
+}
+
+static bool
+iscsi_tgt_node_allow_iscsi_name(struct spdk_iscsi_tgt_node *target, const char *iqn)
+{
+ struct spdk_iscsi_pg_map *pg_map;
+ struct spdk_iscsi_ig_map *ig_map;
+ int rc;
+ bool result = false;
+
+ if (target == NULL || iqn == NULL) {
+ return false;
+ }
+
+ TAILQ_FOREACH(pg_map, &target->pg_map_head, tailq) {
+ TAILQ_FOREACH(ig_map, &pg_map->ig_map_head, tailq) {
+ rc = iscsi_init_grp_allow_iscsi_name(ig_map->ig, iqn, &result);
+ if (rc == 0) {
+ return result;
+ }
+ }
+ }
+
+ return false;
+}
+
+int
+iscsi_send_tgts(struct spdk_iscsi_conn *conn, const char *iiqn,
+ const char *iaddr, const char *tiqn, uint8_t *data, int alloc_len,
+ int data_len)
+{
+ char buf[MAX_TMPBUF];
+ struct spdk_iscsi_portal_grp *pg;
+ struct spdk_iscsi_pg_map *pg_map;
+ struct spdk_iscsi_portal *p;
+ struct spdk_iscsi_tgt_node *target;
+ char *host;
+ int total;
+ int len;
+ int rc;
+
+ if (conn == NULL) {
+ return 0;
+ }
+
+ total = data_len;
+ if (alloc_len < 1) {
+ return 0;
+ }
+ if (total >= alloc_len) {
+ total = alloc_len;
+ data[total - 1] = '\0';
+ return total;
+ }
+
+ pthread_mutex_lock(&g_iscsi.mutex);
+ TAILQ_FOREACH(target, &g_iscsi.target_head, tailq) {
+ if (strcasecmp(tiqn, "ALL") != 0
+ && strcasecmp(tiqn, target->name) != 0) {
+ continue;
+ }
+ rc = iscsi_tgt_node_allow_iscsi_name(target, iiqn);
+ if (rc == 0) {
+ continue;
+ }
+
+ /* DO SENDTARGETS */
+ len = snprintf((char *) data + total, alloc_len - total,
+ "TargetName=%s", target->name);
+ total += len + 1;
+
+ /* write to data */
+ TAILQ_FOREACH(pg_map, &target->pg_map_head, tailq) {
+ pg = pg_map->pg;
+ TAILQ_FOREACH(p, &pg->head, per_pg_tailq) {
+ if (alloc_len - total < 1) {
+ pthread_mutex_unlock(&g_iscsi.mutex);
+ /* TODO: long text responses support */
+ SPDK_ERRLOG("SPDK doesn't support long text responses now, "
+ "you can use larger MaxRecvDataSegmentLength"
+ "value in initiator\n");
+ return alloc_len;
+ }
+ host = p->host;
+ /* wildcard? */
+ if (strcasecmp(host, "[::]") == 0
+ || strcasecmp(host, "0.0.0.0") == 0) {
+ if (spdk_sock_is_ipv6(conn->sock)) {
+ snprintf(buf, sizeof buf, "[%s]",
+ conn->target_addr);
+ host = buf;
+ } else if (spdk_sock_is_ipv4(conn->sock)) {
+ snprintf(buf, sizeof buf, "%s",
+ conn->target_addr);
+ host = buf;
+ } else {
+ /* skip portal for the family */
+ continue;
+ }
+ }
+ SPDK_DEBUGLOG(SPDK_LOG_ISCSI,
+ "TargetAddress=%s:%s,%d\n",
+ host, p->port, pg->tag);
+ len = snprintf((char *) data + total,
+ alloc_len - total,
+ "TargetAddress=%s:%s,%d",
+ host, p->port, pg->tag);
+ total += len + 1;
+ }
+ }
+ }
+ pthread_mutex_unlock(&g_iscsi.mutex);
+
+ return total;
+}
+
+struct spdk_iscsi_tgt_node *
+iscsi_find_tgt_node(const char *target_name)
+{
+ struct spdk_iscsi_tgt_node *target;
+
+ if (target_name == NULL) {
+ return NULL;
+ }
+ TAILQ_FOREACH(target, &g_iscsi.target_head, tailq) {
+ if (strcasecmp(target_name, target->name) == 0) {
+ return target;
+ }
+ }
+ SPDK_DEBUGLOG(SPDK_LOG_ISCSI, "can't find target %s\n", target_name);
+ return NULL;
+}
+
+static int
+iscsi_tgt_node_register(struct spdk_iscsi_tgt_node *target)
+{
+ pthread_mutex_lock(&g_iscsi.mutex);
+
+ if (iscsi_find_tgt_node(target->name) != NULL) {
+ pthread_mutex_unlock(&g_iscsi.mutex);
+ return -EEXIST;
+ }
+
+ TAILQ_INSERT_TAIL(&g_iscsi.target_head, target, tailq);
+
+ pthread_mutex_unlock(&g_iscsi.mutex);
+ return 0;
+}
+
+static int
+iscsi_tgt_node_unregister(struct spdk_iscsi_tgt_node *target)
+{
+ struct spdk_iscsi_tgt_node *t;
+
+ TAILQ_FOREACH(t, &g_iscsi.target_head, tailq) {
+ if (t == target) {
+ TAILQ_REMOVE(&g_iscsi.target_head, t, tailq);
+ return 0;
+ }
+ }
+
+ return -1;
+}
+
+static struct spdk_iscsi_ig_map *
+iscsi_pg_map_find_ig_map(struct spdk_iscsi_pg_map *pg_map,
+ struct spdk_iscsi_init_grp *ig)
+{
+ struct spdk_iscsi_ig_map *ig_map;
+
+ TAILQ_FOREACH(ig_map, &pg_map->ig_map_head, tailq) {
+ if (ig_map->ig == ig) {
+ return ig_map;
+ }
+ }
+
+ return NULL;
+}
+
+static struct spdk_iscsi_ig_map *
+iscsi_pg_map_add_ig_map(struct spdk_iscsi_pg_map *pg_map,
+ struct spdk_iscsi_init_grp *ig)
+{
+ struct spdk_iscsi_ig_map *ig_map;
+
+ if (iscsi_pg_map_find_ig_map(pg_map, ig) != NULL) {
+ return NULL;
+ }
+
+ ig_map = malloc(sizeof(*ig_map));
+ if (ig_map == NULL) {
+ return NULL;
+ }
+
+ ig_map->ig = ig;
+ ig->ref++;
+ pg_map->num_ig_maps++;
+ TAILQ_INSERT_TAIL(&pg_map->ig_map_head, ig_map, tailq);
+
+ return ig_map;
+}
+
+static void
+_iscsi_pg_map_delete_ig_map(struct spdk_iscsi_pg_map *pg_map,
+ struct spdk_iscsi_ig_map *ig_map)
+{
+ TAILQ_REMOVE(&pg_map->ig_map_head, ig_map, tailq);
+ pg_map->num_ig_maps--;
+ ig_map->ig->ref--;
+ free(ig_map);
+}
+
+static int
+iscsi_pg_map_delete_ig_map(struct spdk_iscsi_pg_map *pg_map,
+ struct spdk_iscsi_init_grp *ig)
+{
+ struct spdk_iscsi_ig_map *ig_map;
+
+ ig_map = iscsi_pg_map_find_ig_map(pg_map, ig);
+ if (ig_map == NULL) {
+ return -ENOENT;
+ }
+
+ _iscsi_pg_map_delete_ig_map(pg_map, ig_map);
+ return 0;
+}
+
+static void
+iscsi_pg_map_delete_all_ig_maps(struct spdk_iscsi_pg_map *pg_map)
+{
+ struct spdk_iscsi_ig_map *ig_map, *tmp;
+
+ TAILQ_FOREACH_SAFE(ig_map, &pg_map->ig_map_head, tailq, tmp) {
+ _iscsi_pg_map_delete_ig_map(pg_map, ig_map);
+ }
+}
+
+static struct spdk_iscsi_pg_map *
+iscsi_tgt_node_find_pg_map(struct spdk_iscsi_tgt_node *target,
+ struct spdk_iscsi_portal_grp *pg)
+{
+ struct spdk_iscsi_pg_map *pg_map;
+
+ TAILQ_FOREACH(pg_map, &target->pg_map_head, tailq) {
+ if (pg_map->pg == pg) {
+ return pg_map;
+ }
+ }
+
+ return NULL;
+}
+
+static struct spdk_iscsi_pg_map *
+iscsi_tgt_node_add_pg_map(struct spdk_iscsi_tgt_node *target,
+ struct spdk_iscsi_portal_grp *pg)
+{
+ struct spdk_iscsi_pg_map *pg_map;
+ char port_name[MAX_TMPBUF];
+ int rc;
+
+ if (iscsi_tgt_node_find_pg_map(target, pg) != NULL) {
+ return NULL;
+ }
+
+ if (target->num_pg_maps >= SPDK_SCSI_DEV_MAX_PORTS) {
+ SPDK_ERRLOG("Number of PG maps is more than allowed (max=%d)\n",
+ SPDK_SCSI_DEV_MAX_PORTS);
+ return NULL;
+ }
+
+ pg_map = malloc(sizeof(*pg_map));
+ if (pg_map == NULL) {
+ return NULL;
+ }
+
+ snprintf(port_name, sizeof(port_name), "%s,t,0x%4.4x",
+ spdk_scsi_dev_get_name(target->dev), pg->tag);
+ rc = spdk_scsi_dev_add_port(target->dev, pg->tag, port_name);
+ if (rc != 0) {
+ free(pg_map);
+ return NULL;
+ }
+
+ TAILQ_INIT(&pg_map->ig_map_head);
+ pg_map->num_ig_maps = 0;
+ pg->ref++;
+ pg_map->pg = pg;
+ target->num_pg_maps++;
+ TAILQ_INSERT_TAIL(&target->pg_map_head, pg_map, tailq);
+
+ return pg_map;
+}
+
+static void
+_iscsi_tgt_node_delete_pg_map(struct spdk_iscsi_tgt_node *target,
+ struct spdk_iscsi_pg_map *pg_map)
+{
+ TAILQ_REMOVE(&target->pg_map_head, pg_map, tailq);
+ target->num_pg_maps--;
+ pg_map->pg->ref--;
+
+ spdk_scsi_dev_delete_port(target->dev, pg_map->pg->tag);
+
+ free(pg_map);
+}
+
+static int
+iscsi_tgt_node_delete_pg_map(struct spdk_iscsi_tgt_node *target,
+ struct spdk_iscsi_portal_grp *pg)
+{
+ struct spdk_iscsi_pg_map *pg_map;
+
+ pg_map = iscsi_tgt_node_find_pg_map(target, pg);
+ if (pg_map == NULL) {
+ return -ENOENT;
+ }
+
+ if (pg_map->num_ig_maps > 0) {
+ SPDK_DEBUGLOG(SPDK_LOG_ISCSI, "delete %d ig_maps forcefully\n",
+ pg_map->num_ig_maps);
+ }
+
+ iscsi_pg_map_delete_all_ig_maps(pg_map);
+ _iscsi_tgt_node_delete_pg_map(target, pg_map);
+ return 0;
+}
+
+static void
+iscsi_tgt_node_delete_ig_maps(struct spdk_iscsi_tgt_node *target,
+ struct spdk_iscsi_init_grp *ig)
+{
+ struct spdk_iscsi_pg_map *pg_map, *tmp;
+
+ TAILQ_FOREACH_SAFE(pg_map, &target->pg_map_head, tailq, tmp) {
+ iscsi_pg_map_delete_ig_map(pg_map, ig);
+ if (pg_map->num_ig_maps == 0) {
+ _iscsi_tgt_node_delete_pg_map(target, pg_map);
+ }
+ }
+}
+
+static void
+iscsi_tgt_node_delete_all_pg_maps(struct spdk_iscsi_tgt_node *target)
+{
+ struct spdk_iscsi_pg_map *pg_map, *tmp;
+
+ TAILQ_FOREACH_SAFE(pg_map, &target->pg_map_head, tailq, tmp) {
+ iscsi_pg_map_delete_all_ig_maps(pg_map);
+ _iscsi_tgt_node_delete_pg_map(target, pg_map);
+ }
+}
+
+static void
+_iscsi_tgt_node_destruct(void *cb_arg, int rc)
+{
+ struct spdk_iscsi_tgt_node *target = cb_arg;
+ iscsi_tgt_node_destruct_cb destruct_cb_fn = target->destruct_cb_fn;
+ void *destruct_cb_arg = target->destruct_cb_arg;
+
+ if (rc != 0) {
+ if (destruct_cb_fn) {
+ destruct_cb_fn(destruct_cb_arg, rc);
+ }
+ return;
+ }
+
+ pthread_mutex_lock(&g_iscsi.mutex);
+ iscsi_tgt_node_delete_all_pg_maps(target);
+ pthread_mutex_unlock(&g_iscsi.mutex);
+
+ pthread_mutex_destroy(&target->mutex);
+ free(target);
+
+ if (destruct_cb_fn) {
+ destruct_cb_fn(destruct_cb_arg, 0);
+ }
+}
+
+static int
+iscsi_tgt_node_check_active_conns(void *arg)
+{
+ struct spdk_iscsi_tgt_node *target = arg;
+
+ if (iscsi_get_active_conns(target) != 0) {
+ return SPDK_POLLER_BUSY;
+ }
+
+ spdk_poller_unregister(&target->destruct_poller);
+
+ spdk_scsi_dev_destruct(target->dev, _iscsi_tgt_node_destruct, target);
+
+ return SPDK_POLLER_BUSY;
+}
+
+static void
+iscsi_tgt_node_destruct(struct spdk_iscsi_tgt_node *target,
+ iscsi_tgt_node_destruct_cb cb_fn, void *cb_arg)
+{
+ if (target == NULL) {
+ if (cb_fn) {
+ cb_fn(cb_arg, -ENOENT);
+ }
+ return;
+ }
+
+ if (target->destructed) {
+ SPDK_ERRLOG("Destructing %s is already started\n", target->name);
+ if (cb_fn) {
+ cb_fn(cb_arg, -EBUSY);
+ }
+ return;
+ }
+
+ target->destructed = true;
+ target->destruct_cb_fn = cb_fn;
+ target->destruct_cb_arg = cb_arg;
+
+ iscsi_conns_request_logout(target);
+
+ if (iscsi_get_active_conns(target) != 0) {
+ target->destruct_poller = SPDK_POLLER_REGISTER(iscsi_tgt_node_check_active_conns,
+ target, 10);
+ } else {
+ spdk_scsi_dev_destruct(target->dev, _iscsi_tgt_node_destruct, target);
+ }
+
+}
+
+static int
+iscsi_tgt_node_delete_pg_ig_map(struct spdk_iscsi_tgt_node *target,
+ int pg_tag, int ig_tag)
+{
+ struct spdk_iscsi_portal_grp *pg;
+ struct spdk_iscsi_init_grp *ig;
+ struct spdk_iscsi_pg_map *pg_map;
+ struct spdk_iscsi_ig_map *ig_map;
+
+ pg = iscsi_portal_grp_find_by_tag(pg_tag);
+ if (pg == NULL) {
+ SPDK_ERRLOG("%s: PortalGroup%d not found\n", target->name, pg_tag);
+ return -ENOENT;
+ }
+ ig = iscsi_init_grp_find_by_tag(ig_tag);
+ if (ig == NULL) {
+ SPDK_ERRLOG("%s: InitiatorGroup%d not found\n", target->name, ig_tag);
+ return -ENOENT;
+ }
+
+ pg_map = iscsi_tgt_node_find_pg_map(target, pg);
+ if (pg_map == NULL) {
+ SPDK_ERRLOG("%s: PortalGroup%d is not mapped\n", target->name, pg_tag);
+ return -ENOENT;
+ }
+ ig_map = iscsi_pg_map_find_ig_map(pg_map, ig);
+ if (ig_map == NULL) {
+ SPDK_ERRLOG("%s: InitiatorGroup%d is not mapped\n", target->name, pg_tag);
+ return -ENOENT;
+ }
+
+ _iscsi_pg_map_delete_ig_map(pg_map, ig_map);
+ if (pg_map->num_ig_maps == 0) {
+ _iscsi_tgt_node_delete_pg_map(target, pg_map);
+ }
+
+ return 0;
+}
+
+static int
+iscsi_tgt_node_add_pg_ig_map(struct spdk_iscsi_tgt_node *target,
+ int pg_tag, int ig_tag)
+{
+ struct spdk_iscsi_portal_grp *pg;
+ struct spdk_iscsi_pg_map *pg_map;
+ struct spdk_iscsi_init_grp *ig;
+ struct spdk_iscsi_ig_map *ig_map;
+ bool new_pg_map = false;
+
+ pg = iscsi_portal_grp_find_by_tag(pg_tag);
+ if (pg == NULL) {
+ SPDK_ERRLOG("%s: PortalGroup%d not found\n", target->name, pg_tag);
+ return -ENOENT;
+ }
+ ig = iscsi_init_grp_find_by_tag(ig_tag);
+ if (ig == NULL) {
+ SPDK_ERRLOG("%s: InitiatorGroup%d not found\n", target->name, ig_tag);
+ return -ENOENT;
+ }
+
+ /* get existing pg_map or create new pg_map and add it to target */
+ pg_map = iscsi_tgt_node_find_pg_map(target, pg);
+ if (pg_map == NULL) {
+ pg_map = iscsi_tgt_node_add_pg_map(target, pg);
+ if (pg_map == NULL) {
+ goto failed;
+ }
+ new_pg_map = true;
+ }
+
+ /* create new ig_map and add it to pg_map */
+ ig_map = iscsi_pg_map_add_ig_map(pg_map, ig);
+ if (ig_map == NULL) {
+ goto failed;
+ }
+
+ return 0;
+
+failed:
+ if (new_pg_map) {
+ _iscsi_tgt_node_delete_pg_map(target, pg_map);
+ }
+
+ return -1;
+}
+
+int
+iscsi_target_node_add_pg_ig_maps(struct spdk_iscsi_tgt_node *target,
+ int *pg_tag_list, int *ig_tag_list, uint16_t num_maps)
+{
+ uint16_t i;
+ int rc;
+
+ pthread_mutex_lock(&g_iscsi.mutex);
+ for (i = 0; i < num_maps; i++) {
+ rc = iscsi_tgt_node_add_pg_ig_map(target, pg_tag_list[i],
+ ig_tag_list[i]);
+ if (rc != 0) {
+ SPDK_ERRLOG("could not add map to target\n");
+ goto invalid;
+ }
+ }
+ pthread_mutex_unlock(&g_iscsi.mutex);
+ return 0;
+
+invalid:
+ for (; i > 0; --i) {
+ iscsi_tgt_node_delete_pg_ig_map(target, pg_tag_list[i - 1],
+ ig_tag_list[i - 1]);
+ }
+ pthread_mutex_unlock(&g_iscsi.mutex);
+ return -1;
+}
+
+int
+iscsi_target_node_remove_pg_ig_maps(struct spdk_iscsi_tgt_node *target,
+ int *pg_tag_list, int *ig_tag_list, uint16_t num_maps)
+{
+ uint16_t i;
+ int rc;
+
+ pthread_mutex_lock(&g_iscsi.mutex);
+ for (i = 0; i < num_maps; i++) {
+ rc = iscsi_tgt_node_delete_pg_ig_map(target, pg_tag_list[i],
+ ig_tag_list[i]);
+ if (rc != 0) {
+ SPDK_ERRLOG("could not delete map from target\n");
+ goto invalid;
+ }
+ }
+ pthread_mutex_unlock(&g_iscsi.mutex);
+ return 0;
+
+invalid:
+ for (; i > 0; --i) {
+ rc = iscsi_tgt_node_add_pg_ig_map(target, pg_tag_list[i - 1],
+ ig_tag_list[i - 1]);
+ if (rc != 0) {
+ iscsi_tgt_node_delete_all_pg_maps(target);
+ break;
+ }
+ }
+ pthread_mutex_unlock(&g_iscsi.mutex);
+ return -1;
+}
+
+static int
+check_iscsi_name(const char *name)
+{
+ const unsigned char *up = (const unsigned char *) name;
+ size_t n;
+
+ /* valid iSCSI name no larger than 223 bytes */
+ if (strlen(name) > MAX_TARGET_NAME) {
+ return -1;
+ }
+
+ /* valid iSCSI name? */
+ for (n = 0; up[n] != 0; n++) {
+ if (up[n] > 0x00U && up[n] <= 0x2cU) {
+ return -1;
+ }
+ if (up[n] == 0x2fU) {
+ return -1;
+ }
+ if (up[n] >= 0x3bU && up[n] <= 0x40U) {
+ return -1;
+ }
+ if (up[n] >= 0x5bU && up[n] <= 0x60U) {
+ return -1;
+ }
+ if (up[n] >= 0x7bU && up[n] <= 0x7fU) {
+ return -1;
+ }
+ if (isspace(up[n])) {
+ return -1;
+ }
+ }
+ /* valid format? */
+ if (strncasecmp(name, "iqn.", 4) == 0) {
+ /* iqn.YYYY-MM.reversed.domain.name */
+ if (!isdigit(up[4]) || !isdigit(up[5]) || !isdigit(up[6])
+ || !isdigit(up[7]) || up[8] != '-' || !isdigit(up[9])
+ || !isdigit(up[10]) || up[11] != '.') {
+ SPDK_ERRLOG("invalid iqn format. "
+ "expect \"iqn.YYYY-MM.reversed.domain.name\"\n");
+ return -1;
+ }
+ } else if (strncasecmp(name, "eui.", 4) == 0) {
+ /* EUI-64 -> 16bytes */
+ /* XXX */
+ } else if (strncasecmp(name, "naa.", 4) == 0) {
+ /* 64bit -> 16bytes, 128bit -> 32bytes */
+ /* XXX */
+ }
+ /* OK */
+ return 0;
+}
+
+bool
+iscsi_check_chap_params(bool disable, bool require, bool mutual, int group)
+{
+ if (group < 0) {
+ SPDK_ERRLOG("Invalid auth group ID (%d)\n", group);
+ return false;
+ }
+ if ((!disable && !require && !mutual) || /* Auto */
+ (disable && !require && !mutual) || /* None */
+ (!disable && require && !mutual) || /* CHAP */
+ (!disable && require && mutual)) { /* CHAP Mutual */
+ return true;
+ }
+ SPDK_ERRLOG("Invalid combination of CHAP params (d=%d,r=%d,m=%d)\n",
+ disable, require, mutual);
+ return false;
+}
+
+struct spdk_iscsi_tgt_node *iscsi_tgt_node_construct(int target_index,
+ const char *name, const char *alias,
+ int *pg_tag_list, int *ig_tag_list, uint16_t num_maps,
+ const char *bdev_name_list[], int *lun_id_list, int num_luns,
+ int queue_depth,
+ bool disable_chap, bool require_chap, bool mutual_chap, int chap_group,
+ bool header_digest, bool data_digest)
+{
+ char fullname[MAX_TMPBUF];
+ struct spdk_iscsi_tgt_node *target;
+ int rc;
+
+ if (!iscsi_check_chap_params(disable_chap, require_chap,
+ mutual_chap, chap_group)) {
+ return NULL;
+ }
+
+ if (num_maps == 0) {
+ SPDK_ERRLOG("num_maps = 0\n");
+ return NULL;
+ }
+
+ if (name == NULL) {
+ SPDK_ERRLOG("TargetName not found\n");
+ return NULL;
+ }
+
+ if (strncasecmp(name, "iqn.", 4) != 0
+ && strncasecmp(name, "eui.", 4) != 0
+ && strncasecmp(name, "naa.", 4) != 0) {
+ snprintf(fullname, sizeof(fullname), "%s:%s", g_iscsi.nodebase, name);
+ } else {
+ snprintf(fullname, sizeof(fullname), "%s", name);
+ }
+
+ if (check_iscsi_name(fullname) != 0) {
+ SPDK_ERRLOG("TargetName %s contains an invalid character or format.\n",
+ name);
+ return NULL;
+ }
+
+ target = calloc(1, sizeof(*target));
+ if (!target) {
+ SPDK_ERRLOG("could not allocate target\n");
+ return NULL;
+ }
+
+ rc = pthread_mutex_init(&target->mutex, NULL);
+ if (rc != 0) {
+ SPDK_ERRLOG("tgt_node%d: mutex_init() failed\n", target->num);
+ iscsi_tgt_node_destruct(target, NULL, NULL);
+ return NULL;
+ }
+
+ target->num = target_index;
+
+ memcpy(target->name, fullname, strlen(fullname));
+
+ if (alias != NULL) {
+ if (strlen(alias) > MAX_TARGET_NAME) {
+ iscsi_tgt_node_destruct(target, NULL, NULL);
+ return NULL;
+ }
+ memcpy(target->alias, alias, strlen(alias));
+ }
+
+ target->dev = spdk_scsi_dev_construct(fullname, bdev_name_list, lun_id_list, num_luns,
+ SPDK_SPC_PROTOCOL_IDENTIFIER_ISCSI, NULL, NULL);
+ if (!target->dev) {
+ SPDK_ERRLOG("Could not construct SCSI device\n");
+ iscsi_tgt_node_destruct(target, NULL, NULL);
+ return NULL;
+ }
+
+ TAILQ_INIT(&target->pg_map_head);
+ rc = iscsi_target_node_add_pg_ig_maps(target, pg_tag_list,
+ ig_tag_list, num_maps);
+ if (rc != 0) {
+ SPDK_ERRLOG("could not add map to target\n");
+ iscsi_tgt_node_destruct(target, NULL, NULL);
+ return NULL;
+ }
+
+ target->disable_chap = disable_chap;
+ target->require_chap = require_chap;
+ target->mutual_chap = mutual_chap;
+ target->chap_group = chap_group;
+ target->header_digest = header_digest;
+ target->data_digest = data_digest;
+
+ if (queue_depth > 0 && ((uint32_t)queue_depth <= g_iscsi.MaxQueueDepth)) {
+ target->queue_depth = queue_depth;
+ } else {
+ SPDK_DEBUGLOG(SPDK_LOG_ISCSI, "QueueDepth %d is invalid and %d is used instead.\n",
+ queue_depth, g_iscsi.MaxQueueDepth);
+ target->queue_depth = g_iscsi.MaxQueueDepth;
+ }
+
+ rc = iscsi_tgt_node_register(target);
+ if (rc != 0) {
+ SPDK_ERRLOG("register target is failed\n");
+ iscsi_tgt_node_destruct(target, NULL, NULL);
+ return NULL;
+ }
+
+ return target;
+}
+
+static int
+iscsi_parse_tgt_node(struct spdk_conf_section *sp)
+{
+ char buf[MAX_TMPBUF];
+ struct spdk_iscsi_tgt_node *target;
+ int pg_tag_list[MAX_TARGET_MAP], ig_tag_list[MAX_TARGET_MAP];
+ int num_target_maps;
+ const char *alias, *pg_tag, *ig_tag;
+ const char *ag_tag;
+ const char *val, *name;
+ int target_num, chap_group, pg_tag_i, ig_tag_i;
+ bool header_digest, data_digest;
+ bool disable_chap, require_chap, mutual_chap;
+ int i;
+ int lun_id_list[SPDK_SCSI_DEV_MAX_LUN];
+ const char *bdev_name_list[SPDK_SCSI_DEV_MAX_LUN];
+ int num_luns, queue_depth;
+
+ target_num = spdk_conf_section_get_num(sp);
+
+ SPDK_DEBUGLOG(SPDK_LOG_ISCSI, "add unit %d\n", target_num);
+
+ data_digest = false;
+ header_digest = false;
+
+ name = spdk_conf_section_get_val(sp, "TargetName");
+
+ if (name == NULL) {
+ SPDK_ERRLOG("tgt_node%d: TargetName not found\n", target_num);
+ return -1;
+ }
+
+ alias = spdk_conf_section_get_val(sp, "TargetAlias");
+
+ /* Setup initiator and portal group mapping */
+ val = spdk_conf_section_get_val(sp, "Mapping");
+ if (val == NULL) {
+ /* no map */
+ SPDK_ERRLOG("tgt_node%d: no Mapping\n", target_num);
+ return -1;
+ }
+
+ for (i = 0; i < MAX_TARGET_MAP; i++) {
+ val = spdk_conf_section_get_nmval(sp, "Mapping", i, 0);
+ if (val == NULL) {
+ break;
+ }
+ pg_tag = spdk_conf_section_get_nmval(sp, "Mapping", i, 0);
+ ig_tag = spdk_conf_section_get_nmval(sp, "Mapping", i, 1);
+ if (pg_tag == NULL || ig_tag == NULL) {
+ SPDK_ERRLOG("tgt_node%d: mapping error\n", target_num);
+ return -1;
+ }
+ if (strncasecmp(pg_tag, "PortalGroup",
+ strlen("PortalGroup")) != 0
+ || sscanf(pg_tag, "%*[^0-9]%d", &pg_tag_i) != 1) {
+ SPDK_ERRLOG("tgt_node%d: mapping portal error\n", target_num);
+ return -1;
+ }
+ if (strncasecmp(ig_tag, "InitiatorGroup",
+ strlen("InitiatorGroup")) != 0
+ || sscanf(ig_tag, "%*[^0-9]%d", &ig_tag_i) != 1) {
+ SPDK_ERRLOG("tgt_node%d: mapping initiator error\n", target_num);
+ return -1;
+ }
+ if (pg_tag_i < 1 || ig_tag_i < 1) {
+ SPDK_ERRLOG("tgt_node%d: invalid group tag\n", target_num);
+ return -1;
+ }
+ pg_tag_list[i] = pg_tag_i;
+ ig_tag_list[i] = ig_tag_i;
+ }
+
+ num_target_maps = i;
+
+ /* Setup AuthMethod */
+ val = spdk_conf_section_get_val(sp, "AuthMethod");
+ disable_chap = false;
+ require_chap = false;
+ mutual_chap = false;
+ if (val != NULL) {
+ for (i = 0; ; i++) {
+ val = spdk_conf_section_get_nmval(sp, "AuthMethod", 0, i);
+ if (val == NULL) {
+ break;
+ }
+ if (strcasecmp(val, "CHAP") == 0) {
+ require_chap = true;
+ } else if (strcasecmp(val, "Mutual") == 0) {
+ mutual_chap = true;
+ } else if (strcasecmp(val, "Auto") == 0) {
+ disable_chap = false;
+ require_chap = false;
+ mutual_chap = false;
+ } else if (strcasecmp(val, "None") == 0) {
+ disable_chap = true;
+ require_chap = false;
+ mutual_chap = false;
+ } else {
+ SPDK_ERRLOG("tgt_node%d: unknown auth\n", target_num);
+ return -1;
+ }
+ }
+ if (mutual_chap && !require_chap) {
+ SPDK_ERRLOG("tgt_node%d: Mutual but not CHAP\n", target_num);
+ return -1;
+ }
+ }
+ if (disable_chap) {
+ SPDK_DEBUGLOG(SPDK_LOG_ISCSI, "AuthMethod None\n");
+ } else if (!require_chap) {
+ SPDK_DEBUGLOG(SPDK_LOG_ISCSI, "AuthMethod Auto\n");
+ } else {
+ SPDK_DEBUGLOG(SPDK_LOG_ISCSI, "AuthMethod CHAP %s\n",
+ mutual_chap ? "Mutual" : "");
+ }
+
+ val = spdk_conf_section_get_val(sp, "AuthGroup");
+ if (val == NULL) {
+ chap_group = 0;
+ } else {
+ ag_tag = val;
+ if (strcasecmp(ag_tag, "None") == 0) {
+ chap_group = 0;
+ } else {
+ if (strncasecmp(ag_tag, "AuthGroup",
+ strlen("AuthGroup")) != 0
+ || sscanf(ag_tag, "%*[^0-9]%d", &chap_group) != 1) {
+ SPDK_ERRLOG("tgt_node%d: auth group error\n", target_num);
+ return -1;
+ }
+ if (chap_group == 0) {
+ SPDK_ERRLOG("tgt_node%d: invalid auth group 0\n", target_num);
+ return -1;
+ }
+ }
+ }
+ if (chap_group == 0) {
+ SPDK_DEBUGLOG(SPDK_LOG_ISCSI, "AuthGroup None\n");
+ } else {
+ SPDK_DEBUGLOG(SPDK_LOG_ISCSI, "AuthGroup AuthGroup%d\n", chap_group);
+ }
+
+ val = spdk_conf_section_get_val(sp, "UseDigest");
+ if (val != NULL) {
+ for (i = 0; ; i++) {
+ val = spdk_conf_section_get_nmval(sp, "UseDigest", 0, i);
+ if (val == NULL) {
+ break;
+ }
+ if (strcasecmp(val, "Header") == 0) {
+ header_digest = true;
+ } else if (strcasecmp(val, "Data") == 0) {
+ data_digest = true;
+ } else if (strcasecmp(val, "Auto") == 0) {
+ header_digest = false;
+ data_digest = false;
+ } else {
+ SPDK_ERRLOG("tgt_node%d: unknown digest\n", target_num);
+ return -1;
+ }
+ }
+ }
+ if (!header_digest && !data_digest) {
+ SPDK_DEBUGLOG(SPDK_LOG_ISCSI, "UseDigest Auto\n");
+ } else {
+ SPDK_DEBUGLOG(SPDK_LOG_ISCSI, "UseDigest %s %s\n",
+ header_digest ? "Header" : "",
+ data_digest ? "Data" : "");
+ }
+
+ val = spdk_conf_section_get_val(sp, "QueueDepth");
+ if (val == NULL) {
+ queue_depth = g_iscsi.MaxQueueDepth;
+ } else {
+ queue_depth = (int) strtol(val, NULL, 10);
+ }
+
+ num_luns = 0;
+
+ for (i = 0; i < SPDK_SCSI_DEV_MAX_LUN; i++) {
+ snprintf(buf, sizeof(buf), "LUN%d", i);
+ val = spdk_conf_section_get_val(sp, buf);
+ if (val == NULL) {
+ continue;
+ }
+
+ bdev_name_list[num_luns] = val;
+ lun_id_list[num_luns] = i;
+ num_luns++;
+ }
+
+ if (num_luns == 0) {
+ SPDK_ERRLOG("tgt_node%d: No LUN specified for target %s.\n", target_num, name);
+ return -1;
+ }
+
+ target = iscsi_tgt_node_construct(target_num, name, alias,
+ pg_tag_list, ig_tag_list, num_target_maps,
+ bdev_name_list, lun_id_list, num_luns, queue_depth,
+ disable_chap, require_chap, mutual_chap, chap_group,
+ header_digest, data_digest);
+
+ if (target == NULL) {
+ SPDK_ERRLOG("tgt_node%d: add_iscsi_target_node error\n", target_num);
+ return -1;
+ }
+
+ for (i = 0; i < SPDK_SCSI_DEV_MAX_LUN; i++) {
+ struct spdk_scsi_lun *lun = spdk_scsi_dev_get_lun(target->dev, i);
+
+ if (lun) {
+ SPDK_INFOLOG(SPDK_LOG_ISCSI, "device %d: LUN%d %s\n",
+ spdk_scsi_dev_get_id(target->dev),
+ spdk_scsi_lun_get_id(lun),
+ spdk_scsi_lun_get_bdev_name(lun));
+ }
+ }
+
+ return 0;
+}
+
+int iscsi_parse_tgt_nodes(void)
+{
+ struct spdk_conf_section *sp;
+ int rc;
+
+ SPDK_DEBUGLOG(SPDK_LOG_ISCSI, "iscsi_parse_tgt_nodes\n");
+
+ sp = spdk_conf_first_section(NULL);
+ while (sp != NULL) {
+ if (spdk_conf_section_match_prefix(sp, "TargetNode")) {
+ int tag = spdk_conf_section_get_num(sp);
+
+ if (tag > SPDK_TN_TAG_MAX) {
+ SPDK_ERRLOG("tag %d is invalid\n", tag);
+ return -1;
+ }
+ rc = iscsi_parse_tgt_node(sp);
+ if (rc < 0) {
+ SPDK_ERRLOG("spdk_iscsi_parse_tgt_node() failed\n");
+ return -1;
+ }
+ }
+ sp = spdk_conf_next_section(sp);
+ }
+ return 0;
+}
+
+void
+iscsi_shutdown_tgt_nodes(void)
+{
+ struct spdk_iscsi_tgt_node *target;
+
+ pthread_mutex_lock(&g_iscsi.mutex);
+ while (!TAILQ_EMPTY(&g_iscsi.target_head)) {
+ target = TAILQ_FIRST(&g_iscsi.target_head);
+ TAILQ_REMOVE(&g_iscsi.target_head, target, tailq);
+
+ pthread_mutex_unlock(&g_iscsi.mutex);
+
+ iscsi_tgt_node_destruct(target, NULL, NULL);
+
+ pthread_mutex_lock(&g_iscsi.mutex);
+ }
+ pthread_mutex_unlock(&g_iscsi.mutex);
+}
+
+void
+iscsi_shutdown_tgt_node_by_name(const char *target_name,
+ iscsi_tgt_node_destruct_cb cb_fn, void *cb_arg)
+{
+ struct spdk_iscsi_tgt_node *target;
+
+ pthread_mutex_lock(&g_iscsi.mutex);
+ target = iscsi_find_tgt_node(target_name);
+ if (target != NULL) {
+ iscsi_tgt_node_unregister(target);
+ pthread_mutex_unlock(&g_iscsi.mutex);
+
+ iscsi_tgt_node_destruct(target, cb_fn, cb_arg);
+
+ return;
+ }
+ pthread_mutex_unlock(&g_iscsi.mutex);
+
+ if (cb_fn) {
+ cb_fn(cb_arg, -ENOENT);
+ }
+}
+
+bool
+iscsi_tgt_node_is_destructed(struct spdk_iscsi_tgt_node *target)
+{
+ return target->destructed;
+}
+
+int
+iscsi_tgt_node_cleanup_luns(struct spdk_iscsi_conn *conn,
+ struct spdk_iscsi_tgt_node *target)
+{
+ int i;
+ struct spdk_iscsi_task *task;
+
+ for (i = 0; i < SPDK_SCSI_DEV_MAX_LUN; i++) {
+ struct spdk_scsi_lun *lun = spdk_scsi_dev_get_lun(target->dev, i);
+
+ if (!lun) {
+ continue;
+ }
+
+ /* we create a fake management task per LUN to cleanup */
+ task = iscsi_task_get(conn, NULL, iscsi_task_mgmt_cpl);
+ if (!task) {
+ SPDK_ERRLOG("Unable to acquire task\n");
+ return -1;
+ }
+
+ task->scsi.target_port = conn->target_port;
+ task->scsi.initiator_port = conn->initiator_port;
+ task->scsi.lun = lun;
+
+ iscsi_op_abort_task_set(task, SPDK_SCSI_TASK_FUNC_LUN_RESET);
+ }
+
+ return 0;
+}
+
+void iscsi_tgt_node_delete_map(struct spdk_iscsi_portal_grp *portal_group,
+ struct spdk_iscsi_init_grp *initiator_group)
+{
+ struct spdk_iscsi_tgt_node *target;
+
+ pthread_mutex_lock(&g_iscsi.mutex);
+ TAILQ_FOREACH(target, &g_iscsi.target_head, tailq) {
+ if (portal_group) {
+ iscsi_tgt_node_delete_pg_map(target, portal_group);
+ }
+ if (initiator_group) {
+ iscsi_tgt_node_delete_ig_maps(target, initiator_group);
+ }
+ }
+ pthread_mutex_unlock(&g_iscsi.mutex);
+}
+
+int
+iscsi_tgt_node_add_lun(struct spdk_iscsi_tgt_node *target,
+ const char *bdev_name, int lun_id)
+{
+ struct spdk_scsi_dev *dev;
+ int rc;
+
+ if (target->num_active_conns > 0) {
+ SPDK_ERRLOG("Target has active connections (count=%d)\n",
+ target->num_active_conns);
+ return -1;
+ }
+
+ if (lun_id < -1 || lun_id >= SPDK_SCSI_DEV_MAX_LUN) {
+ SPDK_ERRLOG("Specified LUN ID (%d) is invalid\n", lun_id);
+ return -1;
+ }
+
+ dev = target->dev;
+ if (dev == NULL) {
+ SPDK_ERRLOG("SCSI device is not found\n");
+ return -1;
+ }
+
+ rc = spdk_scsi_dev_add_lun(dev, bdev_name, lun_id, NULL, NULL);
+ if (rc != 0) {
+ SPDK_ERRLOG("spdk_scsi_dev_add_lun failed\n");
+ return -1;
+ }
+
+ return 0;
+}
+
+int
+iscsi_tgt_node_set_chap_params(struct spdk_iscsi_tgt_node *target,
+ bool disable_chap, bool require_chap,
+ bool mutual_chap, int32_t chap_group)
+{
+ if (!iscsi_check_chap_params(disable_chap, require_chap,
+ mutual_chap, chap_group)) {
+ return -EINVAL;
+ }
+
+ pthread_mutex_lock(&target->mutex);
+ target->disable_chap = disable_chap;
+ target->require_chap = require_chap;
+ target->mutual_chap = mutual_chap;
+ target->chap_group = chap_group;
+ pthread_mutex_unlock(&target->mutex);
+
+ return 0;
+}
+
+static const char *target_nodes_section = \
+ "\n"
+ "# Users should change the TargetNode section(s) below to match the\n"
+ "# desired iSCSI target node configuration.\n"
+ "# TargetName, Mapping, LUN0 are minimum required\n";
+
+#define TARGET_NODE_TMPL \
+"[TargetNode%d]\n" \
+" Comment \"Target%d\"\n" \
+" TargetName %s\n" \
+" TargetAlias \"%s\"\n"
+
+#define TARGET_NODE_PGIG_MAPPING_TMPL \
+" Mapping PortalGroup%d InitiatorGroup%d\n"
+
+#define TARGET_NODE_AUTH_TMPL \
+" AuthMethod %s\n" \
+" AuthGroup %s\n" \
+" UseDigest %s\n"
+
+#define TARGET_NODE_QD_TMPL \
+" QueueDepth %d\n\n"
+
+#define TARGET_NODE_LUN_TMPL \
+" LUN%d %s\n"
+
+void
+iscsi_tgt_nodes_config_text(FILE *fp)
+{
+ int l = 0;
+ struct spdk_scsi_dev *dev = NULL;
+ struct spdk_iscsi_tgt_node *target = NULL;
+ struct spdk_iscsi_pg_map *pg_map;
+ struct spdk_iscsi_ig_map *ig_map;
+
+ /* Create target nodes section */
+ fprintf(fp, "%s", target_nodes_section);
+
+ TAILQ_FOREACH(target, &g_iscsi.target_head, tailq) {
+ int idx;
+ const char *authmethod = "None";
+ char authgroup[32] = "None";
+ const char *usedigest = "Auto";
+
+ dev = target->dev;
+ if (NULL == dev) { continue; }
+
+ idx = target->num;
+ fprintf(fp, TARGET_NODE_TMPL, idx, idx, target->name, spdk_scsi_dev_get_name(dev));
+
+ TAILQ_FOREACH(pg_map, &target->pg_map_head, tailq) {
+ TAILQ_FOREACH(ig_map, &pg_map->ig_map_head, tailq) {
+ fprintf(fp, TARGET_NODE_PGIG_MAPPING_TMPL,
+ pg_map->pg->tag,
+ ig_map->ig->tag);
+ }
+ }
+
+ if (target->disable_chap) {
+ authmethod = "None";
+ } else if (!target->require_chap) {
+ authmethod = "Auto";
+ } else if (target->mutual_chap) {
+ authmethod = "CHAP Mutual";
+ } else {
+ authmethod = "CHAP";
+ }
+
+ if (target->chap_group > 0) {
+ snprintf(authgroup, sizeof(authgroup), "AuthGroup%d", target->chap_group);
+ }
+
+ if (target->header_digest) {
+ usedigest = "Header";
+ } else if (target->data_digest) {
+ usedigest = "Data";
+ }
+
+ fprintf(fp, TARGET_NODE_AUTH_TMPL,
+ authmethod, authgroup, usedigest);
+
+ for (l = 0; l < SPDK_SCSI_DEV_MAX_LUN; l++) {
+ struct spdk_scsi_lun *lun = spdk_scsi_dev_get_lun(dev, l);
+
+ if (!lun) {
+ continue;
+ }
+
+ fprintf(fp, TARGET_NODE_LUN_TMPL,
+ spdk_scsi_lun_get_id(lun),
+ spdk_scsi_lun_get_bdev_name(lun));
+ }
+
+ fprintf(fp, TARGET_NODE_QD_TMPL,
+ target->queue_depth);
+ }
+}
+
+static void
+iscsi_tgt_node_info_json(struct spdk_iscsi_tgt_node *target,
+ struct spdk_json_write_ctx *w)
+{
+ struct spdk_iscsi_pg_map *pg_map;
+ struct spdk_iscsi_ig_map *ig_map;
+ int i;
+
+ spdk_json_write_object_begin(w);
+
+ spdk_json_write_named_string(w, "name", target->name);
+
+ if (target->alias[0] != '\0') {
+ spdk_json_write_named_string(w, "alias_name", target->alias);
+ }
+
+ spdk_json_write_named_array_begin(w, "pg_ig_maps");
+ TAILQ_FOREACH(pg_map, &target->pg_map_head, tailq) {
+ TAILQ_FOREACH(ig_map, &pg_map->ig_map_head, tailq) {
+ spdk_json_write_object_begin(w);
+ spdk_json_write_named_int32(w, "pg_tag", pg_map->pg->tag);
+ spdk_json_write_named_int32(w, "ig_tag", ig_map->ig->tag);
+ spdk_json_write_object_end(w);
+ }
+ }
+ spdk_json_write_array_end(w);
+
+ spdk_json_write_named_array_begin(w, "luns");
+ for (i = 0; i < SPDK_SCSI_DEV_MAX_LUN; i++) {
+ struct spdk_scsi_lun *lun = spdk_scsi_dev_get_lun(target->dev, i);
+
+ if (lun) {
+ spdk_json_write_object_begin(w);
+ spdk_json_write_named_string(w, "bdev_name", spdk_scsi_lun_get_bdev_name(lun));
+ spdk_json_write_named_int32(w, "lun_id", spdk_scsi_lun_get_id(lun));
+ spdk_json_write_object_end(w);
+ }
+ }
+ spdk_json_write_array_end(w);
+
+ spdk_json_write_named_int32(w, "queue_depth", target->queue_depth);
+
+ spdk_json_write_named_bool(w, "disable_chap", target->disable_chap);
+ spdk_json_write_named_bool(w, "require_chap", target->require_chap);
+ spdk_json_write_named_bool(w, "mutual_chap", target->mutual_chap);
+ spdk_json_write_named_int32(w, "chap_group", target->chap_group);
+
+ spdk_json_write_named_bool(w, "header_digest", target->header_digest);
+ spdk_json_write_named_bool(w, "data_digest", target->data_digest);
+
+ spdk_json_write_object_end(w);
+}
+
+static void
+iscsi_tgt_node_config_json(struct spdk_iscsi_tgt_node *target,
+ struct spdk_json_write_ctx *w)
+{
+ spdk_json_write_object_begin(w);
+
+ spdk_json_write_named_string(w, "method", "iscsi_create_target_node");
+
+ spdk_json_write_name(w, "params");
+ iscsi_tgt_node_info_json(target, w);
+
+ spdk_json_write_object_end(w);
+}
+
+void
+iscsi_tgt_nodes_info_json(struct spdk_json_write_ctx *w)
+{
+ struct spdk_iscsi_tgt_node *target;
+
+ TAILQ_FOREACH(target, &g_iscsi.target_head, tailq) {
+ iscsi_tgt_node_info_json(target, w);
+ }
+}
+
+void
+iscsi_tgt_nodes_config_json(struct spdk_json_write_ctx *w)
+{
+ struct spdk_iscsi_tgt_node *target;
+
+ TAILQ_FOREACH(target, &g_iscsi.target_head, tailq) {
+ iscsi_tgt_node_config_json(target, w);
+ }
+}
diff --git a/src/spdk/lib/iscsi/tgt_node.h b/src/spdk/lib/iscsi/tgt_node.h
new file mode 100644
index 000000000..2787fac91
--- /dev/null
+++ b/src/spdk/lib/iscsi/tgt_node.h
@@ -0,0 +1,147 @@
+/*-
+ * BSD LICENSE
+ *
+ * Copyright (C) 2008-2012 Daisuke Aoyama <aoyama@peach.ne.jp>.
+ * Copyright (c) Intel Corporation.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ * * Neither the name of Intel Corporation nor the names of its
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef SPDK_ISCSI_TGT_NODE_H_
+#define SPDK_ISCSI_TGT_NODE_H_
+
+#include "spdk/stdinc.h"
+
+#include "iscsi/iscsi.h"
+
+struct spdk_iscsi_conn;
+struct spdk_iscsi_init_grp;
+struct spdk_iscsi_portal_grp;
+struct spdk_iscsi_portal;
+struct spdk_json_write_ctx;
+
+#define MAX_TARGET_MAP 256
+#define SPDK_TN_TAG_MAX 0x0000ffff
+
+typedef void (*iscsi_tgt_node_destruct_cb)(void *cb_arg, int rc);
+
+struct spdk_iscsi_ig_map {
+ struct spdk_iscsi_init_grp *ig;
+ TAILQ_ENTRY(spdk_iscsi_ig_map) tailq;
+};
+
+struct spdk_iscsi_pg_map {
+ struct spdk_iscsi_portal_grp *pg;
+ int num_ig_maps;
+ TAILQ_HEAD(, spdk_iscsi_ig_map) ig_map_head;
+ TAILQ_ENTRY(spdk_iscsi_pg_map) tailq ;
+};
+
+struct spdk_iscsi_tgt_node {
+ int num;
+ char name[MAX_TARGET_NAME + 1];
+ char alias[MAX_TARGET_NAME + 1];
+
+ pthread_mutex_t mutex;
+
+ bool disable_chap;
+ bool require_chap;
+ bool mutual_chap;
+ int chap_group;
+ bool header_digest;
+ bool data_digest;
+ int queue_depth;
+
+ struct spdk_scsi_dev *dev;
+ /**
+ * Counts number of active iSCSI connections associated with this
+ * target node.
+ */
+ uint32_t num_active_conns;
+ struct spdk_iscsi_poll_group *pg;
+
+ int num_pg_maps;
+ TAILQ_HEAD(, spdk_iscsi_pg_map) pg_map_head;
+ TAILQ_ENTRY(spdk_iscsi_tgt_node) tailq;
+
+ bool destructed;
+ struct spdk_poller *destruct_poller;
+ iscsi_tgt_node_destruct_cb destruct_cb_fn;
+ void *destruct_cb_arg;
+};
+
+int iscsi_parse_tgt_nodes(void);
+
+void iscsi_shutdown_tgt_nodes(void);
+void iscsi_shutdown_tgt_node_by_name(const char *target_name,
+ iscsi_tgt_node_destruct_cb cb_fn, void *cb_arg);
+bool iscsi_tgt_node_is_destructed(struct spdk_iscsi_tgt_node *target);
+int iscsi_send_tgts(struct spdk_iscsi_conn *conn, const char *iiqn,
+ const char *iaddr, const char *tiqn, uint8_t *data, int alloc_len,
+ int data_len);
+
+/*
+ * bdev_name_list and lun_id_list are equal sized arrays of size num_luns.
+ * bdev_name_list refers to the names of the bdevs that will be used for the LUNs on the
+ * new target node.
+ * lun_id_list refers to the LUN IDs that will be used for the LUNs on the target node.
+ */
+struct spdk_iscsi_tgt_node *iscsi_tgt_node_construct(int target_index,
+ const char *name, const char *alias,
+ int *pg_tag_list, int *ig_tag_list, uint16_t num_maps,
+ const char *bdev_name_list[], int *lun_id_list, int num_luns,
+ int queue_depth,
+ bool disable_chap, bool require_chap, bool mutual_chap, int chap_group,
+ bool header_digest, bool data_digest);
+
+bool iscsi_check_chap_params(bool disable, bool require, bool mutual, int group);
+
+int iscsi_target_node_add_pg_ig_maps(struct spdk_iscsi_tgt_node *target,
+ int *pg_tag_list, int *ig_tag_list,
+ uint16_t num_maps);
+int iscsi_target_node_remove_pg_ig_maps(struct spdk_iscsi_tgt_node *target,
+ int *pg_tag_list, int *ig_tag_list,
+ uint16_t num_maps);
+
+bool iscsi_tgt_node_access(struct spdk_iscsi_conn *conn,
+ struct spdk_iscsi_tgt_node *target, const char *iqn,
+ const char *addr);
+struct spdk_iscsi_tgt_node *iscsi_find_tgt_node(const char *target_name);
+int iscsi_tgt_node_cleanup_luns(struct spdk_iscsi_conn *conn,
+ struct spdk_iscsi_tgt_node *target);
+void iscsi_tgt_node_delete_map(struct spdk_iscsi_portal_grp *portal_group,
+ struct spdk_iscsi_init_grp *initiator_group);
+int iscsi_tgt_node_add_lun(struct spdk_iscsi_tgt_node *target,
+ const char *bdev_name, int lun_id);
+int iscsi_tgt_node_set_chap_params(struct spdk_iscsi_tgt_node *target,
+ bool disable_chap, bool require_chap,
+ bool mutual_chap, int32_t chap_group);
+void iscsi_tgt_nodes_config_text(FILE *fp);
+void iscsi_tgt_nodes_info_json(struct spdk_json_write_ctx *w);
+void iscsi_tgt_nodes_config_json(struct spdk_json_write_ctx *w);
+#endif /* SPDK_ISCSI_TGT_NODE_H_ */