📄 iscsi-login.c
字号:
} else session->irrelevant_keys_bitmap |= IRRELEVANT_MAXCONNECTIONS; text = value_end; } else if (iscsi_find_key_value("ErrorRecoveryLevel", text, end, &value, &value_end)) { if (strcmp(value, "0")) { iscsi_host_err(session, "Login negotiation failed, " "can't accept ErrorRecovery %s\n", value); return LOGIN_NEGOTIATION_FAILED; } text = value_end; } else if (iscsi_find_key_value ("X-com.cisco.protocol", text, end, &value, &value_end)) { if (strcmp(value, "NotUnderstood") && strcmp(value, "Reject") && strcmp(value, "Irrelevant") && strcmp(value, "draft20")) { /* if we didn't get a compatible protocol, fail */ iscsi_host_err(session, "Login version mismatch, " "can't accept protocol %s\n", value); return LOGIN_VERSION_MISMATCH; } text = value_end; } else if (iscsi_find_key_value("X-com.cisco.PingTimeout", text, end, &value, &value_end)) /* we don't really care what the target ends up using */ text = value_end; else if (iscsi_find_key_value("X-com.cisco.sendAsyncText", text, end, &value, &value_end)) /* we don't bother for the target response */ text = value_end; else { iscsi_host_err(session, "Login negotiation failed, couldn't " "recognize text %s\n", text); return LOGIN_NEGOTIATION_FAILED; } *data = text; return LOGIN_OK;}static enum iscsi_login_statuscheck_security_stage_status(struct iscsi_session *session, struct iscsi_acl *auth_client){ int debug_status = 0; switch (acl_recv_end(auth_client)) { case AUTH_STATUS_CONTINUE: /* continue sending PDUs */ break; case AUTH_STATUS_PASS: break; case AUTH_STATUS_NO_ERROR: /* treat this as an error, * since we should get a * different code */ case AUTH_STATUS_ERROR: case AUTH_STATUS_FAIL: default: if (acl_get_dbg_status(auth_client, &debug_status) != AUTH_STATUS_NO_ERROR) iscsi_host_err(session, "Login authentication failed " "with target %s, %s\n", session->target_name, acl_dbg_status_to_text(debug_status)); else iscsi_host_err(session, "Login authentication failed " "with target %s\n", session->target_name); return LOGIN_AUTHENTICATION_FAILED; } return LOGIN_OK;}/* * this assumes the text data is always NULL terminated. The caller can * always arrange for that by using a slightly larger buffer than the max PDU * size, and then appending a NULL to the PDU. */static enum iscsi_login_statusiscsi_process_login_response(struct iscsi_session *session, struct iscsi_login_rsp_hdr *login_rsp_pdu, char *data, int max_data_length){ int transit = login_rsp_pdu->flags & ISCSI_FLAG_LOGIN_TRANSIT; char *text = data; char *end; int pdu_current_stage, pdu_next_stage; enum iscsi_login_status ret; struct iscsi_acl *auth_client = session->auth_client_block ? session->auth_client_block : NULL; end = text + ntoh24(login_rsp_pdu->dlength) + 1; if (end >= (data + max_data_length)) { iscsi_host_err(session, "Login failed, process_login_response " "buffer too small to guarantee NULL " "termination\n"); return LOGIN_FAILED; } /* guarantee a trailing NUL */ *end = '\0'; /* if the response status was success, sanity check the response */ if (login_rsp_pdu->status_class == ISCSI_STATUS_CLS_SUCCESS) { /* check the active version */ if (login_rsp_pdu->active_version != ISCSI_DRAFT20_VERSION) { iscsi_host_err(session, "Login version mismatch, " "received incompatible active iSCSI " "version 0x%02x, expected version " "0x%02x\n", login_rsp_pdu->active_version, ISCSI_DRAFT20_VERSION); return LOGIN_VERSION_MISMATCH; } /* make sure the current stage matches */ pdu_current_stage = (login_rsp_pdu->flags & ISCSI_FLAG_LOGIN_CURRENT_STAGE_MASK) >> 2; if (pdu_current_stage != session->current_stage) { iscsi_host_err(session, "Received invalid login PDU, " "current stage mismatch, session %d, " "response %d\n", session->current_stage, pdu_current_stage); return LOGIN_INVALID_PDU; } /* * make sure that we're actually advancing if the T-bit is set */ pdu_next_stage = login_rsp_pdu->flags & ISCSI_FLAG_LOGIN_NEXT_STAGE_MASK; if (transit && (pdu_next_stage <= session->current_stage)) return LOGIN_INVALID_PDU; } if (session->current_stage == ISCSI_SECURITY_NEGOTIATION_STAGE) { if (acl_recv_begin(auth_client) != AUTH_STATUS_NO_ERROR) { iscsi_host_err(session, "Login failed because " "acl_recv_begin failed\n"); return LOGIN_FAILED; } if (acl_recv_transit_bit(auth_client, transit) != AUTH_STATUS_NO_ERROR) { iscsi_host_err(session, "Login failed because " "acl_recv_transit_bit failed\n"); return LOGIN_FAILED; } } /* scan the text data */ while (text && (text < end)) { /* skip any NULs separating each text key=value pair */ while ((text < end) && (*text == '\0')) text++; if (text >= end) break; /* handle keys appropriate for each stage */ switch (session->current_stage) { case ISCSI_SECURITY_NEGOTIATION_STAGE:{ ret = get_security_text_keys(session, &text, auth_client, end); if (ret != LOGIN_OK) return ret; break; } case ISCSI_OP_PARMS_NEGOTIATION_STAGE:{ ret = get_op_params_text_keys(session, &text, end); if (ret != LOGIN_OK) return ret; break; } default: return LOGIN_FAILED; } } if (session->current_stage == ISCSI_SECURITY_NEGOTIATION_STAGE) { ret = check_security_stage_status(session, auth_client); if (ret != LOGIN_OK) return ret; } /* record some of the PDU fields for later use */ session->tsih = ntohs(login_rsp_pdu->tsih); session->exp_cmd_sn = ntohl(login_rsp_pdu->expcmdsn); session->max_cmd_sn = ntohl(login_rsp_pdu->maxcmdsn); if (login_rsp_pdu->status_class == ISCSI_STATUS_CLS_SUCCESS) session->exp_stat_sn = ntohl(login_rsp_pdu->statsn) + 1; if (transit) { /* advance to the next stage */ session->partial_response = 0; session->current_stage = login_rsp_pdu->flags & ISCSI_FLAG_LOGIN_NEXT_STAGE_MASK; session->irrelevant_keys_bitmap = 0; } else /* * we got a partial response, don't advance, * more negotiation to do */ session->partial_response = 1; return LOGIN_OK; /* this PDU is ok, though the login process * may not be done yet */}static intadd_params_normal_session(struct iscsi_session *session, struct iscsi_hdr *pdu, char *data, int max_data_length){ char value[AUTH_STR_MAX_LEN]; /* these are only relevant for normal sessions */ if (!iscsi_add_text(session, pdu, data, max_data_length, "InitialR2T", session->initial_r2t ? "Yes" : "No")) return 0; if (!iscsi_add_text(session, pdu, data, max_data_length, "ImmediateData", session->immediate_data ? "Yes" : "No")) return 0; sprintf(value, "%d", session->max_burst_len); if (!iscsi_add_text(session, pdu, data, max_data_length, "MaxBurstLength", value)) return 0; sprintf(value, "%d",session->first_burst_len); if (!iscsi_add_text(session, pdu, data, max_data_length, "FirstBurstLength", value)) return 0; /* these we must have */ if (!iscsi_add_text(session, pdu, data, max_data_length, "MaxOutstandingR2T", "1")) return 0; if (!iscsi_add_text(session, pdu, data, max_data_length, "MaxConnections", "1")) return 0; if (!iscsi_add_text(session, pdu, data, max_data_length, "DataPDUInOrder", "Yes")) return 0; if (!iscsi_add_text(session, pdu, data, max_data_length, "DataSequenceInOrder", "Yes")) return 0; return 1;}static intadd_vendor_specific_text(struct iscsi_session *session, struct iscsi_hdr *pdu, char *data, int max_data_length){ char value[AUTH_STR_MAX_LEN]; /* * adjust the target's PingTimeout for normal sessions, * so that it matches the driver's ping timeout. The * network probably has the same latency in both * directions, so the values ought to match. */ if (session->ping_timeout >= 0) { sprintf(value, "%d", session->ping_timeout); if (!iscsi_add_text(session, pdu, data, max_data_length, "X-com.cisco.PingTimeout", value)) return 0; } if (session->send_async_text >= 0) if (!iscsi_add_text(session, pdu, data, max_data_length, "X-com.cisco.sendAsyncText", session->send_async_text ? "Yes" : "No")) return 0; /* * vendor-specific protocol specification. list of protocol level * strings in order of preference allowable values are: draft<n> * (e.g. draft8), rfc<n> (e.g. rfc666). * For example: "X-com.cisco.protocol=draft20,draft8" requests draft 20, * or 8 if 20 isn't supported. "X-com.cisco.protocol=draft8,draft20" * requests draft 8, or 20 if 8 isn't supported. Targets that * understand this key SHOULD return the protocol level they selected * as a response to this key, though the active_version may be * sufficient to distinguish which protocol was chosen. * Note: This probably won't work unless we start in op param stage, * since the security stage limits what keys we can send, and we'd need * to have sent this on the first PDU of the login. Keep sending it for * informational use, and so that we can sanity check things later if * the RFC and draft20 are using the same active version number, * but have non-trivial differences. */ if (!iscsi_add_text(session, pdu, data, max_data_length, "X-com.cisco.protocol", "draft20")) return 0; return 1;}static intcheck_irrelevant_keys(struct iscsi_session *session, struct iscsi_hdr *pdu, char *data, int max_data_length){ /* If you receive irrelevant keys, just check them from the irrelevant * keys bitmap and respond with the key=Irrelevant text */ if (session->irrelevant_keys_bitmap & IRRELEVANT_MAXCONNECTIONS) if (!iscsi_add_text(session, pdu, data, max_data_length, "MaxConnections", "Irrelevant")) return 0; if (session->irrelevant_keys_bitmap & IRRELEVANT_INITIALR2T) if (!iscsi_add_text(session, pdu, data, max_data_length, "InitialR2T", "Irrelevant")) return 0; if (session->irrelevant_keys_bitmap & IRRELEVANT_IMMEDIATEDATA) if (!iscsi_add_text(session, pdu, data, max_data_length, "ImmediateData", "Irrelevant")) return 0; if (session->irrelevant_keys_bitmap & IRRELEVANT_MAXBURSTLENGTH) if (!iscsi_add_text(session, pdu, data, max_data_length, "MaxBurstLength", "Irrelevant")) return 0; if (session->irrelevant_keys_bitmap & IRRELEVANT_FIRSTBURSTLENGTH) if (!iscsi_add_text(session, pdu, data, max_data_length, "FirstBurstLength", "Irrelevant")) return 0; if (session->irrelevant_keys_bitmap & IRRELEVANT_MAXOUTSTANDINGR2T) if (!iscsi_add_text(session, pdu, data, max_data_length, "MaxOutstandingR2T", "Irrelevant")) return 0; if (session->irrelevant_keys_bitmap & IRRELEVANT_DATAPDUINORDER) if (!iscsi_add_text(session, pdu, data, max_data_length, "DataPDUInOrder", "Irrelevant")) return 0; if (session->irrelevant_keys_bitmap & IRRELEVANT_DATASEQUENCEINORDER ) if (!iscsi_add_text(session, pdu, data, max_data_length, "DataSequenceInOrder", "Irrelevant")) return 0; return 1;}static intfill_crc_digest_text(struct iscsi_session *session, struct iscsi_hdr *pdu, char *data, int max_data_length){ switch (session->header_digest) { case ISCSI_DIGEST_NONE: if (!iscsi_add_text(session, pdu, data, max_data_length, "HeaderDigest", "None")) return 0; break; case ISCSI_DIGEST_CRC32C: if (!iscsi_add_text(session, pdu, data, max_data_length, "HeaderDigest", "CRC32C")) return 0; break; case ISCSI_DIGEST_CRC32C_NONE: if (!iscsi_add_text(session, pdu, data, max_data_length, "HeaderDigest", "CRC32C,None")) return 0; break; default: case ISCSI_DIGEST_NONE_CRC32C: if (!iscsi_add_text(session, pdu, data, max_data_length, "HeaderDigest", "None,CRC32C")) return 0; break; } switch (session->data_digest) { case ISCSI_DIGEST_NONE: if (!iscsi_add_text(session, pdu, data, max_data_length, "DataDigest", "None")) return 0; break; case ISCSI_DIGEST_CRC32C: if (!iscsi_add_text(session, pdu, data, max_data_length, "DataDigest", "CRC32C")) return 0; break; case ISCSI_DIGEST_CRC32C_NONE: if (!iscsi_add_text(session, pdu, data, max_data_length, "DataDigest", "CRC32C,None")) return 0; break; default: case ISCSI_DIGEST_NONE_CRC32C: if (!iscsi_add_text(session, pdu, data, max_data_length, "DataDigest", "None,CRC32C")) return 0; break; } return 1;}static intfill_op_params_text(struct iscsi_session *session, struct iscsi_hdr *pdu, char *data, int max_data_length, int *transit){ char value[AUTH_STR_MAX_LEN]; /* we always try to go from op params to full feature stage */ session->current_stage = ISCSI_OP_PARMS_NEGOTIATION_STAGE; session->next_stage = ISCSI_FULL_FEATURE_PHASE; *transit = 1; /* * If we haven't gotten a partial response, then either we shouldn't be * here, or we just switched to this stage, and need to start offering * keys. */ if (!session->partial_response) { /* * request the desired settings the first time * we are in this stage */ if (!fill_crc_digest_text(session, pdu, data, max_data_length)) return 0; sprintf(value, "%d", session->max_recv_data_segment_len); if (!iscsi_add_text(session, pdu, data, max_data_length, "MaxRecvDataSegmentLength", value)) return 0; sprintf(value, "%d", session->def_time2wait); if (!iscsi_add_text(session, pdu, data, max_data_length, "DefaultTime2Wait", value)) return 0; sprintf(value, "%d", session->def_time2retain); if (!iscsi_add_text(session, pdu, data, max_data_length, "DefaultTime2Retain", value)) return 0; if (!iscsi_add_text(session, pdu, data, max_data_length, "IFMarker", "No")) return 0; if (!iscsi_add_text(session, pdu, data, max_data_length, "OFMarker", "No")) return 0; if (!iscsi_add_text(session, pdu, data, max_data_length, "ErrorRecoveryLevel", "0")) return 0; if (session->type == ISCSI_SESSION_TYPE_NORMAL) if (!add_params_normal_session(session, pdu, data, max_data_length))
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -