diff --git a/COPYING b/COPYING index dddde82..ac8d5bf 100644 --- a/COPYING +++ b/COPYING @@ -15,16 +15,8 @@ A copy of LGPL 2.1 is included in the file LICENCE-LGPL-2.1.txt but can also be This is the licence that applies to the libiscsi library and its use. -src/ld_iscsi.c -============== -This LD_PRELOAD utility is licenced under GNU Lesser General Public License -as published by the Free Software Foundation; either version 2.1 of the -License, or (at your option) any later version. - - - -The src, examples and test-tool directories EXCEPT src/ld_iscsi.c -============================================================= +The src, examples and test-tool directories +=========================================== The utility and example applications using this library, i.e. the src and the examples directories, are licenced under the GNU General Public License as published by the Free Software Foundation; either version 2 of the diff --git a/Makefile.am b/Makefile.am index bd2ca58..abefb53 100644 --- a/Makefile.am +++ b/Makefile.am @@ -29,5 +29,5 @@ pkgconfig_DATA = libiscsi.pc iscsi_includedir = $(includedir)/iscsi dist_iscsi_include_HEADERS = include/iscsi.h include/scsi-lowlevel.h dist_noinst_HEADERS = include/iscsi-private.h include/md5.h include/slist.h \ - include/iser-private.h + include/iser-private.h include/utils.h diff --git a/README.md b/README.md index f5d4f91..0886e58 100644 --- a/README.md +++ b/README.md @@ -46,6 +46,7 @@ Username and password for bidirectional CHAP authentication: target_user= target_password= header_digest= +data_digest= Transport: iser @@ -126,6 +127,15 @@ be overridden by an application by calling iscsi_set_header_digest() if the application wants to force a specific setting. +Data Digest +=========== + +Libiscsi supports DataDigest. By default, libiscsi will offer None so that +Data digest will not be used, no matter what the target setting is. This can +be overridden by an application by calling iscsi_set_data_digest() if the +application wants to force a specific setting. + + Patches ======= diff --git a/configure.ac b/configure.ac index c81b916..13ae3bb 100644 --- a/configure.ac +++ b/configure.ac @@ -78,8 +78,43 @@ AM_CONDITIONAL([BUILD_EXAMPLES], AC_CONFIG_HEADERS([config.h]) -AC_CHECK_LIB([gcrypt], [gcry_control]) -AM_CONDITIONAL([HAVE_LIBGCRYPT], [test $ac_cv_lib_gcrypt_gcry_control = yes]) +AC_ARG_WITH([gnutls], + [AS_HELP_STRING([--with-gnutls], + [Use gnutls to compute MD5])], + [WITH_GNUTLS=$withval], + [WITH_GNUTLS=auto]) + +AC_ARG_WITH([libgcrypt], + [AS_HELP_STRING([--with-libgcrypt], + [Use libgcrypt to compute MD5])], + [WITH_LIBGCRYPT=$withval], + [WITH_LIBGCRYPT=auto]) + +if test "$WITH_GNUTLS" != no; then + AC_CHECK_LIB([gnutls], [gnutls_hash_init]) + if test "$WITH_GNUTLS" = yes && test "$ac_cv_lib_gnutls_gnutls_hash_init" != yes; then + AC_MSG_ERROR([gnutls not found]) + fi + WITH_GNUTLS=$ac_cv_lib_gnutls_gnutls_hash_init +fi +if test "$WITH_GNUTLS" = yes; then + WITH_LIBGCRYPT=no +fi + +if test "$WITH_LIBGCRYPT" != no; then + AC_CHECK_LIB([gcrypt], [gcry_control]) + if test "$WITH_LIBGCRYPT" = yes && test "$ac_cv_lib_gcrypt_gcry_control" != yes; then + AC_MSG_ERROR([libgcrypt not found]) + fi + WITH_LIBGCRYPT=$ac_cv_lib_gcrypt_gcry_control +fi + +NEED_MD5=no +if test "$WITH_GNUTLS" = no && test "$WITH_LIBGCRYPT" = no; then + NEED_MD5=yes +fi +AM_CONDITIONAL([NEED_MD5], + [expr "$NEED_MD5" : yes > /dev/null 2>&1]) # For MinGW. AC_CHECK_LIB([ws2_32], [gethostbyname]) diff --git a/include/iscsi-private.h b/include/iscsi-private.h index 82a328f..4a06658 100644 --- a/include/iscsi-private.h +++ b/include/iscsi-private.h @@ -61,6 +61,13 @@ struct iscsi_in_pdu { long long data_pos; unsigned char *data; + + /* + * Some data structures wrt Data Digest (if negociated) + */ + unsigned char data_digest_buf[ISCSI_DIGEST_SIZE]; + int received_data_digest_bytes; + uint32_t calculated_data_digest; }; void iscsi_free_iscsi_in_pdu(struct iscsi_context *iscsi, struct iscsi_in_pdu *in); @@ -105,6 +112,8 @@ struct iscsi_context { uint32_t statsn; enum iscsi_header_digest want_header_digest; enum iscsi_header_digest header_digest; + enum iscsi_data_digest want_data_digest; + enum iscsi_data_digest data_digest; int fd; int is_connected; @@ -272,6 +281,8 @@ struct iscsi_pdu { struct iscsi_scsi_cbdata scsi_cbdata; time_t scsi_timeout; uint32_t expxferlen; + + uint32_t calculated_data_digest; }; struct iscsi_pdu *iscsi_allocate_pdu(struct iscsi_context *iscsi, @@ -293,6 +304,7 @@ void iscsi_pdu_set_ritt(struct iscsi_pdu *pdu, uint32_t ritt); void iscsi_pdu_set_datasn(struct iscsi_pdu *pdu, uint32_t datasn); void iscsi_pdu_set_bufferoffset(struct iscsi_pdu *pdu, uint32_t bufferoffset); void iscsi_cancel_pdus(struct iscsi_context *iscsi); +void iscsi_cancel_lun_pdus(struct iscsi_context *iscsi, uint32_t lun); int iscsi_pdu_add_data(struct iscsi_context *iscsi, struct iscsi_pdu *pdu, const unsigned char *dptr, int dsize); int iscsi_queue_pdu(struct iscsi_context *iscsi, struct iscsi_pdu *pdu); @@ -350,6 +362,9 @@ void* iscsi_szmalloc(struct iscsi_context *iscsi, size_t size); void iscsi_sfree(struct iscsi_context *iscsi, void* ptr); uint32_t crc32c(uint8_t *buf, int len); +void crc32c_init(uint32_t *crc_ptr); +uint32_t crc32c_chain(uint32_t crc, uint8_t *buf, int len); +uint32_t crc32c_chain_done(uint32_t crc); struct scsi_task *iscsi_scsi_get_task_from_pdu(struct iscsi_pdu *pdu); diff --git a/include/iscsi.h b/include/iscsi.h index 8196f64..af557e7 100644 --- a/include/iscsi.h +++ b/include/iscsi.h @@ -335,6 +335,29 @@ enum iscsi_header_digest { EXTERN int iscsi_set_header_digest(struct iscsi_context *iscsi, enum iscsi_header_digest header_digest); +/* + * Types of data digest we support. Default is NONE + */ +enum iscsi_data_digest { + ISCSI_DATA_DIGEST_NONE = 0, + ISCSI_DATA_DIGEST_NONE_CRC32C = 1, + ISCSI_DATA_DIGEST_CRC32C_NONE = 2, + ISCSI_DATA_DIGEST_CRC32C = 3, + ISCSI_DATA_DIGEST_LAST = ISCSI_DATA_DIGEST_CRC32C +}; + +/* + * Set the desired data digest for a scsi context. + * Data digest can only be set/changed before the context + * is logged in to the target. + * + * Returns: + * 0: success + * <0: error + */ +EXTERN int iscsi_set_data_digest(struct iscsi_context *iscsi, + enum iscsi_data_digest data_digest); + /* * Specify the username and password to use for chap authentication */ diff --git a/lib/Makefile.am b/lib/Makefile.am index b81fd3d..ba6aaee 100644 --- a/lib/Makefile.am +++ b/lib/Makefile.am @@ -12,7 +12,7 @@ if TARGET_OS_IS_WIN32 libiscsipriv_la_SOURCES += ../win32/win32_compat.c endif -if !HAVE_LIBGCRYPT +if NEED_MD5 libiscsipriv_la_SOURCES += md5.c endif @@ -45,7 +45,7 @@ endif EXTRA_libiscsi_la_DEPENDENCIES = libiscsi.syms -SOCURRENT=9 +SOCURRENT=10 SOREVISON=0 SOAGE=0 libiscsi_la_LDFLAGS = \ diff --git a/lib/connect.c b/lib/connect.c index 5234e46..1960ef2 100644 --- a/lib/connect.c +++ b/lib/connect.c @@ -431,6 +431,7 @@ static int reconnect(struct iscsi_context *iscsi, int force) iscsi_set_targetname(tmp_iscsi, iscsi->target_name); iscsi_set_header_digest(tmp_iscsi, iscsi->want_header_digest); + iscsi_set_data_digest(tmp_iscsi, iscsi->want_data_digest); iscsi_set_initiator_username_pwd(tmp_iscsi, iscsi->user, iscsi->passwd); iscsi_set_target_username_pwd(tmp_iscsi, iscsi->target_user, iscsi->target_passwd); diff --git a/lib/crc32c.c b/lib/crc32c.c index 10180ba..313cad0 100644 --- a/lib/crc32c.c +++ b/lib/crc32c.c @@ -118,3 +118,22 @@ uint32_t crc32c(uint8_t *buf, int len) return crc^0xffffffff; } +void crc32c_init(uint32_t *crc_ptr) +{ + if (crc_ptr) + *crc_ptr = 0xffffffff; +} + +uint32_t crc32c_chain(uint32_t crc, uint8_t *buf, int len) +{ + while (len-- > 0) { + crc = (crc>>8) ^ crctable[(crc ^ (*buf++)) & 0xFF]; + } + return crc; +} + +uint32_t crc32c_chain_done(uint32_t crc) +{ + return crc^0xffffffff; +} + diff --git a/lib/init.c b/lib/init.c index 6bcc761..cb3baeb 100644 --- a/lib/init.c +++ b/lib/init.c @@ -244,6 +244,7 @@ iscsi_create_context(const char *initiator_name) iscsi->want_immediate_data = ISCSI_IMMEDIATE_DATA_YES; iscsi->use_immediate_data = ISCSI_IMMEDIATE_DATA_YES; iscsi->want_header_digest = ISCSI_HEADER_DIGEST_NONE_CRC32C; + iscsi->want_data_digest = ISCSI_DATA_DIGEST_NONE; iscsi->tcp_keepcnt=3; iscsi->tcp_keepintvl=30; @@ -492,6 +493,25 @@ iscsi_set_header_digest(struct iscsi_context *iscsi, return 0; } +int +iscsi_set_data_digest(struct iscsi_context *iscsi, + enum iscsi_data_digest data_digest) +{ + if (iscsi->is_loggedin) { + iscsi_set_error(iscsi, "trying to set data digest while " + "logged in"); + return -1; + } + if ((unsigned)data_digest > ISCSI_DATA_DIGEST_LAST) { + iscsi_set_error(iscsi, "invalid data digest value"); + return -1; + } + + iscsi->want_data_digest = data_digest; + + return 0; +} + int iscsi_is_logged_in(struct iscsi_context *iscsi) { @@ -602,19 +622,32 @@ iscsi_parse_url(struct iscsi_context *iscsi, const char *url, int full) if (value != NULL) { *value++ = 0; } - if (!strcmp(key, "header_digest")) { - if (!strcmp(value, "crc32c")) { - iscsi_set_header_digest( - iscsi, ISCSI_HEADER_DIGEST_CRC32C); - } else if (!strcmp(value, "none")) { - iscsi_set_header_digest( - iscsi, ISCSI_HEADER_DIGEST_NONE); - } else { - iscsi_set_error(iscsi, - "Invalid URL argument for header_digest: %s", value); - return NULL; - } - } + if (!strcmp(key, "header_digest")) { + if (!strcmp(value, "crc32c")) { + iscsi_set_header_digest( + iscsi, ISCSI_HEADER_DIGEST_CRC32C); + } else if (!strcmp(value, "none")) { + iscsi_set_header_digest( + iscsi, ISCSI_HEADER_DIGEST_NONE); + } else { + iscsi_set_error(iscsi, + "Invalid URL argument for header_digest: %s", value); + return NULL; + } + } + if (!strcmp(key, "data_digest")) { + if (!strcmp(value, "crc32c")) { + iscsi_set_data_digest( + iscsi, ISCSI_DATA_DIGEST_CRC32C); + } else if (!strcmp(value, "none")) { + iscsi_set_data_digest( + iscsi, ISCSI_DATA_DIGEST_NONE); + } else { + iscsi_set_error(iscsi, + "Invalid URL argument for data_digest: %s", value); + return NULL; + } + } if (!strcmp(key, "target_user")) { target_user = value; } else if (!strcmp(key, "target_password")) { diff --git a/lib/libiscsi.def b/lib/libiscsi.def index c56fe59..6b59726 100644 --- a/lib/libiscsi.def +++ b/lib/libiscsi.def @@ -115,6 +115,7 @@ iscsi_set_initial_r2t iscsi_set_log_level iscsi_set_log_fn iscsi_set_header_digest +iscsi_set_data_digest iscsi_set_initiator_username_pwd iscsi_set_isid_en iscsi_set_isid_oui diff --git a/lib/libiscsi.syms.in b/lib/libiscsi.syms.in index 2eb50a7..daf2b0f 100644 --- a/lib/libiscsi.syms.in +++ b/lib/libiscsi.syms.in @@ -116,6 +116,7 @@ iscsi_set_alias iscsi_set_bind_interfaces iscsi_set_cache_allocations iscsi_set_header_digest +iscsi_set_data_digest iscsi_set_immediate_data iscsi_set_initial_r2t iscsi_set_initiator_username_pwd diff --git a/lib/login.c b/lib/login.c index 8251d16..268c409 100644 --- a/lib/login.c +++ b/lib/login.c @@ -44,6 +44,10 @@ #include "iscsi-private.h" #include "scsi-lowlevel.h" #include "md5.h" + +#ifdef HAVE_LIBGNUTLS +#include +#endif #ifdef HAVE_LIBGCRYPT #include #endif @@ -202,7 +206,24 @@ iscsi_login_add_datadigest(struct iscsi_context *iscsi, struct iscsi_pdu *pdu) return 0; } - strncpy(str,"DataDigest=None",MAX_STRING_SIZE); + switch (iscsi->want_data_digest) { + case ISCSI_DATA_DIGEST_NONE: + strncpy(str,"DataDigest=None",MAX_STRING_SIZE); + break; + case ISCSI_DATA_DIGEST_NONE_CRC32C: + strncpy(str,"DataDigest=None,CRC32C",MAX_STRING_SIZE); + break; + case ISCSI_DATA_DIGEST_CRC32C_NONE: + strncpy(str,"DataDigest=CRC32C,None",MAX_STRING_SIZE); + break; + case ISCSI_DATA_DIGEST_CRC32C: + strncpy(str,"DataDigest=CRC32C",MAX_STRING_SIZE); + break; + default: + iscsi_set_error(iscsi, "invalid data digest value"); + return -1; + } + if (iscsi_pdu_add_data(iscsi, pdu, (unsigned char *)str, strlen(str)+1) != 0) { iscsi_set_error(iscsi, "Out-of-memory: pdu add data failed."); @@ -681,41 +702,61 @@ i2h(int i) return i + '0'; } -#ifndef HAVE_LIBGCRYPT -typedef struct MD5Context *gcry_md_hd_t; -#define gcry_md_write MD5Update -#define GCRY_MD_MD5 1 +#if defined HAVE_LIBGNUTLS +#define md5_context_t gnutls_hash_hd_t +#define md5_open(hd) gnutls_hash_init(hd, GNUTLS_DIG_MD5) +#define md5_write gnutls_hash +#define md5_read gnutls_hash_output -static void gcry_md_open(gcry_md_hd_t *hd, int algo, unsigned int flags) +static void md5_close(md5_context_t h) +{ + unsigned char digest[16]; + + gnutls_hash_deinit(h, digest); +} + +#elif defined HAVE_LIBGCRYPT +typedef gcry_md_hd_t md5_context_t; +#define md5_open(hd) gcry_md_open(hd, GCRY_MD_MD5, 0) +#define md5_write gcry_md_write +#define md5_close gcry_md_close + +static void md5_read(md5_context_t h, uint8_t *result) +{ + memcpy(result, gcry_md_read(h, 0), 16); +} +#else +typedef struct MD5Context *md5_context_t; +#define md5_write MD5Update + +static void md5_open(md5_context_t *hd) { - assert(algo == GCRY_MD_MD5 && flags == 0); *hd = malloc(sizeof(struct MD5Context)); if (*hd) { MD5Init(*hd); } } -static void gcry_md_putc(gcry_md_hd_t h, unsigned char c) -{ - MD5Update(h, &c, 1); -} - -static char *gcry_md_read(gcry_md_hd_t h, int algo) +static void md5_read(md5_context_t h, uint8_t *result) { unsigned char digest[16]; - assert(algo == 0 || algo == GCRY_MD_MD5); MD5Final(digest, h); - return memcpy(h->buf, digest, sizeof(digest)); + memcpy(result, digest, sizeof(digest)); } -static void gcry_md_close(gcry_md_hd_t h) +static void md5_close(md5_context_t h) { memset(h, 0, sizeof(*h)); free(h); } #endif +static inline void md5_putc(md5_context_t h, unsigned char c) +{ + md5_write(h, &c, 1); +} + /* size of the challenge used for bidirectional chap */ #define TARGET_CHAP_C_SIZE 32 @@ -726,7 +767,7 @@ iscsi_login_add_chap_response(struct iscsi_context *iscsi, struct iscsi_pdu *pdu char * strp; unsigned char c, cc[2]; unsigned char digest[CHAP_R_SIZE]; - gcry_md_hd_t ctx; + md5_context_t ctx; int i; if (iscsi->current_phase != ISCSI_PDU_LOGIN_CSG_SECNEG @@ -739,22 +780,22 @@ iscsi_login_add_chap_response(struct iscsi_context *iscsi, struct iscsi_pdu *pdu return -1; } - gcry_md_open(&ctx, GCRY_MD_MD5, 0); + md5_open(&ctx); if (ctx == NULL) { iscsi_set_error(iscsi, "Cannot create MD5 algorithm"); return -1; } - gcry_md_putc(ctx, iscsi->chap_i); - gcry_md_write(ctx, (unsigned char *)iscsi->passwd, strlen(iscsi->passwd)); + md5_putc(ctx, iscsi->chap_i); + md5_write(ctx, (unsigned char *)iscsi->passwd, strlen(iscsi->passwd)); strp = iscsi->chap_c; while (*strp != 0) { c = (h2i(strp[0]) << 4) | h2i(strp[1]); strp += 2; - gcry_md_putc(ctx, c); + md5_putc(ctx, c); } - memcpy(digest, gcry_md_read(ctx, 0), sizeof(digest)); - gcry_md_close(ctx); + md5_read(ctx, digest); + md5_close(ctx); strncpy(str,"CHAP_R=0x",MAX_STRING_SIZE); if (iscsi_pdu_add_data(iscsi, pdu, (unsigned char *)str, strlen(str)) @@ -822,20 +863,19 @@ iscsi_login_add_chap_response(struct iscsi_context *iscsi, struct iscsi_pdu *pdu return -1; } - gcry_md_open(&ctx, GCRY_MD_MD5, 0); + md5_open(&ctx); if (ctx == NULL) { iscsi_set_error(iscsi, "Cannot create MD5 algorithm"); return -1; } - gcry_md_putc(ctx, iscsi->target_chap_i); - gcry_md_write(ctx, (unsigned char *)iscsi->target_passwd, + md5_putc(ctx, iscsi->target_chap_i); + md5_write(ctx, (unsigned char *)iscsi->target_passwd, strlen(iscsi->target_passwd)); - gcry_md_write(ctx, (unsigned char *)target_chap_c, + md5_write(ctx, (unsigned char *)target_chap_c, TARGET_CHAP_C_SIZE); - memcpy(iscsi->target_chap_r, gcry_md_read(ctx, 0), - sizeof(iscsi->target_chap_r)); - gcry_md_close(ctx); + md5_read(ctx, iscsi->target_chap_r); + md5_close(ctx); } return 0; @@ -1200,6 +1240,16 @@ iscsi_process_login_reply(struct iscsi_context *iscsi, struct iscsi_pdu *pdu, } } + if (!strncmp(ptr, "DataDigest=", 11)) { + if (!strcmp(ptr + 11, "CRC32C")) { + iscsi->want_data_digest + = ISCSI_DATA_DIGEST_CRC32C; + } else { + iscsi->want_data_digest + = ISCSI_DATA_DIGEST_NONE; + } + } + if (!strncmp(ptr, "FirstBurstLength=", 17)) { iscsi->first_burst_length = strtol(ptr + 17, NULL, 10); } @@ -1370,6 +1420,7 @@ iscsi_process_login_reply(struct iscsi_context *iscsi, struct iscsi_pdu *pdu, iscsi->is_loggedin = 1; iscsi_itt_post_increment(iscsi); iscsi->header_digest = iscsi->want_header_digest; + iscsi->data_digest = iscsi->want_data_digest; ISCSI_LOG(iscsi, 2, "login successful"); pdu->callback(iscsi, SCSI_STATUS_GOOD, NULL, pdu->private_data); } else { diff --git a/lib/pdu.c b/lib/pdu.c index 5a97e43..be0a646 100644 --- a/lib/pdu.c +++ b/lib/pdu.c @@ -225,6 +225,9 @@ iscsi_allocate_pdu(struct iscsi_context *iscsi, enum iscsi_opcode opcode, /* flags */ pdu->flags = flags; + /* DataDigest - may or may not be calculated. Initialize anyway. */ + crc32c_init(&pdu->calculated_data_digest); + return pdu; } @@ -537,6 +540,25 @@ iscsi_process_pdu(struct iscsi_context *iscsi, struct iscsi_in_pdu *in) } } + /* verify data checksum ... */ + if (iscsi->data_digest != ISCSI_DATA_DIGEST_NONE) { + int dsl = scsi_get_uint32(&in->hdr[4]) & 0x00ffffff; + /* ... but only if some data is present. */ + if (dsl) { + uint32_t crc_rcvd = 0; + uint32_t crc = crc32c_chain_done(in->calculated_data_digest); + + crc_rcvd |= in->data_digest_buf[0]; + crc_rcvd |= in->data_digest_buf[1] << 8; + crc_rcvd |= in->data_digest_buf[2] << 16; + crc_rcvd |= in->data_digest_buf[3] << 24; + if (crc != crc_rcvd) { + iscsi_set_error(iscsi, "data 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; @@ -947,3 +969,36 @@ iscsi_cancel_pdus(struct iscsi_context *iscsi) iscsi->drv->free_pdu(iscsi, pdu); } } + +void +iscsi_cancel_lun_pdus(struct iscsi_context *iscsi, uint32_t lun) +{ + struct iscsi_pdu *pdu; + struct iscsi_pdu *next_pdu; + uint32_t cmdsn_gap = 0; + struct scsi_task * task = NULL; + + for (pdu = iscsi->outqueue; pdu; pdu = next_pdu) { + next_pdu = pdu->next; + task = iscsi_scsi_get_task_from_pdu(pdu); + + if (cmdsn_gap > 0) { + iscsi_pdu_set_cmdsn(pdu, pdu->cmdsn - cmdsn_gap); + } + if (task == NULL || task->lun != lun) { + continue; + } + if (!(pdu->outdata.data[0] & ISCSI_PDU_IMMEDIATE) && + (pdu->outdata.data[0] & 0x3f) != ISCSI_PDU_DATA_OUT) { + iscsi->cmdsn--; + cmdsn_gap++; + } + ISCSI_LIST_REMOVE(&iscsi->outqueue, pdu); + iscsi_set_error(iscsi, "command cancelled"); + if (pdu->callback) { + pdu->callback(iscsi, SCSI_STATUS_CANCELLED, + NULL, pdu->private_data); + } + iscsi->drv->free_pdu(iscsi, pdu); + } +} diff --git a/lib/scsi-lowlevel.c b/lib/scsi-lowlevel.c index 1b49853..e23ec0d 100644 --- a/lib/scsi-lowlevel.c +++ b/lib/scsi-lowlevel.c @@ -79,7 +79,9 @@ scsi_free_scsi_task(struct scsi_task *task) } free(task->datain.data); + task->datain.data = NULL; free(task); + task = NULL; } struct scsi_task * diff --git a/lib/socket.c b/lib/socket.c index db7f667..d8fbfc9 100644 --- a/lib/socket.c +++ b/lib/socket.c @@ -62,6 +62,7 @@ #include #endif +#include #include #include #include @@ -514,7 +515,7 @@ iscsi_out_queue_length(struct iscsi_context *iscsi) } ssize_t -iscsi_iovector_readv_writev(struct iscsi_context *iscsi, struct scsi_iovector *iovector, uint32_t pos, ssize_t count, int do_write) +iscsi_iovector_readv_writev(struct iscsi_context *iscsi, struct scsi_iovector *iovector, uint32_t pos, ssize_t count, uint32_t *data_digest_ptr, int do_write) { struct scsi_iovec *iov, *iov2; int niov; @@ -598,6 +599,19 @@ iscsi_iovector_readv_writev(struct iscsi_context *iscsi, struct scsi_iovector *i n = readv(iscsi->fd, (struct iovec*) iov, niov); } + /* Update the data digest */ + if (data_digest_ptr && n > 0) { + int i; + size_t bytes_to_crc = n; + struct iovec *iov_ptr = (struct iovec*)iov; + + for ( i=0; iov_ptr && iiov_len); + *data_digest_ptr = crc32c_chain(*data_digest_ptr, iov_ptr->iov_base, chunk); + bytes_to_crc -= chunk; + } + } + /* restore original values */ iov->iov_base = (void*) ((uintptr_t)iov->iov_base - pos); iov->iov_len += pos; @@ -619,6 +633,7 @@ iscsi_read_from_socket(struct iscsi_context *iscsi) { struct iscsi_in_pdu *in; ssize_t hdr_size, data_size, count, padding_size; + bool do_data_digest = (iscsi->data_digest != ISCSI_DATA_DIGEST_NONE); do { hdr_size = ISCSI_HEADER_SIZE(iscsi->header_digest); @@ -628,6 +643,7 @@ iscsi_read_from_socket(struct iscsi_context *iscsi) iscsi_set_error(iscsi, "Out-of-memory: failed to malloc iscsi_in_pdu"); return -1; } + crc32c_init(&(iscsi->incoming->calculated_data_digest)); iscsi->incoming->hdr = iscsi_smalloc(iscsi, hdr_size); if (iscsi->incoming->hdr == NULL) { iscsi_set_error(iscsi, "Out-of-memory"); @@ -682,7 +698,7 @@ iscsi_read_from_socket(struct iscsi_context *iscsi) iovector_in = iscsi_get_scsi_task_iovector_in(iscsi, in); if (iovector_in != NULL && count > padding_size) { uint32_t offset = scsi_get_uint32(&in->hdr[40]); - count = iscsi_iovector_readv_writev(iscsi, iovector_in, in->data_pos + offset, count - padding_size, 0); + count = iscsi_iovector_readv_writev(iscsi, iovector_in, in->data_pos + offset, count - padding_size, do_data_digest ? &(in->calculated_data_digest) : NULL, 0); } else { if (iovector_in == NULL) { if (in->data == NULL) { @@ -695,6 +711,8 @@ iscsi_read_from_socket(struct iscsi_context *iscsi) buf = &in->data[in->data_pos]; } count = recv(iscsi->fd, (void *)buf, count, 0); + if (do_data_digest && count > 0) + in->calculated_data_digest = crc32c_chain(in->calculated_data_digest, buf, count); } if (count == 0) { /* remote side has closed the socket. */ @@ -713,6 +731,28 @@ iscsi_read_from_socket(struct iscsi_context *iscsi) break; } + /* Handle Data Digest receive */ + if (data_size != 0 && do_data_digest && + in->received_data_digest_bytes < ISCSI_DIGEST_SIZE) { + + count = recv(iscsi->fd, (void *)(in->data_digest_buf + in->received_data_digest_bytes), ISCSI_DIGEST_SIZE - in->received_data_digest_bytes, 0); + if (count == 0) { + /* remote side has closed the socket. */ + return -1; + } + if (count < 0) { + if (errno == EINTR || errno == EAGAIN) { + break; + } + return -1; + } + in->received_data_digest_bytes += count; + + if (in->received_data_digest_bytes < ISCSI_DIGEST_SIZE) { + break; + } + } + iscsi->incoming = NULL; if (iscsi_process_pdu(iscsi, in) != 0) { iscsi_free_iscsi_in_pdu(iscsi, in); @@ -751,6 +791,7 @@ iscsi_write_to_socket(struct iscsi_context *iscsi) struct iscsi_pdu *pdu; static char padding_buf[3]; int socket_flags = 0; + bool do_data_digest = (iscsi->data_digest != ISCSI_DATA_DIGEST_NONE); #ifdef MSG_NOSIGNAL socket_flags |= MSG_NOSIGNAL; @@ -848,7 +889,7 @@ iscsi_write_to_socket(struct iscsi_context *iscsi) count = iscsi_iovector_readv_writev(iscsi, iovector_out, pdu->payload_offset + pdu->payload_written, - pdu->payload_len - pdu->payload_written, 1); + pdu->payload_len - pdu->payload_written, do_data_digest ? &(pdu->calculated_data_digest) : NULL, 1); if (count == -1) { if (errno == EAGAIN || errno == EWOULDBLOCK) { return 0; @@ -873,12 +914,56 @@ iscsi_write_to_socket(struct iscsi_context *iscsi) "socket :%d", errno); return -1; } + + if (do_data_digest) + pdu->calculated_data_digest = crc32c_chain(pdu->calculated_data_digest, (uint8_t *)padding_buf, count); + pdu->payload_written += count; } + /* if we havent written the full padding yet. */ + if (pdu->payload_written < total) { + return 0; + } + + /* + * Maybe update the total again, and write the digest, but only if + * 1. DataDigest has been negociated, and + * 2. We have actually written some data + */ + if (do_data_digest && pdu->payload_written) { + uint32_t data_digest = crc32c_chain_done(pdu->calculated_data_digest); + char data_digest_buf[ISCSI_DIGEST_SIZE]; + + total += ISCSI_DIGEST_SIZE; + + data_digest_buf[3] = (data_digest >> 24); + data_digest_buf[2] = (data_digest >> 16); + data_digest_buf[1] = (data_digest >> 8); + data_digest_buf[0] = (data_digest); + + /* Write data digest */ + if (pdu->payload_written < total) { + int todo = total - pdu->payload_written; + count = send(iscsi->fd, data_digest_buf + (ISCSI_DIGEST_SIZE - todo), todo, socket_flags); + if (count == -1) { + if (errno == EAGAIN || errno == EWOULDBLOCK) { + return 0; + } + iscsi_set_error(iscsi, "Error when writing to " + "socket :%d", errno); + return -1; + } + + pdu->payload_written += count; + } + } + + /* if we havent written everything yet. */ if (pdu->payload_written != total) { return 0; } + if (pdu->flags & ISCSI_PDU_CORK_WHEN_SENT) { iscsi->is_corked = 1; } diff --git a/lib/task_mgmt.c b/lib/task_mgmt.c index b0ad251..f3e6824 100644 --- a/lib/task_mgmt.c +++ b/lib/task_mgmt.c @@ -130,7 +130,7 @@ iscsi_task_mgmt_lun_reset_async(struct iscsi_context *iscsi, uint32_t lun, iscsi_command_cb cb, void *private_data) { - iscsi_scsi_cancel_all_tasks(iscsi); + iscsi_cancel_lun_pdus(iscsi, lun); return iscsi_task_mgmt_async(iscsi, lun, ISCSI_TM_LUN_RESET, diff --git a/packaging/RPM/libiscsi.spec.in b/packaging/RPM/libiscsi.spec.in index 4afa9d3..19602b0 100644 --- a/packaging/RPM/libiscsi.spec.in +++ b/packaging/RPM/libiscsi.spec.in @@ -1,6 +1,6 @@ Name: libiscsi Summary: iSCSI client library -Version: 1.19.0 +Version: 1.20.0 Release: 1GITHASH%{?dist} License: LGPLv2+ Group: System Environment/Libraries @@ -75,9 +75,13 @@ to iSCSI servers without having to set up the Linux iSCSI initiator. %{_bindir}/iscsi-perf %{_bindir}/iscsi-readcapacity16 %{_bindir}/iscsi-swp +%{_bindir}/iscsi-discard +%{_bindir}/iscsi-md5sum +%{_bindir}/iscsi-pr %{_mandir}/man1/iscsi-inq.1.gz %{_mandir}/man1/iscsi-ls.1.gz %{_mandir}/man1/iscsi-swp.1.gz +%{_mandir}/man1/iscsi-md5sum.1.gz %package devel Summary: iSCSI client development libraries @@ -109,6 +113,8 @@ Test tool for iSCSI/SCSI targets %changelog +* Mon Feb 5 2024 : 1.20.0 + - Various updates to the test tool * Sun Jul 14 2019 : 1.19.0 - iSER improvements - Add support to senable/disable digests via URL arguments @@ -229,7 +235,7 @@ Test tool for iSCSI/SCSI targets - Change all default iqn names so they are valid iqn names. * Mon Jul 9 2012 : 1.5.0 - Make sure we can handle racy eventsystems which might call us for - POLLIN eventhough there is no longer any data to read from the socket. + POLLIN even though there is no longer any data to read from the socket. - Only set up tcp keepalives on systems that support them. - Only export symbols we really want to make public - FreeBSD and Illumos does not define SOL_TCP diff --git a/test-tool/iscsi-support.c b/test-tool/iscsi-support.c index 9363140..9375952 100644 --- a/test-tool/iscsi-support.c +++ b/test-tool/iscsi-support.c @@ -202,7 +202,7 @@ static int check_result(const char *opcode, struct scsi_device *sdev, } if (status == SCSI_STATUS_GOOD && task->status != SCSI_STATUS_GOOD) { logging(LOG_NORMAL, - "[FAILED] %s command failed with status %d / sense key %s(0x%02x) / ASCQ %s(0x%04x)", + "[FAILED] %s command failed with status 0x%x / sense key %s(0x%02x) / ASCQ %s(0x%04x)", opcode, task->status, scsi_sense_key_str(task->sense.key), task->sense.key, scsi_sense_ascq_str(task->sense.ascq), task->sense.ascq); @@ -457,6 +457,8 @@ void logging(int level, const char *format, ...) va_list ap; static char message[1024]; int ret; + struct timespec ts; + struct tm tm; if (loglevel < level) { return; @@ -467,6 +469,17 @@ void logging(int level, const char *format, ...) return; } + if (clock_gettime(CLOCK_REALTIME, &ts) == -1) { + return; + } + + if (!localtime_r(&ts.tv_sec, &tm)) { + return; + } + + printf(" %04d-%02d-%02d %02d:%02d:%02d.%06d ", + tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday, tm.tm_hour, tm.tm_min, tm.tm_sec, (int)ts.tv_nsec / 1000); + va_start(ap, format); ret = vsnprintf(message, 1024, format, ap); va_end(ap); @@ -2735,7 +2748,7 @@ int set_swp(struct scsi_device *sdev) logging(LOG_VERBOSE, "[SUCCESS] CONTROL page fetched."); ms = scsi_datain_unmarshall(sense_task); - if (ms == NULL) { + if (ms == NULL || ms->pages == NULL) { logging(LOG_NORMAL, "failed to unmarshall mode sense datain " "blob"); ret = -1; diff --git a/test-tool/test_compareandwrite_miscompare.c b/test-tool/test_compareandwrite_miscompare.c index 04a9fb5..a8bdef3 100644 --- a/test-tool/test_compareandwrite_miscompare.c +++ b/test-tool/test_compareandwrite_miscompare.c @@ -95,7 +95,7 @@ test_compareandwrite_miscompare(void) for (j = 0; j < i * block_size; j++) { if (scratch[j] != 'A') { logging(LOG_VERBOSE, "[FAILED] Data changed " - "eventhough there was a miscompare"); + "even though there was a miscompare"); CU_FAIL("Block was written to"); return; } @@ -151,7 +151,7 @@ test_compareandwrite_miscompare(void) for (j = 0; j < i * block_size; j++) { if (scratch[j] != 'A') { logging(LOG_VERBOSE, "[FAILED] Data changed " - "eventhough there was a miscompare"); + "even though there was a miscompare"); CU_FAIL("Block was written to"); return; } diff --git a/test-tool/test_sanitize_block_erase.c b/test-tool/test_sanitize_block_erase.c index 15940be..cea72b9 100644 --- a/test-tool/test_sanitize_block_erase.c +++ b/test-tool/test_sanitize_block_erase.c @@ -161,7 +161,7 @@ check_unmap(void) lbas->descriptors[i].lba + lbas->descriptors[i].num_blocks); if (lbas->descriptors[i].provisioning == SCSI_PROVISIONING_TYPE_MAPPED) { logging(LOG_VERBOSE, "[FAILED] Descriptor %d is MAPPED." - "All descriptors shoudl be either DEALLOCATED " + "All descriptors should be either DEALLOCATED " "or ANCHORED after SANITIZE", i); CU_FAIL("[FAILED] LBA status descriptor is MAPPED."); } diff --git a/test-tool/test_sanitize_reset.c b/test-tool/test_sanitize_reset.c index fffc55a..eed451f 100644 --- a/test-tool/test_sanitize_reset.c +++ b/test-tool/test_sanitize_reset.c @@ -58,7 +58,7 @@ test_sanitize_reset(void) return; } - logging(LOG_VERBOSE, "Send an asyncronous SANITIZE to the target."); + logging(LOG_VERBOSE, "Send an asynchronous SANITIZE to the target."); data.size = block_size + 4; data.data = alloca(data.size); memset(&data.data[4], 0, block_size); diff --git a/utils/iscsi-perf.c b/utils/iscsi-perf.c index b43d949..f2f6aee 100644 --- a/utils/iscsi-perf.c +++ b/utils/iscsi-perf.c @@ -151,7 +151,7 @@ void cb(struct iscsi_context *iscsi, int status, void *command_data, void *priva if (status == SCSI_STATUS_BUSY || (status == SCSI_STATUS_CHECK_CONDITION && task->sense.key == SCSI_SENSE_UNIT_ATTENTION)) { if (client->retry_cnt++ > 4 * max_in_flight) { - fprintf(stderr, "maxium number of command retries reached...\n"); + fprintf(stderr, "maximum number of command retries reached...\n"); client->err_cnt++; goto out; } diff --git a/utils/iscsi-pr.c b/utils/iscsi-pr.c index 309e6ca..71ae48c 100644 --- a/utils/iscsi-pr.c +++ b/utils/iscsi-pr.c @@ -236,7 +236,7 @@ static void print_help(const char *bin) fprintf(stderr, " -S, --param-sark=SARK PR Out: parameter service action reservation key (SARK is in hex)\n"); fprintf(stderr, " -T, --prout-type=TYPE PR Out: type field\n"); fprintf(stderr, " -G, --register PR Out: Register\n"); - fprintf(stderr, " -R, --reserve PR Out: Reserve, SARK only(register sark implicity)\n"); + fprintf(stderr, " -R, --reserve PR Out: Reserve, SARK only(register sark implicitly)\n"); fprintf(stderr, " -L, --release PR Out: Release, SARK only. This program releases TYPE 7 and TYPE 8 only\n"); fprintf(stderr, " -C, --clear PR Out: Clear, SARK only\n"); fprintf(stderr, " -P, --preempt PR Out: Preempt, use SARK to preempt reservation from RK\n");