void iscsi_adjust_maxexpcmdsn(struct iscsi_context *iscsi, struct iscsi_in_pdu *in) { uint32_t maxcmdsn = scsi_get_uint32(&in->hdr[32]); if (iscsi_serial32_compare(maxcmdsn, iscsi->maxcmdsn) > 0) { iscsi->maxcmdsn = maxcmdsn; } uint32_t expcmdsn = scsi_get_uint32(&in->hdr[28]); if (iscsi_serial32_compare(expcmdsn, iscsi->expcmdsn) > 0) { iscsi->expcmdsn = expcmdsn; } }
void iscsi_adjust_statsn(struct iscsi_context *iscsi, struct iscsi_in_pdu *in) { uint32_t statsn = scsi_get_uint32(&in->hdr[24]); uint32_t itt = scsi_get_uint32(&in->hdr[16]); if (itt == 0xffffffff) { /* target will not increase statsn if itt == 0xffffffff */ statsn--; } if (iscsi_serial32_compare(statsn, iscsi->statsn) > 0) { iscsi->statsn = statsn; } }
void test_prin_read_keys_simple(void) { int ret = 0; int al; logging(LOG_VERBOSE, LOG_BLANK_LINE); logging(LOG_VERBOSE, "Test Persistent Reserve IN READ_KEYS works."); ret = prin_read_keys(iscsic, tgt_lun, &task, NULL); if (ret == -2) { logging(LOG_NORMAL, "[SKIPPED] PERSISTEN RESERVE IN is not implemented."); CU_PASS("PERSISTENT RESERVE IN is not implemented."); return; } CU_ASSERT_EQUAL(ret, 0); logging(LOG_VERBOSE, "Test DATA-IN is at least 8 bytes."); if (task->datain.size < 8) { logging(LOG_NORMAL, "[FAILED] DATA-IN returned less than 8 bytes"); return; } logging(LOG_VERBOSE, "Test ADDITIONAL_LENGTH matches DATA_IN size."); al = scsi_get_uint32(&task->datain.data[4]); if (al != task->datain.size - 8) { logging(LOG_NORMAL, "[FAILED] ADDITIONAL_LENGTH was %d bytes but %d was expected.", al, task->datain.size - 8); return; } }
int iscsi_get_pdu_data_size(const unsigned char *hdr) { int size; size = scsi_get_uint32(&hdr[4]) & 0x00ffffff; return size; }
int iscsi_get_pdu_padding_size(const unsigned char *hdr) { int data_size, padded_size; data_size = scsi_get_uint32(&hdr[4]) & 0x00ffffff; padded_size = (data_size+3) & 0xfffffffc; return padded_size - data_size; }
int iscsi_process_logout_reply(struct iscsi_context *iscsi, struct iscsi_pdu *pdu, struct iscsi_in_pdu *in) { uint32_t maxcmdsn, expcmdsn; maxcmdsn = scsi_get_uint32(&in->hdr[32]); if (iscsi_serial32_compare(maxcmdsn, iscsi->maxcmdsn) > 0) { iscsi->maxcmdsn = maxcmdsn; } expcmdsn = scsi_get_uint32(&in->hdr[28]); if (iscsi_serial32_compare(expcmdsn, iscsi->expcmdsn) > 0) { iscsi->expcmdsn = expcmdsn; } iscsi->is_loggedin = 0; pdu->callback(iscsi, SCSI_STATUS_GOOD, NULL, pdu->private_data); return 0; }
int iscsi_process_target_nop_in(struct iscsi_context *iscsi, struct iscsi_in_pdu *in) { uint32_t ttt = scsi_get_uint32(&in->hdr[20]); uint32_t itt = scsi_get_uint32(&in->hdr[16]); uint32_t lun = scsi_get_uint16(&in->hdr[8]); ISCSI_LOG(iscsi, (iscsi->nops_in_flight > 1) ? 1 : 6, "NOP-In received (pdu->itt %08x, pdu->ttt %08x, pdu->lun %8x, iscsi->maxcmdsn %08x, iscsi->expcmdsn %08x, iscsi->statsn %08x)", itt, ttt, lun, iscsi->maxcmdsn, iscsi->expcmdsn, iscsi->statsn); /* if the server does not want a response */ if (ttt == 0xffffffff) { return 0; } iscsi_send_target_nop_out(iscsi, ttt, lun); return 0; }
int iscsi_process_target_nop_in(struct iscsi_context *iscsi, struct iscsi_in_pdu *in) { uint32_t ttt; uint32_t statsn; ttt = scsi_get_uint32(&in->hdr[20]); statsn = scsi_get_uint32(&in->hdr[24]); if (statsn > iscsi->statsn) { iscsi->statsn = statsn; } /* if the server does not want a response */ if (ttt == 0xffffffff) { return 0; } iscsi_send_target_nop_out(iscsi, ttt); return 0; }
int iscsi_process_target_nop_in(struct iscsi_context *iscsi, struct iscsi_in_pdu *in) { uint32_t ttt; ttt = scsi_get_uint32(&in->hdr[20]); iscsi_adjust_statsn(iscsi, in); /* if the server does not want a response */ if (ttt == 0xffffffff) { return 0; } iscsi_send_target_nop_out(iscsi, ttt); return 0; }
int iscsi_process_reject(struct iscsi_context *iscsi, struct iscsi_in_pdu *in) { int size = in->data_pos; uint32_t itt; struct iscsi_pdu *pdu; if (size < ISCSI_RAW_HEADER_SIZE) { iscsi_set_error(iscsi, "size of REJECT payload is too small." "Need >= %d bytes but got %d.", ISCSI_RAW_HEADER_SIZE, (int)size); return -1; } itt = scsi_get_uint32(&in->data[16]); if (iscsi->log_level > 1) { iscsi_dump_pdu_header(iscsi, in->data); } for (pdu = iscsi->waitpdu; pdu; pdu = pdu->next) { if (pdu->itt == itt) { break; } } if (pdu == NULL) { iscsi_set_error(iscsi, "Can not match REJECT with" "any outstanding pdu with itt:0x%08x", itt); return -1; } pdu->callback(iscsi, SCSI_STATUS_ERROR, NULL, pdu->private_data); SLIST_REMOVE(&iscsi->waitpdu, pdu); iscsi_free_pdu(iscsi, pdu); return 0; }
int iscsi_process_pdu(struct iscsi_context *iscsi, struct iscsi_in_pdu *in) { uint32_t itt; enum iscsi_opcode opcode; struct iscsi_pdu *pdu; uint8_t ahslen; opcode = in->hdr[0] & 0x3f; ahslen = in->hdr[4]; itt = scsi_get_uint32(&in->hdr[16]); if (ahslen != 0) { iscsi_set_error(iscsi, "cant handle expanded headers yet"); return -1; } if (opcode == ISCSI_PDU_REJECT) { iscsi_set_error(iscsi, "Request was rejected with reason: 0x%02x (%s)", in->hdr[2], iscsi_reject_reason_str(in->hdr[2])); if (iscsi_process_reject(iscsi, in) != 0) { return -1; } return 0; } if (opcode == ISCSI_PDU_NOP_IN && itt == 0xffffffff) { if (iscsi_process_target_nop_in(iscsi, in) != 0) { return -1; } return 0; } for (pdu = iscsi->waitpdu; pdu; pdu = pdu->next) { enum iscsi_opcode expected_response = pdu->response_opcode; int is_finished = 1; if (pdu->itt != itt) { continue; } /* we have a special case with scsi-command opcodes, * they are replied to by either a scsi-response * or a data-in, or a combination of both. */ if (opcode == ISCSI_PDU_DATA_IN && expected_response == ISCSI_PDU_SCSI_RESPONSE) { expected_response = ISCSI_PDU_DATA_IN; } /* Another special case is if we get a R2T. * In this case we should find the original request and just send an additional * DATAOUT segment for this task. */ if (opcode == ISCSI_PDU_R2T) { expected_response = ISCSI_PDU_R2T; } if (opcode != expected_response) { iscsi_set_error(iscsi, "Got wrong opcode back for " "itt:%d got:%d expected %d", itt, opcode, pdu->response_opcode); return -1; } switch (opcode) { case ISCSI_PDU_LOGIN_RESPONSE: if (iscsi_process_login_reply(iscsi, pdu, in) != 0) { SLIST_REMOVE(&iscsi->waitpdu, pdu); iscsi_free_pdu(iscsi, pdu); iscsi_set_error(iscsi, "iscsi login reply " "failed"); return -1; } break; case ISCSI_PDU_TEXT_RESPONSE: if (iscsi_process_text_reply(iscsi, pdu, in) != 0) { SLIST_REMOVE(&iscsi->waitpdu, pdu); iscsi_free_pdu(iscsi, pdu); iscsi_set_error(iscsi, "iscsi text reply " "failed"); return -1; } break; case ISCSI_PDU_LOGOUT_RESPONSE: if (iscsi_process_logout_reply(iscsi, pdu, in) != 0) { SLIST_REMOVE(&iscsi->waitpdu, pdu); iscsi_free_pdu(iscsi, pdu); iscsi_set_error(iscsi, "iscsi logout reply " "failed"); return -1; } break; case ISCSI_PDU_SCSI_RESPONSE: if (iscsi_process_scsi_reply(iscsi, pdu, in) != 0) { SLIST_REMOVE(&iscsi->waitpdu, pdu); iscsi_free_pdu(iscsi, pdu); iscsi_set_error(iscsi, "iscsi response reply " "failed"); return -1; } break; case ISCSI_PDU_DATA_IN: if (iscsi_process_scsi_data_in(iscsi, pdu, in, &is_finished) != 0) { SLIST_REMOVE(&iscsi->waitpdu, pdu); iscsi_free_pdu(iscsi, pdu); iscsi_set_error(iscsi, "iscsi data in " "failed"); return -1; } break; case ISCSI_PDU_NOP_IN: if (iscsi_process_nop_out_reply(iscsi, pdu, in) != 0) { SLIST_REMOVE(&iscsi->waitpdu, pdu); iscsi_free_pdu(iscsi, pdu); iscsi_set_error(iscsi, "iscsi nop-in failed"); return -1; } break; case ISCSI_PDU_SCSI_TASK_MANAGEMENT_RESPONSE: if (iscsi_process_task_mgmt_reply(iscsi, pdu, in) != 0) { SLIST_REMOVE(&iscsi->waitpdu, pdu); iscsi_free_pdu(iscsi, pdu); iscsi_set_error(iscsi, "iscsi task-mgmt failed"); return -1; } break; case ISCSI_PDU_R2T: if (iscsi_process_r2t(iscsi, pdu, in) != 0) { SLIST_REMOVE(&iscsi->waitpdu, pdu); iscsi_free_pdu(iscsi, pdu); iscsi_set_error(iscsi, "iscsi r2t " "failed"); return -1; } is_finished = 0; break; default: iscsi_set_error(iscsi, "Don't know how to handle " "opcode 0x%02x", opcode); return -1; } if (is_finished) { SLIST_REMOVE(&iscsi->waitpdu, pdu); iscsi_free_pdu(iscsi, pdu); } return 0; } return 0; }
static int iscsi_read_from_socket(struct iscsi_context *iscsi) { struct iscsi_in_pdu *in; ssize_t data_size, count, padding_size; if (iscsi->incoming == NULL) { iscsi->incoming = iscsi_szmalloc(iscsi, sizeof(struct iscsi_in_pdu)); iscsi->incoming->hdr = iscsi_szmalloc(iscsi, ISCSI_RAW_HEADER_SIZE + ISCSI_DIGEST_SIZE); if (iscsi->incoming == NULL) { iscsi_set_error(iscsi, "Out-of-memory: failed to malloc iscsi_in_pdu"); return -1; } } in = iscsi->incoming; /* first we must read the header, including any digests */ if (in->hdr_pos < ISCSI_HEADER_SIZE) { /* try to only read the header, the socket is nonblocking, so * no need to limit the read to what is available in the socket */ count = ISCSI_HEADER_SIZE - in->hdr_pos; count = recv(iscsi->fd, &in->hdr[in->hdr_pos], count, 0); if (count == 0) { return -1; } if (count < 0) { if (errno == EINTR || errno == EAGAIN) { return 0; } iscsi_set_error(iscsi, "read from socket failed, " "errno:%d", errno); return -1; } in->hdr_pos += count; } if (in->hdr_pos < ISCSI_HEADER_SIZE) { /* we don't have the full header yet, so return */ return 0; } padding_size = iscsi_get_pdu_padding_size(&in->hdr[0]); data_size = iscsi_get_pdu_data_size(&in->hdr[0]) + padding_size; if (data_size < 0 || data_size > (ssize_t)iscsi->initiator_max_recv_data_segment_length) { iscsi_set_error(iscsi, "Invalid data size received from target (%d)", (int)data_size); return -1; } if (data_size != 0) { unsigned char padding_buf[3]; unsigned char *buf = padding_buf; struct scsi_iovector * iovector_in; count = data_size - in->data_pos; /* first try to see if we already have a user buffer */ iovector_in = iscsi_get_scsi_task_iovector_in(iscsi, in); if (iovector_in != NULL && count > padding_size) { uint32_t offset = scsi_get_uint32(&in->hdr[40]); count = iscsi_iovector_readv_writev(iscsi, iovector_in, in->data_pos + offset, count - padding_size, 0); } else { if (iovector_in == NULL) { if (in->data == NULL) { in->data = iscsi_malloc(iscsi, data_size); if (in->data == NULL) { iscsi_set_error(iscsi, "Out-of-memory: failed to malloc iscsi_in_pdu->data(%d)", (int)data_size); return -1; } } buf = &in->data[in->data_pos]; } count = recv(iscsi->fd, buf, count, 0); } if (count == 0) { return -1; } if (count < 0) { if (errno == EINTR || errno == EAGAIN) { return 0; } iscsi_set_error(iscsi, "read from socket failed, " "errno:%d %s", errno, iscsi_get_error(iscsi)); return -1; } in->data_pos += count; } if (in->data_pos < data_size) { return 0; } ISCSI_LIST_ADD_END(&iscsi->inqueue, in); iscsi->incoming = NULL; while (iscsi->inqueue != NULL) { struct iscsi_in_pdu *current = iscsi->inqueue; if (iscsi_process_pdu(iscsi, current) != 0) { return -1; } ISCSI_LIST_REMOVE(&iscsi->inqueue, current); iscsi_free_iscsi_in_pdu(iscsi, current); } return 0; }
int iscsi_process_login_reply(struct iscsi_context *iscsi, struct iscsi_pdu *pdu, struct iscsi_in_pdu *in) { uint32_t status, maxcmdsn, expcmdsn; char *ptr = (char *)in->data; int size = in->data_pos; status = scsi_get_uint16(&in->hdr[36]); iscsi->statsn = scsi_get_uint16(&in->hdr[24]); maxcmdsn = scsi_get_uint32(&in->hdr[32]); if (iscsi_serial32_compare(maxcmdsn, iscsi->maxcmdsn) > 0) { iscsi->maxcmdsn = maxcmdsn; } expcmdsn = scsi_get_uint32(&in->hdr[28]); if (iscsi_serial32_compare(expcmdsn, iscsi->expcmdsn) > 0) { iscsi->expcmdsn = expcmdsn; } /* XXX here we should parse the data returned in case the target * renegotiated some some parameters. * we should also do proper handshaking if the target is not yet * prepared to transition to the next stage */ while (size > 0) { char *end; int len; end = memchr(ptr, 0, size); if (end == NULL) { iscsi_set_error(iscsi, "NUL not found after offset %ld " "when parsing login data", (long)((unsigned char *)ptr - in->data)); pdu->callback(iscsi, SCSI_STATUS_ERROR, NULL, pdu->private_data); return -1; } len = end - ptr; if (len == 0) { break; } /* parse the strings */ if (!strncmp(ptr, "TargetAddress=", 14)) { strncpy(iscsi->target_address,ptr+14,MAX_STRING_SIZE); } if (!strncmp(ptr, "HeaderDigest=", 13)) { if (!strcmp(ptr + 13, "CRC32C")) { iscsi->want_header_digest = ISCSI_HEADER_DIGEST_CRC32C; } else { iscsi->want_header_digest = ISCSI_HEADER_DIGEST_NONE; } } if (!strncmp(ptr, "FirstBurstLength=", 17)) { iscsi->first_burst_length = strtol(ptr + 17, NULL, 10); } if (!strncmp(ptr, "InitialR2T=", 11)) { if (!strcmp(ptr + 11, "No")) { iscsi->use_initial_r2t = ISCSI_INITIAL_R2T_NO; } else { iscsi->use_initial_r2t = ISCSI_INITIAL_R2T_YES; } } if (!strncmp(ptr, "ImmediateData=", 14)) { if (!strcmp(ptr + 14, "No")) { iscsi->use_immediate_data = ISCSI_IMMEDIATE_DATA_NO; } else if (iscsi->want_immediate_data == ISCSI_IMMEDIATE_DATA_NO) { /* If we negotiated NO, it doesnt matter what * the target said. ImmediateData is NO. */ iscsi->use_immediate_data = ISCSI_IMMEDIATE_DATA_NO; } else { iscsi->use_immediate_data = ISCSI_IMMEDIATE_DATA_YES; } } if (!strncmp(ptr, "MaxBurstLength=", 15)) { iscsi->max_burst_length = strtol(ptr + 15, NULL, 10); } if (!strncmp(ptr, "MaxRecvDataSegmentLength=", 25)) { iscsi->target_max_recv_data_segment_length = strtol(ptr + 25, NULL, 10); } if (!strncmp(ptr, "AuthMethod=", 11)) { if (!strcmp(ptr + 11, "CHAP")) { iscsi->secneg_phase = ISCSI_LOGIN_SECNEG_PHASE_SELECT_ALGORITHM; } } if (!strncmp(ptr, "CHAP_A=", 7)) { iscsi->chap_a = atoi(ptr+7); iscsi->secneg_phase = ISCSI_LOGIN_SECNEG_PHASE_SEND_RESPONSE; } if (!strncmp(ptr, "CHAP_I=", 7)) { iscsi->chap_i = atoi(ptr+7); iscsi->secneg_phase = ISCSI_LOGIN_SECNEG_PHASE_SEND_RESPONSE; } if (!strncmp(ptr, "CHAP_C=0x", 9)) { strncpy(iscsi->chap_c,ptr+9,MAX_STRING_SIZE); iscsi->secneg_phase = ISCSI_LOGIN_SECNEG_PHASE_SEND_RESPONSE; } ptr += len + 1; size -= len + 1; } if (status == SCSI_STATUS_REDIRECT && iscsi->target_address[0]) { ISCSI_LOG(iscsi, 2, "target requests redirect to %s",iscsi->target_address); pdu->callback(iscsi, SCSI_STATUS_REDIRECT, NULL, pdu->private_data); return 0; } if (status != 0) { iscsi_set_error(iscsi, "Failed to log in to target. Status: %s(%d)", login_error_str(status), status); pdu->callback(iscsi, SCSI_STATUS_ERROR, NULL, pdu->private_data); return 0; } if (in->hdr[1] & ISCSI_PDU_LOGIN_TRANSIT) { iscsi->current_phase = (in->hdr[1] & ISCSI_PDU_LOGIN_NSG_FF) << 2; } if ((in->hdr[1] & ISCSI_PDU_LOGIN_TRANSIT) && (in->hdr[1] & ISCSI_PDU_LOGIN_NSG_FF) == ISCSI_PDU_LOGIN_NSG_FF) { iscsi->is_loggedin = 1; iscsi_itt_post_increment(iscsi); iscsi->header_digest = iscsi->want_header_digest; ISCSI_LOG(iscsi, 2, "login successful"); pdu->callback(iscsi, SCSI_STATUS_GOOD, NULL, pdu->private_data); } else { if (iscsi_login_async(iscsi, pdu->callback, pdu->private_data) != 0) { iscsi_set_error(iscsi, "Failed to send continuation login pdu"); pdu->callback(iscsi, SCSI_STATUS_ERROR, NULL, pdu->private_data); } } return 0; }