Files
libiscsi/test-tool/1031_unsolicited_data_out.c
Ronnie Sahlberg 9e9c6946c0 TESTS: Add a test that a target handles an unsolicited DATA-OUT correctly.
Send a large number of DATA-OUT PDUs that do not have a matching SCSI-COMMAND
PDU and verify that the target responds correctly. Either by terminating the
session or by just ignoring the data.

Verify also that the target is not "surprised" and crashes.

Signed-off-by: Ronnie Sahlberg <ronniesahlberg@gmail.com>
2012-10-30 18:44:35 -07:00

271 lines
6.5 KiB
C

/*
Copyright (C) 2012 by Ronnie Sahlberg <ronniesahlberg@gmail.com>
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 <http://www.gnu.org/licenses/>.
*/
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <arpa/inet.h>
#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;
}