// SPDX-License-Identifier: GPL-2.0-or-later /******************************************************************************* * This file contains main functions related to iSCSI Parameter negotiation. * * (c) Copyright 2007-2013 Datera, Inc. * * Author: Nicholas A. Bellinger <nab@linux-iscsi.org> *
******************************************************************************/
list_for_each_entry(param, &conn->param_list->param_list, p_list) { if (!strncmp(param->name, SESSIONTYPE, 11)) { if (!IS_PSTATE_ACCEPTOR(param)) {
pr_err("SessionType key not received" " in first login request.\n");
iscsit_tx_login_rsp(conn, ISCSI_STATUS_CLS_INITIATOR_ERR,
ISCSI_LOGIN_STATUS_MISSING_FIELDS); return -1;
} if (!strncmp(param->value, DISCOVERY, 9)) return 0;
}
if (!strncmp(param->name, INITIATORNAME, 13)) { if (!IS_PSTATE_ACCEPTOR(param)) { if (!login->leading_connection) continue;
pr_err("InitiatorName key not received" " in first login request.\n");
iscsit_tx_login_rsp(conn, ISCSI_STATUS_CLS_INITIATOR_ERR,
ISCSI_LOGIN_STATUS_MISSING_FIELDS); return -1;
}
/* * For non-leading connections, double check that the * received InitiatorName matches the existing session's * struct iscsi_node_acl.
*/ if (!login->leading_connection) {
se_nacl = conn->sess->se_sess->se_node_acl; if (!se_nacl) {
pr_err("Unable to locate" " struct se_node_acl\n");
iscsit_tx_login_rsp(conn,
ISCSI_STATUS_CLS_INITIATOR_ERR,
ISCSI_LOGIN_STATUS_TGT_NOT_FOUND); return -1;
}
padding = ((-login->rsp_length) & 3); /* * Before sending the last login response containing the transition * bit for full-feature-phase, go ahead and start up TX/RX threads * now to avoid potential resource allocation failures after the * final login response has been sent.
*/ if (login->login_complete) { int rc = iscsit_start_kthreads(conn); if (rc) {
iscsit_tx_login_rsp(conn, ISCSI_STATUS_CLS_TARGET_ERR,
ISCSI_LOGIN_STATUS_NO_RESOURCES); return -1;
}
}
if (conn->conn_transport->iscsit_put_login_tx(conn, login,
login->rsp_length + padding) < 0) goto err;
spin_lock(&conn->login_worker_lock);
set_bit(LOGIN_FLAGS_WORKER_RUNNING, &conn->login_flags);
spin_unlock(&conn->login_worker_lock); /* * If iscsi_target_do_login_rx() has been invoked by ->sk_data_ready() * before initial PDU processing in iscsi_target_start_negotiation() * has completed, go ahead and retry until it's cleared. * * Otherwise if the TCP connection drops while this is occurring, * iscsi_target_start_negotiation() will detect the failure, call * cancel_delayed_work_sync(&conn->login_work), and cleanup the * remaining iscsi connection resources from iscsi_np process context.
*/ if (iscsi_target_sk_check_flag(conn, LOGIN_FLAGS_INITIAL_PDU)) {
schedule_delayed_work(&conn->login_work, msecs_to_jiffies(10)); return;
}
spin_lock(&tpg->tpg_state_lock);
state = (tpg->tpg_state == TPG_STATE_ACTIVE);
spin_unlock(&tpg->tpg_state_lock);
if (!state) {
pr_debug("iscsi_target_do_login_rx: tpg_state != TPG_STATE_ACTIVE\n"); goto err;
}
if (iscsi_target_sk_check_close(conn)) {
pr_debug("iscsi_target_do_login_rx, TCP state CLOSE\n"); goto err;
}
allow_signal(SIGINT);
rc = iscsit_set_login_timer_kworker(conn, current); if (rc < 0) { /* The login timer has already expired */
pr_debug("iscsi_target_do_login_rx, login failed\n"); goto err;
}
pr_debug("iscsi_target_do_login_rx after rx_login_io, %p, %s:%d\n",
conn, current->comm, current->pid);
/* * LOGIN_FLAGS_READ_ACTIVE is cleared so that sk_data_ready * could be triggered again after this. * * LOGIN_FLAGS_WRITE_ACTIVE is cleared after we successfully * process a login PDU, so that sk_state_chage can do login * cleanup as needed if the socket is closed. If a delayed work is * ongoing (LOGIN_FLAGS_WRITE_ACTIVE or LOGIN_FLAGS_READ_ACTIVE), * sk_state_change will leave the cleanup to the delayed work or * it will schedule a delayed work to do cleanup.
*/ if (conn->sock) { struct sock *sk = conn->sock->sk;
if (test_bit(LOGIN_FLAGS_READ_ACTIVE, &conn->login_flags) ||
test_bit(LOGIN_FLAGS_WRITE_ACTIVE, &conn->login_flags)) {
pr_debug("Got LOGIN_FLAGS_{READ|WRITE}_ACTIVE=1" " sk_state_change conn: %p\n", conn); if (state)
set_bit(LOGIN_FLAGS_CLOSED, &conn->login_flags);
write_unlock_bh(&sk->sk_callback_lock);
orig_state_change(sk); return;
} if (test_bit(LOGIN_FLAGS_CLOSED, &conn->login_flags)) {
pr_debug("Got LOGIN_FLAGS_CLOSED=1 sk_state_change conn: %p\n",
conn);
write_unlock_bh(&sk->sk_callback_lock);
orig_state_change(sk); return;
} /* * If the TCP connection has dropped, go ahead and set LOGIN_FLAGS_CLOSED, * but only queue conn->login_work -> iscsi_target_do_login_rx() * processing if LOGIN_FLAGS_INITIAL_PDU has already been cleared. * * When iscsi_target_do_login_rx() runs, iscsi_target_sk_check_close() * will detect the dropped TCP connection from delayed workqueue context. * * If LOGIN_FLAGS_INITIAL_PDU is still set, which means the initial * iscsi_target_start_negotiation() is running, iscsi_target_do_login() * via iscsi_target_sk_check_close() or iscsi_target_start_negotiation() * via iscsi_target_sk_check_and_clear() is responsible for detecting the * dropped TCP connection in iscsi_np process context, and cleaning up * the remaining iscsi connection resources.
*/ if (state) {
pr_debug("iscsi_target_sk_state_change got failed state\n");
set_bit(LOGIN_FLAGS_CLOSED, &conn->login_flags);
state = test_bit(LOGIN_FLAGS_INITIAL_PDU, &conn->login_flags);
write_unlock_bh(&sk->sk_callback_lock);
orig_state_change(sk);
if (!state)
schedule_delayed_work(&conn->login_work, 0); return;
}
write_unlock_bh(&sk->sk_callback_lock);
orig_state_change(sk);
}
/* * NOTE: We check for existing sessions or connections AFTER the initiator * has been successfully authenticated in order to protect against faked * ISID/TSIH combinations.
*/ staticint iscsi_target_check_for_existing_instances( struct iscsit_conn *conn, struct iscsi_login *login)
{ if (login->checked_for_existing) return 0;
login->checked_for_existing = 1;
if (!login->tsih) return iscsi_check_for_session_reinstatement(conn); else return iscsi_login_post_auth_non_zero_tsih(conn, login->cid,
login->initial_exp_statsn);
}
if (conn->sess->sess_ops->SessionType) { /* * For SessionType=Discovery
*/ return conn->tpg->tpg_attrib.authentication;
} /* * For SessionType=Normal
*/
se_nacl = conn->sess->se_sess->se_node_acl; if (!se_nacl) {
pr_debug("Unknown ACL is trying to connect\n"); returntrue;
}
if (se_nacl->dynamic_node_acl) {
pr_debug("Dynamic ACL %s is trying to connect\n",
se_nacl->initiatorname); return conn->tpg->tpg_attrib.authentication;
}
pr_debug("Known ACL %s is trying to connect\n",
se_nacl->initiatorname);
nacl = to_iscsi_nacl(se_nacl); if (nacl->node_attrib.authentication == NA_AUTHENTICATION_INHERITED) return conn->tpg->tpg_attrib.authentication;
while (1) { if (++pdu_count > MAX_LOGIN_PDUS) {
pr_err("MAX_LOGIN_PDUS count reached.\n");
iscsit_tx_login_rsp(conn, ISCSI_STATUS_CLS_TARGET_ERR,
ISCSI_LOGIN_STATUS_TARGET_ERROR); return -1;
}
switch (ISCSI_LOGIN_CURRENT_STAGE(login_req->flags)) { case 0:
login_rsp->flags &= ~ISCSI_FLAG_LOGIN_CURRENT_STAGE_MASK; if (iscsi_target_handle_csg_zero(conn, login) < 0) return -1; break; case 1:
login_rsp->flags |= ISCSI_FLAG_LOGIN_CURRENT_STAGE1; if (iscsi_target_handle_csg_one(conn, login) < 0) return -1; if (login_rsp->flags & ISCSI_FLAG_LOGIN_TRANSIT) { /* * Check to make sure the TCP connection has not * dropped asynchronously while session reinstatement * was occurring in this kthread context, before * transitioning to full feature phase operation.
*/ if (iscsi_target_sk_check_close(conn)) return -1;
tmpbuf = kmemdup_nul(login->req_buf, payload_length, GFP_KERNEL); if (!tmpbuf) {
pr_err("Unable to allocate memory for tmpbuf.\n"); return -1;
}
start = tmpbuf;
end = (start + payload_length);
/* * Locate the initial keys expected from the Initiator node in * the first login request in order to progress with the login phase.
*/ while (start < end) { if (iscsi_extract_key_value(start, &key, &value) < 0) {
ret = -1; goto out;
}
start += strlen(key) + strlen(value) + 2;
} /* * See 5.3. Login Phase.
*/ if (!i_buf) {
pr_err("InitiatorName key not received" " in first login request.\n");
iscsit_tx_login_rsp(conn, ISCSI_STATUS_CLS_INITIATOR_ERR,
ISCSI_LOGIN_STATUS_MISSING_FIELDS);
ret = -1; goto out;
} /* * Convert the incoming InitiatorName to lowercase following * RFC-3720 3.2.6.1. section c) that says that iSCSI IQNs * are NOT case sensitive.
*/
iscsi_initiatorname_tolower(i_buf);
if (!s_buf) { if (!login->leading_connection) goto get_target;
pr_err("SessionType key not received" " in first login request.\n");
iscsit_tx_login_rsp(conn, ISCSI_STATUS_CLS_INITIATOR_ERR,
ISCSI_LOGIN_STATUS_MISSING_FIELDS);
ret = -1; goto out;
}
/* * Use default portal group for discovery sessions.
*/
sessiontype = strncmp(s_buf, DISCOVERY, 9); if (!sessiontype) { if (!login->leading_connection) goto get_target;
sess->sess_ops->SessionType = 1;
/* * Serialize access across the discovery struct iscsi_portal_group to * process login attempt.
*/
conn->tpg = iscsit_global->discovery_tpg; if (iscsit_access_np(np, conn->tpg) < 0) {
iscsit_tx_login_rsp(conn, ISCSI_STATUS_CLS_TARGET_ERR,
ISCSI_LOGIN_STATUS_SVC_UNAVAILABLE);
conn->tpg = NULL;
ret = -1; goto out;
}
ret = 0; goto alloc_tags;
}
get_target: if (!t_buf) {
pr_err("TargetName key not received" " in first login request while" " SessionType=Normal.\n");
iscsit_tx_login_rsp(conn, ISCSI_STATUS_CLS_INITIATOR_ERR,
ISCSI_LOGIN_STATUS_MISSING_FIELDS);
ret = -1; goto out;
}
/* * Locate Target IQN from Storage Node.
*/
tiqn = iscsit_get_tiqn_for_login(t_buf); if (!tiqn) {
pr_err("Unable to locate Target IQN: %s in" " Storage Node\n", t_buf);
iscsit_tx_login_rsp(conn, ISCSI_STATUS_CLS_TARGET_ERR,
ISCSI_LOGIN_STATUS_SVC_UNAVAILABLE);
ret = -1; goto out;
}
pr_debug("Located Storage Object: %s\n", tiqn->tiqn);
/* * Locate Target Portal Group from Storage Node.
*/
conn->tpg = iscsit_get_tpg_from_np(tiqn, np, &tpg_np); if (!conn->tpg) {
pr_err("Unable to locate Target Portal Group" " on %s\n", tiqn->tiqn);
iscsit_put_tiqn_for_login(tiqn);
iscsit_tx_login_rsp(conn, ISCSI_STATUS_CLS_TARGET_ERR,
ISCSI_LOGIN_STATUS_SVC_UNAVAILABLE);
ret = -1; goto out;
}
conn->tpg_np = tpg_np;
pr_debug("Located Portal Group Object: %hu\n", conn->tpg->tpgt);
/* * Serialize access across the struct iscsi_portal_group to * process login attempt.
*/ if (iscsit_access_np(np, conn->tpg) < 0) {
kref_put(&tpg_np->tpg_np_kref, iscsit_login_kref_put);
iscsit_put_tiqn_for_login(tiqn);
iscsit_tx_login_rsp(conn, ISCSI_STATUS_CLS_TARGET_ERR,
ISCSI_LOGIN_STATUS_SVC_UNAVAILABLE);
conn->tpg = NULL;
ret = -1; goto out;
}
/* * conn->sess->node_acl will be set when the referenced * struct iscsit_session is located from received ISID+TSIH in * iscsi_login_non_zero_tsih_s2().
*/ if (!login->leading_connection) {
ret = 0; goto out;
}
/* * This value is required in iscsi_login_zero_tsih_s2()
*/
sess->sess_ops->SessionType = 0;
/* * Locate incoming Initiator IQN reference from Storage Node.
*/
sess->se_sess->se_node_acl = core_tpg_check_initiator_node_acl(
&conn->tpg->tpg_se_tpg, i_buf); if (!sess->se_sess->se_node_acl) {
pr_err("iSCSI Initiator Node: %s is not authorized to" " access iSCSI target portal group: %hu.\n",
i_buf, conn->tpg->tpgt);
iscsit_tx_login_rsp(conn, ISCSI_STATUS_CLS_INITIATOR_ERR,
ISCSI_LOGIN_STATUS_TGT_FORBIDDEN);
ret = -1; goto out;
}
se_nacl = sess->se_sess->se_node_acl;
queue_depth = se_nacl->queue_depth; /* * Setup pre-allocated tags based upon allowed per NodeACL CmdSN * depth for non immediate commands, plus extra tags for immediate * commands. * * Also enforce a ISCSIT_MIN_TAGS to prevent unnecessary contention * in per-cpu-ida tag allocation logic + small queue_depth.
*/
alloc_tags:
tag_num = max_t(u32, ISCSIT_MIN_TAGS, queue_depth);
tag_num = (tag_num * 2) + ISCSIT_EXTRA_TAGS;
tag_size = sizeof(struct iscsit_cmd) + conn->conn_transport->priv_size;
ret = transport_alloc_session_tags(sess->se_sess, tag_num, tag_size); if (ret < 0) {
iscsit_tx_login_rsp(conn, ISCSI_STATUS_CLS_TARGET_ERR,
ISCSI_LOGIN_STATUS_NO_RESOURCES);
ret = -1;
}
out:
kfree(tmpbuf); return ret;
}
int iscsi_target_start_negotiation( struct iscsi_login *login, struct iscsit_conn *conn)
{ int ret;
if (conn->sock) { struct sock *sk = conn->sock->sk;
write_lock_bh(&sk->sk_callback_lock);
set_bit(LOGIN_FLAGS_READY, &conn->login_flags);
set_bit(LOGIN_FLAGS_INITIAL_PDU, &conn->login_flags);
write_unlock_bh(&sk->sk_callback_lock);
} /* * If iscsi_target_do_login returns zero to signal more PDU * exchanges are required to complete the login, go ahead and * clear LOGIN_FLAGS_INITIAL_PDU but only if the TCP connection * is still active. * * Otherwise if TCP connection dropped asynchronously, go ahead * and perform connection cleanup now.
*/
ret = iscsi_target_do_login(conn, login); if (!ret) {
spin_lock(&conn->login_worker_lock);
if (iscsi_target_sk_check_and_clear(conn, LOGIN_FLAGS_INITIAL_PDU))
ret = -1; elseif (!test_bit(LOGIN_FLAGS_WORKER_RUNNING, &conn->login_flags)) { if (iscsit_set_login_timer_kworker(conn, NULL) < 0) { /* * The timeout has expired already. * Schedule login_work to perform the cleanup.
*/
schedule_delayed_work(&conn->login_work, 0);
}
}
spin_unlock(&conn->login_worker_lock);
}
if (ret < 0) {
iscsi_target_restore_sock_callbacks(conn);
iscsi_remove_failed_auth_entry(conn);
} if (ret != 0) {
iscsit_stop_login_timer(conn);
cancel_delayed_work_sync(&conn->login_work);
iscsi_target_nego_release(conn);
}
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.