From: Petr Pavlu <petr.pavlu@xxxxxxxx> Function iscsi_target_do_login() attempts to cancel a connection when a number of login exchanges reaches MAX_LOGIN_PDUS (7). This is done by having a local counter and incrementing+checking it as the function processes requests in a loop. A problem is that since the login rework in back in 2013, the function always processes only a single request and the loop is terminated at the end of the first iteration. This means the counter reaches only value 1 and any excess number of login requests is never rejected. Fix the problem by introducing iscsi_login.negotiation_exchanges counter and update the logic to count exchanges per each login phase as described in RFC 7143: > 6.2. Text Mode Negotiation: > [...] > In the Login Phase (see Section 6.3), every stage is a separate > negotiation. [...] > [...] > An iSCSI initiator or target MAY terminate a negotiation that does > not terminate within an implementation-specific reasonable time or > number of exchanges but SHOULD allow at least six (6) exchanges. Fixes: d381a8010a05 ("iscsi-target: Add login negotiation multi-plexing support") Signed-off-by: Petr Pavlu <petr.pavlu@xxxxxxxx> --- drivers/target/iscsi/iscsi_target_nego.c | 92 ++++++++++++------------ include/target/iscsi/iscsi_target_core.h | 1 + 2 files changed, 47 insertions(+), 46 deletions(-) diff --git a/drivers/target/iscsi/iscsi_target_nego.c b/drivers/target/iscsi/iscsi_target_nego.c index c0ed6f8e5c5b..a5077ea09f6c 100644 --- a/drivers/target/iscsi/iscsi_target_nego.c +++ b/drivers/target/iscsi/iscsi_target_nego.c @@ -970,65 +970,65 @@ static int iscsi_target_handle_csg_one(struct iscsi_conn *conn, struct iscsi_log static int iscsi_target_do_login(struct iscsi_conn *conn, struct iscsi_login *login) { - int pdu_count = 0; struct iscsi_login_req *login_req; struct iscsi_login_rsp *login_rsp; login_req = (struct iscsi_login_req *) login->req; login_rsp = (struct iscsi_login_rsp *) login->rsp; - 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); + 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; - } - - 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) + 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; - if (login_rsp->flags & ISCSI_FLAG_LOGIN_TRANSIT) { - /* - * Check to make sure the TCP connection has not - * dropped asynchronously while session reinstatement - * was occuring in this kthread context, before - * transitioning to full feature phase operation. - */ - if (iscsi_target_sk_check_close(conn)) - return -1; - login->tsih = conn->sess->tsih; - login->login_complete = 1; - iscsi_target_restore_sock_callbacks(conn); - if (iscsi_target_do_tx_login_io(conn, - login) < 0) - return -1; - return 1; - } - break; - default: - pr_err("Illegal CSG: %d received from" - " Initiator, protocol error.\n", - ISCSI_LOGIN_CURRENT_STAGE(login_req->flags)); - break; + login->tsih = conn->sess->tsih; + login->login_complete = 1; + iscsi_target_restore_sock_callbacks(conn); + if (iscsi_target_do_tx_login_io(conn, login) < 0) + return -1; + return 1; } + break; + default: + pr_err("Illegal CSG: %d received from Initiator," + " protocol error.\n", + ISCSI_LOGIN_CURRENT_STAGE(login_req->flags)); + break; + } - if (iscsi_target_do_tx_login_io(conn, login) < 0) + if (login_rsp->flags & ISCSI_FLAG_LOGIN_TRANSIT) + login->negotiation_exchanges = 0; + else { + login->negotiation_exchanges++; + if (login->negotiation_exchanges >= 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; - - if (login_rsp->flags & ISCSI_FLAG_LOGIN_TRANSIT) { - login_rsp->flags &= ~ISCSI_FLAG_LOGIN_TRANSIT; - login_rsp->flags &= ~ISCSI_FLAG_LOGIN_NEXT_STAGE_MASK; } - break; + } + + if (iscsi_target_do_tx_login_io(conn, login) < 0) + return -1; + + if (login_rsp->flags & ISCSI_FLAG_LOGIN_TRANSIT) { + login_rsp->flags &= ~ISCSI_FLAG_LOGIN_TRANSIT; + login_rsp->flags &= ~ISCSI_FLAG_LOGIN_NEXT_STAGE_MASK; } return 0; diff --git a/include/target/iscsi/iscsi_target_core.h b/include/target/iscsi/iscsi_target_core.h index 1eccb2ac7d02..b6a5e1cf3f77 100644 --- a/include/target/iscsi/iscsi_target_core.h +++ b/include/target/iscsi/iscsi_target_core.h @@ -705,6 +705,7 @@ struct iscsi_login { u32 rsp_length; u16 cid; u16 tsih; + u8 negotiation_exchanges; char req[ISCSI_HDR_LEN]; char rsp[ISCSI_HDR_LEN]; char *req_buf; -- 2.35.1