Ignore padding when an iovector is supplied
The iSCSI protocol adds padding to a data packet if the data size is not a multiple of four. The iovector provided by QEMU does not include such padding, and libiscsi then complains that there was a protocol error. This patch fixes this by reading the padding in a separate "recv" system call. These packets anyway do not happen in the data path, where the packet size is a multiple of 512. This fixes QEMU's scsi-generic backend, which triggered the problem when the target sent a 66-byte INQUIRY response. Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
This commit is contained in:
@@ -269,6 +269,7 @@ struct scsi_task;
|
|||||||
void iscsi_pdu_set_cdb(struct iscsi_pdu *pdu, struct scsi_task *task);
|
void iscsi_pdu_set_cdb(struct iscsi_pdu *pdu, struct scsi_task *task);
|
||||||
|
|
||||||
int iscsi_get_pdu_data_size(const unsigned char *hdr);
|
int iscsi_get_pdu_data_size(const unsigned char *hdr);
|
||||||
|
int iscsi_get_pdu_padding_size(const unsigned char *hdr);
|
||||||
int iscsi_process_pdu(struct iscsi_context *iscsi, struct iscsi_in_pdu *in);
|
int iscsi_process_pdu(struct iscsi_context *iscsi, struct iscsi_in_pdu *in);
|
||||||
|
|
||||||
int iscsi_process_login_reply(struct iscsi_context *iscsi,
|
int iscsi_process_login_reply(struct iscsi_context *iscsi,
|
||||||
|
|||||||
13
lib/pdu.c
13
lib/pdu.c
@@ -230,11 +230,22 @@ iscsi_get_pdu_data_size(const unsigned char *hdr)
|
|||||||
int size;
|
int size;
|
||||||
|
|
||||||
size = scsi_get_uint32(&hdr[4]) & 0x00ffffff;
|
size = scsi_get_uint32(&hdr[4]) & 0x00ffffff;
|
||||||
size = (size+3) & 0xfffffffc;
|
|
||||||
|
|
||||||
return size;
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
enum iscsi_reject_reason {
|
enum iscsi_reject_reason {
|
||||||
ISCSI_REJECT_RESERVED = 0x01,
|
ISCSI_REJECT_RESERVED = 0x01,
|
||||||
ISCSI_REJECT_DATA_DIGEST_ERROR = 0x02,
|
ISCSI_REJECT_DATA_DIGEST_ERROR = 0x02,
|
||||||
|
|||||||
48
lib/socket.c
48
lib/socket.c
@@ -482,7 +482,7 @@ static int
|
|||||||
iscsi_read_from_socket(struct iscsi_context *iscsi)
|
iscsi_read_from_socket(struct iscsi_context *iscsi)
|
||||||
{
|
{
|
||||||
struct iscsi_in_pdu *in;
|
struct iscsi_in_pdu *in;
|
||||||
ssize_t data_size, count;
|
ssize_t data_size, count, padding_size;
|
||||||
|
|
||||||
if (iscsi->incoming == NULL) {
|
if (iscsi->incoming == NULL) {
|
||||||
iscsi->incoming = iscsi_szmalloc(iscsi, sizeof(struct iscsi_in_pdu));
|
iscsi->incoming = iscsi_szmalloc(iscsi, sizeof(struct iscsi_in_pdu));
|
||||||
@@ -519,31 +519,36 @@ iscsi_read_from_socket(struct iscsi_context *iscsi)
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
data_size = iscsi_get_pdu_data_size(&in->hdr[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) {
|
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);
|
iscsi_set_error(iscsi, "Invalid data size received from target (%d)", (int)data_size);
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
if (data_size != 0) {
|
if (data_size != 0) {
|
||||||
unsigned char *buf = NULL;
|
unsigned char padding_buf[3];
|
||||||
|
unsigned char *buf = padding_buf;
|
||||||
struct scsi_iovector * iovector_in;
|
struct scsi_iovector * iovector_in;
|
||||||
|
|
||||||
count = data_size - in->data_pos;
|
count = data_size - in->data_pos;
|
||||||
|
|
||||||
/* first try to see if we already have a user buffer */
|
/* first try to see if we already have a user buffer */
|
||||||
iovector_in = iscsi_get_scsi_task_iovector_in(iscsi, in);
|
iovector_in = iscsi_get_scsi_task_iovector_in(iscsi, in);
|
||||||
if (iovector_in != NULL) {
|
if (iovector_in != NULL && count > padding_size) {
|
||||||
uint32_t offset = scsi_get_uint32(&in->hdr[40]);
|
uint32_t offset = scsi_get_uint32(&in->hdr[40]);
|
||||||
count = iscsi_iovector_readv_writev(iscsi, iovector_in, in->data_pos + offset, count, 0);
|
count = iscsi_iovector_readv_writev(iscsi, iovector_in, in->data_pos + offset, count - padding_size, 0);
|
||||||
} else {
|
} else {
|
||||||
if (in->data == NULL) {
|
if (iovector_in == NULL) {
|
||||||
in->data = iscsi_malloc(iscsi, data_size);
|
|
||||||
if (in->data == NULL) {
|
if (in->data == NULL) {
|
||||||
iscsi_set_error(iscsi, "Out-of-memory: failed to malloc iscsi_in_pdu->data(%d)", (int)data_size);
|
in->data = iscsi_malloc(iscsi, data_size);
|
||||||
return -1;
|
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];
|
||||||
}
|
}
|
||||||
buf = &in->data[in->data_pos];
|
|
||||||
count = recv(iscsi->fd, buf, count, 0);
|
count = recv(iscsi->fd, buf, count, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -588,7 +593,9 @@ static int
|
|||||||
iscsi_write_to_socket(struct iscsi_context *iscsi)
|
iscsi_write_to_socket(struct iscsi_context *iscsi)
|
||||||
{
|
{
|
||||||
ssize_t count;
|
ssize_t count;
|
||||||
|
size_t total;
|
||||||
struct iscsi_pdu *pdu;
|
struct iscsi_pdu *pdu;
|
||||||
|
static char padding_buf[3];
|
||||||
|
|
||||||
if (iscsi->fd == -1) {
|
if (iscsi->fd == -1) {
|
||||||
iscsi_set_error(iscsi, "trying to write but not connected");
|
iscsi_set_error(iscsi, "trying to write but not connected");
|
||||||
@@ -664,6 +671,27 @@ iscsi_write_to_socket(struct iscsi_context *iscsi)
|
|||||||
pdu->payload_written += count;
|
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, 0);
|
||||||
|
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_DELETE_WHEN_SENT) {
|
if (pdu->flags & ISCSI_PDU_DELETE_WHEN_SENT) {
|
||||||
iscsi_free_pdu(iscsi, pdu);
|
iscsi_free_pdu(iscsi, pdu);
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user