Example #1
0
static int
iscsi_login_add_authalgorithm(struct iscsi_context *iscsi, struct iscsi_pdu *pdu)
{
	char str[MAX_STRING_SIZE+1];

	if (iscsi->current_phase != ISCSI_PDU_LOGIN_CSG_SECNEG
	|| iscsi->secneg_phase != ISCSI_LOGIN_SECNEG_PHASE_SELECT_ALGORITHM) {
		return 0;
	}

	strncpy(str,"CHAP_A=5",MAX_STRING_SIZE);
	if (iscsi_pdu_add_data(iscsi, pdu, (unsigned char *)str, strlen(str)+1)
	    != 0) {
		iscsi_set_error(iscsi, "Out-of-memory: pdu add data failed.");
		return -1;
	}

	return 0;
}
Example #2
0
static int
iscsi_login_add_ofmarker(struct iscsi_context *iscsi, struct iscsi_pdu *pdu)
{
	char str[MAX_STRING_SIZE+1];

	/* We only send OFMarker during opneg */
	if (iscsi->current_phase != ISCSI_PDU_LOGIN_CSG_OPNEG) {
		return 0;
	}

	strncpy(str,"OFMarker=No",MAX_STRING_SIZE);
	if (iscsi_pdu_add_data(iscsi, pdu, (unsigned char *)str, strlen(str)+1)
	    != 0) {
		iscsi_set_error(iscsi, "Out-of-memory: pdu add data failed.");
		return -1;
	}

	return 0;
}
Example #3
0
static int
iscsi_login_add_authmethod(struct iscsi_context *iscsi, struct iscsi_pdu *pdu)
{
	char str[MAX_STRING_SIZE+1];

	if (iscsi->current_phase != ISCSI_PDU_LOGIN_CSG_SECNEG
	|| iscsi->secneg_phase != ISCSI_LOGIN_SECNEG_PHASE_OFFER_CHAP) {
		return 0;
	}

	strncpy(str,"AuthMethod=CHAP,None",MAX_STRING_SIZE);
	if (iscsi_pdu_add_data(iscsi, pdu, (unsigned char *)str, strlen(str)+1)
	    != 0) {
		iscsi_set_error(iscsi, "Out-of-memory: pdu add data failed.");
		return -1;
	}

	return 0;
}
Example #4
0
int
iscsi_disconnect(struct iscsi_context *iscsi)
{
	if (iscsi->fd == -1) {
		iscsi_set_error(iscsi, "Trying to disconnect "
				"but not connected");
		return -1;
	}

	close(iscsi->fd);
	
	if (iscsi->connected_portal)
		DPRINTF(iscsi,2,"disconnected from portal %s",iscsi->connected_portal);

	iscsi->fd  = -1;
	iscsi->is_connected = 0;

	return 0;
}
Example #5
0
int iscsi_init_transport(struct iscsi_context *iscsi,
			 enum iscsi_transport_type transport) {
	iscsi->transport = transport;

	switch (iscsi->transport) {
	case TCP_TRANSPORT:
		iscsi_init_tcp_transport(iscsi);
		break;
#ifdef HAVE_LINUX_ISER
	case ISER_TRANSPORT:
		iscsi_init_iser_transport(iscsi);
		break;
#endif
	default:
		iscsi_set_error(iscsi, "Unfamiliar transport type");
		return -1;
	}

	return 0;
}
Example #6
0
void
iscsi_free_pdu(struct iscsi_context *iscsi, struct iscsi_pdu *pdu)
{
	if (pdu == NULL) {
		iscsi_set_error(iscsi, "trying to free NULL pdu");
		return;
	}

	free(pdu->outdata.data);
	pdu->outdata.data = NULL;

	free(pdu->indata.data);
	pdu->indata.data = NULL;

	if (pdu->scsi_cbdata) {
		iscsi_free_scsi_cbdata(pdu->scsi_cbdata);
		pdu->scsi_cbdata = NULL;
	}

	free(pdu);
}
Example #7
0
static int
iscsi_tcp_disconnect(struct iscsi_context *iscsi)
{
	if (iscsi->fd == -1) {
		iscsi_set_error(iscsi, "Trying to disconnect "
				"but not connected");
		return -1;
	}

	close(iscsi->fd);

	if (!(iscsi->pending_reconnect && iscsi->old_iscsi) &&
	    iscsi->connected_portal[0]) {
		ISCSI_LOG(iscsi, 2, "disconnected from portal %s",iscsi->connected_portal);
	}

	iscsi->fd  = -1;
	iscsi->is_connected = 0;
	iscsi->is_corked = 0;

	return 0;
}
Example #8
0
File: pdu.c Project: Swind/libiscsi
void
iscsi_free_pdu(struct iscsi_context *iscsi, struct iscsi_pdu *pdu)
{
	if (pdu == NULL) {
		iscsi_set_error(iscsi, "trying to free NULL pdu");
		return;
	}

	if (pdu->outdata.size <= iscsi->smalloc_size) {
		iscsi_sfree(iscsi, pdu->outdata.data);
	} else {
		iscsi_free(iscsi, pdu->outdata.data);
	}
	pdu->outdata.data = NULL;

	if (pdu->indata.size <= iscsi->smalloc_size) {
		iscsi_sfree(iscsi, pdu->indata.data);
	} else {
		iscsi_free(iscsi, pdu->indata.data);
	}
	pdu->indata.data = NULL;

	iscsi_sfree(iscsi, pdu);
}
Example #9
0
int
iscsi_process_text_reply(struct iscsi_context *iscsi, struct iscsi_pdu *pdu,
			 struct iscsi_in_pdu *in)
{
	struct iscsi_discovery_address *targets = NULL;
	unsigned char *ptr = in->data;
	int size = in->data_pos;

	/* verify the response looks sane */
	if (in->hdr[1] != ISCSI_PDU_TEXT_FINAL) {
		iscsi_set_error(iscsi, "unsupported flags in text "
				"reply %02x", in->hdr[1]);
		if (pdu->callback) {
			pdu->callback(iscsi, SCSI_STATUS_ERROR, NULL,
			              pdu->private_data);
		}
		return -1;
	}

