Merge branch 'master' into docs

This commit is contained in:
Folkert van Heusden
2024-09-20 06:33:37 +02:00
24 changed files with 414 additions and 72 deletions

12
COPYING
View File

@@ -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

View File

@@ -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

View File

@@ -46,6 +46,7 @@ Username and password for bidirectional CHAP authentication:
target_user=<account>
target_password=<password>
header_digest=<crc32c|none>
data_digest=<crc32c|none>
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
=======

View File

@@ -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])

View File

@@ -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);

View File

@@ -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
*/

View File

@@ -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 = \

View File

@@ -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);

View File

@@ -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;
}

View File

@@ -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")) {

View File

@@ -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

View File

@@ -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

View File

@@ -44,6 +44,10 @@
#include "iscsi-private.h"
#include "scsi-lowlevel.h"
#include "md5.h"
#ifdef HAVE_LIBGNUTLS
#include <gnutls/crypto.h>
#endif
#ifdef HAVE_LIBGCRYPT
#include <gcrypt.h>
#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 {

View File

@@ -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);
}
}

View File

@@ -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 *

View File

@@ -62,6 +62,7 @@
#include <sys/uio.h>
#endif
#include <stdbool.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
@@ -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 && i<niov && bytes_to_crc; iov_ptr++, i++) {
size_t chunk = MIN(bytes_to_crc, iov_ptr->iov_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;
}

View File

@@ -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,

View File

@@ -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

View File

@@ -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;

View File

@@ -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;
}

View File

@@ -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.");
}

View File

@@ -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);

View File

@@ -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;
}

View File

@@ -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");