diff --git a/Makefile.am b/Makefile.am index 12ee18f..7242d7a 100644 --- a/Makefile.am +++ b/Makefile.am @@ -148,7 +148,8 @@ bin_iscsi_test_SOURCES = test-tool/iscsi-test.c \ test-tool/1020_bufferoffset_invalid.c \ test-tool/1030_unsolicited_data_overflow.c \ test-tool/1031_unsolicited_data_out.c \ - test-tool/1040_saturate_maxcmdsn.c + test-tool/1040_saturate_maxcmdsn.c \ + test-tool/1041_unsolicited_immediate_data.c endif diff --git a/include/iscsi.h b/include/iscsi.h index ec5168f..56237ce 100644 --- a/include/iscsi.h +++ b/include/iscsi.h @@ -102,7 +102,7 @@ int iscsi_set_immediate_data(struct iscsi_context *iscsi, enum iscsi_immediate_d * This can be set on a context before it has been logged in to the target * and controls how the initiator will try to negotiate the initial r2t. * - * Default is for libiscsi to try to negotiate ISCSI_INITIAL_R2T_YES + * Default is for libiscsi to try to negotiate ISCSI_INITIAL_R2T_NO */ enum iscsi_initial_r2t { ISCSI_INITIAL_R2T_NO = 0, diff --git a/test-tool/1041_unsolicited_immediate_data.c b/test-tool/1041_unsolicited_immediate_data.c new file mode 100644 index 0000000..db79b50 --- /dev/null +++ b/test-tool/1041_unsolicited_immediate_data.c @@ -0,0 +1,187 @@ +/* + Copyright (C) 2012 by Ronnie Sahlberg + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, see . +*/ + +#include +#include +#include "iscsi.h" +#include "iscsi-private.h" +#include "scsi-lowlevel.h" +#include "iscsi-test.h" +#include + +uint32_t block_size; +int pdu_was_valid; + +/* one block sent as immediate data. PDU should have F-bit set + * and datasegmentlength should be a single block. + */ +static int my_queue_immediate_data(struct iscsi_context *iscsi _U_, struct iscsi_pdu *pdu) +{ + pdu_was_valid = 1; + + if (!(pdu->outdata.data[1] & 0x80)) { + printf("SCSI-Command PDU with immediate data did not have the F-flag set.\n"); + pdu_was_valid = 0; + return 0; + } + if ( (*(uint32_t *)&pdu->outdata.data[4] & 0x00ffffff) != htonl(block_size)) { + printf("SCSI-Command PDU did not have one block of immediate data.\n"); + pdu_was_valid = 0; + return 0; + } + return 1; +} + +int T1041_unsolicited_immediate_data(const char *initiator, const char *url, int data_loss, int show_info) +{ + struct iscsi_context *iscsi; + struct scsi_task *task; + struct scsi_readcapacity16 *rc16; + int ret, lun; + struct iscsi_url *iscsi_url; + unsigned char data[4096]; + + printf("1041_unsolicited_immediate_data:\n"); + printf("================================\n"); + if (show_info) { + printf("Test we can send unsolicited data to the target\n"); + printf("1, Login to target with IMMEDIATE_DATA=YES and INITIAL_R2T=YES.\n"); + printf("2, Write one block to the target as immediate data.\n"); + printf("3, Verify that the PDU sent has the F-flag set.\n"); + printf("4, Verify that the PDU sent has of immediate data.\n"); + printf("\n"); + return 0; + } + + iscsi = iscsi_context_login(initiator, url, &lun); + if (iscsi == NULL) { + printf("Failed to login to target\n"); + return -1; + } + + iscsi_url = iscsi_parse_full_url(iscsi, url); + if (iscsi_url == NULL) { + printf("Failed to parse iscsi url\n"); + return -1; + } + + /* find the size of the LUN */ + task = iscsi_readcapacity16_sync(iscsi, lun); + if (task == NULL) { + printf("Failed to send READCAPACITY16 command: %s\n", iscsi_get_error(iscsi)); + ret = -1; + goto finished; + } + if (task->status != SCSI_STATUS_GOOD) { + printf("READCAPACITY16 command: failed with sense. %s\n", iscsi_get_error(iscsi)); + ret = -1; + scsi_free_scsi_task(task); + goto finished; + } + rc16 = scsi_datain_unmarshall(task); + if (rc16 == NULL) { + printf("failed to unmarshall READCAPACITY16 data. %s\n", iscsi_get_error(iscsi)); + ret = -1; + scsi_free_scsi_task(task); + goto finished; + } + block_size = rc16->block_length; + + scsi_free_scsi_task(task); + + + if (!data_loss) { + printf("--dataloss flag is not set. Skipping test\n"); + ret = -2; + goto finished; + } + + + ret = 0; + + + /* This setting will allow us to send unsolicited data as + * immediate data but not as a data-out. + */ + printf("Login to target with IMMEDIATE_DATA=YES and INITIAL_R2T=YES ... "); + iscsi_destroy_context(iscsi); + iscsi = iscsi_create_context(initiator); + iscsi_set_targetname(iscsi, iscsi_url->target); + iscsi_set_session_type(iscsi, ISCSI_SESSION_NORMAL); + iscsi_set_header_digest(iscsi, ISCSI_HEADER_DIGEST_NONE_CRC32C); + iscsi_set_immediate_data(iscsi, ISCSI_IMMEDIATE_DATA_YES); + iscsi_set_initial_r2t(iscsi, ISCSI_INITIAL_R2T_YES); + if (iscsi_full_connect_sync(iscsi, iscsi_url->portal, lun) != 0) { + printf("[FAILED]\n"); + printf("Failed to log in to target with IMMEDIATE_DATA=YES and INITIAL_R2T=YES %s\n", iscsi_get_error(iscsi)); + ret = -1; + goto finished; + } + if (iscsi->use_immediate_data != ISCSI_IMMEDIATE_DATA_YES) { + printf("[FAILED]\n"); + printf("Failed to negotiate IMMEDIATE_DATA==YES with target\n"); + ret = -1; + goto finished; + } + if (iscsi->use_initial_r2t != ISCSI_INITIAL_R2T_YES) { + printf("[FAILED]\n"); + printf("Failed to negotiate INITIAL_R2T==YES with target\n"); + ret = -1; + goto finished; + } + printf("[OK]\n"); + + + + printf("Write one block as immediate data ... "); + local_iscsi_queue_pdu = my_queue_immediate_data; + task = iscsi_write10_sync(iscsi, lun, 0, data, block_size, block_size, 0, 0, 0, 0, 0); + local_iscsi_queue_pdu = NULL; + /* Verify that the PDU we sent had the F-bit set and that + * datasegmentlength was one block. + */ + if (pdu_was_valid == 0) { + printf("[FAILED]\n"); + printf("PDU to send was invalid.\n"); + ret = -1; + goto finished; + } + if (task == NULL) { + printf("[FAILED]\n"); + printf("Failed to send write10 command: %s\n", iscsi_get_error(iscsi)); + ret = -1; + goto finished; + } + if (task->status != SCSI_STATUS_GOOD) { + printf("[FAILED]\n"); + printf("Write10 command: failed with sense. %s\n", iscsi_get_error(iscsi)); + ret = -1; + scsi_free_scsi_task(task); + goto finished; + } + + scsi_free_scsi_task(task); + printf("[OK]\n"); + + + +finished: + iscsi_destroy_context(iscsi); + iscsi_destroy_url(iscsi_url); + + return ret; +} diff --git a/test-tool/iscsi-test.c b/test-tool/iscsi-test.c index dee15bd..d7cf9b5 100644 --- a/test-tool/iscsi-test.c +++ b/test-tool/iscsi-test.c @@ -250,6 +250,8 @@ struct scsi_test tests[] = { */ { "T1040_saturate_maxcmdsn", T1040_saturate_maxcmdsn }, +{ "T1041_unsolicited__immediate_data", T1041_unsolicited_immediate_data }, + { NULL, NULL } }; diff --git a/test-tool/iscsi-test.h b/test-tool/iscsi-test.h index 41c401b..49a8fe5 100644 --- a/test-tool/iscsi-test.h +++ b/test-tool/iscsi-test.h @@ -183,3 +183,4 @@ int T1020_bufferoffset_invalid(const char *initiator, const char *url, int data_ int T1030_unsolicited_data_overflow(const char *initiator, const char *url, int data_loss, int show_info); int T1031_unsolicited_data_out(const char *initiator, const char *url, int data_loss, int show_info); int T1040_saturate_maxcmdsn(const char *initiator, const char *url, int data_loss, int show_info); +int T1041_unsolicited_immediate_data(const char *initiator, const char *url, int data_loss, int show_info);