	while (size > 0) {
		unsigned char *end;
		int len;

		end = memchr(ptr, 0, size);
		if (end == NULL) {
			iscsi_set_error(iscsi, "NUL not found after offset %ld "
					"when parsing discovery data",
					(long)(ptr - in->data));
			if (pdu->callback) {
				pdu->callback(iscsi, SCSI_STATUS_ERROR, NULL,
				              pdu->private_data);
			}
			iscsi_free_discovery_addresses(iscsi, targets);
			return -1;
		}

		len = end - ptr;
		if (len == 0) {
			break;
		}

		/* parse the strings */
		if (!strncmp((char *)ptr, "TargetName=", 11)) {
			struct iscsi_discovery_address *target;

			target = iscsi_zmalloc(iscsi, sizeof(struct iscsi_discovery_address));
			if (target == NULL) {
				iscsi_set_error(iscsi, "Failed to allocate "
						"data for new discovered "
						"target");
				if (pdu->callback) {
					pdu->callback(iscsi, SCSI_STATUS_ERROR, NULL,
					              pdu->private_data);
				}
				iscsi_free_discovery_addresses(iscsi, targets);
				return -1;
			}
			target->target_name = iscsi_strdup(iscsi,(char *)ptr+11);
			if (target->target_name == NULL) {
				iscsi_set_error(iscsi, "Failed to allocate "
						"data for new discovered "
						"target name");
				if (pdu->callback) {
					pdu->callback(iscsi, SCSI_STATUS_ERROR, NULL,
					              pdu->private_data);
				}
				iscsi_free(iscsi, target);
				target = NULL;
				iscsi_free_discovery_addresses(iscsi, targets);
				return -1;
			}
			target->next = targets;
			targets = target;
		} else if (!strncmp((char *)ptr, "TargetAddress=", 14)) {
			struct iscsi_target_portal *portal;

			if (targets == NULL) {
				iscsi_set_error(iscsi, "Invalid discovery "
						"reply");
				if (pdu->callback) {
					pdu->callback(iscsi, SCSI_STATUS_ERROR, NULL,
					              pdu->private_data);
				}
				iscsi_free_discovery_addresses(iscsi, targets);
				return -1;
			}
			portal = iscsi_zmalloc(iscsi, sizeof(struct iscsi_target_portal));
			if (portal == NULL) {
				iscsi_set_error(iscsi, "Failed to malloc "
						"portal structure");
				if (pdu->callback) {
					pdu->callback(iscsi, SCSI_STATUS_ERROR, NULL,
					              pdu->private_data);
				}
				iscsi_free_discovery_addresses(iscsi, targets);
				return -1;
			}

			portal->next = targets->portals;
			targets->portals = portal;

			portal->portal = iscsi_strdup(iscsi, (char *)ptr+14);
			if (portal->portal == NULL) {
				iscsi_set_error(iscsi, "Failed to allocate "
						"data for new discovered "
						"target address");
				if (pdu->callback) {
					pdu->callback(iscsi, SCSI_STATUS_ERROR, NULL,
					              pdu->private_data);
				}
				iscsi_free_discovery_addresses(iscsi, targets);
				return -1;
			}
		} else {
			iscsi_set_error(iscsi, "Don't know how to handle "
					"discovery string : %s", ptr);
			if (pdu->callback) {
				pdu->callback(iscsi, SCSI_STATUS_ERROR, NULL,
				              pdu->private_data);
			}
			iscsi_free_discovery_addresses(iscsi, targets);
			return -1;
		}

		ptr  += len + 1;
		size -= len + 1;
	}

	if (pdu->callback) {
		pdu->callback(iscsi, SCSI_STATUS_GOOD, targets, pdu->private_data);
	}
	iscsi_free_discovery_addresses(iscsi, targets);

