📄 initiator_negotiate.c
字号:
/* common/initiator_negotiate.c * * This defines the functions used in the Login phase * by the iSCSI initiator for parameter negotiation * * vi: set autoindent tabstop=8 shiftwidth=4 : * * This file contains auxilliary functions for iscsi initiator * code that are responsible for dealing with error recovery. * * Copyright (C) 2001-2003 InterOperability Lab (IOL) * University of New Hampshier (UNH) * Durham, NH 03824 * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2, or (at your option) * any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, * USA. * * The name of IOL and/or UNH may not be used to endorse or promote * products derived from this software without specific prior * written permission.*//* * The code for redirection during login is based on code first written * by Scott Cranston of Equal Logic (scranston@equallogic.com).*//* * Converted to: indent -kr -i8 -ts8 -sob -l80 -ss -bs -psl on 10/8/2003 . * */#include <linux/kernel.h>#include <linux/types.h>#include <linux/slab.h>#include <linux/ctype.h>#include <linux/string.h>#include <linux/socket.h>#include <linux/ip.h>#include "iscsi_common.h"#include "debug.h"#include "my_memory.h"/* chap support - CHONG */#include "../security/chap/chap.h"#include "../security/srp/srp.h"#include "../security/misc/misc_func.h"#include "text_param.h"#include "initiator_negotiate.h"/* check the correctness of target ip address which should be in format of * TargetAddress=domainname[:port][,portal-group-tag] */intcheck_target_address(char *value, int when){ int retval = 0; return retval;}/* when get here, we already know that opcode is login response. * returns 1 if valid redirection, 0 if other success, -1 if any error */static intcheck_login_response(struct generic_pdu *inputpdu, struct generic_pdu *outputpdu){ int retval = 0; char *tsix; struct iscsi_targ_login_rsp *login_rsp; struct iscsi_init_login_cmnd *login_req; TRACE(TRACE_ENTER_LEAVE, "Enter check_login_response\n"); tsix = "TSIH"; login_rsp = (struct iscsi_targ_login_rsp *) inputpdu; login_req = (struct iscsi_init_login_cmnd *) outputpdu; /* check that reserved fields in the login response are all zero */ if ((login_rsp->opcode & OLD_X_BIT) || (login_rsp->flags & (W_BIT | BRO_BIT)) || login_rsp->rsvd1 || login_rsp->rsvd2 || login_rsp->rsvd3) { TRACE_WARNING ("reserved fields in Login Response header not all 0\n"); } if ((inputpdu->status_class != STAT_CLASS_SUCCESS)) { /* the target sent a login reject */ TRACE_ERROR ("Login Response with non-zero StatusClass 0x%02x, " "StatusDetail 0x%02x\n", inputpdu->status_class, inputpdu->status_detail); /* Draft 20, Section 5.3.1 Login Phase Start */ if ((inputpdu->flags & T_BIT) || (inputpdu->flags & CSG) != 0 || (inputpdu->flags & NSG) != 0) { /* protocol error */ TRACE_ERROR ("non-zero Status-Class MUST have T=0, CSG=0 & NSG=0\n"); retval = -1; } if (inputpdu->status_class == STAT_CLASS_REDIRECTION) { /* target redirecting login to different IP address/Port number */ if (inputpdu->status_detail < STAT_DETAIL_TARG_MOVED_TEMP && inputpdu->status_detail > STAT_DETAIL_TARG_MOVED_PERM) { TRACE_ERROR ("invalid Status Detail 0x%02x for Redirection\n", inputpdu->status_detail); retval = -1; } else { /* valid redirection detail, there * should be a TargetAddress * key attached to this PDU containing * the redirection info */ retval = 1; } } else if (inputpdu->status_class == STAT_CLASS_INITIATOR) { /* Initiator Error (not a format error) */ if (inputpdu->status_detail > STAT_DETAIL_INVALID_DURING_LOGIN) { TRACE_ERROR ("invalid Status Detail 0x%02x for Initiator Error\n", inputpdu->status_detail); } retval = -1; } else if (inputpdu->status_class == STAT_CLASS_TARGET) { /* Target Error */ if (inputpdu->status_detail > STAT_DETAIL_OUT_OF_RESOURCE) { TRACE_ERROR ("invalid Status Detail 0x%02x for Target Error\n", inputpdu->status_detail); } retval = -1; } else { /* Bad status class */ TRACE_ERROR("invalid Status Class 0x%02x\n", inputpdu->status_class); retval = -1; } } /* status_class is 0, status_detail should also be 0 */ else if ((inputpdu->status_detail != 0x0)) { TRACE_ERROR("Login Response with zero StatusClass but non-zero " "StatusDetail 0x%02x\n", inputpdu->status_detail); retval = -1; } else if ((inputpdu->flags & CSG) != (outputpdu->flags & CSG)) { /* target's state not the same as initiator's state */ /* protocol error */ TRACE_ERROR ("inconsistant state! Target in state %d, initiator in %d\n", (inputpdu->flags & CSG) >> CSG_SHIFT, (outputpdu->flags & CSG) >> CSG_SHIFT); retval = -1; } else if (memcmp(login_rsp->isid, login_req->isid, 6) != 0) { /* target changed the isid */ /* protocol error */ TRACE_ERROR("target changed the isid\n"); retval = -1; } else if (!(inputpdu->flags & T_BIT)) { /* target does not want to change stage */ if (inputpdu->flags & NSG) { /* check reserved field, but only give * warning if we find errors */ if (TRACE_TEST(TRACE_ISCSI)) { TRACE_WARNING("NSG %d should be 0 when T bit is 0\n", inputpdu->flags & NSG); } } } /* input T bit is set to 1, target wants to change stage */ else if (!(outputpdu->flags & T_BIT)) { /* but initiator did not want to change stage yet */ /* protocol error */ TRACE_ERROR ("Login Response with T bit set but Initiator's T bit " "not set \n"); retval = -1; } else if ((inputpdu->flags & NSG) <= ((inputpdu->flags & CSG) >> CSG_SHIFT) || (inputpdu->flags & NSG) == NSG2) { /* target wants to go to an illegal stage */ /* protocol error */ TRACE_ERROR("target trying to change to illegal stage %d\n", inputpdu->flags & NSG); retval = -1; } else if ((inputpdu->flags & NSG) == NSG3) { /* target going to FFP, can only do this if initiator * asked for it * Draft 20, Section 5.3 Login Phase */ if ((outputpdu->flags & NSG) != NSG3) { TRACE_ERROR ("target trying to change to FFP, initiator wanted " "to change to Operational stage only\n"); retval = -1; } else if (login_rsp->tsih == 0) { /* protocol error */ TRACE_ERROR ("target's final login response has %s = 0\n", tsix); retval = -1; } } else if (login_req->tsih != login_rsp->tsih) { TRACE_ERROR("target changed initiator's %s from %u to %u\n", tsix, be16_to_cpu(login_req->tsih), be16_to_cpu(login_rsp->tsih)); } TRACE(TRACE_ENTER_LEAVE, "Leave check_login_response retval = %d\n", retval); return retval;}/* Called to send a Login PDU already set up in outputpdu * and then to read back a login response into inputpdu * Returns 1 on redirection, 0 on other success, -1 on any error */static intwrite_and_read(struct socket *sock, struct generic_pdu *inputpdu, struct generic_pdu *outputpdu, int *target_version_max, int *target_version_active, __u32 flags){ int padding = 0; int retval = 0; __u32 size; TRACE(TRACE_ENTER_LEAVE, "Enter write_and_read\n"); /* send output */ if (iscsi_send_msg(sock, outputpdu, flags) < 0) { TRACE(TRACE_DEBUG, "iscsi_send_msg failed\n"); return -1; } /* After sending the outputpdu, reset it's text len to 0 */ outputpdu->text_length = 0; /* wait for input from target */ size = ISCSI_HDR_LEN; /* NO packets sent or received during login will have * digests of any kind */ if (iscsi_recv_msg(sock, size, (char *) inputpdu, flags) < 0) { TRACE(TRACE_DEBUG, "iscsi_recv_msg failed\n"); return -1; } /* Also get the text associated with it */ inputpdu->text_length = be32_to_cpu(inputpdu->length); /* Include the padding bytes too */ padding = inputpdu->text_length + ((-inputpdu->text_length) & 3); if (inputpdu->text_length > MAX_TEXT_LEN) { TRACE_ERROR ("DSL %u greater than default MaxRecvDataSegmentLength %d\n", inputpdu->text_length, MAX_TEXT_LEN); return -1; } if (padding > 0 && iscsi_recv_msg(sock, padding, inputpdu->text, flags) < 0) { TRACE(TRACE_DEBUG, "iscsi_recv_msg failed\n"); return -1; } if ((inputpdu->opcode & ISCSI_OPCODE) == ISCSI_TARG_LOGIN_RSP) { /* as expected, this is a login response, check it */ TRACE(TRACE_ISCSI, "Got Login Rsp, ITT %u\n", ntohl(inputpdu->init_task_tag)); if (TRACE_TEST(TRACE_ISCSI_FULL)) print_targ_login_rsp((struct iscsi_targ_login_rsp *) inputpdu); /* Check the initiator task tag */ if (inputpdu->init_task_tag != outputpdu->init_task_tag) { if (TRACE_TEST(TRACE_ISCSI)) { TRACE_WARNING("received init_task_tag = %d, " "expected %d\n", be32_to_cpu(inputpdu->init_task_tag), be32_to_cpu(outputpdu->init_task_tag)); } } /* Check recved version_max and version_active fields * for consistency */ if (inputpdu->version_max < inputpdu->version_active) { if (TRACE_TEST(TRACE_ISCSI)) { TRACE_WARNING("received version_max = 0x%02x " "less than received version_active 0x%02x\n", inputpdu->version_max, inputpdu->version_active); } } /* The received version_max and version_active fields * must not change */ /* Draft 20, Section 10.13.1 Version-max (for Login Response) * "All Login responses within the Login Phase MUST * carry the same Version-max. * * Draft 20, Section 10.13.2 Version-active (for * Login Response) * "All Login responses within the Login Phase * MUST carry the same Version-active. * The initiator MUST use the value presented as * response to the first login request." */ if (*target_version_max < 0) { /* first login response, just remember its values */ *target_version_max = inputpdu->version_max; *target_version_active = inputpdu->version_active; } else if (*target_version_max != inputpdu->version_max) { if (TRACE_TEST(TRACE_ISCSI)) { TRACE_WARNING("received version_max = 0x%02x " "differs from received version_max 0x%02x\n", inputpdu->version_max, *target_version_max); } } else if (*target_version_active != inputpdu->version_active) { if (TRACE_TEST(TRACE_ISCSI)) { TRACE_WARNING("received version_active = 0x%02x" " differs from received version_active 0x%02x\n", inputpdu->version_active, *target_version_active); } } /* Check the version_active field to see if we can use it * For now, we send version_min == version_max, so * target has no choice */ if (inputpdu->version_active != outputpdu->version_active) { TRACE_ERROR ("received version_active = 0x%02x, expected 0x%02x\n", inputpdu->version_active, outputpdu->version_active); retval = -1; } /* Check the C bit, which we do not use yet */ else if (inputpdu->flags & C_BIT) { /* C bit is set in Login Response */ /* Draft 20, Section 10.13.7 C (Continue) Bit * "A Login Response with the C bit set must have the * T bit set to 0." */ if (inputpdu->flags & T_BIT) { TRACE_ERROR ("C=1 in Login Response requires T=0, not T=1\n"); } else { /* for now */ TRACE_ERROR ("C=1 in Login Response not implemented yet\n"); } retval = -1; } else retval = check_login_response(inputpdu, outputpdu); } else if ((inputpdu->opcode & ISCSI_OPCODE) == ISCSI_TARG_RJT) { /* protocol error */ TRACE_ERROR("got Reject PDU\n"); print_targ_rjt((struct iscsi_targ_rjt *) inputpdu); retval = -1; } else { TRACE_ERROR("received opcode = 0x%02x, expected 0x%02x\n", inputpdu->opcode & ISCSI_OPCODE, ISCSI_TARG_LOGIN_RSP); retval = -1; /* this is fatal */ } TRACE(TRACE_ENTER_LEAVE, "Leave write_and_read, retval %d\n", retval); return retval;}/* states to drive authentication steps during security phase */enum security_steps { ss_initial, // 0 ss_find_chap_a_i_c, // 1 ss_find_chap_n_r, // 2 ss_find_srp_g_s, // 3 ss_find_srp_b, // 4 ss_find_srp_h, // 5 ss_done, // 6 ss_error // 7};/* forward defs */intss_initial_func(enum security_steps *security_step, struct unknown_key ** unknown_key_list, struct parameter_type *p_param_tbl, struct auth_parameter_type p_auth_param, int noperational, struct parameter_type *auth_p, struct generic_pdu *inputpdu, struct generic_pdu *outputpdu);intss_find_chap_a_i_c_func(enum security_steps *security_step, struct unknown_key ** unknown_key_list, struct parameter_type *p_param_tbl, struct auth_parameter_type p_auth_param, int noperational, struct parameter_type *auth_p, struct generic_pdu *inputpdu, struct generic_pdu *outputpdu, __u32 * got_keys, __u32 * got_value);intss_find_chap_n_r_func(enum security_steps *security_step, struct unknown_key ** unknown_key_list, struct parameter_type *p_param_tbl, struct auth_parameter_type p_auth_param, int noperational, struct generic_pdu *inputpdu, struct generic_pdu *outputpdu, __u32 * got_keys, __u32 * got_value, int *count);intss_find_srp_g_s_func( enum security_steps *security_step, struct unknown_key ** unknown_key_list, struct parameter_type *p_param_tbl, struct auth_parameter_type p_auth_param, int noperational, struct generic_pdu *inputpdu, struct generic_pdu *outputpdu, __u32 * got_keys );intss_find_srp_b_func( enum security_steps *security_step, struct unknown_key ** unknown_key_list, struct parameter_type *p_param_tbl, struct auth_parameter_type p_auth_param, int noperational, struct generic_pdu *inputpdu, struct generic_pdu *outputpdu, __u32 * got_keys );intss_find_srp_h_func( enum security_steps *security_step, struct unknown_key ** unknown_key_list, struct parameter_type *p_param_tbl, struct auth_parameter_type p_auth_param, int noperational, struct generic_pdu *inputpdu, struct generic_pdu *outputpdu, __u32 * got_keys, int *count);intss_done_func( enum security_steps *security_step, struct unknown_key ** unknown_key_list, struct parameter_type *p_param_tbl, struct auth_parameter_type p_auth_param, int noperational, struct generic_pdu *inputpdu, struct generic_pdu *outputpdu, __u32 * got_keys,
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -