Merge branch 'master' into read_batch_pdu2
This commit is contained in:
@@ -23,34 +23,39 @@
|
||||
#include <poll.h>
|
||||
#include <getopt.h>
|
||||
#include <unistd.h>
|
||||
#include <limits.h>
|
||||
#include "iscsi.h"
|
||||
#include "scsi-lowlevel.h"
|
||||
|
||||
const char *initiator = "iqn.2010-11.ronnie:iscsi-inq";
|
||||
int max_in_flight = 50;
|
||||
int blocks_per_io = 200;
|
||||
uint32_t max_in_flight = 50;
|
||||
uint32_t blocks_per_io = 200;
|
||||
|
||||
struct client {
|
||||
int finished;
|
||||
int in_flight;
|
||||
uint32_t in_flight;
|
||||
|
||||
struct iscsi_context *src_iscsi;
|
||||
int src_lun;
|
||||
int src_blocksize;
|
||||
uint64_t src_num_blocks;
|
||||
struct scsi_inquiry_device_designator src_tgt_desig;
|
||||
uint64_t pos;
|
||||
|
||||
struct iscsi_context *dst_iscsi;
|
||||
int dst_lun;
|
||||
int dst_blocksize;
|
||||
uint64_t dst_num_blocks;
|
||||
struct scsi_inquiry_device_designator dst_tgt_desig;
|
||||
int use_16_for_rw;
|
||||
int use_xcopy;
|
||||
int progress;
|
||||
int ignore_errors;
|
||||
};
|
||||
|
||||
|
||||
void fill_read_queue(struct client *client);
|
||||
void fill_xcopy_queue(struct client *client);
|
||||
|
||||
struct write_task {
|
||||
struct scsi_task *rt;
|
||||
@@ -153,7 +158,7 @@ void read_cb(struct iscsi_context *iscsi _U_, int status, void *command_data, vo
|
||||
|
||||
void fill_read_queue(struct client *client)
|
||||
{
|
||||
int num_blocks;
|
||||
uint32_t num_blocks;
|
||||
|
||||
while(client->in_flight < max_in_flight && client->pos < client->src_num_blocks) {
|
||||
struct scsi_task *task;
|
||||
@@ -185,14 +190,343 @@ void fill_read_queue(struct client *client)
|
||||
}
|
||||
}
|
||||
|
||||
int populate_tgt_desc(unsigned char *desc,
|
||||
struct scsi_inquiry_device_designator *tgt_desig,
|
||||
int rel_init_port_id, uint32_t block_size)
|
||||
{
|
||||
desc[0] = IDENT_DESCR_TGT_DESCR;
|
||||
desc[1] = 0; /* peripheral type */
|
||||
desc[2] = (rel_init_port_id >> 8) & 0xFF;
|
||||
desc[3] = rel_init_port_id & 0xFF;
|
||||
desc[4] = tgt_desig->code_set;
|
||||
desc[5] = (tgt_desig->designator_type & 0xF)
|
||||
| ((tgt_desig->association & 3) << 4);
|
||||
desc[7] = tgt_desig->designator_length;
|
||||
memcpy(desc + 8, tgt_desig->designator, tgt_desig->designator_length);
|
||||
|
||||
desc[28] = 0;
|
||||
desc[29] = (block_size >> 16) & 0xFF;
|
||||
desc[30] = (block_size >> 8) & 0xFF;
|
||||
desc[31] = block_size & 0xFF;
|
||||
|
||||
return 32;
|
||||
}
|
||||
|
||||
int populate_seg_desc_hdr(unsigned char *hdr, int dc, int cat, int src_index,
|
||||
int dst_index)
|
||||
{
|
||||
int desc_len = 28;
|
||||
|
||||
hdr[0] = BLK_TO_BLK_SEG_DESCR;
|
||||
hdr[1] = ((dc << 1) | cat) & 0xFF;
|
||||
hdr[2] = (desc_len >> 8) & 0xFF;
|
||||
hdr[3] = (desc_len - SEG_DESC_SRC_INDEX_OFFSET) & 0xFF; /* don't account for the first 4 bytes in descriptor header*/
|
||||
hdr[4] = (src_index >> 8) & 0xFF;
|
||||
hdr[5] = src_index & 0xFF;
|
||||
hdr[6] = (dst_index >> 8) & 0xFF;
|
||||
hdr[7] = dst_index & 0xFF;
|
||||
|
||||
return desc_len;
|
||||
}
|
||||
|
||||
int populate_seg_desc_b2b(unsigned char *desc, int dc, int cat,
|
||||
int src_index, int dst_index, int num_blks,
|
||||
uint64_t src_lba, uint64_t dst_lba)
|
||||
{
|
||||
int desc_len = populate_seg_desc_hdr(desc, dc, cat,
|
||||
src_index, dst_index);
|
||||
|
||||
desc[10] = (num_blks >> 8) & 0xFF;
|
||||
desc[11] = num_blks & 0xFF;
|
||||
desc[12] = (src_lba >> 56) & 0xFF;
|
||||
desc[13] = (src_lba >> 48) & 0xFF;
|
||||
desc[14] = (src_lba >> 40) & 0xFF;
|
||||
desc[15] = (src_lba >> 32) & 0xFF;
|
||||
desc[16] = (src_lba >> 24) & 0xFF;
|
||||
desc[17] = (src_lba >> 16) & 0xFF;
|
||||
desc[18] = (src_lba >> 8) & 0xFF;
|
||||
desc[19] = src_lba & 0xFF;
|
||||
desc[20] = (dst_lba >> 56) & 0xFF;
|
||||
desc[21] = (dst_lba >> 48) & 0xFF;
|
||||
desc[22] = (dst_lba >> 40) & 0xFF;
|
||||
desc[23] = (dst_lba >> 32) & 0xFF;
|
||||
desc[24] = (dst_lba >> 24) & 0xFF;
|
||||
desc[25] = (dst_lba >> 16) & 0xFF;
|
||||
desc[26] = (dst_lba >> 8) & 0xFF;
|
||||
desc[27] = dst_lba & 0xFF;
|
||||
|
||||
return desc_len;
|
||||
}
|
||||
|
||||
void populate_param_header(unsigned char *buf, int list_id, int str, int list_id_usage, int prio, int tgt_desc_len, int seg_desc_len, int inline_data_len)
|
||||
{
|
||||
buf[0] = list_id;
|
||||
buf[1] = ((str & 1) << 5) | ((list_id_usage & 3) << 3) | (prio & 7);
|
||||
buf[2] = (tgt_desc_len >> 8) & 0xFF;
|
||||
buf[3] = tgt_desc_len & 0xFF;
|
||||
buf[8] = (seg_desc_len >> 24) & 0xFF;
|
||||
buf[9] = (seg_desc_len >> 16) & 0xFF;
|
||||
buf[10] = (seg_desc_len >> 8) & 0xFF;
|
||||
buf[11] = seg_desc_len & 0xFF;
|
||||
buf[12] = (inline_data_len >> 24) & 0xFF;
|
||||
buf[13] = (inline_data_len >> 16) & 0xFF;
|
||||
buf[14] = (inline_data_len >> 8) & 0xFF;
|
||||
buf[15] = inline_data_len & 0xFF;
|
||||
}
|
||||
|
||||
void xcopy_cb(struct iscsi_context *iscsi _U_, int status, void *command_data, void *private_data)
|
||||
{
|
||||
struct client *client = (struct client *)private_data;
|
||||
struct scsi_task *task = command_data;
|
||||
|
||||
if (status == SCSI_STATUS_CHECK_CONDITION) {
|
||||
printf("XCOPY failed with sense key:%d ascq:%04x\n",
|
||||
task->sense.key, task->sense.ascq);
|
||||
scsi_free_scsi_task(task);
|
||||
exit(10);
|
||||
}
|
||||
|
||||
if (status != SCSI_STATUS_GOOD) {
|
||||
printf("XCOPY failed with %s\n", iscsi_get_error(iscsi));
|
||||
if (!client->ignore_errors) {
|
||||
scsi_free_scsi_task(task);
|
||||
exit(10);
|
||||
}
|
||||
}
|
||||
|
||||
client->in_flight--;
|
||||
fill_xcopy_queue(client);
|
||||
|
||||
if (client->progress) {
|
||||
printf("\r%"PRIu64" of %"PRIu64" blocks transferred.",
|
||||
client->pos, client->src_num_blocks);
|
||||
}
|
||||
|
||||
if ((client->in_flight == 0) && (client->pos == client->src_num_blocks)) {
|
||||
client->finished = 1;
|
||||
if (client->progress) {
|
||||
printf("\n");
|
||||
}
|
||||
}
|
||||
scsi_free_scsi_task(task);
|
||||
}
|
||||
|
||||
void fill_xcopy_queue(struct client *client)
|
||||
{
|
||||
while (client->in_flight < max_in_flight && client->pos < client->src_num_blocks) {
|
||||
struct scsi_task *task;
|
||||
struct iscsi_data data;
|
||||
unsigned char *xcopybuf;
|
||||
int offset;
|
||||
uint32_t num_blocks;
|
||||
int tgt_desc_len;
|
||||
int seg_desc_len;
|
||||
|
||||
client->in_flight++;
|
||||
|
||||
num_blocks = client->src_num_blocks - client->pos;
|
||||
if (num_blocks > blocks_per_io) {
|
||||
num_blocks = blocks_per_io;
|
||||
}
|
||||
|
||||
data.size = XCOPY_DESC_OFFSET +
|
||||
32 * 2 + /* IDENT_DESCR_TGT_DESCR */
|
||||
28; /* BLK_TO_BLK_SEG_DESCR */
|
||||
data.data = malloc(data.size);
|
||||
if (data.data == NULL) {
|
||||
printf("failed to alloc XCOPY buffer\n");
|
||||
exit(10);
|
||||
}
|
||||
|
||||
xcopybuf = data.data;
|
||||
memset(xcopybuf, 0, data.size);
|
||||
|
||||
/* Initialise CSCD list with one src + one dst descriptor */
|
||||
offset = XCOPY_DESC_OFFSET;
|
||||
offset += populate_tgt_desc(xcopybuf + offset,
|
||||
&client->src_tgt_desig,
|
||||
0, client->src_blocksize);
|
||||
offset += populate_tgt_desc(xcopybuf + offset,
|
||||
&client->dst_tgt_desig,
|
||||
0, client->dst_blocksize);
|
||||
tgt_desc_len = offset - XCOPY_DESC_OFFSET;
|
||||
|
||||
/* Initialise one segment descriptor */
|
||||
seg_desc_len = populate_seg_desc_b2b(xcopybuf + offset, 0, 0,
|
||||
0, 1, num_blocks, client->pos, client->pos);
|
||||
offset += seg_desc_len;
|
||||
|
||||
/* Initialise the parameter list header */
|
||||
populate_param_header(xcopybuf, 1, 0, LIST_ID_USAGE_DISCARD, 0,
|
||||
tgt_desc_len, seg_desc_len, 0);
|
||||
|
||||
task = iscsi_extended_copy_task(client->src_iscsi,
|
||||
client->src_lun,
|
||||
&data, xcopy_cb, client);
|
||||
if (task == NULL) {
|
||||
printf("failed to send XCOPY command\n");
|
||||
exit(10);
|
||||
}
|
||||
|
||||
client->pos += num_blocks;
|
||||
}
|
||||
}
|
||||
|
||||
void cscd_ident_inq(struct iscsi_context *iscsi,
|
||||
int lun,
|
||||
struct scsi_inquiry_device_designator *_tgt_desig)
|
||||
{
|
||||
struct scsi_task *task = NULL;
|
||||
struct scsi_inquiry_device_identification *inq_di = NULL;
|
||||
struct scsi_inquiry_device_designator *desig, *tgt_desig = NULL;
|
||||
enum scsi_designator_type prev_type = 0;
|
||||
|
||||
/* check what type of lun we have */
|
||||
task = iscsi_inquiry_sync(iscsi, lun, 1,
|
||||
SCSI_INQUIRY_PAGECODE_DEVICE_IDENTIFICATION, 255);
|
||||
if (task == NULL || task->status != SCSI_STATUS_GOOD) {
|
||||
fprintf(stderr, "failed to send inquiry command: %s\n",
|
||||
iscsi_get_error(iscsi));
|
||||
exit(10);
|
||||
}
|
||||
|
||||
inq_di = scsi_datain_unmarshall(task);
|
||||
if (inq_di == NULL) {
|
||||
fprintf(stderr, "failed to unmarshall inquiry datain blob\n");
|
||||
exit(10);
|
||||
}
|
||||
|
||||
for (desig = inq_di->designators; desig; desig = desig->next) {
|
||||
switch (desig->designator_type) {
|
||||
case SCSI_DESIGNATOR_TYPE_VENDOR_SPECIFIC:
|
||||
case SCSI_DESIGNATOR_TYPE_T10_VENDORT_ID:
|
||||
case SCSI_DESIGNATOR_TYPE_EUI_64:
|
||||
case SCSI_DESIGNATOR_TYPE_NAA:
|
||||
if (prev_type <= desig->designator_type) {
|
||||
tgt_desig = desig;
|
||||
prev_type = desig->designator_type;
|
||||
}
|
||||
default:
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
if (tgt_desig == NULL) {
|
||||
fprintf(stderr, "No suitalble target descriptor format found");
|
||||
exit(10);
|
||||
}
|
||||
|
||||
/* copy what's needed for XCOPY */
|
||||
_tgt_desig->code_set = tgt_desig->code_set;
|
||||
_tgt_desig->association = tgt_desig->association;
|
||||
_tgt_desig->designator_type = tgt_desig->designator_type;
|
||||
_tgt_desig->designator_length = tgt_desig->designator_length;
|
||||
_tgt_desig->designator = malloc(tgt_desig->designator_length);
|
||||
memcpy(_tgt_desig->designator, tgt_desig->designator, tgt_desig->designator_length);
|
||||
|
||||
scsi_free_scsi_task(task);
|
||||
}
|
||||
|
||||
void cscd_param_check(struct iscsi_context *iscsi,
|
||||
int lun,
|
||||
uint32_t blocksize)
|
||||
{
|
||||
struct scsi_task *task = NULL;
|
||||
struct scsi_copy_results_op_params *opp;
|
||||
uint32_t io_segment_bytes;
|
||||
|
||||
task = iscsi_receive_copy_results_sync(iscsi, lun,
|
||||
SCSI_COPY_RESULTS_OP_PARAMS, 0, 1024);
|
||||
if (task == NULL || task->status != SCSI_STATUS_GOOD) {
|
||||
fprintf(stderr, "XCOPY RECEIVE COPY RESULTS failed: %s\n",
|
||||
iscsi_get_error(iscsi));
|
||||
exit(10);
|
||||
}
|
||||
|
||||
opp = scsi_datain_unmarshall(task);
|
||||
if (opp == NULL) {
|
||||
fprintf(stderr, "failed to unmarshall XCOPY RCR datain blob\n");
|
||||
exit(10);
|
||||
}
|
||||
|
||||
if (opp->max_target_desc_count < 2) {
|
||||
fprintf(stderr, "XCOPY max CSCD desc count %d too small\n",
|
||||
opp->max_target_desc_count);
|
||||
exit(10);
|
||||
}
|
||||
if (opp->max_segment_desc_count < 1) {
|
||||
fprintf(stderr, "XCOPY max segment desc count %d too small\n",
|
||||
opp->max_segment_desc_count);
|
||||
exit(10);
|
||||
}
|
||||
|
||||
io_segment_bytes = blocks_per_io * blocksize;
|
||||
if (io_segment_bytes > opp->max_segment_length) {
|
||||
fprintf(stderr,
|
||||
"%u bytes per I/O exceeds XCOPY max segment len %u\n",
|
||||
io_segment_bytes, opp->max_segment_length);
|
||||
exit(10);
|
||||
}
|
||||
if (blocks_per_io > USHRT_MAX) {
|
||||
fprintf(stderr,
|
||||
"%u blocks per I/O exceeds XCOPY field width max %u\n",
|
||||
blocks_per_io, USHRT_MAX);
|
||||
exit(10);
|
||||
}
|
||||
|
||||
scsi_free_scsi_task(task);
|
||||
}
|
||||
|
||||
void readcap(struct iscsi_context *iscsi, int lun, int use_16,
|
||||
int *_blocksize, uint64_t *_num_blocks)
|
||||
{
|
||||
struct scsi_task *task;
|
||||
|
||||
if (use_16) {
|
||||
struct scsi_readcapacity16 *rc16;
|
||||
|
||||
task = iscsi_readcapacity16_sync(iscsi, lun);
|
||||
if (task == NULL || task->status != SCSI_STATUS_GOOD) {
|
||||
fprintf(stderr,
|
||||
"failed to send readcapacity command\n");
|
||||
exit(10);
|
||||
}
|
||||
rc16 = scsi_datain_unmarshall(task);
|
||||
if (rc16 == NULL) {
|
||||
fprintf(stderr,
|
||||
"failed to unmarshall readcapacity16 data\n");
|
||||
exit(10);
|
||||
}
|
||||
*_blocksize = rc16->block_length;
|
||||
*_num_blocks = rc16->returned_lba + 1;
|
||||
} else {
|
||||
struct scsi_readcapacity10 *rc10;
|
||||
|
||||
task = iscsi_readcapacity10_sync(iscsi, lun, 0, 0);
|
||||
if (task == NULL || task->status != SCSI_STATUS_GOOD) {
|
||||
fprintf(stderr,
|
||||
"failed to send readcapacity command\n");
|
||||
exit(10);
|
||||
}
|
||||
rc10 = scsi_datain_unmarshall(task);
|
||||
if (rc10 == NULL) {
|
||||
fprintf(stderr,
|
||||
"failed to unmarshall readcapacity10 data\n");
|
||||
exit(10);
|
||||
}
|
||||
*_blocksize = rc10->block_size;
|
||||
*_num_blocks = rc10->lba;
|
||||
}
|
||||
|
||||
scsi_free_scsi_task(task);
|
||||
return;
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
char *src_url = NULL;
|
||||
char *dst_url = NULL;
|
||||
struct iscsi_url *iscsi_url;
|
||||
struct scsi_task *task;
|
||||
struct scsi_readcapacity10 *rc10;
|
||||
struct scsi_readcapacity16 *rc16;
|
||||
int c;
|
||||
struct pollfd pfd[2];
|
||||
struct client client;
|
||||
@@ -203,6 +537,7 @@ int main(int argc, char *argv[])
|
||||
{"initiator-name", required_argument, NULL, 'i'},
|
||||
{"progress", no_argument, NULL, 'p'},
|
||||
{"16", no_argument, NULL, '6'},
|
||||
{"xcopy", no_argument, NULL, 'x'},
|
||||
{"max", required_argument, NULL, 'm'},
|
||||
{"blocks", required_argument, NULL, 'b'},
|
||||
{"ignore-errors", no_argument, NULL, 'n'},
|
||||
@@ -214,6 +549,8 @@ int main(int argc, char *argv[])
|
||||
|
||||
while ((c = getopt_long(argc, argv, "d:s:i:m:b:p6n", long_options,
|
||||
&option_index)) != -1) {
|
||||
char *endptr;
|
||||
|
||||
switch (c) {
|
||||
case 'd':
|
||||
dst_url = optarg;
|
||||
@@ -230,11 +567,24 @@ int main(int argc, char *argv[])
|
||||
case '6':
|
||||
client.use_16_for_rw = 1;
|
||||
break;
|
||||
case 'x':
|
||||
client.use_xcopy = 1;
|
||||
break;
|
||||
case 'm':
|
||||
max_in_flight = atoi(optarg);
|
||||
max_in_flight = strtoul(optarg, &endptr, 10);
|
||||
if (*endptr != '\0' || max_in_flight == UINT_MAX) {
|
||||
fprintf(stderr, "Invalid max in flight: %s\n",
|
||||
optarg);
|
||||
exit(10);
|
||||
}
|
||||
break;
|
||||
case 'b':
|
||||
blocks_per_io = atoi(optarg);
|
||||
blocks_per_io = strtoul(optarg, &endptr, 10);
|
||||
if (*endptr != '\0' || blocks_per_io == UINT_MAX) {
|
||||
fprintf(stderr, "Invalid blocks per I/O: %s\n",
|
||||
optarg);
|
||||
exit(10);
|
||||
}
|
||||
break;
|
||||
case 'n':
|
||||
client.ignore_errors = 1;
|
||||
@@ -278,34 +628,14 @@ int main(int argc, char *argv[])
|
||||
client.src_lun = iscsi_url->lun;
|
||||
iscsi_destroy_url(iscsi_url);
|
||||
|
||||
if (client.use_16_for_rw) {
|
||||
task = iscsi_readcapacity16_sync(client.src_iscsi, client.src_lun);
|
||||
if (task == NULL || task->status != SCSI_STATUS_GOOD) {
|
||||
fprintf(stderr, "failed to send readcapacity command\n");
|
||||
exit(10);
|
||||
}
|
||||
rc16 = scsi_datain_unmarshall(task);
|
||||
if (rc16 == NULL) {
|
||||
fprintf(stderr, "failed to unmarshall readcapacity16 data\n");
|
||||
exit(10);
|
||||
}
|
||||
client.src_blocksize = rc16->block_length;
|
||||
client.src_num_blocks = rc16->returned_lba + 1;
|
||||
scsi_free_scsi_task(task);
|
||||
} else {
|
||||
task = iscsi_readcapacity10_sync(client.src_iscsi, client.src_lun, 0, 0);
|
||||
if (task == NULL || task->status != SCSI_STATUS_GOOD) {
|
||||
fprintf(stderr, "failed to send readcapacity command\n");
|
||||
exit(10);
|
||||
}
|
||||
rc10 = scsi_datain_unmarshall(task);
|
||||
if (rc10 == NULL) {
|
||||
fprintf(stderr, "failed to unmarshall readcapacity10 data\n");
|
||||
exit(10);
|
||||
}
|
||||
client.src_blocksize = rc10->block_size;
|
||||
client.src_num_blocks = rc10->lba;
|
||||
scsi_free_scsi_task(task);
|
||||
readcap(client.src_iscsi, client.src_lun, client.use_16_for_rw,
|
||||
&client.src_blocksize, &client.src_num_blocks);
|
||||
|
||||
if (client.use_xcopy) {
|
||||
cscd_ident_inq(client.src_iscsi, client.src_lun,
|
||||
&client.src_tgt_desig);
|
||||
cscd_param_check(client.src_iscsi, client.src_lun,
|
||||
client.src_blocksize);
|
||||
}
|
||||
|
||||
client.dst_iscsi = iscsi_create_context(initiator);
|
||||
@@ -330,34 +660,14 @@ int main(int argc, char *argv[])
|
||||
client.dst_lun = iscsi_url->lun;
|
||||
iscsi_destroy_url(iscsi_url);
|
||||
|
||||
if (client.use_16_for_rw) {
|
||||
task = iscsi_readcapacity16_sync(client.dst_iscsi, client.dst_lun);
|
||||
if (task == NULL || task->status != SCSI_STATUS_GOOD) {
|
||||
fprintf(stderr, "failed to send readcapacity command\n");
|
||||
exit(10);
|
||||
}
|
||||
rc16 = scsi_datain_unmarshall(task);
|
||||
if (rc16 == NULL) {
|
||||
fprintf(stderr, "failed to unmarshall readcapacity16 data\n");
|
||||
exit(10);
|
||||
}
|
||||
client.dst_blocksize = rc16->block_length;
|
||||
client.dst_num_blocks = rc16->returned_lba + 1;
|
||||
scsi_free_scsi_task(task);
|
||||
} else {
|
||||
task = iscsi_readcapacity10_sync(client.dst_iscsi, client.dst_lun, 0, 0);
|
||||
if (task == NULL || task->status != SCSI_STATUS_GOOD) {
|
||||
fprintf(stderr, "failed to send readcapacity command\n");
|
||||
exit(10);
|
||||
}
|
||||
rc10 = scsi_datain_unmarshall(task);
|
||||
if (rc10 == NULL) {
|
||||
fprintf(stderr, "failed to unmarshall readcapacity10 data\n");
|
||||
exit(10);
|
||||
}
|
||||
client.dst_blocksize = rc10->block_size;
|
||||
client.dst_num_blocks = rc10->lba;
|
||||
scsi_free_scsi_task(task);
|
||||
readcap(client.dst_iscsi, client.dst_lun, client.use_16_for_rw,
|
||||
&client.dst_blocksize, &client.dst_num_blocks);
|
||||
|
||||
if (client.use_xcopy) {
|
||||
cscd_ident_inq(client.dst_iscsi, client.dst_lun,
|
||||
&client.dst_tgt_desig);
|
||||
cscd_param_check(client.dst_iscsi, client.dst_lun,
|
||||
client.dst_blocksize);
|
||||
}
|
||||
|
||||
if (client.src_blocksize != client.dst_blocksize) {
|
||||
@@ -370,7 +680,11 @@ int main(int argc, char *argv[])
|
||||
exit(10);
|
||||
}
|
||||
|
||||
fill_read_queue(&client);
|
||||
if (client.use_xcopy) {
|
||||
fill_xcopy_queue(&client);
|
||||
} else {
|
||||
fill_read_queue(&client);
|
||||
}
|
||||
|
||||
while (client.finished == 0) {
|
||||
pfd[0].fd = iscsi_get_fd(client.src_iscsi);
|
||||
|
||||
@@ -350,10 +350,11 @@ void* iscsi_zmalloc(struct iscsi_context *iscsi, size_t size);
|
||||
void* iscsi_realloc(struct iscsi_context *iscsi, void* ptr, size_t size);
|
||||
void iscsi_free(struct iscsi_context *iscsi, void* ptr);
|
||||
char* iscsi_strdup(struct iscsi_context *iscsi, const char* str);
|
||||
void* iscsi_smalloc(struct iscsi_context *iscsi, size_t size);
|
||||
void* iscsi_szmalloc(struct iscsi_context *iscsi, size_t size);
|
||||
void iscsi_sfree(struct iscsi_context *iscsi, void* ptr);
|
||||
|
||||
unsigned long crc32c(char *buf, int len);
|
||||
uint32_t crc32c(uint8_t *buf, int len);
|
||||
|
||||
struct scsi_task *iscsi_scsi_get_task_from_pdu(struct iscsi_pdu *pdu);
|
||||
|
||||
|
||||
@@ -36,7 +36,7 @@ struct sockaddr;
|
||||
struct scsi_iovec;
|
||||
|
||||
/* API VERSION */
|
||||
#define LIBISCSI_API_VERSION (20160603)
|
||||
#define LIBISCSI_API_VERSION (20170105)
|
||||
|
||||
/* FEATURES */
|
||||
#define LIBISCSI_FEATURE_IOVECTOR (1)
|
||||
@@ -1129,6 +1129,16 @@ iscsi_report_supported_opcodes_task(struct iscsi_context *iscsi, int lun,
|
||||
uint32_t alloc_len,
|
||||
iscsi_command_cb cb, void *private_data);
|
||||
|
||||
EXTERN struct scsi_task *
|
||||
iscsi_receive_copy_results_task(struct iscsi_context *iscsi, int lun,
|
||||
int sa, int list_id, int alloc_len,
|
||||
iscsi_command_cb cb, void *private_data);
|
||||
|
||||
EXTERN struct scsi_task *
|
||||
iscsi_extended_copy_task(struct iscsi_context *iscsi, int lun,
|
||||
struct iscsi_data *param_data,
|
||||
iscsi_command_cb cb, void *private_data);
|
||||
|
||||
/*
|
||||
* Sync commands for SCSI
|
||||
*/
|
||||
@@ -1454,6 +1464,14 @@ iscsi_report_supported_opcodes_sync(struct iscsi_context *iscsi, int lun,
|
||||
int opcode, int sa,
|
||||
uint32_t alloc_len);
|
||||
|
||||
EXTERN struct scsi_task *
|
||||
iscsi_extended_copy_sync(struct iscsi_context *iscsi, int lun,
|
||||
struct iscsi_data *param_data);
|
||||
|
||||
EXTERN struct scsi_task *
|
||||
iscsi_receive_copy_results_sync(struct iscsi_context *iscsi, int lun,
|
||||
int sa, int list_id, int alloc_len);
|
||||
|
||||
/*
|
||||
* These functions are used when the application wants to specify its own buffers to read the data
|
||||
* from the DATA-IN PDUs into, or write the data to DATA-OUT PDUs from.
|
||||
|
||||
@@ -43,7 +43,7 @@
|
||||
/* */
|
||||
/*****************************************************************/
|
||||
|
||||
static unsigned long crctable[256] = {
|
||||
uint32_t crctable[256] = {
|
||||
0x00000000L, 0xF26B8303L, 0xE13B70F7L, 0x1350F3F4L,
|
||||
0xC79A971FL, 0x35F1141CL, 0x26A1E7E8L, 0xD4CA64EBL,
|
||||
0x8AD958CFL, 0x78B2DBCCL, 0x6BE22838L, 0x9989AB3BL,
|
||||
@@ -110,9 +110,9 @@ static unsigned long crctable[256] = {
|
||||
0xBE2DA0A5L, 0x4C4623A6L, 0x5F16D052L, 0xAD7D5351L
|
||||
};
|
||||
|
||||
unsigned long crc32c(char *buf, int len)
|
||||
uint32_t crc32c(uint8_t *buf, int len)
|
||||
{
|
||||
unsigned long crc = 0xffffffff;
|
||||
uint32_t crc = 0xffffffff;
|
||||
while (len-- > 0) {
|
||||
crc = (crc>>8) ^ crctable[(crc ^ (*buf++)) & 0xFF];
|
||||
}
|
||||
|
||||
13
lib/init.c
13
lib/init.c
@@ -111,15 +111,22 @@ char* iscsi_strdup(struct iscsi_context *iscsi, const char* str) {
|
||||
return str2;
|
||||
}
|
||||
|
||||
void* iscsi_szmalloc(struct iscsi_context *iscsi, size_t size) {
|
||||
void* iscsi_smalloc(struct iscsi_context *iscsi, size_t size) {
|
||||
void *ptr;
|
||||
if (size > iscsi->smalloc_size) return NULL;
|
||||
if (iscsi->smalloc_free > 0) {
|
||||
ptr = iscsi->smalloc_ptrs[--iscsi->smalloc_free];
|
||||
memset(ptr, 0, iscsi->smalloc_size);
|
||||
iscsi->smallocs++;
|
||||
} else {
|
||||
ptr = iscsi_zmalloc(iscsi, iscsi->smalloc_size);
|
||||
ptr = iscsi_malloc(iscsi, iscsi->smalloc_size);
|
||||
}
|
||||
return ptr;
|
||||
}
|
||||
|
||||
void* iscsi_szmalloc(struct iscsi_context *iscsi, size_t size) {
|
||||
void *ptr = iscsi_smalloc(iscsi, size);
|
||||
if (ptr) {
|
||||
memset(ptr, 0, size);
|
||||
}
|
||||
return ptr;
|
||||
}
|
||||
|
||||
@@ -2611,6 +2611,52 @@ iscsi_report_supported_opcodes_task(struct iscsi_context *iscsi, int lun,
|
||||
return task;
|
||||
}
|
||||
|
||||
struct scsi_task *
|
||||
iscsi_receive_copy_results_task(struct iscsi_context *iscsi, int lun,
|
||||
int sa, int list_id, int alloc_len,
|
||||
iscsi_command_cb cb, void *private_data)
|
||||
{
|
||||
struct scsi_task *task;
|
||||
|
||||
task = scsi_cdb_receive_copy_results(sa, list_id, alloc_len);
|
||||
if (task == NULL) {
|
||||
iscsi_set_error(iscsi, "Out-of-memory: Failed to create "
|
||||
"RECEIVE COPY RESULTS cdb.");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (iscsi_scsi_command_async(iscsi, lun, task, cb,
|
||||
NULL, private_data) != 0) {
|
||||
scsi_free_scsi_task(task);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return task;
|
||||
}
|
||||
|
||||
struct scsi_task *
|
||||
iscsi_extended_copy_task(struct iscsi_context *iscsi, int lun,
|
||||
struct iscsi_data *param_data,
|
||||
iscsi_command_cb cb, void *private_data)
|
||||
{
|
||||
struct scsi_task *task;
|
||||
|
||||
task = scsi_cdb_extended_copy(param_data->size);
|
||||
if (task == NULL) {
|
||||
iscsi_set_error(iscsi, "Out-of-memory: Failed to create "
|
||||
"EXTENDED COPY cdb.");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (iscsi_scsi_command_async(iscsi, lun, task, cb,
|
||||
param_data, private_data) != 0) {
|
||||
scsi_free_scsi_task(task);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return task;
|
||||
}
|
||||
|
||||
struct scsi_task *
|
||||
iscsi_scsi_get_task_from_pdu(struct iscsi_pdu *pdu)
|
||||
{
|
||||
|
||||
@@ -83,6 +83,10 @@ iscsi_release6_sync
|
||||
iscsi_release6_task
|
||||
iscsi_report_supported_opcodes_sync
|
||||
iscsi_report_supported_opcodes_task
|
||||
iscsi_extended_copy_sync
|
||||
iscsi_extended_copy_task
|
||||
iscsi_receive_copy_results_sync
|
||||
iscsi_receive_copy_results_task
|
||||
iscsi_reconnect
|
||||
iscsi_sanitize_sync
|
||||
iscsi_sanitize_task
|
||||
|
||||
@@ -81,6 +81,10 @@ iscsi_release6_sync
|
||||
iscsi_release6_task
|
||||
iscsi_report_supported_opcodes_sync
|
||||
iscsi_report_supported_opcodes_task
|
||||
iscsi_extended_copy_sync
|
||||
iscsi_extended_copy_task
|
||||
iscsi_receive_copy_results_sync
|
||||
iscsi_receive_copy_results_task
|
||||
iscsi_reconnect
|
||||
iscsi_sanitize_sync
|
||||
iscsi_sanitize_task
|
||||
|
||||
@@ -1217,6 +1217,8 @@ iscsi_process_login_reply(struct iscsi_context *iscsi, struct iscsi_pdu *pdu,
|
||||
must_have_chap_r = 0;
|
||||
}
|
||||
|
||||
ISCSI_LOG(iscsi, 6, "TargetLoginReply: %s", ptr);
|
||||
|
||||
ptr += len + 1;
|
||||
size -= len + 1;
|
||||
}
|
||||
|
||||
15
lib/pdu.c
15
lib/pdu.c
@@ -36,6 +36,7 @@
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <inttypes.h>
|
||||
#include "iscsi.h"
|
||||
#include "iscsi-private.h"
|
||||
#include "scsi-lowlevel.h"
|
||||
@@ -429,6 +430,20 @@ iscsi_process_pdu(struct iscsi_context *iscsi, struct iscsi_in_pdu *in)
|
||||
uint8_t ahslen = in->hdr[4];
|
||||
struct iscsi_pdu *pdu;
|
||||
|
||||
/* verify header checksum */
|
||||
if (iscsi->header_digest != ISCSI_HEADER_DIGEST_NONE) {
|
||||
uint32_t crc, crc_rcvd = 0;
|
||||
crc = crc32c(in->hdr, ISCSI_RAW_HEADER_SIZE);
|
||||
crc_rcvd |= in->hdr[ISCSI_RAW_HEADER_SIZE+0];
|
||||
crc_rcvd |= in->hdr[ISCSI_RAW_HEADER_SIZE+1] << 8;
|
||||
crc_rcvd |= in->hdr[ISCSI_RAW_HEADER_SIZE+2] << 16;
|
||||
crc_rcvd |= in->hdr[ISCSI_RAW_HEADER_SIZE+3] << 24;
|
||||
if (crc != crc_rcvd) {
|
||||
iscsi_set_error(iscsi, "header checksum verification failed: calculated 0x%" PRIx32 " received 0x%" PRIx32, crc, crc_rcvd);
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
if (ahslen != 0) {
|
||||
iscsi_set_error(iscsi, "cant handle expanded headers yet");
|
||||
return -1;
|
||||
|
||||
47
lib/socket.c
47
lib/socket.c
@@ -366,6 +366,7 @@ iscsi_connect_async(struct iscsi_context *iscsi, const char *portal,
|
||||
}
|
||||
|
||||
freeaddrinfo(ai);
|
||||
strncpy(iscsi->connected_portal, portal, MAX_STRING_SIZE);
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -669,6 +670,25 @@ iscsi_read_from_socket(struct iscsi_context *iscsi)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int iscsi_pdu_update_headerdigest(struct iscsi_context *iscsi, struct iscsi_pdu *pdu)
|
||||
{
|
||||
uint32_t crc;
|
||||
|
||||
if (pdu->outdata.size < ISCSI_RAW_HEADER_SIZE + ISCSI_DIGEST_SIZE) {
|
||||
iscsi_set_error(iscsi, "PDU too small (%u) to contain header digest",
|
||||
(unsigned int) pdu->outdata.size);
|
||||
return -1;
|
||||
}
|
||||
|
||||
crc = crc32c(pdu->outdata.data, ISCSI_RAW_HEADER_SIZE);
|
||||
|
||||
pdu->outdata.data[ISCSI_RAW_HEADER_SIZE+3] = (crc >> 24);
|
||||
pdu->outdata.data[ISCSI_RAW_HEADER_SIZE+2] = (crc >> 16);
|
||||
pdu->outdata.data[ISCSI_RAW_HEADER_SIZE+1] = (crc >> 8);
|
||||
pdu->outdata.data[ISCSI_RAW_HEADER_SIZE+0] = (crc);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
iscsi_write_to_socket(struct iscsi_context *iscsi)
|
||||
{
|
||||
@@ -718,7 +738,13 @@ iscsi_write_to_socket(struct iscsi_context *iscsi)
|
||||
|
||||
/* set exp statsn */
|
||||
iscsi_pdu_set_expstatsn(iscsi->outqueue_current, iscsi->statsn + 1);
|
||||
|
||||
|
||||
/* calculate header checksum */
|
||||
if (iscsi->header_digest != ISCSI_HEADER_DIGEST_NONE &&
|
||||
iscsi_pdu_update_headerdigest(iscsi, iscsi->outqueue_current) != 0) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
ISCSI_LIST_REMOVE(&iscsi->outqueue, iscsi->outqueue_current);
|
||||
if (!(iscsi->outqueue_current->flags & ISCSI_PDU_DELETE_WHEN_SENT)) {
|
||||
/* we have to add the pdu to the waitqueue already here
|
||||
@@ -948,23 +974,6 @@ static int iscsi_tcp_queue_pdu(struct iscsi_context *iscsi,
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (iscsi->header_digest != ISCSI_HEADER_DIGEST_NONE) {
|
||||
unsigned long crc;
|
||||
|
||||
if (pdu->outdata.size < ISCSI_RAW_HEADER_SIZE + 4) {
|
||||
iscsi_set_error(iscsi, "PDU too small (%u) to contain header digest",
|
||||
(unsigned int) pdu->outdata.size);
|
||||
return -1;
|
||||
}
|
||||
|
||||
crc = crc32c((char *)pdu->outdata.data, ISCSI_RAW_HEADER_SIZE);
|
||||
|
||||
pdu->outdata.data[ISCSI_RAW_HEADER_SIZE+3] = (crc >> 24)&0xff;
|
||||
pdu->outdata.data[ISCSI_RAW_HEADER_SIZE+2] = (crc >> 16)&0xff;
|
||||
pdu->outdata.data[ISCSI_RAW_HEADER_SIZE+1] = (crc >> 8)&0xff;
|
||||
pdu->outdata.data[ISCSI_RAW_HEADER_SIZE+0] = (crc) &0xff;
|
||||
}
|
||||
|
||||
iscsi_add_to_outqueue(iscsi, pdu);
|
||||
|
||||
return 0;
|
||||
@@ -973,7 +982,7 @@ static int iscsi_tcp_queue_pdu(struct iscsi_context *iscsi,
|
||||
void
|
||||
iscsi_free_iscsi_in_pdu(struct iscsi_context *iscsi, struct iscsi_in_pdu *in)
|
||||
{
|
||||
iscsi_free(iscsi, in->hdr);
|
||||
iscsi_sfree(iscsi, in->hdr);
|
||||
iscsi_free(iscsi, in->data);
|
||||
in->data=NULL;
|
||||
iscsi_sfree(iscsi, in);
|
||||
|
||||
40
lib/sync.c
40
lib/sync.c
@@ -1667,6 +1667,46 @@ iscsi_report_supported_opcodes_sync(struct iscsi_context *iscsi, int lun,
|
||||
return state.task;
|
||||
}
|
||||
|
||||
struct scsi_task *
|
||||
iscsi_receive_copy_results_sync(struct iscsi_context *iscsi, int lun,
|
||||
int sa, int list_id, int alloc_len)
|
||||
{
|
||||
struct iscsi_sync_state state;
|
||||
|
||||
memset(&state, 0, sizeof(state));
|
||||
|
||||
if (iscsi_receive_copy_results_task(iscsi, lun, sa, list_id, alloc_len,
|
||||
scsi_sync_cb, &state) == NULL) {
|
||||
iscsi_set_error(iscsi, "Failed to send RECEIVE COPY RESULTS"
|
||||
" command");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
event_loop(iscsi, &state);
|
||||
|
||||
return state.task;
|
||||
}
|
||||
|
||||
struct scsi_task *
|
||||
iscsi_extended_copy_sync(struct iscsi_context *iscsi, int lun,
|
||||
struct iscsi_data *param_data)
|
||||
{
|
||||
struct iscsi_sync_state state;
|
||||
|
||||
memset(&state, 0, sizeof(state));
|
||||
|
||||
if (iscsi_extended_copy_task(iscsi, lun, param_data,
|
||||
scsi_sync_cb, &state) == NULL) {
|
||||
iscsi_set_error(iscsi, "Failed to send EXTENDED COPY"
|
||||
" command");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
event_loop(iscsi, &state);
|
||||
|
||||
return state.task;
|
||||
}
|
||||
|
||||
struct scsi_task *
|
||||
iscsi_scsi_command_sync(struct iscsi_context *iscsi, int lun,
|
||||
struct scsi_task *task, struct iscsi_data *data)
|
||||
|
||||
@@ -4,7 +4,8 @@ AM_CFLAGS = $(WARN_CFLAGS)
|
||||
LDADD = ../lib/libiscsi.la
|
||||
|
||||
noinst_PROGRAMS = prog_reconnect prog_reconnect_timeout prog_noop_reply \
|
||||
prog_readwrite_iov prog_timeout prog_read_all_pdus
|
||||
prog_readwrite_iov prog_timeout prog_read_all_pdus \
|
||||
prog_header_digest
|
||||
|
||||
T = `ls test_*.sh`
|
||||
|
||||
|
||||
@@ -28,6 +28,10 @@ shutdown_target() {
|
||||
${TGTADM} --op delete --mode system
|
||||
}
|
||||
|
||||
enable_header_digest() {
|
||||
${TGTADM} --op update --mode target --tid 1 -n HeaderDigest -v CRC32C
|
||||
}
|
||||
|
||||
create_lun() {
|
||||
# Setup LUN
|
||||
truncate --size=100M ${TGTLUN}
|
||||
|
||||
257
tests/prog_header_digest.c
Normal file
257
tests/prog_header_digest.c
Normal file
@@ -0,0 +1,257 @@
|
||||
/* -*- mode:c; tab-width:8; c-basic-offset:8; indent-tabs-mode:nil; -*- */
|
||||
/*
|
||||
Copyright (C) 2015 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/>.
|
||||
*/
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include "config.h"
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_POLL_H
|
||||
#include <poll.h>
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_UNISTD_H
|
||||
#include <unistd.h>
|
||||
#endif
|
||||
|
||||
#include <getopt.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdint.h>
|
||||
#include <string.h>
|
||||
#include <sys/time.h>
|
||||
|
||||
#include "iscsi.h"
|
||||
#include "iscsi-private.h"
|
||||
#include "scsi-lowlevel.h"
|
||||
|
||||
#ifndef discard_const
|
||||
#define discard_const(ptr) ((void *)((intptr_t)(ptr)))
|
||||
#endif
|
||||
|
||||
const char *initiator = "iqn.2007-10.com.github:sahlberg:libiscsi:prog-header-digest";
|
||||
|
||||
struct client_state {
|
||||
int finished;
|
||||
int status;
|
||||
int lun;
|
||||
};
|
||||
|
||||
#define TIMER_START(x) gettimeofday(&x, NULL)
|
||||
#define TIMER_ELAPSED(x, y) do { \
|
||||
struct timeval t; \
|
||||
int wrap = 0; \
|
||||
gettimeofday(&t, NULL); \
|
||||
if (t.tv_usec < x.tv_usec) wrap = 1; \
|
||||
y.tv_sec = t.tv_sec - x.tv_sec - wrap; \
|
||||
y.tv_usec = wrap * 10000000 + t.tv_usec - x.tv_usec; \
|
||||
} while(0)
|
||||
|
||||
void event_loop(struct iscsi_context *iscsi, struct client_state *state,
|
||||
int timeout)
|
||||
{
|
||||
struct pollfd pfd;
|
||||
struct timeval start_time, elapsed_time;
|
||||
|
||||
TIMER_START(start_time);
|
||||
while (state->finished == 0) {
|
||||
pfd.fd = iscsi_get_fd(iscsi);
|
||||
pfd.events = iscsi_which_events(iscsi);
|
||||
|
||||
if (poll(&pfd, 1, 1000) < 0) {
|
||||
fprintf(stderr, "Poll failed");
|
||||
exit(10);
|
||||
}
|
||||
if (iscsi_service(iscsi, pfd.revents) < 0) {
|
||||
fprintf(stderr, "iscsi_service failed with : %s\n",
|
||||
iscsi_get_error(iscsi));
|
||||
exit(10);
|
||||
}
|
||||
TIMER_ELAPSED(start_time, elapsed_time);
|
||||
if (timeout && elapsed_time.tv_sec > timeout) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void tur_cb(struct iscsi_context *iscsi _U_, int status,
|
||||
void *command_data _U_, void *private_data)
|
||||
{
|
||||
struct client_state *state = (struct client_state *)private_data;
|
||||
|
||||
if (status != 0) {
|
||||
fprintf(stderr, "TestUnitReady failed\n");
|
||||
state->status = status;
|
||||
}
|
||||
|
||||
state->finished = 1;
|
||||
}
|
||||
|
||||
void print_usage(void)
|
||||
{
|
||||
fprintf(stderr, "Usage: prog_header_digest [-?|--help] [--usage] "
|
||||
"[-i|--initiator-name=iqn-name]\n"
|
||||
"\t\t<iscsi-portal-url>\n");
|
||||
fprintf(stderr, "\n");
|
||||
fprintf(stderr, "This command is used to test that if the target "
|
||||
"disconnects libiscsi will automatically reconnect and "
|
||||
"re-issue all queued tasks.\n");
|
||||
}
|
||||
|
||||
void print_help(void)
|
||||
{
|
||||
fprintf(stderr, "Usage: prog_header_digest [OPTION...] <iscsi-url>\n");
|
||||
fprintf(stderr, " -i, --initiator-name=iqn-name "
|
||||
"Initiatorname to use\n");
|
||||
fprintf(stderr, "\n");
|
||||
fprintf(stderr, "Help options:\n");
|
||||
fprintf(stderr, " -?, --help "
|
||||
"Show this help message\n");
|
||||
fprintf(stderr, " --usage "
|
||||
"Display brief usage message\n");
|
||||
fprintf(stderr, "\n");
|
||||
fprintf(stderr, "iSCSI Portal URL format : %s\n",
|
||||
ISCSI_PORTAL_URL_SYNTAX);
|
||||
fprintf(stderr, "\n");
|
||||
fprintf(stderr, "<host> is either of:\n");
|
||||
fprintf(stderr, " \"hostname\" iscsi.example\n");
|
||||
fprintf(stderr, " \"ipv4-address\" 10.1.1.27\n");
|
||||
fprintf(stderr, " \"ipv6-address\" [fce0::1]\n");
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
struct iscsi_context *iscsi;
|
||||
struct iscsi_url *iscsi_url = NULL;
|
||||
struct client_state state;
|
||||
const char *url = NULL;
|
||||
int c;
|
||||
static int show_help = 0, show_usage = 0, debug = 0;
|
||||
|
||||
static struct option long_options[] = {
|
||||
{"help", no_argument, NULL, 'h'},
|
||||
{"usage", no_argument, NULL, 'u'},
|
||||
{"debug", no_argument, NULL, 'd'},
|
||||
{"initiator-name", required_argument, NULL, 'i'},
|
||||
{0, 0, 0, 0}
|
||||
};
|
||||
int option_index;
|
||||
|
||||
while ((c = getopt_long(argc, argv, "h?uUdi:s", long_options,
|
||||
&option_index)) != -1) {
|
||||
switch (c) {
|
||||
case 'h':
|
||||
case '?':
|
||||
show_help = 1;
|
||||
break;
|
||||
case 'u':
|
||||
show_usage = 1;
|
||||
break;
|
||||
case 'd':
|
||||
debug = 1;
|
||||
break;
|
||||
case 'i':
|
||||
initiator = optarg;
|
||||
break;
|
||||
default:
|
||||
fprintf(stderr, "Unrecognized option '%c'\n\n", c);
|
||||
print_help();
|
||||
exit(0);
|
||||
}
|
||||
}
|
||||
|
||||
if (show_help != 0) {
|
||||
print_help();
|
||||
exit(0);
|
||||
}
|
||||
|
||||
if (show_usage != 0) {
|
||||
print_usage();
|
||||
exit(0);
|
||||
}
|
||||
|
||||
if (optind != argc -1) {
|
||||
print_usage();
|
||||
exit(0);
|
||||
}
|
||||
|
||||
memset(&state, 0, sizeof(state));
|
||||
|
||||
if (argv[optind] != NULL) {
|
||||
url = strdup(argv[optind]);
|
||||
}
|
||||
if (url == NULL) {
|
||||
fprintf(stderr, "You must specify iscsi target portal.\n");
|
||||
print_usage();
|
||||
exit(10);
|
||||
}
|
||||
|
||||
iscsi = iscsi_create_context(initiator);
|
||||
if (iscsi == NULL) {
|
||||
printf("Failed to create context\n");
|
||||
exit(10);
|
||||
}
|
||||
|
||||
if (debug > 0) {
|
||||
iscsi_set_log_level(iscsi, debug);
|
||||
iscsi_set_log_fn(iscsi, iscsi_log_to_stderr);
|
||||
}
|
||||
|
||||
iscsi_url = iscsi_parse_full_url(iscsi, url);
|
||||
|
||||
if (url) {
|
||||
free(discard_const(url));
|
||||
}
|
||||
|
||||
if (iscsi_url == NULL) {
|
||||
fprintf(stderr, "Failed to parse URL: %s\n",
|
||||
iscsi_get_error(iscsi));
|
||||
exit(10);
|
||||
}
|
||||
|
||||
iscsi_set_session_type(iscsi, ISCSI_SESSION_NORMAL);
|
||||
printf("Enable Header Digest\n");
|
||||
iscsi_set_header_digest(iscsi, ISCSI_HEADER_DIGEST_CRC32C);
|
||||
|
||||
printf("Disable iscsi reconnect on session failure\n");
|
||||
iscsi_set_noautoreconnect(iscsi, 1);
|
||||
|
||||
state.lun = iscsi_url->lun;
|
||||
if (iscsi_full_connect_sync(iscsi, iscsi_url->portal, iscsi_url->lun)
|
||||
!= 0) {
|
||||
fprintf(stderr, "iscsi_connect failed. %s\n",
|
||||
iscsi_get_error(iscsi));
|
||||
exit(10);
|
||||
}
|
||||
|
||||
printf("Verify that the connection works\n");
|
||||
if (iscsi_testunitready_task(iscsi, state.lun,
|
||||
tur_cb, &state) == NULL) {
|
||||
fprintf(stderr, "testunitready failed\n");
|
||||
exit(10);
|
||||
}
|
||||
event_loop(iscsi, &state, 3);
|
||||
|
||||
iscsi_destroy_url(iscsi_url);
|
||||
iscsi_disconnect(iscsi);
|
||||
iscsi_destroy_context(iscsi);
|
||||
|
||||
if (state.status != 0) {
|
||||
exit(10);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
18
tests/test_0500_header_digest.sh
Executable file
18
tests/test_0500_header_digest.sh
Executable file
@@ -0,0 +1,18 @@
|
||||
#!/bin/sh
|
||||
|
||||
. ./functions.sh
|
||||
|
||||
echo "Header Digest tests"
|
||||
|
||||
start_target "nop_interval=1,nop_count=3"
|
||||
enable_header_digest
|
||||
create_lun
|
||||
|
||||
echo -n "Test that we can connect to a target requiring Header Digest ..."
|
||||
./prog_header_digest -i ${IQNINITIATOR} iscsi://${TGTPORTAL}/${IQNTARGET}/1 > /dev/null || failure
|
||||
success
|
||||
|
||||
shutdown_target
|
||||
delete_lun
|
||||
|
||||
exit 0
|
||||
Reference in New Issue
Block a user