	return 0;
}
Example #10
0
int
iscsi_tcp_service(struct iscsi_context *iscsi, int revents)
{
	if (iscsi->fd < 0) {
		return 0;
	}

	if (iscsi->pending_reconnect) {
		if (time(NULL) >= iscsi->next_reconnect) {
			return iscsi_reconnect(iscsi);
		} else {
			if (iscsi->old_iscsi) {
				return 0;
			}
		}
	}

	if (revents & POLLERR) {
		int err = 0;
		socklen_t err_size = sizeof(err);

		if (getsockopt(iscsi->fd, SOL_SOCKET, SO_ERROR,
			       (char *)&err, &err_size) != 0 || err != 0) {
			if (err == 0) {
				err = errno;
			}
			iscsi_set_error(iscsi, "iscsi_service: socket error "
					"%s(%d).",
					strerror(err), err);
		} else {
			iscsi_set_error(iscsi, "iscsi_service: POLLERR, "
					"Unknown socket error.");
		}
		if (iscsi->socket_status_cb) {
			iscsi->socket_status_cb(iscsi, SCSI_STATUS_ERROR, NULL,
						iscsi->connect_data);
			iscsi->socket_status_cb = NULL;
		}
		return iscsi_service_reconnect_if_loggedin(iscsi);
	}
	if (revents & POLLHUP) {
		iscsi_set_error(iscsi, "iscsi_service: POLLHUP, "
				"socket error.");
		if (iscsi->socket_status_cb) {
			iscsi->socket_status_cb(iscsi, SCSI_STATUS_ERROR, NULL,
						iscsi->connect_data);
			iscsi->socket_status_cb = NULL;
		}
		return iscsi_service_reconnect_if_loggedin(iscsi);
	}

	if (iscsi->is_connected == 0 && revents&POLLOUT) {
		int err = 0;
		socklen_t err_size = sizeof(err);
		struct sockaddr_in local;
		socklen_t local_l = sizeof(local);

		if (getsockopt(iscsi->fd, SOL_SOCKET, SO_ERROR,
			       (char *)&err, &err_size) != 0 || err != 0) {
			if (err == 0) {
				err = errno;
			}
			iscsi_set_error(iscsi, "iscsi_service: socket error "
					"%s(%d) while connecting.",
					strerror(err), err);
			if (iscsi->socket_status_cb) {
				iscsi->socket_status_cb(iscsi, SCSI_STATUS_ERROR,
							NULL, iscsi->connect_data);
				iscsi->socket_status_cb = NULL;
			}

			return iscsi_service_reconnect_if_loggedin(iscsi);
		}

		if (getsockname(iscsi->fd, (struct sockaddr *) &local, &local_l) == 0) {
			ISCSI_LOG(iscsi, 2, "connection established (%s:%u -> %s)", inet_ntoa(local.sin_addr),
						(unsigned)ntohs(local.sin_port),iscsi->connected_portal);
		}

		iscsi->is_connected = 1;
		if (iscsi->socket_status_cb) {
			iscsi->socket_status_cb(iscsi, SCSI_STATUS_GOOD, NULL,
						iscsi->connect_data);
			iscsi->socket_status_cb = NULL;
		}
		return 0;
	}

	if (revents & POLLIN) {
		if (iscsi_read_from_socket(iscsi) != 0) {
			return iscsi_service_reconnect_if_loggedin(iscsi);
		}
	}
	if (revents & POLLOUT) {
		if (iscsi_write_to_socket(iscsi) != 0) {
			return iscsi_service_reconnect_if_loggedin(iscsi);
		}
	}
	iscsi_timeout_scan(iscsi);

	return 0;
}
Example #11
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;
}
Example #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;
}
Example #13
0
ssize_t
iscsi_iovector_readv_writev(struct iscsi_context *iscsi, struct scsi_iovector *iovector, uint32_t pos, ssize_t count, int do_write)
{
        struct scsi_iovec *iov, *iov2;
        int niov;
        uint32_t len2;
        size_t _len2;
        ssize_t n;

        if (iovector->iov == NULL) {
		errno = EINVAL;
		return -1;
	}

	if (pos < iovector->offset) {
		iscsi_set_error(iscsi, "iovector reset. pos is smaller than"
				"current offset");
		errno = EINVAL;
		return -1;
	}

	if (iovector->niov <= iovector->consumed) {
		/* someone issued a read/write but did not provide enough user buffers for all the data.
		 * maybe someone tried to read just 512 bytes off a MMC device?
		 */
		errno = EINVAL;
		return -1;
	}

	/* iov is a pointer to the first iovec to pass */
	iov = &iovector->iov[iovector->consumed];
	pos -= iovector->offset;

	/* forward until iov points to the first iov to pass */
	while (pos >= iov->iov_len) {
		iovector->offset += iov->iov_len;
		iovector->consumed++;
		pos -= iov->iov_len;
		if (iovector->niov <= iovector->consumed) {
			errno = EINVAL;
			return -1;
		}
		iov = &iovector->iov[iovector->consumed];
	}

	iov2 = iov;         /* iov2 is a pointer to the last iovec to pass */
	niov = 1;           /* number of iovectors to pass */
	len2 = pos + count; /* adjust length of iov2 */

	/* forward until iov2 points to the last iovec we pass later. it might
	   happen that we have a lot of iovectors but are limited by count */
	while (len2 > iov2->iov_len) {
		niov++;
		if (iovector->niov < iovector->consumed + niov) {
			errno = EINVAL;
			return -1;
		}
		len2 -= iov2->iov_len;
		iov2 = &iovector->iov[iovector->consumed + niov - 1];
	}

	/* we might limit the length of the last iovec we pass to readv/writev
	   store its orignal length to restore it later */
	_len2 = iov2->iov_len;

	/* adjust base+len of start iovec and len of last iovec */
	iov2->iov_len = len2;
	iov->iov_base = (void*) ((uintptr_t)iov->iov_base + pos);
	iov->iov_len -= pos;

	if (do_write) {
		n = writev(iscsi->fd, (struct iovec*) iov, niov);
	} else {
		n = readv(iscsi->fd, (struct iovec*) iov, niov);
	}

	/* restore original values */
	iov->iov_base = (void*) ((uintptr_t)iov->iov_base - pos);
	iov->iov_len += pos;
	iov2->iov_len = _len2;

	if (n > count) {
		/* we read/write more bytes than expected, this MUST not happen */
		errno = EINVAL;
		return -1;
	}
	return n;
}
Example #14
0
int
iscsi_service(struct iscsi_context *iscsi, int revents)
{
	if (revents & POLLERR) {
		int err = 0;
		socklen_t err_size = sizeof(err);

		if (getsockopt(iscsi->fd, SOL_SOCKET, SO_ERROR,
			       &err, &err_size) != 0 || err != 0) {
			if (err == 0) {
				err = errno;
			}
			iscsi_set_error(iscsi, "iscsi_service: socket error "
					"%s(%d).",
					strerror(err), err);
		} else {
			iscsi_set_error(iscsi, "iscsi_service: POLLERR, "
					"Unknown socket error.");
		}
		iscsi->socket_status_cb(iscsi, SCSI_STATUS_ERROR, NULL,
					iscsi->connect_data);
		return iscsi_service_reconnect_if_loggedin(iscsi);
	}
	if (revents & POLLHUP) {
		iscsi_set_error(iscsi, "iscsi_service: POLLHUP, "
				"socket error.");
		iscsi->socket_status_cb(iscsi, SCSI_STATUS_ERROR, NULL,
					iscsi->connect_data);
		return iscsi_service_reconnect_if_loggedin(iscsi);
	}

	if (iscsi->is_connected == 0 && iscsi->fd != -1 && revents&POLLOUT) {
		int err = 0;
		socklen_t err_size = sizeof(err);
		if (getsockopt(iscsi->fd, SOL_SOCKET, SO_ERROR,
			       &err, &err_size) != 0 || err != 0) {
			if (err == 0) {
				err = errno;
			}
			iscsi_set_error(iscsi, "iscsi_service: socket error "
					"%s(%d) while connecting.",
					strerror(err), err);
			iscsi->socket_status_cb(iscsi, SCSI_STATUS_ERROR,
						NULL, iscsi->connect_data);
			return iscsi_service_reconnect_if_loggedin(iscsi);
		}

		DPRINTF(iscsi,2,"connection to %s established",iscsi->connected_portal);

		iscsi->is_connected = 1;
		iscsi->socket_status_cb(iscsi, SCSI_STATUS_GOOD, NULL,
					iscsi->connect_data);
		return 0;
	}

	if (revents & POLLOUT && iscsi->outqueue != NULL) {
		if (iscsi_write_to_socket(iscsi) != 0) {
			return iscsi_service_reconnect_if_loggedin(iscsi);
		}
	}
	if (revents & POLLIN) {
		if (iscsi_read_from_socket(iscsi) != 0) {
			return iscsi_service_reconnect_if_loggedin(iscsi);
		}
	}

	return 0;
}
Example #15
0
int
iscsi_connect_async(struct iscsi_context *iscsi, const char *portal,
		    iscsi_command_cb cb, void *private_data)
{
	int port = 3260;
	char *str;
	char *addr, *host;
	struct addrinfo *ai = NULL;
	union socket_address sa;
	int socksize;

	ISCSI_LOG(iscsi, 2, "connecting to portal %s",portal);

	if (iscsi->fd != -1) {
		iscsi_set_error(iscsi,
				"Trying to connect but already connected.");
		return -1;
	}

	addr = iscsi_strdup(iscsi, portal);
	if (addr == NULL) {
		iscsi_set_error(iscsi, "Out-of-memory: "
				"Failed to strdup portal address.");
		return -1;
	}
	host = addr;

	/* check if we have a target portal group tag */
	str = strrchr(host, ',');
	if (str != NULL) {
		str[0] = 0;
	}

	str = strrchr(host, ':');
	if (str != NULL) {
		if (strchr(str, ']') == NULL) {
			if (str != NULL) {
				port = atoi(str+1);
				str[0] = 0;
			}
		}
	}

	/* ipv6 in [...] form ? */
	if (host[0] == '[') {
		host ++;
		str = strchr(host, ']');
		if (str == NULL) {
			iscsi_free(iscsi, addr);
			iscsi_set_error(iscsi, "Invalid target:%s  "
				"Missing ']' in IPv6 address", portal);
			return -1;
		}
		*str = 0;
	}

	/* is it a hostname ? */
	if (getaddrinfo(host, NULL, NULL, &ai) != 0) {
		iscsi_free(iscsi, addr);
		iscsi_set_error(iscsi, "Invalid target:%s  "
			"Can not resolv into IPv4/v6.", portal);
		return -1;
	}
	iscsi_free(iscsi, addr);

	memset(&sa, 0, sizeof(sa));
	switch (ai->ai_family) {
	case AF_INET:
		socksize = sizeof(struct sockaddr_in);
		memcpy(&sa.sin, ai->ai_addr, socksize);
		sa.sin.sin_port = htons(port);
#ifdef HAVE_SOCK_SIN_LEN
		sa.sin.sin_len = socksize;
#endif
		break;
#ifdef HAVE_SOCKADDR_IN6
	case AF_INET6:
		socksize = sizeof(struct sockaddr_in6);
		memcpy(&sa.sin6, ai->ai_addr, socksize);
		sa.sin6.sin6_port = htons(port);
#ifdef HAVE_SOCK_SIN_LEN
		sa.sin6.sin6_len = socksize;
#endif
		break;
#endif
	default:
		iscsi_set_error(iscsi, "Unknown address family :%d. "
				"Only IPv4/IPv6 supported so far.",
				ai->ai_family);
		freeaddrinfo(ai);
		return -1;

	}

	iscsi->socket_status_cb  = cb;
	iscsi->connect_data      = private_data;

	if (iscsi->t->connect(iscsi, &sa, ai->ai_family) < 0) {
		iscsi_set_error(iscsi, "Couldn't connect transport");
		freeaddrinfo(ai);
		return -1;
	}

	freeaddrinfo(ai);
	return 0;
}
Example #16
0
static int
iscsi_read_from_socket(struct iscsi_context *iscsi)
{
	struct iscsi_in_pdu *in;
	ssize_t data_size, count;

	if (iscsi->incoming == NULL) {
		iscsi->incoming = malloc(sizeof(struct iscsi_in_pdu));
		if (iscsi->incoming == NULL) {
			iscsi_set_error(iscsi, "Out-of-memory: failed to malloc iscsi_in_pdu");
			return -1;
		}
		memset(iscsi->incoming, 0, sizeof(struct iscsi_in_pdu));
	}
	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 dont have the full header yet, so return */
		return 0;
	}

	data_size = iscsi_get_pdu_data_size(&in->hdr[0]);
	if (data_size < 0 || data_size > 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 *buf = NULL;

		count = data_size - in->data_pos;

		/* first try to see if we already have a user buffer */
		buf = iscsi_get_user_in_buffer(iscsi, in, in->data_pos, &count);
		/* if not, allocate one */
		if (buf == NULL) {
			if (in->data == NULL) {
				in->data = malloc(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", errno);
			return -1;
		}
		in->data_pos += count;
	}

	if (in->data_pos < data_size) {
		return 0;
	}

	SLIST_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;
		}
		SLIST_REMOVE(&iscsi->inqueue, current);
		iscsi_free_iscsi_in_pdu(current);
	}


	return 0;
}
Example #17
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 = ntohl(*(uint32_t *)&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, "Dont 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;
}
Example #18
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;
}
Example #19
0
int
iscsi_login_async(struct iscsi_context *iscsi, iscsi_command_cb cb,
		  void *private_data)
{
	struct iscsi_pdu *pdu;
	int transit;

	if (iscsi->login_attempts++ > 10) {
		iscsi_set_error(iscsi, "login took too many tries."
				" giving up.");
		return -1;
	}
 
	if (iscsi->is_loggedin != 0) {
		iscsi_set_error(iscsi, "Trying to login while already logged "
				"in.");
		return -1;
	}

	switch (iscsi->session_type) {
	case ISCSI_SESSION_DISCOVERY:
	case ISCSI_SESSION_NORMAL:
		break;
	default:
		iscsi_set_error(iscsi, "trying to login without setting "
				"session type.");
		return -1;
	}

	pdu = iscsi_allocate_pdu_with_itt_flags(iscsi,
				ISCSI_PDU_LOGIN_REQUEST,
				ISCSI_PDU_LOGIN_RESPONSE,
				iscsi->itt, 0);
	if (pdu == NULL) {
		iscsi_set_error(iscsi, "Out-of-memory: Failed to allocate "
				"login pdu.");
		return -1;
	}

	/* login request */
	iscsi_pdu_set_immediate(pdu);

	if (!iscsi->user[0]) {
		iscsi->current_phase = ISCSI_PDU_LOGIN_CSG_OPNEG;
	}

	if (iscsi->current_phase == ISCSI_PDU_LOGIN_CSG_SECNEG) {
		iscsi->next_phase    = ISCSI_PDU_LOGIN_NSG_OPNEG;
	}
	if (iscsi->current_phase == ISCSI_PDU_LOGIN_CSG_OPNEG) {
		iscsi->next_phase    = ISCSI_PDU_LOGIN_NSG_FF;
	}

	transit = 0;
	if (iscsi->current_phase == ISCSI_PDU_LOGIN_CSG_OPNEG) {
		transit = ISCSI_PDU_LOGIN_TRANSIT;
	}
	if (iscsi->current_phase == ISCSI_PDU_LOGIN_CSG_SECNEG) {
		if (iscsi->secneg_phase == ISCSI_LOGIN_SECNEG_PHASE_OFFER_CHAP) {
			transit = ISCSI_PDU_LOGIN_TRANSIT;
		}
		if (iscsi->secneg_phase == ISCSI_LOGIN_SECNEG_PHASE_SEND_RESPONSE) {
			transit = ISCSI_PDU_LOGIN_TRANSIT;
		}
	}

	/* flags */
	iscsi_pdu_set_pduflags(pdu, transit
					| iscsi->current_phase
					| iscsi->next_phase);


	/* initiator name */
	if (iscsi_login_add_initiatorname(iscsi, pdu) != 0) {
		iscsi_free_pdu(iscsi, pdu);
		return -1;
	}

	/* optional alias */
	if (iscsi->alias[0]) {
		if (iscsi_login_add_alias(iscsi, pdu) != 0) {
			iscsi_free_pdu(iscsi, pdu);
			return -1;
		}
	}

	/* target name */
	if (iscsi->session_type == ISCSI_SESSION_NORMAL) {
		if (iscsi_login_add_targetname(iscsi, pdu) != 0) {
			iscsi_free_pdu(iscsi, pdu);
			return -1;
		}
	}

	/* session type */
	if (iscsi_login_add_sessiontype(iscsi, pdu) != 0) {
		iscsi_free_pdu(iscsi, pdu);
		return -1;
	}

	/* header digest */
	if (iscsi_login_add_headerdigest(iscsi, pdu) != 0) {
		iscsi_free_pdu(iscsi, pdu);
		return -1;
	}

	/* auth method */
	if (iscsi_login_add_authmethod(iscsi, pdu) != 0) {
		iscsi_free_pdu(iscsi, pdu);
		return -1;
	}

	/* auth algorithm */
	if (iscsi_login_add_authalgorithm(iscsi, pdu) != 0) {
		iscsi_free_pdu(iscsi, pdu);
		return -1;
	}

	/* chap username */
	if (iscsi_login_add_chap_username(iscsi, pdu) != 0) {
		iscsi_free_pdu(iscsi, pdu);
		return -1;
	}

	/* chap response */
	if (iscsi_login_add_chap_response(iscsi, pdu) != 0) {
		iscsi_free_pdu(iscsi, pdu);
		return -1;
	}

	/* data digest */
	if (iscsi_login_add_datadigest(iscsi, pdu) != 0) {
		iscsi_free_pdu(iscsi, pdu);
		return -1;
	}

	/* initial r2t */
	if (iscsi_login_add_initialr2t(iscsi, pdu) != 0) {
		iscsi_free_pdu(iscsi, pdu);
		return -1;
	}

	/* immediate data */
	if (iscsi_login_add_immediatedata(iscsi, pdu) != 0) {
		iscsi_free_pdu(iscsi, pdu);
		return -1;
	}

	/* max burst length */
	if (iscsi_login_add_maxburstlength(iscsi, pdu) != 0) {
		iscsi_free_pdu(iscsi, pdu);
		return -1;
	}

	/* first burst length */
	if (iscsi_login_add_firstburstlength(iscsi, pdu) != 0) {
		iscsi_free_pdu(iscsi, pdu);
		return -1;
	}

	/* default time 2 wait */
	if (iscsi_login_add_defaulttime2wait(iscsi, pdu) != 0) {
		iscsi_free_pdu(iscsi, pdu);
		return -1;
	}

	/* default time 2 retain */
	if (iscsi_login_add_defaulttime2retain(iscsi, pdu) != 0) {
		iscsi_free_pdu(iscsi, pdu);
		return -1;
	}

	/* max outstanding r2t */
	if (iscsi_login_add_maxoutstandingr2t(iscsi, pdu) != 0) {
		iscsi_free_pdu(iscsi, pdu);
		return -1;
	}

	/* errorrecoverylevel */
	if (iscsi_login_add_errorrecoverylevel(iscsi, pdu) != 0) {
		iscsi_free_pdu(iscsi, pdu);
		return -1;
	}

	/* ifmarker */
	if (iscsi_login_add_ifmarker(iscsi, pdu) != 0) {
		iscsi_free_pdu(iscsi, pdu);
		return -1;
	}

	/* ofmarker */
	if (iscsi_login_add_ofmarker(iscsi, pdu) != 0) {
		iscsi_free_pdu(iscsi, pdu);
		return -1;
	}

	/* maxconnections */
	if (iscsi_login_add_maxconnections(iscsi, pdu) != 0) {
		iscsi_free_pdu(iscsi, pdu);
		return -1;
	}

	/* max recv data segment length */
	if (iscsi_login_add_maxrecvdatasegmentlength(iscsi, pdu) != 0) {
		iscsi_free_pdu(iscsi, pdu);
		return -1;
	}

	/* data pdu in order */
	if (iscsi_login_add_datapduinorder(iscsi, pdu) != 0) {
		iscsi_free_pdu(iscsi, pdu);
		return -1;
	}

	/* data sequence in order */
	if (iscsi_login_add_datasequenceinorder(iscsi, pdu) != 0) {
		iscsi_free_pdu(iscsi, pdu);
		return -1;
	}


	pdu->callback     = cb;
	pdu->private_data = private_data;

	if (iscsi_queue_pdu(iscsi, pdu) != 0) {
		iscsi_set_error(iscsi, "Out-of-memory: failed to queue iscsi "
				"pdu.");
		iscsi_free_pdu(iscsi, pdu);
		return -1;
	}

	return 0;
}
Example #20
0
static int
iscsi_login_add_chap_response(struct iscsi_context *iscsi, struct iscsi_pdu *pdu)
{
	char str[MAX_STRING_SIZE+1];
	char * strp;
	unsigned char c, cc[2];
	unsigned char digest[16];
	gcry_md_hd_t ctx;
	int i;

	if (iscsi->current_phase != ISCSI_PDU_LOGIN_CSG_SECNEG
	|| iscsi->secneg_phase != ISCSI_LOGIN_SECNEG_PHASE_SEND_RESPONSE) {
		return 0;
	}

	gcry_md_open(&ctx, GCRY_MD_MD5, 0);
	if (!ctx) {
		iscsi_set_error(iscsi, "Cannot create MD5 algorithm");
		return -1;
	}

	if (!iscsi->chap_c[0]) {
		iscsi_set_error(iscsi, "No CHAP challenge found");
		return -1;
	}
	gcry_md_putc(ctx, iscsi->chap_i);
	gcry_md_write(ctx, (unsigned char *)iscsi->passwd, strlen(iscsi->passwd));

	strp = iscsi->chap_c;
	while (*strp != 0) {
		c = (h2i(strp[0]) << 4) | h2i(strp[1]);
		strp += 2;
		gcry_md_putc(ctx, c);
	}
	memcpy(digest, gcry_md_read(ctx, 0), sizeof(digest));
	gcry_md_close(ctx);

	strncpy(str,"CHAP_R=0x",MAX_STRING_SIZE);
	if (iscsi_pdu_add_data(iscsi, pdu, (unsigned char *)str, strlen(str))
	    != 0) {
		iscsi_set_error(iscsi, "Out-of-memory: pdu add data failed.");
		return -1;
	}

	for (i=0; i<16; i++) {
		c = digest[i];
		cc[0] = i2h((c >> 4)&0x0f);
		cc[1] = i2h((c     )&0x0f);
		if (iscsi_pdu_add_data(iscsi, pdu, &cc[0], 2) != 0) {
			iscsi_set_error(iscsi, "Out-of-memory: pdu add data "
				"failed.");
			return -1;
		}
	}
	c = 0;
	if (iscsi_pdu_add_data(iscsi, pdu, &c, 1) != 0) {
		iscsi_set_error(iscsi, "Out-of-memory: pdu add data "
			"failed.");
		return -1;
	}

	return 0;
}
Example #21
0
struct iscsi_context *
iscsi_create_context(const char *initiator_name)
{
	struct iscsi_context *iscsi;
	size_t required = ISCSI_RAW_HEADER_SIZE + ISCSI_DIGEST_SIZE;
	char *ca;

	if (!initiator_name[0]) {
		return NULL;
	}

	iscsi = malloc(sizeof(struct iscsi_context));
	if (iscsi == NULL) {
		return NULL;
	}

	memset(iscsi, 0, sizeof(struct iscsi_context));

	/* initalize transport of context */
	if (iscsi_init_transport(iscsi, TCP_TRANSPORT)) {
		iscsi_set_error(iscsi, "Failed allocating transport");
		return NULL;
	}

	strncpy(iscsi->initiator_name,initiator_name,MAX_STRING_SIZE);

	iscsi->fd = -1;

	srand(time(NULL) ^ getpid() ^ (uint32_t) ((uintptr_t) iscsi));

	/* initialize to a "random" isid */
	iscsi_set_isid_random(iscsi, rand(), 0);

	/* assume we start in security negotiation phase */
	iscsi->current_phase = ISCSI_PDU_LOGIN_CSG_SECNEG;
	iscsi->next_phase    = ISCSI_PDU_LOGIN_NSG_OPNEG;
	iscsi->secneg_phase  = ISCSI_LOGIN_SECNEG_PHASE_OFFER_CHAP;

	iscsi->max_burst_length                       = 262144;
	iscsi->first_burst_length                     = 262144;
	iscsi->initiator_max_recv_data_segment_length = 262144;
	iscsi->target_max_recv_data_segment_length    = 8192;
	iscsi->want_initial_r2t                       = ISCSI_INITIAL_R2T_NO;
	iscsi->use_initial_r2t                        = ISCSI_INITIAL_R2T_YES;
	iscsi->want_immediate_data                    = ISCSI_IMMEDIATE_DATA_YES;
	iscsi->use_immediate_data                     = ISCSI_IMMEDIATE_DATA_YES;
	iscsi->want_header_digest                     = ISCSI_HEADER_DIGEST_NONE_CRC32C;

	iscsi->tcp_keepcnt=3;
	iscsi->tcp_keepintvl=30;
	iscsi->tcp_keepidle=30;
	
	iscsi->reconnect_max_retries = -1;

	if (getenv("LIBISCSI_DEBUG") != NULL) {
		iscsi_set_log_level(iscsi, atoi(getenv("LIBISCSI_DEBUG")));
		iscsi_set_log_fn(iscsi, iscsi_log_to_stderr);
	}

	if (getenv("LIBISCSI_TCP_USER_TIMEOUT") != NULL) {
		iscsi_set_tcp_user_timeout(iscsi,atoi(getenv("LIBISCSI_TCP_USER_TIMEOUT")));
	}

	if (getenv("LIBISCSI_TCP_KEEPCNT") != NULL) {
		iscsi_set_tcp_keepcnt(iscsi,atoi(getenv("LIBISCSI_TCP_KEEPCNT")));
	}

	if (getenv("LIBISCSI_TCP_KEEPINTVL") != NULL) {
		iscsi_set_tcp_keepintvl(iscsi,atoi(getenv("LIBISCSI_TCP_KEEPINTVL")));
	}

	if (getenv("LIBISCSI_TCP_KEEPIDLE") != NULL) {
		iscsi_set_tcp_keepidle(iscsi,atoi(getenv("LIBISCSI_TCP_KEEPIDLE")));
	}

	if (getenv("LIBISCSI_TCP_SYNCNT") != NULL) {
		iscsi_set_tcp_syncnt(iscsi,atoi(getenv("LIBISCSI_TCP_SYNCNT")));
	}

	if (getenv("LIBISCSI_BIND_INTERFACES") != NULL) {
		iscsi_set_bind_interfaces(iscsi,getenv("LIBISCSI_BIND_INTERFACES"));
	}

	/* iscsi->smalloc_size is the size for small allocations. this should be
	   max(ISCSI_HEADER_SIZE, sizeof(struct iscsi_pdu), sizeof(struct iscsi_in_pdu))
	   rounded up to the next power of 2. */
	required = MAX(required, sizeof(struct iscsi_pdu));
	required = MAX(required, sizeof(struct iscsi_in_pdu));
	iscsi->smalloc_size = 1;
	while (iscsi->smalloc_size < required) {
		iscsi->smalloc_size <<= 1;
	}
	ISCSI_LOG(iscsi,5,"small allocation size is %d byte", iscsi->smalloc_size);

	ca = getenv("LIBISCSI_CACHE_ALLOCATIONS");
	if (!ca || atoi(ca) != 0) {
		iscsi->cache_allocations = 1;
	}

	return iscsi;
}
Example #22
0
int
iscsi_nop_out_async(struct iscsi_context *iscsi, iscsi_command_cb cb,
		    unsigned char *data, int len, void *private_data)
{
	struct iscsi_pdu *pdu;

	if (iscsi->old_iscsi || iscsi->pending_reconnect) {
		ISCSI_LOG(iscsi, (iscsi->nops_in_flight > 1) ? 1 : 6,
		    "NOP Out Send NOT SEND while reconnecting (nops_in_flight: %d, iscsi->maxcmdsn %08x, iscsi->expcmdsn %08x)",
		    iscsi->nops_in_flight, iscsi->maxcmdsn, iscsi->expcmdsn);
		return 0;
	}

	if (iscsi->is_loggedin == 0) {
		iscsi_set_error(iscsi, "trying to send nop-out while not "
				"logged in");
		return -1;
	}

	pdu = iscsi_allocate_pdu(iscsi,
				 ISCSI_PDU_NOP_OUT,
				 ISCSI_PDU_NOP_IN,
				 iscsi_itt_post_increment(iscsi),
				 ISCSI_PDU_DROP_ON_RECONNECT);
	if (pdu == NULL) {
		iscsi_set_error(iscsi, "Failed to allocate nop-out pdu");
		return -1;
	}

	/* flags */
	iscsi_pdu_set_pduflags(pdu, 0x80);

	/* ttt */
	iscsi_pdu_set_ttt(pdu, 0xffffffff);

	/* lun */
	iscsi_pdu_set_lun(pdu, 0);

	/* cmdsn */
	iscsi_pdu_set_cmdsn(pdu, iscsi->cmdsn++);

	pdu->callback     = cb;
	pdu->private_data = private_data;

	if (data != NULL && len > 0) {
		if (iscsi_pdu_add_data(iscsi, pdu, data, len) != 0) {
			iscsi_set_error(iscsi, "Failed to add outdata to nop-out");
			iscsi->drv->free_pdu(iscsi, pdu);
			return -1;
		}
	}

	if (iscsi_queue_pdu(iscsi, pdu) != 0) {
		iscsi_set_error(iscsi, "failed to queue iscsi nop-out pdu");
		iscsi->drv->free_pdu(iscsi, pdu);
		return -1;
	}

	iscsi->nops_in_flight++;
	ISCSI_LOG(iscsi, (iscsi->nops_in_flight > 1) ? 1 : 6,
	          "NOP Out Send (nops_in_flight: %d, pdu->cmdsn %08x, pdu->itt %08x, pdu->ttt %08x, iscsi->maxcmdsn %08x, iscsi->expcmdsn %08x)",
	          iscsi->nops_in_flight, pdu->cmdsn, pdu->itt, 0xffffffff, iscsi->maxcmdsn, iscsi->expcmdsn);

	return 0;
}
Example #23
0
int
iscsi_process_text_reply(struct iscsi_context *iscsi, struct iscsi_pdu *pdu,
			 struct iscsi_in_pdu *in)
{
	struct iscsi_discovery_address *targets = NULL;
	unsigned char *ptr = in->data;
	int size = in->data_pos;

