Esempio n. 1
0
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;
	}
}
Esempio n. 2
0
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;
	}
}
Esempio n. 4
0
int
iscsi_get_pdu_data_size(const unsigned char *hdr)
{
	int size;

	size = scsi_get_uint32(&hdr[4]) & 0x00ffffff;

	return size;
}
Esempio n. 5
0
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;
}
Esempio n. 6
0
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;
}
Esempio n. 7
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;
}
Esempio n. 8
0
File: pdu.c Progetto: Swind/libiscsi
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;
}
Esempio n. 9
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;
}
Esempio n. 10
0
File: pdu.c Progetto: Swind/libiscsi
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;
}
Esempio n. 11
0
File: pdu.c Progetto: Swind/libiscsi
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;
}
Esempio n. 12
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;
}
Esempio n. 13
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;
}