Beispiel #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;
	}
}
Beispiel #2
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;
}
Beispiel #3
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;
	}
}
Beispiel #4
0
void
iscsi_add_to_outqueue(struct iscsi_context *iscsi, struct iscsi_pdu *pdu)
{
	struct iscsi_pdu *current = iscsi->outqueue;
	struct iscsi_pdu *last = NULL;

	if (iscsi->scsi_timeout > 0) {
		pdu->scsi_timeout = time(NULL) + iscsi->scsi_timeout;
	} else {
		pdu->scsi_timeout = 0;
	}

	if (iscsi->outqueue == NULL) {
		iscsi->outqueue = pdu;
		pdu->next = NULL;
		return;
	}
	
	/* queue pdus in ascending order of CmdSN. 
	 * ensure that pakets with the same CmdSN are kept in FIFO order.
	 * immediate PDUs are queued in front of queue with the CmdSN
	 * of the first element in the outqueue.
	 */

	if (pdu->outdata.data[0] & ISCSI_PDU_IMMEDIATE) {
		iscsi_pdu_set_cmdsn(pdu, current->cmdsn);
	}

	do {
		if (iscsi_serial32_compare(pdu->cmdsn, current->cmdsn) < 0 ||
			(pdu->outdata.data[0] & ISCSI_PDU_IMMEDIATE && !(current->outdata.data[0] & ISCSI_PDU_IMMEDIATE))) {
			/* insert PDU before the current */
			if (last != NULL) {
				last->next=pdu;
			} else {
				iscsi->outqueue=pdu;
			}
			pdu->next = current;
			return;
		}
		last=current;
		current=current->next;
	} while (current != NULL);
	
	last->next = pdu;
	pdu->next = NULL;
}
Beispiel #5
0
int
iscsi_tcp_which_events(struct iscsi_context *iscsi)
{
	int events = iscsi->is_connected ? POLLIN : POLLOUT;

	if (iscsi->pending_reconnect && iscsi->old_iscsi &&
		time(NULL) < iscsi->next_reconnect) {
		return 0;
	}

	if (iscsi->outqueue_current != NULL ||
	    (iscsi->outqueue != NULL && !iscsi->is_corked &&
	     (iscsi_serial32_compare(iscsi->outqueue->cmdsn, iscsi->maxcmdsn) <= 0 ||
	      iscsi->outqueue->outdata.data[0] & ISCSI_PDU_IMMEDIATE)
	    )
	   ) {
		events |= POLLOUT;
	}
	return events;
}
Beispiel #6
0
void
iscsi_add_to_outqueue(struct iscsi_context *iscsi, struct iscsi_pdu *pdu)
{
	struct iscsi_pdu *current = iscsi->outqueue;
	struct iscsi_pdu *last = NULL;

	if (iscsi->scsi_timeout > 0) {
		pdu->scsi_timeout = time(NULL) + iscsi->scsi_timeout;
	} else {
		pdu->scsi_timeout = 0;
	}

	if (iscsi->outqueue == NULL) {
		iscsi->outqueue = pdu;
		pdu->next = NULL;
		return;
	}
	
	/* queue pdus in ascending order of CmdSN. 
	 * ensure that pakets with the same CmdSN are kept in FIFO order.
	 */
	do {
		if (iscsi_serial32_compare(pdu->cmdsn, current->cmdsn) < 0 ||
			pdu->flags & ISCSI_PDU_URGENT_DELIVERY) {
			/* insert PDU before the current */
			if (last != NULL) {
				last->next=pdu;
			} else {
				iscsi->outqueue=pdu;
			}
			pdu->next = current;
			return;
		}
		last=current;
		current=current->next;
	} while (current != NULL);
	
	last->next = pdu;
	pdu->next = NULL;
}
Beispiel #7
0
static int
iscsi_write_to_socket(struct iscsi_context *iscsi)
{
	ssize_t count;
	size_t total;
	struct iscsi_pdu *pdu;
	static char padding_buf[3];
	int socket_flags = 0;

#ifdef MSG_NOSIGNAL
	socket_flags |= MSG_NOSIGNAL;
#elif SO_NOSIGPIPE
	socket_flags |= SO_NOSIGPIPE;
#endif

	if (iscsi->fd == -1) {
		iscsi_set_error(iscsi, "trying to write but not connected");
		return -1;
	}

	while (iscsi->outqueue != NULL || iscsi->outqueue_current != NULL) {
		if (iscsi->outqueue_current == NULL) {
			if (iscsi->is_corked) {
				/* connection is corked we are not allowed to send
				 * additional PDUs */
				ISCSI_LOG(iscsi, 6, "iscsi_write_to_socket: socket is corked");
				return 0;
			}
			
			if (iscsi_serial32_compare(iscsi->outqueue->cmdsn, iscsi->maxcmdsn) > 0
				&& !(iscsi->outqueue->outdata.data[0] & ISCSI_PDU_IMMEDIATE)) {
				/* stop sending for non-immediate PDUs. maxcmdsn is reached */
				ISCSI_LOG(iscsi, 6,
				          "iscsi_write_to_socket: maxcmdsn reached (outqueue[0]->cmdsnd %08x > maxcmdsn %08x)",
				          iscsi->outqueue->cmdsn, iscsi->maxcmdsn);
				return 0;
			}

			/* pop first element of the outqueue */
			if (iscsi_serial32_compare(iscsi->outqueue->cmdsn, iscsi->expcmdsn) < 0 &&
				(iscsi->outqueue->outdata.data[0] & 0x3f) != ISCSI_PDU_DATA_OUT) {
				iscsi_set_error(iscsi, "iscsi_write_to_socket: outqueue[0]->cmdsn < expcmdsn (%08x < %08x) opcode %02x",
				                iscsi->outqueue->cmdsn, iscsi->expcmdsn, iscsi->outqueue->outdata.data[0] & 0x3f);
				return -1;
			}
			iscsi->outqueue_current = iscsi->outqueue;
			
			/* set exp statsn */
			iscsi_pdu_set_expstatsn(iscsi->outqueue_current, iscsi->statsn + 1);
			
			ISCSI_LIST_REMOVE(&iscsi->outqueue, iscsi->outqueue_current);
			if (!(iscsi->outqueue_current->flags & ISCSI_PDU_DELETE_WHEN_SENT)) {
				/* we have to add the pdu to the waitqueue already here
				   since the storage might sent a R2T as soon as it has
				   received the header. if we sent immediate data in a
				   cmd PDU the R2T might get lost otherwise. */
				ISCSI_LIST_ADD_END(&iscsi->waitpdu, iscsi->outqueue_current);
			}
		}

		pdu = iscsi->outqueue_current;
		pdu->outdata.size = (pdu->outdata.size + 3) & 0xfffffffc;

		/* Write header and any immediate data */
		if (pdu->outdata_written < pdu->outdata.size) {
			count = send(iscsi->fd,
				     pdu->outdata.data + pdu->outdata_written,
				     pdu->outdata.size - pdu->outdata_written,
				     socket_flags);
			if (count == -1) {
				if (errno == EAGAIN || errno == EWOULDBLOCK) {
					return 0;
				}
				iscsi_set_error(iscsi, "Error when writing to "
						"socket :%d", errno);
				return -1;
			}
			pdu->outdata_written += count;
		}
		/* if we havent written the full header yet. */
		if (pdu->outdata_written != pdu->outdata.size) {
			return 0;
		}

		/* Write any iovectors that might have been passed to us */
		while (pdu->payload_written < pdu->payload_len) {
			struct scsi_iovector* iovector_out;

			iovector_out = iscsi_get_scsi_task_iovector_out(iscsi, pdu);

			if (iovector_out == NULL) {
				iscsi_set_error(iscsi, "Can't find iovector data for DATA-OUT");
				return -1;
			}

			count = iscsi_iovector_readv_writev(iscsi,
				iovector_out,
				pdu->payload_offset + pdu->payload_written,
				pdu->payload_len - pdu->payload_written, 1);
			if (count == -1) {
				if (errno == EAGAIN || errno == EWOULDBLOCK) {
					return 0;
				}
				iscsi_set_error(iscsi, "Error when writing to "
						"socket :%d %s", errno,
						iscsi_get_error(iscsi));
				return -1;
			}

			pdu->payload_written += count;
		}

		total = pdu->payload_len;
		total = (total + 3) & 0xfffffffc;

		/* Write padding */
		if (pdu->payload_written < total) {
			count = send(iscsi->fd, padding_buf, total - pdu->payload_written, socket_flags);
			if (count == -1) {
				if (errno == EAGAIN || errno == EWOULDBLOCK) {
					return 0;
				}
				iscsi_set_error(iscsi, "Error when writing to "
						"socket :%d", errno);
				return -1;
			}
			pdu->payload_written += count;
		}
		/* if we havent written the full padding yet. */
		if (pdu->payload_written != total) {
			return 0;
		}
		if (pdu->flags & ISCSI_PDU_CORK_WHEN_SENT) {
			iscsi->is_corked = 1;
		}
		if (pdu->flags & ISCSI_PDU_DELETE_WHEN_SENT) {
			iscsi->t->free_pdu(iscsi, pdu);
		}
		iscsi->outqueue_current = NULL;
	}
	return 0;
}
Beispiel #8
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;
}