	/* verify the response looks sane */
	if (in->hdr[1] != ISCSI_PDU_TEXT_FINAL) {
		iscsi_set_error(iscsi, "unsupported flags in text "
				"reply %02x", in->hdr[1]);
		pdu->callback(iscsi, SCSI_STATUS_ERROR, NULL,
			      pdu->private_data);
		return -1;
	}

	while (size > 0) {
		int len;

		len = strlen((char *)ptr);

		if (len == 0) {
			break;
		}

		if (len > size) {
			iscsi_set_error(iscsi, "len > size when parsing "
					"discovery data %d>%d", len, size);
			pdu->callback(iscsi, SCSI_STATUS_ERROR, NULL,
				      pdu->private_data);
			iscsi_free_discovery_addresses(targets);
			return -1;
		}

		/* parse the strings */
		if (!strncmp((char *)ptr, "TargetName=", 11)) {
			struct iscsi_discovery_address *target;

			target = malloc(sizeof(struct iscsi_discovery_address));
			if (target == NULL) {
				iscsi_set_error(iscsi, "Failed to allocate "
						"data for new discovered "
						"target");
				pdu->callback(iscsi, SCSI_STATUS_ERROR, NULL,
					      pdu->private_data);
				iscsi_free_discovery_addresses(targets);
				return -1;
			}
			bzero(target, sizeof(struct iscsi_discovery_address));
			target->target_name = strdup((char *)ptr+11);
			if (target->target_name == NULL) {
				iscsi_set_error(iscsi, "Failed to allocate "
						"data for new discovered "
						"target name");
				pdu->callback(iscsi, SCSI_STATUS_ERROR, NULL,
					      pdu->private_data);
				free(target);
				target = NULL;
				iscsi_free_discovery_addresses(targets);
				return -1;
			}
			target->next = targets;
			targets = target;
		} else if (!strncmp((char *)ptr, "TargetAddress=", 14)) {
			targets->target_address = strdup((char *)ptr+14);
			if (targets->target_address == NULL) {
				iscsi_set_error(iscsi, "Failed to allocate "
						"data for new discovered "
						"target address");
				pdu->callback(iscsi, SCSI_STATUS_ERROR, NULL,
					      pdu->private_data);
				iscsi_free_discovery_addresses(targets);
				return -1;
			}
		} else {
			iscsi_set_error(iscsi, "Dont know how to handle "
					"discovery string : %s", ptr);
			pdu->callback(iscsi, SCSI_STATUS_ERROR, NULL,
				      pdu->private_data);
			iscsi_free_discovery_addresses(targets);
			return -1;
		}

		ptr  += len + 1;
		size -= len + 1;
	}

