[iscsi] Send any padding inline with the data segment

Some iSCSI targets respond to a PDU before receiving the padding
bytes.  If the target responds quickly enough, this can cause iPXE to
start processing a new TX PDU before the padding bytes have been sent,
which results in a protocol violation.

Fix by always transmitting the padding bytes along with the data
segment.

Originally-fixed-by: Shyam Iyer <shyam_iyer@dell.com>
Signed-off-by: Michael Brown <mcb30@ipxe.org>
pull/6/head
Michael Brown 2012-03-01 16:26:38 +00:00
parent cb10137e19
commit 1d293776ea
2 changed files with 9 additions and 30 deletions

View File

@ -515,8 +515,6 @@ enum iscsi_tx_state {
ISCSI_TX_AHS, ISCSI_TX_AHS,
/** Sending the data segment */ /** Sending the data segment */
ISCSI_TX_DATA, ISCSI_TX_DATA,
/** Sending the data segment padding */
ISCSI_TX_DATA_PADDING,
}; };
/** State of an iSCSI RX engine */ /** State of an iSCSI RX engine */

View File

@ -570,20 +570,23 @@ static int iscsi_tx_data_out ( struct iscsi_session *iscsi ) {
struct io_buffer *iobuf; struct io_buffer *iobuf;
unsigned long offset; unsigned long offset;
size_t len; size_t len;
size_t pad_len;
offset = ntohl ( data_out->offset ); offset = ntohl ( data_out->offset );
len = ISCSI_DATA_LEN ( data_out->lengths ); len = ISCSI_DATA_LEN ( data_out->lengths );
pad_len = ISCSI_DATA_PAD_LEN ( data_out->lengths );
assert ( iscsi->command != NULL ); assert ( iscsi->command != NULL );
assert ( iscsi->command->data_out ); assert ( iscsi->command->data_out );
assert ( ( offset + len ) <= iscsi->command->data_out_len ); assert ( ( offset + len ) <= iscsi->command->data_out_len );
iobuf = xfer_alloc_iob ( &iscsi->socket, len ); iobuf = xfer_alloc_iob ( &iscsi->socket, ( len + pad_len ) );
if ( ! iobuf ) if ( ! iobuf )
return -ENOMEM; return -ENOMEM;
copy_from_user ( iob_put ( iobuf, len ), copy_from_user ( iob_put ( iobuf, len ),
iscsi->command->data_out, offset, len ); iscsi->command->data_out, offset, len );
memset ( iob_put ( iobuf, pad_len ), 0, pad_len );
return xfer_deliver_iob ( &iscsi->socket, iobuf ); return xfer_deliver_iob ( &iscsi->socket, iobuf );
} }
@ -801,13 +804,17 @@ static int iscsi_tx_login_request ( struct iscsi_session *iscsi ) {
struct iscsi_bhs_login_request *request = &iscsi->tx_bhs.login_request; struct iscsi_bhs_login_request *request = &iscsi->tx_bhs.login_request;
struct io_buffer *iobuf; struct io_buffer *iobuf;
size_t len; size_t len;
size_t pad_len;
len = ISCSI_DATA_LEN ( request->lengths ); len = ISCSI_DATA_LEN ( request->lengths );
iobuf = xfer_alloc_iob ( &iscsi->socket, len ); pad_len = ISCSI_DATA_PAD_LEN ( request->lengths );
iobuf = xfer_alloc_iob ( &iscsi->socket, ( len + pad_len ) );
if ( ! iobuf ) if ( ! iobuf )
return -ENOMEM; return -ENOMEM;
iob_put ( iobuf, len ); iob_put ( iobuf, len );
iscsi_build_login_request_strings ( iscsi, iobuf->data, len ); iscsi_build_login_request_strings ( iscsi, iobuf->data, len );
memset ( iob_put ( iobuf, pad_len ), 0, pad_len );
return xfer_deliver_iob ( &iscsi->socket, iobuf ); return xfer_deliver_iob ( &iscsi->socket, iobuf );
} }
@ -1415,27 +1422,6 @@ static int iscsi_tx_data ( struct iscsi_session *iscsi ) {
} }
} }
/**
* Transmit data padding of an iSCSI PDU
*
* @v iscsi iSCSI session
* @ret rc Return status code
*
* Handle transmission of any data padding in a PDU data segment.
* iscsi::tx_bhs will be valid when this is called.
*/
static int iscsi_tx_data_padding ( struct iscsi_session *iscsi ) {
static const char pad[] = { '\0', '\0', '\0' };
struct iscsi_bhs_common *common = &iscsi->tx_bhs.common;
size_t pad_len;
pad_len = ISCSI_DATA_PAD_LEN ( common->lengths );
if ( ! pad_len )
return 0;
return xfer_deliver_raw ( &iscsi->socket, pad, pad_len );
}
/** /**
* Complete iSCSI PDU transmission * Complete iSCSI PDU transmission
* *
@ -1494,11 +1480,6 @@ static void iscsi_tx_step ( struct iscsi_session *iscsi ) {
case ISCSI_TX_DATA: case ISCSI_TX_DATA:
tx = iscsi_tx_data; tx = iscsi_tx_data;
tx_len = ISCSI_DATA_LEN ( common->lengths ); tx_len = ISCSI_DATA_LEN ( common->lengths );
next_state = ISCSI_TX_DATA_PADDING;
break;
case ISCSI_TX_DATA_PADDING:
tx = iscsi_tx_data_padding;
tx_len = ISCSI_DATA_PAD_LEN ( common->lengths );
next_state = ISCSI_TX_IDLE; next_state = ISCSI_TX_IDLE;
break; break;
case ISCSI_TX_IDLE: case ISCSI_TX_IDLE: