From 2b9abd9ac8f93752d76a55c6ebe3c9d3b0889b2f Mon Sep 17 00:00:00 2001 From: David Disseldorp Date: Wed, 4 Jan 2017 18:22:43 +0100 Subject: [PATCH 01/15] examples/dd: validate block count and max in flight params Signed-off-by: David Disseldorp --- examples/iscsi-dd.c | 25 +++++++++++++++++++------ 1 file changed, 19 insertions(+), 6 deletions(-) diff --git a/examples/iscsi-dd.c b/examples/iscsi-dd.c index 998477e..de029b5 100644 --- a/examples/iscsi-dd.c +++ b/examples/iscsi-dd.c @@ -23,16 +23,17 @@ #include #include #include +#include #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; @@ -153,7 +154,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; @@ -214,6 +215,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; @@ -231,10 +234,20 @@ int main(int argc, char *argv[]) client.use_16_for_rw = 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; From f475436c5abf4312779f6083e6c76dfe05fb095e Mon Sep 17 00:00:00 2001 From: David Disseldorp Date: Tue, 3 Jan 2017 19:33:01 +0100 Subject: [PATCH 02/15] Libiscsi: add support for RECEIVE COPY RESULTS Build on existing scsi_cdb_receive_copy_results() functionality. This request is most commonly used to determine target-side EXTENDED COPY operational parameters. Signed-off-by: David Disseldorp --- include/iscsi.h | 9 +++++++++ lib/iscsi-command.c | 23 +++++++++++++++++++++++ lib/libiscsi.def | 2 ++ lib/libiscsi.syms | 2 ++ lib/sync.c | 20 ++++++++++++++++++++ 5 files changed, 56 insertions(+) diff --git a/include/iscsi.h b/include/iscsi.h index 30917a0..58385de 100644 --- a/include/iscsi.h +++ b/include/iscsi.h @@ -1129,6 +1129,11 @@ 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); + /* * Sync commands for SCSI */ @@ -1454,6 +1459,10 @@ iscsi_report_supported_opcodes_sync(struct iscsi_context *iscsi, int lun, int opcode, int sa, uint32_t alloc_len); +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. diff --git a/lib/iscsi-command.c b/lib/iscsi-command.c index 9f8c0e8..3375974 100644 --- a/lib/iscsi-command.c +++ b/lib/iscsi-command.c @@ -2611,6 +2611,29 @@ 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_scsi_get_task_from_pdu(struct iscsi_pdu *pdu) { diff --git a/lib/libiscsi.def b/lib/libiscsi.def index 81ebe60..3bf1fd8 100644 --- a/lib/libiscsi.def +++ b/lib/libiscsi.def @@ -83,6 +83,8 @@ iscsi_release6_sync iscsi_release6_task iscsi_report_supported_opcodes_sync iscsi_report_supported_opcodes_task +iscsi_receive_copy_results_sync +iscsi_receive_copy_results_task iscsi_reconnect iscsi_sanitize_sync iscsi_sanitize_task diff --git a/lib/libiscsi.syms b/lib/libiscsi.syms index b3f3cac..946dc2c 100644 --- a/lib/libiscsi.syms +++ b/lib/libiscsi.syms @@ -81,6 +81,8 @@ iscsi_release6_sync iscsi_release6_task iscsi_report_supported_opcodes_sync iscsi_report_supported_opcodes_task +iscsi_receive_copy_results_sync +iscsi_receive_copy_results_task iscsi_reconnect iscsi_sanitize_sync iscsi_sanitize_task diff --git a/lib/sync.c b/lib/sync.c index 9fb78e7..c5a82a6 100644 --- a/lib/sync.c +++ b/lib/sync.c @@ -1667,6 +1667,26 @@ 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_scsi_command_sync(struct iscsi_context *iscsi, int lun, struct scsi_task *task, struct iscsi_data *data) From 4bc5f962e2f0655734d0cc9cfe327d2800b7ac79 Mon Sep 17 00:00:00 2001 From: David Disseldorp Date: Tue, 3 Jan 2017 23:58:20 +0100 Subject: [PATCH 03/15] Libiscsi: add support for EXTENDED COPY Build on existing scsi_cdb_extended_copy() functionality. This operation can be used to offload inter and intra LU copies to the target. The API is rather primitive, in that the caller needs to construct the parameter buffer, including CSCD and segment descriptor lists, etc. Signed-off-by: David Disseldorp --- include/iscsi.h | 9 +++++++++ lib/iscsi-command.c | 23 +++++++++++++++++++++++ lib/libiscsi.def | 2 ++ lib/libiscsi.syms | 2 ++ lib/sync.c | 20 ++++++++++++++++++++ 5 files changed, 56 insertions(+) diff --git a/include/iscsi.h b/include/iscsi.h index 58385de..cb8a557 100644 --- a/include/iscsi.h +++ b/include/iscsi.h @@ -1134,6 +1134,11 @@ 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 */ @@ -1459,6 +1464,10 @@ 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); diff --git a/lib/iscsi-command.c b/lib/iscsi-command.c index 3375974..f9ca8e0 100644 --- a/lib/iscsi-command.c +++ b/lib/iscsi-command.c @@ -2634,6 +2634,29 @@ iscsi_receive_copy_results_task(struct iscsi_context *iscsi, int lun, 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) { diff --git a/lib/libiscsi.def b/lib/libiscsi.def index 3bf1fd8..ea5c940 100644 --- a/lib/libiscsi.def +++ b/lib/libiscsi.def @@ -83,6 +83,8 @@ 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 diff --git a/lib/libiscsi.syms b/lib/libiscsi.syms index 946dc2c..5d81f44 100644 --- a/lib/libiscsi.syms +++ b/lib/libiscsi.syms @@ -81,6 +81,8 @@ 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 diff --git a/lib/sync.c b/lib/sync.c index c5a82a6..3b907c0 100644 --- a/lib/sync.c +++ b/lib/sync.c @@ -1687,6 +1687,26 @@ iscsi_receive_copy_results_sync(struct iscsi_context *iscsi, int lun, 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) From aaeb2dd5a5c19cadbef3a683d8a1b4e5b847442f Mon Sep 17 00:00:00 2001 From: David Disseldorp Date: Mon, 2 Jan 2017 01:15:18 +0100 Subject: [PATCH 04/15] examples/dd: add XCOPY support EXTENDED COPY can be triggered with the new --xcopy/-x parameter. When invoked, (--max) EXTENDED COPY requests are dispatched in parallel, with each request attempting to copy (--blocks) from source to destination. Signed-off-by: David Disseldorp --- examples/iscsi-dd.c | 315 +++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 314 insertions(+), 1 deletion(-) diff --git a/examples/iscsi-dd.c b/examples/iscsi-dd.c index de029b5..11d6e15 100644 --- a/examples/iscsi-dd.c +++ b/examples/iscsi-dd.c @@ -39,19 +39,23 @@ struct client { 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; @@ -186,6 +190,293 @@ 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); +} + int main(int argc, char *argv[]) { char *src_url = NULL; @@ -204,6 +495,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'}, @@ -233,6 +525,9 @@ 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 = strtoul(optarg, &endptr, 10); if (*endptr != '\0' || max_in_flight == UINT_MAX) { @@ -321,6 +616,13 @@ int main(int argc, char *argv[]) scsi_free_scsi_task(task); } + 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); if (client.dst_iscsi == NULL) { fprintf(stderr, "Failed to create context\n"); @@ -373,6 +675,13 @@ int main(int argc, char *argv[]) scsi_free_scsi_task(task); } + 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) { fprintf(stderr, "source LUN has different blocksize than destination than destination (%d != %d sectors)\n", client.src_blocksize, client.dst_blocksize); exit(10); @@ -383,7 +692,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); From 30a49f97cba7d0cbdf18ca5a10d6650341dc1bfc Mon Sep 17 00:00:00 2001 From: David Disseldorp Date: Mon, 2 Jan 2017 18:41:15 +0100 Subject: [PATCH 05/15] examples/dd: split out duplicate read capacity code Use a helper function, rather than having duplicate source and destination READ CAPACITY logic. Signed-off-by: David Disseldorp --- examples/iscsi-dd.c | 110 ++++++++++++++++++++------------------------ 1 file changed, 49 insertions(+), 61 deletions(-) diff --git a/examples/iscsi-dd.c b/examples/iscsi-dd.c index 11d6e15..cd09650 100644 --- a/examples/iscsi-dd.c +++ b/examples/iscsi-dd.c @@ -477,14 +477,56 @@ void cscd_param_check(struct iscsi_context *iscsi, 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; @@ -586,35 +628,8 @@ 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, @@ -645,35 +660,8 @@ 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, From b7ab5d05f1090fd4ab7da89259aa078bc16fe0ae Mon Sep 17 00:00:00 2001 From: Ronnie Sahlberg Date: Wed, 4 Jan 2017 18:47:18 -0800 Subject: [PATCH 06/15] TEST: Add a test to verify that HeaderDigest works Signed-off-by: Ronnie Sahlberg --- tests/Makefile.am | 3 +- tests/functions.sh | 4 + tests/prog_header_digest.c | 257 +++++++++++++++++++++++++++++++ tests/test_0500_header_digest.sh | 18 +++ 4 files changed, 281 insertions(+), 1 deletion(-) create mode 100644 tests/prog_header_digest.c create mode 100755 tests/test_0500_header_digest.sh diff --git a/tests/Makefile.am b/tests/Makefile.am index 24abef2..ae99a1b 100644 --- a/tests/Makefile.am +++ b/tests/Makefile.am @@ -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` diff --git a/tests/functions.sh b/tests/functions.sh index 34adbe8..066d7c5 100755 --- a/tests/functions.sh +++ b/tests/functions.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} diff --git a/tests/prog_header_digest.c b/tests/prog_header_digest.c new file mode 100644 index 0000000..4f39191 --- /dev/null +++ b/tests/prog_header_digest.c @@ -0,0 +1,257 @@ +/* -*- mode:c; tab-width:8; c-basic-offset:8; indent-tabs-mode:nil; -*- */ +/* + Copyright (C) 2015 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 . +*/ +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#ifdef HAVE_POLL_H +#include +#endif + +#ifdef HAVE_UNISTD_H +#include +#endif + +#include +#include +#include +#include +#include +#include + +#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\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...] \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, " 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; +} + diff --git a/tests/test_0500_header_digest.sh b/tests/test_0500_header_digest.sh new file mode 100755 index 0000000..d67cbd3 --- /dev/null +++ b/tests/test_0500_header_digest.sh @@ -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 From c68d2c0ddb1739b8c589e329e624fb5032513159 Mon Sep 17 00:00:00 2001 From: Peter Lieven Date: Tue, 3 Jan 2017 12:03:07 +0100 Subject: [PATCH 07/15] init: introduce iscsi_smalloc Signed-off-by: Peter Lieven --- include/iscsi-private.h | 1 + lib/init.c | 13 ++++++++++--- 2 files changed, 11 insertions(+), 3 deletions(-) diff --git a/include/iscsi-private.h b/include/iscsi-private.h index b91af60..60d216e 100644 --- a/include/iscsi-private.h +++ b/include/iscsi-private.h @@ -351,6 +351,7 @@ 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); diff --git a/lib/init.c b/lib/init.c index 9941ceb..1b063cd 100644 --- a/lib/init.c +++ b/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; } From 1a552a8afa06b28fb68114989c30bab38429ddb6 Mon Sep 17 00:00:00 2001 From: Peter Lieven Date: Thu, 5 Jan 2017 12:19:07 +0100 Subject: [PATCH 08/15] socket: do not zero header of incoming PDU we overwrite it anyway Signed-off-by: Peter Lieven --- lib/socket.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/socket.c b/lib/socket.c index 036b4f4..7ab6be8 100644 --- a/lib/socket.c +++ b/lib/socket.c @@ -570,7 +570,7 @@ iscsi_read_from_socket(struct iscsi_context *iscsi) if (iscsi->incoming == NULL) { iscsi->incoming = iscsi_szmalloc(iscsi, sizeof(struct iscsi_in_pdu)); - iscsi->incoming->hdr = iscsi_szmalloc(iscsi, ISCSI_RAW_HEADER_SIZE + ISCSI_DIGEST_SIZE); + iscsi->incoming->hdr = iscsi_smalloc(iscsi, ISCSI_HEADER_SIZE); if (iscsi->incoming == NULL) { iscsi_set_error(iscsi, "Out-of-memory: failed to malloc iscsi_in_pdu"); return -1; From ed1ed27ddedf63e37904428491271ff5426a2c76 Mon Sep 17 00:00:00 2001 From: Peter Lieven Date: Tue, 3 Jan 2017 12:19:12 +0100 Subject: [PATCH 09/15] socket: return in->hdr to smalloc pool commit bc64420 introduced an extra smalloc for the in->hdr, however it did use iscsi_free instead of iscsi_sfree to free it. Signed-off-by: Peter Lieven --- lib/socket.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/socket.c b/lib/socket.c index 7ab6be8..aa25059 100644 --- a/lib/socket.c +++ b/lib/socket.c @@ -979,7 +979,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); From 23738bf1c3ecab68d59e1666911e9a5b9ca35c73 Mon Sep 17 00:00:00 2001 From: Peter Lieven Date: Thu, 5 Jan 2017 12:28:37 +0100 Subject: [PATCH 10/15] socket: calculate header checksum at the right place we mangled the PDU header after calculating the checksum which effectively broke CRC32C header digests completely. Signed-off-by: Peter Lieven --- lib/socket.c | 44 ++++++++++++++++++++++++++------------------ 1 file changed, 26 insertions(+), 18 deletions(-) diff --git a/lib/socket.c b/lib/socket.c index aa25059..caf51a5 100644 --- a/lib/socket.c +++ b/lib/socket.c @@ -675,6 +675,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) +{ + unsigned long 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((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; + return 0; +} + static int iscsi_write_to_socket(struct iscsi_context *iscsi) { @@ -724,7 +743,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 @@ -954,23 +979,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; From 55eacac42544a8e455aa29ba9fa3952e494c1477 Mon Sep 17 00:00:00 2001 From: Peter Lieven Date: Thu, 5 Jan 2017 12:33:47 +0100 Subject: [PATCH 11/15] login: add logging for the negotiation login parameters Signed-off-by: Peter Lieven --- lib/login.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/lib/login.c b/lib/login.c index dd0e7ab..36de085 100644 --- a/lib/login.c +++ b/lib/login.c @@ -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; } From 443b104833718d130f94a0fa3e2cc8bdd6d02984 Mon Sep 17 00:00:00 2001 From: Peter Lieven Date: Thu, 5 Jan 2017 14:39:15 +0100 Subject: [PATCH 12/15] crc32c: use uint_t types Signed-off-by: Peter Lieven --- include/iscsi-private.h | 2 +- lib/crc32c.c | 6 +++--- lib/socket.c | 12 ++++++------ 3 files changed, 10 insertions(+), 10 deletions(-) diff --git a/include/iscsi-private.h b/include/iscsi-private.h index 60d216e..b9f38f7 100644 --- a/include/iscsi-private.h +++ b/include/iscsi-private.h @@ -355,7 +355,7 @@ 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); diff --git a/lib/crc32c.c b/lib/crc32c.c index 0ae2cda..4455171 100644 --- a/lib/crc32c.c +++ b/lib/crc32c.c @@ -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]; } diff --git a/lib/socket.c b/lib/socket.c index caf51a5..3b32ba2 100644 --- a/lib/socket.c +++ b/lib/socket.c @@ -677,7 +677,7 @@ iscsi_read_from_socket(struct iscsi_context *iscsi) static int iscsi_pdu_update_headerdigest(struct iscsi_context *iscsi, struct iscsi_pdu *pdu) { - unsigned long crc; + 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", @@ -685,12 +685,12 @@ static int iscsi_pdu_update_headerdigest(struct iscsi_context *iscsi, struct isc return -1; } - crc = crc32c((char *)pdu->outdata.data, ISCSI_RAW_HEADER_SIZE); + crc = crc32c(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; + 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; } From 1cbeec6bdc951a2a4261d7060e286d6dc38dfa01 Mon Sep 17 00:00:00 2001 From: Peter Lieven Date: Thu, 5 Jan 2017 14:41:21 +0100 Subject: [PATCH 13/15] pdu: verify header digest we never verified the received header digest. do that now. Signed-off-by: Peter Lieven --- lib/pdu.c | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/lib/pdu.c b/lib/pdu.c index 1275b33..63adcc6 100644 --- a/lib/pdu.c +++ b/lib/pdu.c @@ -36,6 +36,7 @@ #include #include #include +#include #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; From 0225c662d0e1e7d9c75c219f98d583aee7a380e4 Mon Sep 17 00:00:00 2001 From: Peter Lieven Date: Thu, 5 Jan 2017 14:47:51 +0100 Subject: [PATCH 14/15] socket: restore connected_portal info this got lost in commit 0d6362f Signed-off-by: Peter Lieven --- lib/socket.c | 1 + 1 file changed, 1 insertion(+) diff --git a/lib/socket.c b/lib/socket.c index 3b32ba2..8eee56d 100644 --- a/lib/socket.c +++ b/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; } From 8784a2b65f6e3a3235eb6ff40f99fc77a971c3ef Mon Sep 17 00:00:00 2001 From: Ronnie Sahlberg Date: Thu, 5 Jan 2017 19:33:40 -0800 Subject: [PATCH 15/15] Bump API version Signed-off-by: Ronnie Sahlberg --- include/iscsi.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/iscsi.h b/include/iscsi.h index cb8a557..9b2b85e 100644 --- a/include/iscsi.h +++ b/include/iscsi.h @@ -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)