	pdu->callback(iscsi, SCSI_STATUS_GOOD, targets, pdu->private_data);
	iscsi_free_discovery_addresses(targets);

	return 0;
}
Example #24
0
static int iscsi_tcp_connect(struct iscsi_context *iscsi, union socket_address *sa, int ai_family) {

	int socksize;

	iscsi->fd = socket(ai_family, SOCK_STREAM, 0);
	if (iscsi->fd == -1) {
		iscsi_set_error(iscsi, "Failed to open iscsi socket. "
				"Errno:%s(%d).", strerror(errno), errno);
		return -1;
	}

	if (iscsi->old_iscsi && iscsi->fd != iscsi->old_iscsi->fd) {
		if (dup2(iscsi->fd, iscsi->old_iscsi->fd) == -1) {
			return -1;
		}
		close(iscsi->fd);
		iscsi->fd = iscsi->old_iscsi->fd;
	}

	set_nonblocking(iscsi->fd);

	iscsi_set_tcp_keepalive(iscsi, iscsi->tcp_keepidle, iscsi->tcp_keepcnt, iscsi->tcp_keepintvl);

	if (iscsi->tcp_user_timeout > 0) {
		set_tcp_user_timeout(iscsi);
	}

	if (iscsi->tcp_syncnt > 0) {
		set_tcp_syncnt(iscsi);
	}

#if __linux
	if (iscsi->bind_interfaces[0]) {
		char *pchr = iscsi->bind_interfaces, *pchr2;
		int iface_n = iface_rr++%iscsi->bind_interfaces_cnt;
		int iface_c = 0;
		do {
			pchr2 = strchr(pchr,',');
			if (iface_c == iface_n) {
				if (pchr2) pchr2[0]=0x00;
				break;
			}
			if (pchr2) {pchr=pchr2+1;}
			iface_c++;
		} while (pchr2);

		int res = setsockopt(iscsi->fd, SOL_SOCKET, SO_BINDTODEVICE, pchr, strlen(pchr));
		if (res < 0) {
			ISCSI_LOG(iscsi,1,"failed to bind to interface '%s': %s",pchr,strerror(errno));
		} else {
			ISCSI_LOG(iscsi,3,"successfully bound to interface '%s'",pchr);
		}
		if (pchr2) pchr2[0]=',';
	}
#endif

	if (set_tcp_sockopt(iscsi->fd, TCP_NODELAY, 1) != 0) {
		ISCSI_LOG(iscsi,1,"failed to set TCP_NODELAY sockopt: %s",strerror(errno));
	} else {
		ISCSI_LOG(iscsi,3,"TCP_NODELAY set to 1");
	}

	socksize = sizeof(struct sockaddr_in);  // Work-around for now, need to fix it

	if (connect(iscsi->fd, &sa->sa, socksize) != 0
		&& errno != EINPROGRESS) {
		iscsi_set_error(iscsi, "Connect failed with errno : "
			"%s(%d)", strerror(errno), errno);
		close(iscsi->fd);
		iscsi->fd = -1;
		return -1;
	}

	return 0;
}
Example #25
0
int
iscsi_connect_async(struct iscsi_context *iscsi, const char *portal,
		    iscsi_command_cb cb, void *private_data)
{
	int port = 3260;
	char *str;
	char *addr, *host;
	struct addrinfo *ai = NULL;
	int socksize;

	DPRINTF(iscsi,2,"connecting to portal %s",portal);

	if (iscsi->fd != -1) {
		iscsi_set_error(iscsi,
				"Trying to connect but already connected.");
		return -1;
	}

	addr = strdup(portal);
	if (addr == NULL) {
		iscsi_set_error(iscsi, "Out-of-memory: "
				"Failed to strdup portal address.");
		return -1;
	}
	host = addr;

	/* check if we have a target portal group tag */
	str = strrchr(host, ',');
	if (str != NULL) {
		str[0] = 0;
	}

	str = strrchr(host, ':');
	if (str != NULL) {
		if (strchr(str, ']') == NULL) {
			if (str != NULL) {
				port = atoi(str+1);
				str[0] = 0;
			}
		}
	}

	/* ipv6 in [...] form ? */
	if (host[0] == '[') {
		host ++;
		str = strchr(host, ']');
		if (str == NULL) {
			free(addr);
			iscsi_set_error(iscsi, "Invalid target:%s  "
				"Missing ']' in IPv6 address", portal);
			return -1;
		}
		*str = 0;		
	}

	/* is it a hostname ? */
	if (getaddrinfo(host, NULL, NULL, &ai) != 0) {
		free(addr);
		iscsi_set_error(iscsi, "Invalid target:%s  "
			"Can not resolv into IPv4/v6.", portal);
		return -1;
 	}
	free(addr);

	switch (ai->ai_family) {
	case AF_INET:
		socksize = sizeof(struct sockaddr_in);
		((struct sockaddr_in *)(ai->ai_addr))->sin_port = htons(port);
#ifdef HAVE_SOCK_SIN_LEN
		((struct sockaddr_in *)(ai->ai_addr))->sin_len = socksize;
#endif
		break;
	case AF_INET6:
		socksize = sizeof(struct sockaddr_in6);
		((struct sockaddr_in6 *)(ai->ai_addr))->sin6_port = htons(port);
#ifdef HAVE_SOCK_SIN_LEN
		((struct sockaddr_in6 *)(ai->ai_addr))->sin6_len = socksize;
#endif
		break;
	default:
		iscsi_set_error(iscsi, "Unknown address family :%d. "
				"Only IPv4/IPv6 supported so far.",
				ai->ai_family);
		freeaddrinfo(ai);
		return -1;

	}

	iscsi->fd = socket(ai->ai_family, SOCK_STREAM, 0);
	if (iscsi->fd == -1) {
		freeaddrinfo(ai);
		iscsi_set_error(iscsi, "Failed to open iscsi socket. "
				"Errno:%s(%d).", strerror(errno), errno);
		return -1;

	}

	iscsi->socket_status_cb  = cb;
	iscsi->connect_data      = private_data;

	set_nonblocking(iscsi->fd);
	
	iscsi_set_tcp_keepalive(iscsi, iscsi->tcp_keepidle, iscsi->tcp_keepcnt, iscsi->tcp_keepintvl);
	
	if (iscsi->tcp_user_timeout > 0) {
		set_tcp_user_timeout(iscsi);
	}

	if (iscsi->tcp_syncnt > 0) {
		set_tcp_syncnt(iscsi);
	}

	if (connect(iscsi->fd, ai->ai_addr, socksize) != 0
	    && errno != EINPROGRESS) {
		iscsi_set_error(iscsi, "Connect failed with errno : "
				"%s(%d)", strerror(errno), errno);
		close(iscsi->fd);
		iscsi->fd = -1;
		freeaddrinfo(ai);
		return -1;
	}

	freeaddrinfo(ai);
	
	if (iscsi->connected_portal) free(discard_const(iscsi->connected_portal));
	iscsi->connected_portal=strdup(portal);
	
	return 0;
}