diff --git a/Makefile.am b/Makefile.am index 31827c4..b750cdb 100644 --- a/Makefile.am +++ b/Makefile.am @@ -145,7 +145,8 @@ bin_iscsi_test_SOURCES = test-tool/iscsi-test.c \ test-tool/1000_cmdsn_invalid.c \ test-tool/1010_datasn_invalid.c \ test-tool/1020_bufferoffset_invalid.c \ - test-tool/1030_unsolicited_data_overflow.c + test-tool/1030_unsolicited_data_overflow.c \ + test-tool/1031_unsolicited_data_out.c endif diff --git a/lib/socket.c b/lib/socket.c index 1376527..16b194f 100644 --- a/lib/socket.c +++ b/lib/socket.c @@ -473,15 +473,21 @@ iscsi_service(struct iscsi_context *iscsi, int revents) iscsi_set_error(iscsi, "iscsi_service: POLLERR, " "Unknown socket error."); } - iscsi->socket_status_cb(iscsi, SCSI_STATUS_ERROR, NULL, - iscsi->connect_data); + 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."); - iscsi->socket_status_cb(iscsi, SCSI_STATUS_ERROR, NULL, - iscsi->connect_data); + 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); } @@ -496,16 +502,23 @@ iscsi_service(struct iscsi_context *iscsi, int revents) 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); + 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); } 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); + if (iscsi->socket_status_cb) { + iscsi->socket_status_cb(iscsi, SCSI_STATUS_GOOD, NULL, + iscsi->connect_data); + iscsi->socket_status_cb = NULL; + } return 0; } diff --git a/test-tool/1031_unsolicited_data_out.c b/test-tool/1031_unsolicited_data_out.c new file mode 100644 index 0000000..1ba1b81 --- /dev/null +++ b/test-tool/1031_unsolicited_data_out.c @@ -0,0 +1,270 @@ +/* + 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 +#include +#include "iscsi.h" +#include "iscsi-private.h" +#include "scsi-lowlevel.h" +#include "iscsi-test.h" + +uint32_t block_size; + +static int +my_iscsi_add_data(struct iscsi_context *iscsi, struct iscsi_data *data, + unsigned char *dptr, int dsize, int pdualignment) +{ + int len, aligned; + unsigned char *buf; + + if (dsize == 0) { + printf("Trying to append zero size data to iscsi_data"); + return -1; + } + + len = data->size + dsize; + aligned = len; + if (pdualignment) { + aligned = (aligned+3)&0xfffffffc; + } + buf = malloc(aligned); + if (buf == NULL) { + printf("failed to allocate buffer for %d bytes", len); + return -1; + } + + if (data->size > 0) { + memcpy(buf, data->data, data->size); + } + memcpy(buf + data->size, dptr, dsize); + if (len != aligned) { + /* zero out any padding at the end */ + memset(buf+len, 0, aligned-len); + } + + free(data->data); + + data->data = buf; + data->size = len; + + return 0; +} + +static int +my_iscsi_pdu_add_data(struct iscsi_context *iscsi, struct iscsi_pdu *pdu, + unsigned char *dptr, int dsize) +{ + if (my_iscsi_add_data(iscsi, &pdu->outdata, dptr, dsize, 1) != 0) { + printf("failed to add data to pdu buffer"); + return -1; + } + + /* update data segment length */ + *(uint32_t *)&pdu->outdata.data[4] = htonl(pdu->outdata.size + - ISCSI_HEADER_SIZE); + + return 0; +} + +static void +my_iscsi_pdu_set_itt(struct iscsi_pdu *pdu, uint32_t itt) +{ + *(uint32_t *)&pdu->outdata.data[16] = htonl(itt); +} + +static void +my_iscsi_pdu_set_expstatsn(struct iscsi_pdu *pdu, uint32_t expstatsnsn) +{ + *(uint32_t *)&pdu->outdata.data[28] = htonl(expstatsnsn); +} + +static void +my_iscsi_pdu_set_pduflags(struct iscsi_pdu *pdu, unsigned char flags) +{ + pdu->outdata.data[1] = flags; +} + +static void +my_iscsi_pdu_set_lun(struct iscsi_pdu *pdu, uint32_t lun) +{ + pdu->outdata.data[8] = lun >> 8; + pdu->outdata.data[9] = lun & 0xff; +} + +static struct iscsi_pdu * +my_iscsi_allocate_pdu_with_itt_flags(struct iscsi_context *iscsi, enum iscsi_opcode opcode, + enum iscsi_opcode response_opcode, uint32_t itt, uint32_t flags) +{ + struct iscsi_pdu *pdu; + + pdu = malloc(sizeof(struct iscsi_pdu)); + if (pdu == NULL) { + printf("failed to allocate pdu"); + return NULL; + } + memset(pdu, 0, sizeof(struct iscsi_pdu)); + + pdu->outdata.size = ISCSI_HEADER_SIZE; + pdu->outdata.data = malloc(pdu->outdata.size); + + if (pdu->outdata.data == NULL) { + printf("failed to allocate pdu header"); + free(pdu); + return NULL; + } + memset(pdu->outdata.data, 0, pdu->outdata.size); + + /* opcode */ + pdu->outdata.data[0] = opcode; + pdu->response_opcode = response_opcode; + + /* isid */ + if (opcode == ISCSI_PDU_LOGIN_REQUEST) { + memcpy(&pdu->outdata.data[8], &iscsi->isid[0], 6); + } + + /* itt */ + my_iscsi_pdu_set_itt(pdu, itt); + pdu->itt = itt; + + /* flags */ + pdu->flags = flags; + + return pdu; +} + +int T1031_unsolicited_data_out(const char *initiator, const char *url, int data_loss, int show_info) +{ + struct iscsi_context *iscsi, *iscsi2; + struct scsi_task *task; + struct scsi_readcapacity16 *rc16; + int i, ret, lun; + unsigned char buf[1024]; + + printf("1031_unsolicited_data_out:\n"); + printf("==========================\n"); + if (show_info) { + printf("Test sending unsolicited DATA-OUT that are not associated with any SCSI-command.\n"); + printf("1, Send 100 DATA-OUT PDUs\n"); + printf("2, Verify the target is still alive\n"); + printf("\n"); + return 0; + } + + iscsi = iscsi_context_login(initiator, url, &lun); + if (iscsi == NULL) { + printf("Failed to login to target\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); + + + ret = 0; + + printf("Send unsolicited DATA-OUT PDUs ... "); + for (i = 0; i < 100; i++) { + struct iscsi_pdu *pdu; + + pdu = my_iscsi_allocate_pdu_with_itt_flags(iscsi, ISCSI_PDU_DATA_OUT, + ISCSI_PDU_NO_PDU, + i + 0x1000, + ISCSI_PDU_DELETE_WHEN_SENT|ISCSI_PDU_NO_CALLBACK); + if (pdu == NULL) { + printf("Failed to allocated PDU. Aborting\n"); + ret = -2; + goto finished; + } + my_iscsi_pdu_set_pduflags(pdu, ISCSI_PDU_SCSI_FINAL); + my_iscsi_pdu_set_lun(pdu, lun); + my_iscsi_pdu_set_expstatsn(pdu, iscsi->statsn+1); + if (my_iscsi_pdu_add_data(iscsi, pdu, buf, sizeof(buf)) != 0) { + printf("Failed to add data to PDU. Aborting\n"); + ret = -2; + goto finished; + } + pdu->callback = NULL; + pdu->private_data = NULL; + if (iscsi_queue_pdu(iscsi, pdu) != 0) { + printf("Failed to queue PDU. Aborting\n"); + ret = -2; + goto finished; + } + } + + /* Send a TUR to drive the eventsystem and make sure the + * DATA-OUT PDUs are flushed + */ + task = iscsi_testunitready_sync(iscsi, lun); + if (task == NULL) { + printf("[FAILED]\n"); + printf("Failed to send TEST UNIT READY command: %s\n", iscsi_get_error(iscsi)); + ret++; + goto test2; + } + if (task->status != SCSI_STATUS_GOOD) { + printf("[FAILED]\n"); + printf("TEST UNIT READY command: failed with sense %s\n", iscsi_get_error(iscsi)); + ret++; + scsi_free_scsi_task(task); + goto test2; + } + scsi_free_scsi_task(task); + printf("[OK]\n"); + + + +test2: + printf("Verify the target is still alive ... "); + iscsi2 = iscsi_context_login(initiator, url, &lun); + if (iscsi2 == NULL) { + printf("[FAILED]\n"); + printf("Target is dead?\n"); + ret = -1; + goto finished; + } + printf("[OK]\n"); + + +finished: + iscsi_destroy_context(iscsi); + iscsi_destroy_context(iscsi2); + return ret; +} diff --git a/test-tool/iscsi-test.c b/test-tool/iscsi-test.c index cad27bd..5ea3705 100644 --- a/test-tool/iscsi-test.c +++ b/test-tool/iscsi-test.c @@ -239,6 +239,7 @@ struct scsi_test tests[] = { /* sending too much unsolicited data */ { "T1030_unsolicited_data_overflow", T1030_unsolicited_data_overflow }, +{ "T1031_unsolicited_data_out", T1031_unsolicited_data_out }, { NULL, NULL } }; diff --git a/test-tool/iscsi-test.h b/test-tool/iscsi-test.h index 604b4df..d597773 100644 --- a/test-tool/iscsi-test.h +++ b/test-tool/iscsi-test.h @@ -181,4 +181,5 @@ int T1000_cmdsn_invalid(const char *initiator, const char *url, int data_loss, i int T1010_datasn_invalid(const char *initiator, const char *url, int data_loss, int show_info); int T1020_bufferoffset_invalid(const char *initiator, const char *url, int data_loss, int show_info); 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);