summaryrefslogtreecommitdiffstats
path: root/src/server
diff options
context:
space:
mode:
authorSebastian Vater2025-08-19 16:53:14 +0200
committerSimon Rettberg2025-12-09 15:33:20 +0100
commit197f25c9c913940c87774c15bded85aa920be14b (patch)
treef3f9cd92be6532bdbc3064e2b1dc1d31e74ee28f /src/server
parent[SERVER] iscsi: Finish login handling, add NOP-In/Out handling (diff)
downloaddnbd3-197f25c9c913940c87774c15bded85aa920be14b.tar.gz
dnbd3-197f25c9c913940c87774c15bded85aa920be14b.tar.xz
dnbd3-197f25c9c913940c87774c15bded85aa920be14b.zip
[SERVER] iscsi: Hook into net.c, text response handling, more features:
- R2T handling - Portal groups - Fixes to login phase handling - Code refactoring - Remove obsolete PDU fields - SCSI INQUIRY handler - Persistent Reservation support - Implement SCSI block based operations - Implement other needed SCSI ops - Disks are now reported as read-only - Doxygen tags - Bugfixes for crashes, memleaks, etc.
Diffstat (limited to 'src/server')
-rw-r--r--src/server/globals.c3
-rw-r--r--src/server/globals.h7
-rw-r--r--src/server/image.c74
-rw-r--r--src/server/image.h2
-rw-r--r--src/server/iscsi.c11489
-rw-r--r--src/server/iscsi.h6309
-rw-r--r--src/server/net.c2
-rw-r--r--src/server/server.c11
8 files changed, 15641 insertions, 2256 deletions
diff --git a/src/server/globals.c b/src/server/globals.c
index f6432cb..34941bf 100644
--- a/src/server/globals.c
+++ b/src/server/globals.c
@@ -32,6 +32,7 @@ atomic_bool _vmdkLegacyMode = false;
atomic_bool _proxyPrivateOnly = false;
atomic_bool _pretendClient = false;
atomic_int _autoFreeDiskSpaceDelay = 3600 * 10;
+atomic_bool _iSCSIServer = true;
// [limits]
atomic_int _maxClients = SERVER_MAX_CLIENTS;
atomic_int _maxImages = SERVER_MAX_IMAGES;
@@ -93,6 +94,7 @@ static int ini_handler(void *custom UNUSED, const char* section, const char* key
SAVE_TO_VAR_UINT( limits, minRequestSize );
SAVE_TO_VAR_BOOL( dnbd3, pretendClient );
SAVE_TO_VAR_INT( dnbd3, autoFreeDiskSpaceDelay );
+ SAVE_TO_VAR_BOOL( dnbd3, iSCSIServer );
if ( strcmp( section, "dnbd3" ) == 0 && strcmp( key, "backgroundReplication" ) == 0 ) {
if ( strcmp( value, "hashblock" ) == 0 ) {
_backgroundReplication = BGR_HASHBLOCK;
@@ -364,6 +366,7 @@ size_t globals_dumpConfig(char *buffer, size_t size)
PBOOL(proxyPrivateOnly);
PBOOL(pretendClient);
PINT(autoFreeDiskSpaceDelay);
+ PBOOL(iSCSIServer);
P_ARG("[limits]\n");
PINT(maxClients);
PINT(maxImages);
diff --git a/src/server/globals.h b/src/server/globals.h
index 5129108..b51a81a 100644
--- a/src/server/globals.h
+++ b/src/server/globals.h
@@ -142,6 +142,7 @@ struct _dnbd3_image
weakref ref_cacheMap; // cache map telling which parts are locally cached, NULL if complete
uint64_t virtualFilesize; // virtual size of image (real size rounded up to multiple of 4k)
uint64_t realFilesize; // actual file size on disk
+ uint64_t wwn; // WorldWideName
ticks atime; // last access time
ticks nextCompletenessEstimate; // next time the completeness estimate should be updated
uint32_t *crc32; // list of crc32 checksums for each 16MiB block in image
@@ -333,6 +334,12 @@ extern atomic_bool _pretendClient;
extern atomic_int _autoFreeDiskSpaceDelay;
/**
+ * Specifies if the iSCSI server should be initialized, enabled
+ * and used upon start of DNBD3 server.
+ */
+extern atomic_bool _iSCSIServer;
+
+/**
* When handling a client request, this sets the maximum amount
* of bytes we prefetch offset right at the end of the client request.
* The prefetch size will be MIN( length * 3, _maxPrefetch ), if
diff --git a/src/server/image.c b/src/server/image.c
index f438c18..a2ff247 100644
--- a/src/server/image.c
+++ b/src/server/image.c
@@ -55,12 +55,14 @@ static dnbd3_cache_map_t* image_loadCacheMap(const char * const imagePath, const
static uint32_t* image_loadCrcList(const char * const imagePath, const int64_t fileSize, uint32_t *masterCrc);
static bool image_checkRandomBlocks(dnbd3_image_t *image, const int count, int fromFd);
static void* closeUnusedFds(void*);
+static dnbd3_image_t* runEnsureOpenCheck(dnbd3_image_t *candidate);
static bool isImageFromUpstream(dnbd3_image_t *image);
static void* saveLoadAllCacheMaps(void*);
static void saveCacheMap(dnbd3_image_t *image);
static void allocCacheMap(dnbd3_image_t *image, bool complete);
static void saveMetaData(dnbd3_image_t *image, ticks *now, time_t walltime);
static void loadImageMeta(dnbd3_image_t *image);
+static void calculateImageWwn(dnbd3_image_t *image);
static void cmfree(ref *ref)
{
@@ -334,6 +336,38 @@ dnbd3_image_t* image_byId(int imgId)
}
/**
+ * Get image by its wwn and revision id.
+ * Locks on imageListLock.
+ */
+dnbd3_image_t* image_getByWwn(uint64_t wwn, uint16_t revision, bool ensureFdOpen)
+{
+ int i;
+ dnbd3_image_t *candidate = NULL;
+
+ mutex_lock( &imageListLock );
+ for ( i = 0; i < _num_images; ++i ) {
+ dnbd3_image_t * const image = _images[i];
+ if ( image == NULL || wwn != image->wwn )
+ continue;
+ if ( revision == image->rid ) {
+ candidate = image;
+ break;
+ } else if ( revision == 0 && (candidate == NULL || candidate->rid < image->rid) ) {
+ candidate = image;
+ }
+ }
+ if ( candidate != NULL ) {
+ candidate->users++;
+ }
+ mutex_unlock( &imageListLock );
+
+ if ( candidate != NULL && ensureFdOpen ) {
+ candidate = runEnsureOpenCheck( candidate );
+ }
+ return candidate;
+}
+
+/**
* Get an image by name+rid. This function increases a reference counter,
* so you HAVE TO CALL image_release for every image_get() call at some
* point...
@@ -358,19 +392,19 @@ dnbd3_image_t* image_get(const char *name, uint16_t revision, bool ensureFdOpen)
candidate = image;
}
}
-
- // Not found
- if ( candidate == NULL ) {
- mutex_unlock( &imageListLock );
- return NULL ;
+ if ( candidate != NULL ) {
+ candidate->users++;
}
-
- candidate->users++;
mutex_unlock( &imageListLock );
- if ( !ensureFdOpen ) // Don't want to re-check
- return candidate;
+ if ( candidate != NULL && ensureFdOpen ) {
+ candidate = runEnsureOpenCheck( candidate );
+ }
+ return candidate;
+}
+static dnbd3_image_t* runEnsureOpenCheck(dnbd3_image_t *candidate)
+{
if ( image_ensureOpen( candidate ) && !candidate->problem.read )
return candidate; // We have a read fd and no read or changed problems
@@ -386,7 +420,7 @@ dnbd3_image_t* image_get(const char *name, uint16_t revision, bool ensureFdOpen)
// make a copy of the image struct but keep the old one around. If/When it's not being used
// anymore, it will be freed automatically.
logadd( LOG_DEBUG1, "Reloading image file %s because of read problem/changed", candidate->path );
- dnbd3_image_t *img = calloc( sizeof(dnbd3_image_t), 1 );
+ dnbd3_image_t *img = calloc( 1, sizeof(dnbd3_image_t) );
img->path = strdup( candidate->path );
img->name = strdup( candidate->name );
img->virtualFilesize = candidate->virtualFilesize;
@@ -399,6 +433,7 @@ dnbd3_image_t* image_get(const char *name, uint16_t revision, bool ensureFdOpen)
img->problem.read = true;
img->problem.changed = candidate->problem.changed;
img->ref_cacheMap = NULL;
+ calculateImageWwn( candidate );
mutex_init( &img->lock, LOCK_IMAGE );
if ( candidate->crc32 != NULL ) {
const size_t mb = IMGSIZE_TO_HASHBLOCKS( candidate->virtualFilesize ) * sizeof(uint32_t);
@@ -426,7 +461,8 @@ dnbd3_image_t* image_get(const char *name, uint16_t revision, bool ensureFdOpen)
// readFd == -1 and problem.read == true
}
- return candidate; // We did all we can, hopefully it's working
+ // We did all we can, hopefully it's working
+ return candidate;
}
/**
@@ -908,6 +944,7 @@ static bool image_load(char *base, char *path, bool withUplink)
image->completenessEstimate = -1;
mutex_init( &image->lock, LOCK_IMAGE );
loadImageMeta( image );
+ calculateImageWwn( image );
// Prevent freeing in cleanup
cache = NULL;
@@ -1609,9 +1646,10 @@ json_t* image_getListAsJson()
addproblem(uplink, 3);
addproblem(queue, 4);
- jsonImage = json_pack( "{sisssisisisisIsi}",
+ jsonImage = json_pack( "{sisssIsisisisisIsi}",
"id", image->id, // id, name, rid never change, so access them without locking
"name", image->name,
+ "wwn", (json_int_t)image->wwn,
"rid", (int) image->rid,
"users", image->users,
"complete", completeness,
@@ -2125,6 +2163,18 @@ static void loadImageMeta(dnbd3_image_t *image)
timing_gets( &image->atime, offset );
}
+static void calculateImageWwn(dnbd3_image_t *image)
+{
+ uint64_t value = 0ULL;
+ const char *name = image->name;
+
+ while ( *name != '\0' ) {
+ value = (value * 131ULL) + *name++;
+ }
+ const uint64_t id_a = (value & 0xFFF000000ULL) << 24ULL;
+ image->wwn = (value & 0xFFFFFFULL) | 0x2000000347000000ULL | id_a;
+}
+
void image_checkForNextFullCheck(void)
{
int i;
diff --git a/src/server/image.h b/src/server/image.h
index df8ac87..a8ef717 100644
--- a/src/server/image.h
+++ b/src/server/image.h
@@ -19,6 +19,8 @@ bool image_ensureOpen(dnbd3_image_t *image);
dnbd3_image_t* image_byId(int imgId);
+dnbd3_image_t* image_getByWwn(uint64_t wwn, uint16_t revision, bool ensureFdOpen);
+
dnbd3_image_t* image_get(const char *name, uint16_t revision, bool checkIfWorking);
bool image_reopenCacheFd(dnbd3_image_t *image, const bool force);
diff --git a/src/server/iscsi.c b/src/server/iscsi.c
index bfda85b..b44f695 100644
--- a/src/server/iscsi.c
+++ b/src/server/iscsi.c
@@ -1,4 +1,4 @@
- /*
+/*
* This file is part of the Distributed Network Block Device 3
*
* Copyright(c) 2025 Sebastian Vater <sebastian.vater@rz.uni-freiburg.de>
@@ -18,7 +18,11 @@
*
*/
+#include <ctype.h>
+#include <errno.h>
+#include <fnmatch.h>
#include <stdarg.h>
+#include <stdbool.h>
#include <stddef.h>
#include <stdio.h>
#include <stdlib.h>
@@ -26,9 +30,23 @@
#include <inttypes.h>
#include <strings.h>
#include <sys/socket.h>
+#include <sys/types.h>
+#include <dnbd3/config.h>
#include <dnbd3/shared/log.h>
+#include <dnbd3/shared/sockhelper.h>
+#include <dnbd3/types.h>
+#include <pthread.h>
#include <time.h>
+#include <unistd.h>
+
+#include "fileutil.h"
+#include "globals.h"
+#include "helper.h"
+#include "image.h"
+#include "ini.h"
#include "iscsi.h"
+#include "locks.h"
+#include "threadpool.h"
/**
* @file iscsi.c
@@ -43,309 +61,6 @@
* @see https://www.rfc-editor.org/rfc/rfc7143
*/
-/// iSCSI connection negotation key and value pair lookup table.
-static const iscsi_key_value_pair_lut_entry iscsi_connection_key_value_pair_lut[] = {
- { (uint8_t *) ISCSI_LOGIN_AUTH_SESSION_TEXT_KEY_HEADER_DIGEST, (uint8_t *) "None", (uint8_t *) "CRC32C\0None\0", ISCSI_TEXT_KEY_VALUE_PAIR_TYPE_LIST, 0L },
- { (uint8_t *) ISCSI_LOGIN_AUTH_SESSION_TEXT_KEY_DATA_DIGEST, (uint8_t *) "None", (uint8_t *) "CRC32C\0None\0", ISCSI_TEXT_KEY_VALUE_PAIR_TYPE_LIST, 0L },
- { (uint8_t *) ISCSI_LOGIN_AUTH_SESSION_TEXT_KEY_MAX_RECV_DS_LEN, (uint8_t *) "8192", (uint8_t *) "512\016777215\0", ISCSI_TEXT_KEY_VALUE_PAIR_TYPE_NUM_DECLARATIVE, ISCSI_TEXT_KEY_VALUE_PAIR_FLAGS_MULTI_NEGOTIATION | ISCSI_TEXT_KEY_VALUE_PAIR_FLAGS_OVERRIDE_DEFAULT },
- { (uint8_t *) ISCSI_LOGIN_AUTH_SESSION_TEXT_KEY_OF_MARKER, (uint8_t *) "No", (uint8_t *) "Yes\0No\0", ISCSI_TEXT_KEY_VALUE_PAIR_TYPE_BOOL_AND, 0L },
- { (uint8_t *) ISCSI_LOGIN_AUTH_SESSION_TEXT_KEY_IF_MARKER, (uint8_t *) "No", (uint8_t *) "Yes\0No\0", ISCSI_TEXT_KEY_VALUE_PAIR_TYPE_BOOL_AND, 0L },
- { (uint8_t *) ISCSI_LOGIN_AUTH_SESSION_TEXT_KEY_OF_MARK_INT, (uint8_t *) "1", (uint8_t *) "1\0""65535\0", ISCSI_TEXT_KEY_VALUE_PAIR_TYPE_NUM_MIN, 0L },
- { (uint8_t *) ISCSI_LOGIN_AUTH_SESSION_TEXT_KEY_IF_MARK_INT, (uint8_t *) "1", (uint8_t *) "1\0""65535\0", ISCSI_TEXT_KEY_VALUE_PAIR_TYPE_NUM_MIN, 0L },
- { (uint8_t *) ISCSI_LOGIN_AUTH_SECURITY_TEXT_KEY_AUTH_METHOD, (uint8_t *) "None", (uint8_t *) "CHAP\0None\0", ISCSI_TEXT_KEY_VALUE_PAIR_TYPE_LIST, 0L },
- { (uint8_t *) ISCSI_LOGIN_AUTH_SECURITY_TEXT_KEY_AUTH_METHOD_CHAP_CHAP_A, (uint8_t *) "5", (uint8_t *) "5\0", ISCSI_TEXT_KEY_VALUE_PAIR_TYPE_LIST, ISCSI_TEXT_KEY_VALUE_PAIR_FLAGS_CHAP_TYPE },
- { (uint8_t *) ISCSI_LOGIN_AUTH_SECURITY_TEXT_KEY_AUTH_METHOD_CHAP_CHAP_N, (uint8_t *) "", (uint8_t *) "\0", ISCSI_TEXT_KEY_VALUE_PAIR_TYPE_DECLARATIVE, ISCSI_TEXT_KEY_VALUE_PAIR_FLAGS_CHAP_TYPE },
- { (uint8_t *) ISCSI_LOGIN_AUTH_SECURITY_TEXT_KEY_AUTH_METHOD_CHAP_CHAP_R, (uint8_t *) "", (uint8_t *) "\0", ISCSI_TEXT_KEY_VALUE_PAIR_TYPE_DECLARATIVE, ISCSI_TEXT_KEY_VALUE_PAIR_FLAGS_CHAP_TYPE },
- { (uint8_t *) ISCSI_LOGIN_AUTH_SECURITY_TEXT_KEY_AUTH_METHOD_CHAP_CHAP_I, (uint8_t *) "", (uint8_t *) "\0", ISCSI_TEXT_KEY_VALUE_PAIR_TYPE_DECLARATIVE, ISCSI_TEXT_KEY_VALUE_PAIR_FLAGS_CHAP_TYPE },
- { (uint8_t *) ISCSI_LOGIN_AUTH_SECURITY_TEXT_KEY_AUTH_METHOD_CHAP_CHAP_C, (uint8_t *) "", (uint8_t *) "\0", ISCSI_TEXT_KEY_VALUE_PAIR_TYPE_DECLARATIVE, ISCSI_TEXT_KEY_VALUE_PAIR_FLAGS_CHAP_TYPE },
- { NULL, NULL, NULL, ISCSI_TEXT_KEY_VALUE_PAIR_TYPE_INVALID, 0L }
-};
-
-/// iSCSI session negotation key and value pair lookup table.
-static const iscsi_key_value_pair_lut_entry iscsi_session_key_value_pair_lut[] = {
- { (uint8_t *) ISCSI_LOGIN_AUTH_SESSION_TEXT_KEY_MAX_CONNECTIONS, (uint8_t *) "1", (uint8_t *) "1\065535\0", ISCSI_TEXT_KEY_VALUE_PAIR_TYPE_NUM_MIN, ISCSI_TEXT_KEY_VALUE_PAIR_FLAGS_DISCOVERY_IGNORE },
- { (uint8_t *) ISCSI_LOGIN_AUTH_SESSION_TEXT_KEY_SEND_TARGETS, (uint8_t *) "", (uint8_t *) "\0", ISCSI_TEXT_KEY_VALUE_PAIR_TYPE_DECLARATIVE, ISCSI_TEXT_KEY_VALUE_PAIR_FLAGS_SPECIAL_HANDLING },
- { (uint8_t *) ISCSI_LOGIN_AUTH_SECURITY_TEXT_KEY_TARGET_NAME, (uint8_t *) "", (uint8_t *) "\0", ISCSI_TEXT_KEY_VALUE_PAIR_TYPE_DECLARATIVE, ISCSI_TEXT_KEY_VALUE_PAIR_FLAGS_TARGET_DECLARATIVE },
- { (uint8_t *) ISCSI_LOGIN_AUTH_SECURITY_TEXT_KEY_INITIATOR_NAME, (uint8_t *) "", (uint8_t *) "\0", ISCSI_TEXT_KEY_VALUE_PAIR_TYPE_DECLARATIVE, 0L },
- { (uint8_t *) ISCSI_LOGIN_AUTH_SECURITY_TEXT_KEY_TARGET_ALIAS, (uint8_t *) "", (uint8_t *) "\0", ISCSI_TEXT_KEY_VALUE_PAIR_TYPE_DECLARATIVE, 0L },
- { (uint8_t *) ISCSI_LOGIN_AUTH_SECURITY_TEXT_KEY_INITIATOR_ALIAS, (uint8_t *) "", (uint8_t *) "\0", ISCSI_TEXT_KEY_VALUE_PAIR_TYPE_DECLARATIVE, 0L },
- { (uint8_t *) ISCSI_LOGIN_AUTH_SECURITY_TEXT_KEY_TARGET_ADDRESS, (uint8_t *) "", (uint8_t *) "\0", ISCSI_TEXT_KEY_VALUE_PAIR_TYPE_DECLARATIVE, ISCSI_TEXT_KEY_VALUE_PAIR_FLAGS_TARGET_DECLARATIVE },
- { (uint8_t *) ISCSI_LOGIN_AUTH_SECURITY_TEXT_KEY_TARGET_PORTAL_GROUP_TAG, (uint8_t *) "1", (uint8_t *) "1\0""65535\0", ISCSI_TEXT_KEY_VALUE_PAIR_TYPE_NUM_DECLARATIVE, ISCSI_TEXT_KEY_VALUE_PAIR_FLAGS_TARGET_DECLARATIVE },
- { (uint8_t *) ISCSI_LOGIN_AUTH_SESSION_TEXT_KEY_INITIAL_R2T, (uint8_t *) "Yes", (uint8_t *) "Yes\0No\0", ISCSI_TEXT_KEY_VALUE_PAIR_TYPE_BOOL_OR, ISCSI_TEXT_KEY_VALUE_PAIR_FLAGS_DISCOVERY_IGNORE },
- { (uint8_t *) ISCSI_LOGIN_AUTH_SESSION_TEXT_KEY_IMMEDIATE_DATA, (uint8_t *) "Yes", (uint8_t *) "Yes\0No\0", ISCSI_TEXT_KEY_VALUE_PAIR_TYPE_BOOL_AND, ISCSI_TEXT_KEY_VALUE_PAIR_FLAGS_DISCOVERY_IGNORE },
- { (uint8_t *) ISCSI_LOGIN_AUTH_SESSION_TEXT_KEY_MAX_BURST_LEN, (uint8_t *) "262144", (uint8_t *) "512\0""16777215\0", ISCSI_TEXT_KEY_VALUE_PAIR_TYPE_NUM_MIN, ISCSI_TEXT_KEY_VALUE_PAIR_FLAGS_DISCOVERY_IGNORE | ISCSI_TEXT_KEY_VALUE_PAIR_FLAGS_USE_OTHER_MAX_VALUE },
- { (uint8_t *) ISCSI_LOGIN_AUTH_SESSION_TEXT_KEY_FIRST_BURST_LEN, (uint8_t *) "65536", (uint8_t *) "512\0""16777215\0", ISCSI_TEXT_KEY_VALUE_PAIR_TYPE_NUM_MIN, ISCSI_TEXT_KEY_VALUE_PAIR_FLAGS_DISCOVERY_IGNORE | ISCSI_TEXT_KEY_VALUE_PAIR_FLAGS_USE_PREVIOUS_VALUE },
- { (uint8_t *) ISCSI_LOGIN_AUTH_SESSION_TEXT_KEY_DEFAULT_TIME_WAIT, (uint8_t *) "2", (uint8_t *) "0\0""3600\0", ISCSI_TEXT_KEY_VALUE_PAIR_TYPE_NUM_MAX, 0L },
- { (uint8_t *) ISCSI_LOGIN_AUTH_SESSION_TEXT_KEY_DEFAULT_TIME_RETAIN, (uint8_t *) "20", (uint8_t *) "0\0""3600\0", ISCSI_TEXT_KEY_VALUE_PAIR_TYPE_NUM_MIN, 0L },
- { (uint8_t *) ISCSI_LOGIN_AUTH_SESSION_TEXT_KEY_MAX_OUTSTANDING_R2T, (uint8_t *) "1", (uint8_t *) "1\0""65536\0", ISCSI_TEXT_KEY_VALUE_PAIR_TYPE_NUM_MIN, ISCSI_TEXT_KEY_VALUE_PAIR_FLAGS_DISCOVERY_IGNORE },
- { (uint8_t *) ISCSI_LOGIN_AUTH_SESSION_TEXT_KEY_DATA_PDU_IN_ORDER, (uint8_t *) "Yes", (uint8_t *) "Yes\0No\0", ISCSI_TEXT_KEY_VALUE_PAIR_TYPE_BOOL_OR, ISCSI_TEXT_KEY_VALUE_PAIR_FLAGS_DISCOVERY_IGNORE },
- { (uint8_t *) ISCSI_LOGIN_AUTH_SESSION_TEXT_KEY_DATA_SEQ_IN_ORDER, (uint8_t *) "Yes", (uint8_t *) "Yes\0No\0", ISCSI_TEXT_KEY_VALUE_PAIR_TYPE_BOOL_OR, ISCSI_TEXT_KEY_VALUE_PAIR_FLAGS_DISCOVERY_IGNORE },
- { (uint8_t *) ISCSI_LOGIN_AUTH_SESSION_TEXT_KEY_ERR_RECOVERY_LEVEL, (uint8_t *) "0", (uint8_t *) "0\0""2\0", ISCSI_TEXT_KEY_VALUE_PAIR_TYPE_NUM_MIN, 0L },
- { (uint8_t *) ISCSI_LOGIN_AUTH_SECURITY_TEXT_KEY_SESSION_TYPE, (uint8_t *) "Normal", (uint8_t *) "Normal\0Discovery\0", ISCSI_TEXT_KEY_VALUE_PAIR_TYPE_DECLARATIVE, 0L },
- { NULL, NULL, NULL, ISCSI_TEXT_KEY_VALUE_PAIR_TYPE_INVALID, 0L }
-};
-
-/**
- * @brief Initializes a global key and value pair with type and list / range informations for fast access.
- *
- * This function is used to initialize the iSCSI
- * global key and value pair list containing
- * the key types and allowed values.
- *
- * @param[in] key_value_pairs Pointer to key and value pair hash map
- * which should store the global key and value
- * informations, may NOT be NULL, so take caution.
- * @param[in] lut Lookup table to use for initialization.
- * NULL is not allowed here, so be careful.
- * @return 0 on success, a negative error code otherwise.
- */
-static int iscsi_global_key_value_pair_init(iscsi_hashmap *key_value_pairs, const iscsi_key_value_pair_lut_entry *lut)
-{
- for ( uint i = 0; lut[i].key != NULL; i++ ) {
- const uint key_len = (uint) strlen( (char *) lut[i].key ) + 1;
- uint8_t *hash_key = iscsi_hashmap_key_create( lut[i].key, key_len );
-
- if ( hash_key == NULL ) {
- logadd( LOG_ERROR, "iscsi_global_key_value_pair_init: Out of memory allocating key" );
-
- return -1L;
- }
-
- iscsi_key_value_pair *key_value_pair = (iscsi_key_value_pair *) malloc( sizeof(struct iscsi_key_value_pair) );
-
- if ( key_value_pair == NULL ) {
- logadd( LOG_ERROR, "iscsi_global_key_value_pair_init: Out of memory allocating key value pair" );
-
- iscsi_hashmap_key_destroy( hash_key );
-
- return -1L;
- }
-
- key_value_pair->value = lut[i].value;
- key_value_pair->list_range = lut[i].list_range;
- key_value_pair->type = lut[i].type;
- key_value_pair->flags = lut[i].flags;
- key_value_pair->state_mask = (1UL << i);
-
- const int rc = iscsi_hashmap_put( key_value_pairs, hash_key, key_len, (uint8_t *) key_value_pair );
-
- if ( rc < 0 ) {
- free( key_value_pair );
- iscsi_hashmap_key_destroy( hash_key );
-
- return rc;
- }
- }
-
- return 0L;
-}
-
-/**
- * @brief Allocates and initializes the iSCSI global vector structure.
- *
- * This function MUST be called before any iSCSI
- * related functions are used.\n
- * It is safe to call this function if the iSCSI
- * global vector already has been initialized
- * in which case this function does nothing.
- *
- * @return 0 if the iSCSI global vector has been initialized
- * successfully and is ready to use, a negative
- * error code otherwise (memory exhausted).
- */
-int iscsi_create()
-{
- if ( iscsi_globvec == NULL ) {
- iscsi_globals *globvec = (iscsi_globals *) malloc( sizeof(struct iscsi_globals) );
-
- if ( globvec == NULL ) {
- logadd( LOG_ERROR, "iscsi_create: Out of memory while allocating iSCSI global vector" );
-
- return -1L;
- }
-
- globvec->devices = iscsi_hashmap_create( 0UL );
-
- if ( globvec->devices == NULL ) {
- logadd( LOG_ERROR, "iscsi_create: Out of memory while allocating iSCSI global vector devices hash map" );
-
- free( globvec );
-
- return -1L;
- }
-
- globvec->portal_groups = iscsi_hashmap_create( 0UL );
-
- if ( globvec->portal_groups == NULL ) {
- logadd( LOG_ERROR, "iscsi_create: Out of memory while allocating iSCSI global vector portal groups hash map" );
-
- iscsi_hashmap_destroy( globvec->devices );
- free( globvec );
-
- return -1L;
- }
-
- globvec->target_nodes = iscsi_hashmap_create( 0UL );
-
- if ( globvec->target_nodes == NULL ) {
- logadd( LOG_ERROR, "iscsi_create: Out of memory while allocating iSCSI global vector target nodes hash map" );
-
- iscsi_hashmap_destroy( globvec->portal_groups );
- iscsi_hashmap_destroy( globvec->devices );
- free( globvec );
-
- return -1L;
- }
-
- globvec->sessions = iscsi_hashmap_create( 0UL );
-
- if ( globvec->sessions == NULL ) {
- logadd( LOG_ERROR, "iscsi_create: Out of memory while allocating iSCSI global vector sessions hash map" );
-
- iscsi_hashmap_destroy( globvec->target_nodes );
- iscsi_hashmap_destroy( globvec->portal_groups );
- iscsi_hashmap_destroy( globvec->devices );
- free( globvec );
-
- return -1L;
- }
-
- globvec->session_key_value_pairs = iscsi_hashmap_create( 32UL );
-
- if ( globvec->session_key_value_pairs == NULL ) {
- logadd( LOG_ERROR, "iscsi_create: Out of memory while allocating iSCSI global vector session key and value pairs hash map" );
-
- iscsi_hashmap_destroy( globvec->sessions );
- iscsi_hashmap_destroy( globvec->target_nodes );
- iscsi_hashmap_destroy( globvec->portal_groups );
- iscsi_hashmap_destroy( globvec->devices );
- free( globvec );
-
- return -1L;
- }
-
- int rc = iscsi_global_key_value_pair_init( globvec->session_key_value_pairs, &iscsi_session_key_value_pair_lut[1] );
-
- if ( globvec->connection_key_value_pairs == NULL ) {
- logadd( LOG_ERROR, "iscsi_create: Out of memory while initializing iSCSI global vector session key and value pairs hash map" );
-
- iscsi_hashmap_iterate( globvec->session_key_value_pairs, iscsi_hashmap_key_destroy_value_callback, NULL );
- iscsi_hashmap_destroy( globvec->session_key_value_pairs );
- iscsi_hashmap_destroy( globvec->sessions );
- iscsi_hashmap_destroy( globvec->target_nodes );
- iscsi_hashmap_destroy( globvec->portal_groups );
- iscsi_hashmap_destroy( globvec->devices );
- free( globvec );
-
- return -1L;
- }
-
- globvec->connections = iscsi_hashmap_create( 0UL );
-
- if ( globvec->connections == NULL ) {
- logadd( LOG_ERROR, "iscsi_create: Out of memory while allocating iSCSI global vector connections hash map" );
-
- iscsi_hashmap_iterate( globvec->session_key_value_pairs, iscsi_hashmap_key_destroy_value_callback, NULL );
- iscsi_hashmap_destroy( globvec->session_key_value_pairs );
- iscsi_hashmap_destroy( globvec->sessions );
- iscsi_hashmap_destroy( globvec->target_nodes );
- iscsi_hashmap_destroy( globvec->portal_groups );
- iscsi_hashmap_destroy( globvec->devices );
- free( globvec );
-
- return -1L;
- }
-
- globvec->connection_key_value_pairs = iscsi_hashmap_create( 32UL );
-
- if ( globvec->connection_key_value_pairs == NULL ) {
- logadd( LOG_ERROR, "iscsi_create: Out of memory while allocating iSCSI global vector connection key and value pairs hash map" );
-
- iscsi_hashmap_destroy( globvec->connections );
- iscsi_hashmap_iterate( globvec->session_key_value_pairs, iscsi_hashmap_key_destroy_value_callback, NULL );
- iscsi_hashmap_destroy( globvec->session_key_value_pairs );
- iscsi_hashmap_destroy( globvec->sessions );
- iscsi_hashmap_destroy( globvec->target_nodes );
- iscsi_hashmap_destroy( globvec->portal_groups );
- iscsi_hashmap_destroy( globvec->devices );
- free( globvec );
-
- return -1L;
- }
-
- rc = iscsi_global_key_value_pair_init( globvec->connection_key_value_pairs, &iscsi_connection_key_value_pair_lut[0] );
-
- if ( rc < 0 ) {
- logadd( LOG_ERROR, "iscsi_create: Out of memory while initializing iSCSI global vector connection key and value pairs hash map" );
-
- iscsi_hashmap_iterate( globvec->connection_key_value_pairs, iscsi_hashmap_key_destroy_value_callback, NULL );
- iscsi_hashmap_destroy( globvec->connection_key_value_pairs );
- iscsi_hashmap_destroy( globvec->connections );
- iscsi_hashmap_iterate( globvec->session_key_value_pairs, iscsi_hashmap_key_destroy_value_callback, NULL );
- iscsi_hashmap_destroy( globvec->session_key_value_pairs );
- iscsi_hashmap_destroy( globvec->sessions );
- iscsi_hashmap_destroy( globvec->target_nodes );
- iscsi_hashmap_destroy( globvec->portal_groups );
- iscsi_hashmap_destroy( globvec->devices );
- free( globvec );
-
- return -1L;
- }
-
- globvec->flags = 0L;
- globvec->max_sessions = 0UL;
- globvec->chap_group = 0L;
-
- iscsi_globvec = globvec;
- }
-
- return 0L;
-}
-
-/**
- * @brief Deallocates all resources acquired by iscsi_create.
- *
- * This function MUST be called before program termination
- * for ensuring proper clean up.\n
- * After this function returns, calling any iSCSI related
- * function except iscsi_create is strictly forbidden.\n
- * It is safe to call this function if the iSCSI global
- * vector already has been destroyed in which case this
- * function does nothing.
- */
-void iscsi_destroy()
-{
- iscsi_globals *globvec = iscsi_globvec;
-
- if ( globvec != NULL ) {
- iscsi_globvec = NULL;
-
- iscsi_hashmap_iterate( globvec->connection_key_value_pairs, iscsi_hashmap_key_destroy_value_callback, NULL );
- iscsi_hashmap_destroy( globvec->connection_key_value_pairs );
- globvec->connection_key_value_pairs = NULL;
-
- iscsi_hashmap_destroy( globvec->connections );
- globvec->connections = NULL;
-
- iscsi_hashmap_iterate( globvec->session_key_value_pairs, iscsi_hashmap_key_destroy_value_callback, NULL );
- iscsi_hashmap_destroy( globvec->session_key_value_pairs );
- globvec->session_key_value_pairs = NULL;
-
- iscsi_hashmap_destroy( globvec->sessions );
- globvec->sessions = NULL;
-
- iscsi_hashmap_destroy( globvec->target_nodes );
- globvec->target_nodes = NULL;
-
- iscsi_hashmap_destroy( globvec->portal_groups );
- globvec->portal_groups = NULL;
-
- iscsi_hashmap_destroy( globvec->devices );
- globvec->devices = NULL;
-
- free( globvec );
- }
-}
/**
* @brief Allocates and appends a buffer and sprintf's it.
@@ -361,7 +76,7 @@ void iscsi_destroy()
uint8_t *iscsi_vsprintf_append_realloc(char *buf, const char *format, va_list args)
{
va_list args_copy;
- uint orig_size = 0;
+ uint orig_size = 0U;
if ( buf != NULL )
orig_size = (uint) strlen( (char *) buf );
@@ -370,7 +85,7 @@ uint8_t *iscsi_vsprintf_append_realloc(char *buf, const char *format, va_list ar
uint new_size = vsnprintf( NULL, 0, format, args_copy );
va_end( args_copy );
- new_size += orig_size + 1;
+ new_size += (uint) (orig_size + 1U);
uint8_t *new_buf = realloc( buf, new_size );
@@ -380,7 +95,7 @@ uint8_t *iscsi_vsprintf_append_realloc(char *buf, const char *format, va_list ar
return NULL;
}
- vsnprintf( (char *) new_buf + orig_size, (new_size - orig_size), format, args );
+ vsnprintf( (char *) (new_buf + orig_size), (new_size - orig_size), format, args );
return new_buf;
}
@@ -444,19 +159,50 @@ uint8_t *iscsi_sprintf_alloc(const char *format, ... )
}
/**
+ * @brief Copies a string with additional padding character to fill in a specified size.
+ *
+ * This function does NOT pad, but truncates
+ * instead if the string length equals or is
+ * larger than the maximum allowed size.
+ *
+ * @param[in] dst Pointer to destination string to copy
+ * with padding and may NOT be NULL, so be
+ * careful.
+ * @param[in] src Pointer to string for copying. NULL
+ * is NOT allowed here, take caution.
+ * @param[in] size Total size in bytes for padding.
+ * @param[in] pad Padding character to use.
+ */
+void iscsi_strcpy_pad(char *dst, const char *src, const size_t size, const int pad)
+{
+ const size_t len = strlen( src );
+
+ if ( len < size ) {
+ memcpy( dst, src, len );
+ memset( (dst + len), pad, (size - len) );
+ } else {
+ memcpy( dst, src, size );
+ }
+}
+
+/**
* @brief Creates an empty hash map with either specified or default capacity.
*
- * Creates a ultra hardcore speed optimized empty hash map and
- * allocates enough buckets to hold default capacity elements.\n
- * The speed optimizations require all keys having a size of
- * a multiple of 8 bytes with zero padding. Also the capacity
- * always nas to be a power of two.\n
- * TODO: Move all hash map related functions to different source file
- * later and implement in a lock-free way for better concurrency.
+ * Creates a ultra hardcore speed optimized empty
+ * hash map and allocates enough buckets to hold
+ * default capacity elements.\n
+ * The speed optimizations require all keys
+ * having a size of a multiple of 8 bytes with
+ * zero padding. Also the capacity always nas
+ * to be a power of two.\n
+ * TODO: Move all hash map related functions to
+ * different source file later and implement in
+ * a lock-free way for better concurrency.
*
* @param[in] capacity Desired initial capacity, will be rounded up
- * to the nearest power of two. If set to 0, a default capacity of
- * 32 buckets will be used instead.
+ * to the nearest power of two. If set to 0, a
+ * default capacity of 32 buckets will be used
+ * instead.
* @return A pointer to the hash map structure or NULL in case of an error.
*/
iscsi_hashmap *iscsi_hashmap_create(const uint capacity)
@@ -469,16 +215,13 @@ iscsi_hashmap *iscsi_hashmap_create(const uint capacity)
return map;
}
- if ( capacity > 0UL ) {
- uint new_capacity = (capacity + 1); // 1UL << (lg(capacity - 1) + 1)
+ if ( capacity > 0U ) {
+ uint new_capacity = (uint) iscsi_align_pow2_ceil( (uint32_t) capacity );
- new_capacity |= (new_capacity >> 1UL);
- new_capacity |= (new_capacity >> 2UL);
- new_capacity |= (new_capacity >> 4UL);
- new_capacity |= (new_capacity >> 8UL);
- new_capacity |= (new_capacity >> 16UL);
+ if ( (capacity + 1U) > (uint) ((new_capacity * 3U) >> 2U) )
+ new_capacity += new_capacity; // If specified capacity does not fit in 75% of requested capacity, double actual size
- map->capacity = ++new_capacity; // Round up actual new capacity to nearest power of two
+ map->capacity = new_capacity; // Round up actual new capacity to nearest power of two
} else {
map->capacity = ISCSI_HASHMAP_DEFAULT_CAPACITY;
}
@@ -493,11 +236,11 @@ iscsi_hashmap *iscsi_hashmap_create(const uint capacity)
return NULL;
}
- map->cap_load = (uint) ((map->capacity * 3UL) >> 2UL); // 75% of capacity
- map->count = 0;
- map->removed_count = 0;
- map->first = NULL;
- map->last = (iscsi_hashmap_bucket *) &map->first;
+ iscsi_list_create( &map->list );
+
+ map->last_insert_id = 0ULL;
+ map->cap_load = (uint) ((map->capacity * 3U) >> 2U); // 75% of capacity
+ map->count = 0U;
return map;
}
@@ -505,10 +248,11 @@ iscsi_hashmap *iscsi_hashmap_create(const uint capacity)
/**
* @brief Deallocates the hash map objects and buckets, not elements. Use iscsi_hashmap_iterate to deallocate the elements themselves.
*
- * Deallocates all buckets and the hash map itself allocated
- * by iscsi_hashmap_create. The elements associated with the
- * buckets are NOT freed by this function, this has to be done
- * either manually or using the function iscsi_hashmap_iterate.
+ * Deallocates all buckets and the hash map itself
+ * allocated by iscsi_hashmap_create. The elements
+ * associated with the buckets are NOT freed by this
+ * function, this has to be done either manually or
+ * using the function iscsi_hashmap_iterate.
*
* @param[in] map Pointer to hash map and its buckets to deallocate.
* If this is NULL, nothing is done.
@@ -516,118 +260,189 @@ iscsi_hashmap *iscsi_hashmap_create(const uint capacity)
void iscsi_hashmap_destroy(iscsi_hashmap *map)
{
if ( map != NULL ) {
- if ( map->buckets != NULL )
+ if ( map->buckets != NULL ) {
free( map->buckets );
+ map->buckets = NULL;
+ }
+
free( map );
}
}
/**
- * @brief Puts an old bucket into a resized hash map.
+ * @brief Creates a key suitable for hash map usage (ensures 8-byte boundary and zero padding).
*
- * Puts an old bucket into a resized hash map.
+ * Creates a key from data and size and ensures
+ * its requirements for usage in hash map buckets.\n
+ * Currently keys to be used in a hash map bucket
+ * require a size of multiple by 8 bytes with
+ * the zero padding.
*
- * @param[in] map Pointer to resized hash map, may NOT be NULL, so
- * be careful.
- * @param[in] old_entry The old bucket to be put into the resized
- * hash map.
- * @return New bucket where the bucket has been put into.
+ * @param[in] data Pointer to data to construct the key
+ * from and may NOT be NULL, so be careful.
+ * @param[in] len Length of the data to construct the key
+ * from, MUST be larger than 0, so be careful.
+ * @return Pointer to generated usable key or NULL in
+ * case of an error (usually memory exhaustion).
*/
-static iscsi_hashmap_bucket *iscsi_hashmap_resize_entry(iscsi_hashmap *map, const iscsi_hashmap_bucket *old_entry)
+uint8_t *iscsi_hashmap_key_create(const uint8_t *data, const size_t len)
{
- uint32_t index = old_entry->hash & (map->capacity - 1);
+ const size_t key_size = ISCSI_ALIGN(len, ISCSI_HASHMAP_KEY_ALIGN);
+ uint8_t *key = (uint8_t *) malloc( key_size );
- for ( ;; ) {
- iscsi_hashmap_bucket *entry = &map->buckets[index];
+ if ( key == NULL ) {
+ logadd( LOG_ERROR, "iscsi_hashmap_key_create: Out of memory while allocating iSCSI hash map key" );
- if ( entry->key == NULL ) {
- *entry = *old_entry;
+ return key;
+ }
- return entry;
- }
+ memcpy( key, data, len );
+ memset( (key + len), 0, (key_size - len) ); // Zero pad additional bytes in case length is not a multiple of 8
- index = (index + 1) & (map->capacity - 1);
- }
+ return key;
}
/**
- * @brief Resizes a hash map by doubling its bucket capacity and purges any removed buckets.
+ * @brief Creates an unique key identifier suitable for hash map usage (ensures 8-byte boundary and zero padding).
*
- * Resizes a hash map by doubling its bucket capacity. if any
- * buckets have been removed, they are finally purged. The
- * old bucket list is freed after the resize operation has
- * been finished.
+ * Creates a unique key identifier by adding
+ * the capacity and element count plus one
+ * together as an unsigned 64-bit integer
+ * and uses the resulting value as key data
+ * which ensure the requirements for usage
+ * in hash map buckets.\n
+ * This function returns the same identifier if
+ * the previously generated key identifier has
+ * NOT been added to the hash map yet.\n
+ * Currently keys to be used in a hash map bucket
+ * require a size of multiple by 8 bytes with
+ * the zero padding.
*
- * @param[in] map Pointer to hash map to resize. This may NOT be
- * NULL, so be careful.
- * @retval -1 An error occured during resize.
- * @retval 0 Hash map has been resized successfully.
+ * @param[in] map Pointer to hash map to construct the key
+ * for and may NOT be NULL, so be careful.
+ * @param[out] key Pointer to key to store the
+ * unique key in. NULL is NOT allowed here, be
+ * careful.
*/
-static int iscsi_hashmap_resize(iscsi_hashmap *map)
+void iscsi_hashmap_key_create_id(iscsi_hashmap *map, uint64_t *key)
{
- const uint old_capacity = map->capacity;
- iscsi_hashmap_bucket *old_buckets = map->buckets;
-
- map->capacity <<= ISCSI_HASHMAP_RESIZE_SHIFT;
-
- map->buckets = (iscsi_hashmap_bucket *) calloc( map->capacity, sizeof(struct iscsi_hashmap_bucket) );
-
- if ( map->buckets == NULL ) {
- map->capacity = old_capacity;
- map->buckets = old_buckets;
+ *key = ++map->last_insert_id;
+}
- return -1;
- }
+/**
+ * @brief Deallocates all resources acquired by iscsi_hashmap_create_key.
+ *
+ * Deallocates a key allocated with the function
+ * iscsi_hashmap_key_create.
+ *
+ * @param[in] key Pointer to key to deallocate, may NOT
+ * be NULL, so be careful.
+ */
+void iscsi_hashmap_key_destroy(uint8_t *key)
+{
+ free( key );
+}
- map->cap_load = (uint) ((map->capacity * 3UL) >> 2UL); // 75% of capacity
- map->last = (iscsi_hashmap_bucket *) &map->first;
- map->count -= map->removed_count;
- map->removed_count = 0;
+/**
+ * @brief Deallocates a key in a hash map.
+ *
+ * Default callback function for deallocation of
+ * hash map resources by simply deallocating
+ * the key.
+ *
+ * @param[in] key Pointer to zero padded key. NULL is
+ * an invalid pointer here, so be careful.
+ * @param[in] key_size Number of bytes for the key.
+ * @param[in] value Value of the key, not used here.
+ * @param[in,out] user_data This argument is not used by
+ * this function and should be always NULL for now, as
+ * there is a possibility for future usage.
+ * @return Always returns 0 as this function cannot fail.
+ */
+int iscsi_hashmap_key_destroy_callback(uint8_t *key, const size_t key_size, uint8_t *value, uint8_t *user_data)
+{
+ iscsi_hashmap_key_destroy( key );
- do {
- iscsi_hashmap_bucket *current = map->last->next;
+ return 0;
+}
- if ( current->key == NULL ) {
- map->last->next = current->next;
+/**
+ * @brief Deallocates a value in a hash map.
+ *
+ * Default callback function for deallocation of
+ * hash map resources by simply deallocating
+ * the value.
+ *
+ * @param[in] key Pointer to zero padded key. NULL is
+ * an invalid pointer here, so be careful.
+ * @param[in] key_size Number of bytes for the key.
+ * @param[in] value Value of the key, NULL is allowed.
+ * @param[in,out] user_data This argument is not used by
+ * this function and should be always NULL for now, as
+ * there is a possibility for future usage.
+ * @return Always returns 0 as this function cannot fail.
+ */
+int iscsi_hashmap_destroy_value_callback(uint8_t *key, const size_t key_size, uint8_t *value, uint8_t *user_data)
+{
+ if ( value != NULL )
+ free( value );
- continue;
- }
+ return 0;
+}
- map->last->next = iscsi_hashmap_resize_entry(map, map->last->next);
- map->last = map->last->next;
- } while ( map->last->next != NULL );
+/**
+ * @brief Deallocates a key / value pair in a hash map by calling free (default destructor).
+ *
+ * Default callback function for deallocation of
+ * allocated hash map resources by simply calling
+ * free.
+ *
+ * @param[in] key Pointer to zero padded key. NULL is
+ * an invalid pointer here, so be careful.
+ * @param[in] key_size Number of bytes for the key,
+ * @param[in] value Value of the key, NULL is allowed.
+ * @param[in,out] user_data This argument is not used by
+ * this function and should be always NULL for now, as
+ * there is a possibility for future usage.
+ * @return Always returns 0 as this function cannot fail.
+ */
+int iscsi_hashmap_key_destroy_value_callback(uint8_t *key, const size_t key_size, uint8_t *value, uint8_t *user_data)
+{
+ if ( value != NULL )
+ free( value );
- free( old_buckets );
+ iscsi_hashmap_key_destroy( key );
return 0;
}
/**
- * @brief Calculates the hash code of data with a specified length.
+ * @brief Compares two hash keys with equal length match.
*
- * Calculates the hash code of data with a specified length.
+ * This function is optimized to compare
+ * 8 bytes at once and requires number
+ * of blocks specified in QWORDs. Both
+ * keys must be equal in size of a
+ * QWORD alignment.
*
- * @param[in] data Pointer to data to be hashed, NULL is NOT
- * an allowed here, so be careful. Data needs 8 byte alignment
- * and needs to be zero padded.
- * @param[in] len Number of bytes of hash data, must be larger
- * than 0 and is rounded up to the nearest 8 byte integer prior
- * calculating the hash code, so be careful.
- * @return Hash code of data.
+ * @param[in] buf Pointer to key buffer of which key
+ * to compare. May NOT be NULL, so be
+ * careful.
+ * @param[in] key Pointer to key to compare with.
+ * NULL is NOT allowed here, take
+ * caution.
+ * @param[in] num_blocks Number of blocks in QWORDs (8 bytes)
+ * to be compared.
*/
-static inline uint32_t iscsi_hashmap_hash_data(const uint8_t *data, const size_t len)
+static inline bool iscsi_hashmap_key_eq(const uint64_t *buf, const uint64_t *key, size_t num_blocks)
{
- const uint64_t *hash_data = (const uint64_t *) data;
- size_t num_blocks = iscsi_align(len, ISCSI_HASHMAP_KEY_ALIGN) >> ISCSI_HASHMAP_KEY_ALIGN_SHIFT;
- uint64_t hash = ISCSI_HASHMAP_HASH_INITIAL;
-
do {
- hash ^= *hash_data++;
- hash *= ISCSI_HASHMAP_HASH_MUL;
+ if ( *buf++ != *key++ )
+ return false;
} while ( --num_blocks > 0UL );
- return (uint32_t) (hash ^ hash >> 32ULL);
+ return true;
}
/**
@@ -649,94 +464,130 @@ static inline uint32_t iscsi_hashmap_hash_data(const uint8_t *data, const size_t
*/
static iscsi_hashmap_bucket *iscsi_hashmap_find_entry(iscsi_hashmap *map, const uint8_t *key, size_t key_size, uint32_t hash)
{
- uint32_t index = hash & (map->capacity - 1);
+ const size_t num_blocks = ISCSI_ALIGN(key_size, ISCSI_HASHMAP_KEY_ALIGN) >> ISCSI_HASHMAP_KEY_ALIGN_SHIFT;
+ uint32_t index = (hash & (map->capacity - 1U));
for ( ;; ) {
iscsi_hashmap_bucket *entry = &map->buckets[index];
- if ( (entry->key == NULL && entry->value == NULL) || (entry->key != NULL && entry->key_size == key_size && entry->hash == hash && (memcmp( entry->key, key, key_size ) == 0)) )
+ if ( ((entry->key == NULL) && (entry->value == NULL)) || ((entry->key != NULL) && (entry->key_size == key_size) && (entry->hash == hash) && iscsi_hashmap_key_eq( (uint64_t *) entry->key, (uint64_t *) key, num_blocks )) )
return entry;
- index = (index + 1) & (map->capacity - 1);
+ index = ((index + 1UL) & (map->capacity - 1U));
}
}
/**
- * @brief Creates a key suitable for hashmap usage (ensures 8-byte boundary and zero padding).
+ * @brief Calculates the hash code of data with a specified length.
*
- * Creates a key from data and size and ensures
- * its requirements for usage in hash map buckets.\n
- * Currently keys to be used in a hash map bucket
- * require a size of multiple by 8 bytes with
- * the zero padding.
+ * Calculates the hash code of data with a specified
+ * length.
*
- * @param[in] data Pointer to data to construct the key
- * from and may NOT be NULL, so be careful.
- * @param[in] len Length of the data to construct the key
- * from, MUST be larger than 0, so be careful.
- * @return Pointer to generated usable key or NULL in
- * case of an error (usually memory exhaustion).
+ * @param[in] data Pointer to data to be hashed, NULL is NOT
+ * an allowed here, so be careful. Data needs 8 byte alignment
+ * and needs to be zero padded.
+ * @param[in] len Number of bytes of hash data, must be larger
+ * than 0 and is rounded up to the nearest 8 byte integer prior
+ * calculating the hash code, so be careful.
+ * @return Hash code of data.
*/
-uint8_t *iscsi_hashmap_key_create(const uint8_t *data, const size_t len)
+static inline uint32_t iscsi_hashmap_key_hash_data(const uint8_t *data, const size_t len)
{
- const size_t key_size = iscsi_align(len, ISCSI_HASHMAP_KEY_ALIGN);
- uint8_t *key = (uint8_t *) malloc( key_size );
-
- if ( key == NULL ) {
- logadd( LOG_ERROR, "iscsi_hashmap_key_create: Out of memory while allocating iSCSI hash map key" );
-
- return key;
- }
+ const uint64_t *hash_data = (const uint64_t *) data;
+ size_t num_blocks = ISCSI_ALIGN(len, ISCSI_HASHMAP_KEY_ALIGN) >> ISCSI_HASHMAP_KEY_ALIGN_SHIFT;
+ uint64_t hash = ISCSI_HASHMAP_HASH_INITIAL;
- memcpy( key, data, len );
- memset( key + len, 0, (key_size - len) ); // Zero pad additional bytes in case length is not a multiple of 8
+ do {
+ hash ^= *hash_data++;
+ hash *= ISCSI_HASHMAP_HASH_MUL;
+ } while ( --num_blocks > 0UL );
- return key;
+ return (uint32_t) (hash ^ hash >> 32ULL);
}
/**
- * @brief Deallocates all resources acquired by iscsi_hashmap_create_key.
+ * @brief Puts an old bucket into a resized hash map.
*
- * Deallocates a key allocated with the function
- * iscsi_hashmap_key_create.
+ * Puts an old bucket into a resized hash map.
*
- * @param[in] key Pointer to key to deallocate, may NOT
- * be NULL, so be careful.
+ * @param[in] map Pointer to resized hash map, may NOT be NULL, so
+ * be careful.
+ * @param[in] old_entry The old bucket to be put into the resized
+ * hash map.
+ * @return New bucket where the bucket has been put into.
*/
-void iscsi_hashmap_key_destroy(uint8_t *key) {
- free( key );
+static iscsi_hashmap_bucket *iscsi_hashmap_resize_entry(iscsi_hashmap *map, const iscsi_hashmap_bucket *old_entry)
+{
+ uint32_t index = (old_entry->hash & (map->capacity - 1U));
+
+ for ( ;; ) {
+ iscsi_hashmap_bucket *entry = &map->buckets[index];
+
+ if ( entry->key == NULL ) {
+ entry->key = old_entry->key;
+ entry->key_size = old_entry->key_size;
+ entry->hash = old_entry->hash;
+ entry->value = old_entry->value;
+
+ return entry;
+ }
+
+ index = ((index + 1) & (map->capacity - 1U));
+ }
}
/**
- * @brief Deallocates all key / value pairs in a hash map by calling free (default destructor).
+ * @brief Resizes a hash map by doubling its bucket capacity.
*
- * Default callback function for deallocation of
- * allocated hash map resources by simply calling
- * free.
+ * Resizes a hash map by doubling its bucket capacity The
+ * old bucket list is freed after the
+ * resize operation has been finished.
*
- * @param[in] key Pointer to zero padded key. NULL is
- * an invalid pointer here, so be careful.
- * @param[in] key_size Number of bytes for the key, MUST
- * be a multiple of 8 bytes which is NOT checked, so
- * be careful.
- * @param[in] value Value of the key, NULL is allowed.
- * @param[in,out] user_data This argument is not used by
- * this function and should be always NULL for now, as
- * there is a possibility for future usage.
- * @return Always returns 0 as this function cannot fail.
+ * @param[in] map Pointer to hash map to resize. This may NOT be
+ * NULL, so be careful.
+ * @retval -1 An error occured during resize.
+ * @retval 0 Hash map has been resized successfully.
*/
-int iscsi_hashmap_key_destroy_value_callback(uint8_t *key, const size_t key_size, uint8_t *value, uint8_t *user_data)
+static int iscsi_hashmap_resize(iscsi_hashmap *map)
{
- if ( value != NULL )
- free( value );
+ const uint old_capacity = map->capacity;
+ iscsi_hashmap_bucket *old_buckets = map->buckets;
+ iscsi_list old_list = {map->list.head, map->list.tail, map->list.pred};
- iscsi_hashmap_key_destroy( key );
+ map->capacity <<= ISCSI_HASHMAP_RESIZE_SHIFT;
+
+ map->buckets = (iscsi_hashmap_bucket *) calloc( map->capacity, sizeof(struct iscsi_hashmap_bucket) );
+
+ if ( map->buckets == NULL ) {
+ map->capacity = old_capacity;
+ map->buckets = old_buckets;
+
+ return -1;
+ }
+
+ map->cap_load = (uint) ((map->capacity * 3U) >> 2U); // 75% of capacity
+
+ iscsi_list_clear( &map->list );
+
+ iscsi_hashmap_bucket *current;
+ iscsi_hashmap_bucket *tmp;
+
+ iscsi_list_foreach_safe_node ( &old_list, current, tmp ) {
+ if ( current->key == NULL )
+ continue;
+
+ current = iscsi_hashmap_resize_entry( map, current );
+
+ iscsi_list_enqueue( &map->list, &current->node );
+ }
- return 0L;
+ free( old_buckets );
+
+ return 0;
}
/**
- * @brief Assigns key / value pair to hash map without making copies.
+ * @brief Assigns key / value pair to hash map at the tail of doubly linked list without making copies.
*
* Adds a key / value pair to a specified hash map
* bucket list, if it doesn't exist already. The
@@ -745,16 +596,16 @@ int iscsi_hashmap_key_destroy_value_callback(uint8_t *key, const size_t key_size
* nor of the value. Keys should be allocated using
* the function iscsi_hashmap_key_create or freed by
* using iscsi_hashmap_key_destroy in order to
- * ensure the alignment and padding requirements.
+ * ensure the alignment and padding requirements.\n
+ * The new pair will always added to the tail of the
+ * linked list.
*
* @param[in] map Pointer to hash map where the key and
* value pair should be added to, may NOT be NULL, so
* be careful.
* @param[in] key Pointer to zero padded key. NULL is
* an invalid pointer here, so be careful.
- * @param[in] key_size Number of bytes for the key, MUST
- * be a multiple of 8 bytes which is NOT checked, so
- * be careful.
+ * @param[in] key_size Number of bytes for the key.
* @param[in] value Value of the key to add, NULL is
* allowed.
* @retval -1 Adding key / value pair would have required
@@ -764,16 +615,14 @@ int iscsi_hashmap_key_destroy_value_callback(uint8_t *key, const size_t key_size
*/
int iscsi_hashmap_put(iscsi_hashmap *map, uint8_t *key, const size_t key_size, uint8_t *value)
{
- if ( ((map->count + 1) > map->cap_load) && (iscsi_hashmap_resize( map ) < 0) )
+ if ( ((map->count + 1U) > map->cap_load) && (iscsi_hashmap_resize( map ) < 0) )
return -1;
- const uint32_t hash = iscsi_hashmap_hash_data( key, key_size );
+ const uint32_t hash = iscsi_hashmap_key_hash_data( key, key_size );
iscsi_hashmap_bucket *entry = iscsi_hashmap_find_entry( map, key, key_size, hash );
if ( entry->key == NULL ) {
- map->last->next = entry;
- map->last = entry;
- entry->next = NULL;
+ iscsi_list_enqueue( &map->list, &entry->node );
map->count++;
@@ -788,7 +637,7 @@ int iscsi_hashmap_put(iscsi_hashmap *map, uint8_t *key, const size_t key_size, u
}
/**
- * @brief Assigns key / value pair to hash map without making copies.
+ * @brief Assigns key / value pair to hash map at the tail of doubly linked list without making copies.
*
* Adds a key / value pair if it doesn't exist
* using the value of `*out_in_val`. If the pair
@@ -808,9 +657,7 @@ int iscsi_hashmap_put(iscsi_hashmap *map, uint8_t *key, const size_t key_size, u
* be careful.
* @param[in] key Pointer to zero padded key. NULL is
* an invalid pointer here, so be careful.
- * @param[in] key_size Number of bytes for the key, MUST
- * be a multiple of 8 bytes which is NOT checked, so
- * be careful.
+ * @param[in] key_size Number of bytes for the key.
* @param[in,out] out_in_value Value of the key to add,
* NULL is allowed.
* @retval -1 Adding key / value pair would have required
@@ -821,23 +668,21 @@ int iscsi_hashmap_put(iscsi_hashmap *map, uint8_t *key, const size_t key_size, u
*/
int iscsi_hashmap_get_put(iscsi_hashmap *map, uint8_t *key, const size_t key_size, uint8_t **out_in_value)
{
- if ( ((map->count + 1) > map->cap_load) && (iscsi_hashmap_resize( map ) < 0) )
+ if ( ((map->count + 1U) > map->cap_load) && (iscsi_hashmap_resize( map ) < 0) )
return -1;
- const uint32_t hash = iscsi_hashmap_hash_data( key, key_size );
+ const uint32_t hash = iscsi_hashmap_key_hash_data( key, key_size );
iscsi_hashmap_bucket *entry = iscsi_hashmap_find_entry( map, key, key_size, hash );
if ( entry->key == NULL ) {
- map->last->next = entry;
- map->last = entry;
- entry->next = NULL;
-
- map->count++;
+ iscsi_list_enqueue( &map->list, &entry->node );
- entry->value = *out_in_value;
entry->key = key;
entry->key_size = key_size;
entry->hash = hash;
+ entry->value = *out_in_value;
+
+ map->count++;
return 0;
}
@@ -867,9 +712,7 @@ int iscsi_hashmap_get_put(iscsi_hashmap *map, uint8_t *key, const size_t key_siz
* be careful.
* @param[in] key Pointer to zero padded key. NULL is
* an invalid pointer here, so be careful.
- * @param[in] key_size Number of bytes for the key, MUST
- * be a multiple of 8 bytes which is NOT checked, so
- * be careful.
+ * @param[in] key_size Number of bytes for the key.
* @param[in] value Value of the key to add, NULL is
* allowed.
* @param[in] callback Callback function which allows,
@@ -889,24 +732,22 @@ int iscsi_hashmap_get_put(iscsi_hashmap *map, uint8_t *key, const size_t key_siz
*/
int iscsi_hashmap_put_free(iscsi_hashmap *map, uint8_t *key, const size_t key_size, uint8_t *value, iscsi_hashmap_callback callback, uint8_t *user_data)
{
- if ( ((map->count + 1) > map->cap_load) && (iscsi_hashmap_resize( map ) < 0) )
+ if ( ((map->count + 1U) > map->cap_load) && (iscsi_hashmap_resize( map ) < 0) )
return -1;
- const uint32_t hash = iscsi_hashmap_hash_data( key, key_size );
+ const uint32_t hash = iscsi_hashmap_key_hash_data( key, key_size );
iscsi_hashmap_bucket *entry = iscsi_hashmap_find_entry( map, key, key_size, hash );
if ( entry->key == NULL ) {
- map->last->next = entry;
- map->last = entry;
- entry->next = NULL;
-
- map->count++;
+ iscsi_list_enqueue( &map->list, &entry->node );
entry->key = key;
entry->key_size = key_size;
entry->hash = hash;
entry->value = value;
+ map->count++;
+
return 0;
}
@@ -924,950 +765,1592 @@ int iscsi_hashmap_put_free(iscsi_hashmap *map, uint8_t *key, const size_t key_si
* Checks whether a specified key exists in a hash map.
*
* @param[in] map Pointer to the hash map to be searched
- * for the key to check for existence and may not be
+ * for the key to check for existence and may NOT be
* NULL, so take caution.
* @param[in] key Pointer to zero padded key. NULL is
* an invalid pointer here, so be careful.
- * @param[in] key_size Number of bytes for the key, MUST
- * be a multiple of 8 bytes which is NOT checked, so
- * be careful.
- * @retval TRUE The key exists.
- * @retval FALSE The key does not exist.
+ * @param[in] key_size Number of bytes for the key.
+ * @retval true The key exists.
+ * @retval false The key does not exist.
*/
-int iscsi_hashmap_contains(iscsi_hashmap *map, const uint8_t *key, const size_t key_size)
+bool iscsi_hashmap_contains(iscsi_hashmap *map, const uint8_t *key, const size_t key_size)
{
- const uint32_t hash = iscsi_hashmap_hash_data( key, key_size );
+ const uint32_t hash = iscsi_hashmap_key_hash_data( key, key_size );
iscsi_hashmap_bucket *entry = iscsi_hashmap_find_entry( map, key, key_size, hash );
- return entry->key != NULL;
+ return (entry->key != NULL);
}
/**
* @brief Retrieves the value of a specified key.
*
- * Retrieves the value of a specified key from a hash map. Since the
- * hash map supports NULL values, it is stored in an output variable.
+ * Retrieves the value of a specified key from a hash
+ * map. Since the hash map supports NULL values, it
+ * is stored in an output variable.
*
* @param[in] map Pointer to the hash map to be searched
- * for the key of which the value should be retrieved and
- * may not be NULL, so take caution.
+ * for the key of which the value should be
+ * retrieved and may NOT be NULL, so take
+ * caution.
* @param[in] key Pointer to zero padded key. NULL is
* an invalid pointer here, so be careful.
- * @param[in] key_size Number of bytes for the key, MUST
- * be a multiple of 8 bytes which is NOT checked, so
- * be careful.
+ * @param[in] key_size Number of bytes for the key.
* @param[out] out_value Pointer where the value of the found key
- * is stored, maybe NULL if either the key's value is NULL or
- * in case the key was not found. The pointer to the value itself
- * may NOT be NULL, so be careful.
- * @retval TRUE The key has been found and its value stored
+ * is stored, maybe NULL if either the key's value
+ * is NULL or in case the key was not found. The
+ * pointer to the value itself may NOT be NULL,
+ * so be careful.
+ * @retval 0 The key has been found and its value stored
* in the 'out_value' parameter.
- * @retval FALSE The key has not been found and NULL has been
+ * @retval -1 The key has not been found and NULL has been
* stored in the 'out_value' parameter.
*/
int iscsi_hashmap_get(iscsi_hashmap *map, const uint8_t *key, const size_t key_size, uint8_t **out_value)
{
- const uint32_t hash = iscsi_hashmap_hash_data( key, key_size );
+ const uint32_t hash = iscsi_hashmap_key_hash_data( key, key_size );
iscsi_hashmap_bucket *entry = iscsi_hashmap_find_entry( map, key, key_size, hash );
*out_value = entry->value;
- return entry->key != NULL;
+ return ((entry->key != NULL) ? 0 : -1);
}
/**
- * @brief Marks an element for removal by setting key and value both to NULL.
+ * @brief Removes an element both from the doubly linked list and by setting key and value both to NULL.
*
- * Removes an element from the bucket list of the hash map.
- * Buckets are marked as removed by setting their key and
- * value to NULL. The actual removal will be done upon next
- * resize operation. If the specified key already has been
- * removed, this function will do nothing.
+ * Removes an element from the bucket list of the
+ * hash map. Removing sets the buckets key and
+ * value to NULL.
+ * If the specified key already has been removed,
+ * this function will do nothing.
*
* @param[in] map Pointer to the hash map to remove from
- * and may not be NULL, so take caution.
+ * and may NOT be NULL, so take caution.
* @param[in] key Pointer to zero padded key. NULL is
* an invalid pointer here, so be careful.
- * @param[in] key_size Number of bytes for the key, MUST
- * be a multiple of 8 bytes which is NOT checked, so
- * be careful.
+ * @param[in] key_size Number of bytes for the key.
*/
void iscsi_hashmap_remove(iscsi_hashmap *map, const uint8_t *key, const size_t key_size)
{
- const uint32_t hash = iscsi_hashmap_hash_data( key, key_size );
+ const uint32_t hash = iscsi_hashmap_key_hash_data( key, key_size );
iscsi_hashmap_bucket *entry = iscsi_hashmap_find_entry( map, key, key_size, hash );
if ( entry->key != NULL ) {
+ iscsi_list_remove( &entry->node );
+
+ map->count--;
+
entry->key = NULL;
entry->value = NULL;
-
- map->removed_count++;
}
}
/**
- * @brief Marks an element for removal by setting key and value both to NULL, but invokes a callback function before actual marking for removal.
+ * @brief Removes an element both from the doubly linked list and by setting key and value both to NULL and but invokes a callback function before actual removal.
*
- * Removes an element from the bucket list of the hash map.\n
- * Buckets are marked as removed by setting their key and
- * value to NULL. The actual removal will be done upon next
- * resize operation. A callback function is invoked if the
- * key to be removed is found in the bucket list and allows,
- * e.g. to free any resources associated with the key. If
- * the key is not found, this function will do nothing.
+ * Removes an element from the bucket list of the
+ * hash map.\n
+ * Removing sets the buckets key and
+ * value to NULL. A callback function is invoked
+ * if the key to be removed is found in the
+ * bucket list and allows, e.g. to free any
+ * resources associated with the key. If the key
+ * is not found, this function will do nothing.
*
* @param[in] map Pointer to the hash map to remove from
- * and may not be NULL, so take caution.
+ * and may NOT be NULL, so take caution.
* @param[in] key Pointer to zero padded key. NULL is
* an invalid pointer here, so be careful.
- * @param[in] key_size Number of bytes for the key, MUST
- * be a multiple of 8 bytes which is NOT checked, so
- * be careful.
+ * @param[in] key_size Number of bytes for the key.
* @param[in] callback Callback function which allows,
- * for example, a dallocation of resources for the
- * key and value pair to be removed. The function is
- * invoked just before marking the key / value pair
- * as removed. This may NOT be NULL, so take caution.
+ * for example, a dallocation of resources for
+ * the key and value pair to be removed. The
+ * function is invoked just before marking the
+ * key / value pair as removed. This may NOT
+ * be NULL, so take caution.
* @param[in,out] user_data Pointer to user specific data
- * passed to the callback function in case more
- * information is needed.
+ * passed to the callback function in case
+ * more information is needed.
*/
void iscsi_hashmap_remove_free(iscsi_hashmap *map, const uint8_t *key, const size_t key_size, iscsi_hashmap_callback callback, uint8_t *user_data)
{
- const uint32_t hash = iscsi_hashmap_hash_data( key, key_size );
+ const uint32_t hash = iscsi_hashmap_key_hash_data( key, key_size );
iscsi_hashmap_bucket *entry = iscsi_hashmap_find_entry( map, key, key_size, hash );
if ( entry->key != NULL ) {
+ iscsi_list_remove( &entry->node );
+
+ map->count--;
+
callback( entry->key, entry->key_size, entry->value, user_data );
entry->key = NULL;
entry->value = NULL;
-
- map->removed_count++;
}
}
/**
- * @brief Retrieves the number of elements of the hash map, ignoring elements marked for removal.
+ * @brief Retrieves the number of elements of the hash map.
*
- * Returns the number of elements stored in the specified
- * hash map. Elements marked for removal are not included.
+ * Returns the number of elements stored in the
+ * specified hash map.
*
* @param[in] map Pointer to the hash map to count the
- * number of elements, may NOT be NULL, so take caution.
+ * number of elements, may NOT be NULL, so
+ * take caution.
* @return Number of elements currently in use by the
- * hash map. Buckets marked for removal are not counted.
+ * hash map.
*/
uint iscsi_hashmap_size(const iscsi_hashmap *map)
{
- return (map->count - map->removed_count);
+ return map->count;
}
/**
- * @brief Iterator with callback function invoked on each element which has not been removed.
+ * @brief Iterator with callback function invoked on each element.
*
- * An iterator through the elements of a specified
- * hash map which uses a callback function for each
- * element not marked for removal, which also can
- * abort the iteration, if necessary.
+ * An iterator through the elements of a
+ * specified hash map which uses a callback
+ * function for each element, which also
+ * can abort the iteration, if necessary.\n
+ * It is safe to remove the current iterating
+ * element in the callback function from the
+ * hash map.
*
* @param[in] map Pointer to the hash map to iterate
* through, may NOT be NULL, so take caution.
* @param[in] callback Callback function to be
- * invoked for each element not marked for removal
- * in the hash map. If the return value of the callback
- * function is below zero, the iteration will stop.
+ * invoked for each element. If the return
+ * value of the callback function is below
+ * zero, the iteration will stop.
* @param[in,out] user_data Pointer to user specific data
* passed to the callback function in case more
* information is needed.
* @return The return code from the last invoked
- * callback function. A negative value indicates an
- * abortion of the iteration process.
+ * callback function. A negative value indicates
+ * an abortion of the iteration process.
*/
int iscsi_hashmap_iterate(iscsi_hashmap *map, iscsi_hashmap_callback callback, uint8_t *user_data)
{
- iscsi_hashmap_bucket *current = map->first;
+ iscsi_hashmap_bucket *current;
+ iscsi_hashmap_bucket *tmp;
int err = 0;
- while ( current != NULL ) {
- if ( current->key != NULL ) {
- err = callback( current->key, current->key_size, current->value, user_data );
+ iscsi_list_foreach_safe_node ( &map->list, current, tmp ) {
+ if ( current->key == NULL )
+ continue;
- if ( err < 0 )
- break;
- }
+ err = callback( current->key, current->key_size, current->value, user_data );
- current = current->next;
+ if ( err < 0 )
+ break;
}
return err;
}
-/**
- * @brief Allocate and initialize an iSCSI BHS packet.
- *
- * Allocates an iSCSI packet data Basic Header Segment (BHS)
- * and zero fills the structure.
- *
- * @return a pointer to BHS structure with all fields
- * initialized or NULL if the allocation failed.
- */
-iscsi_bhs_packet *iscsi_create_packet()
-{
- iscsi_bhs_packet *bhs_pkt = (iscsi_bhs_packet *) malloc( sizeof(struct iscsi_bhs_packet) );
- if ( bhs_pkt == NULL ) {
- logadd( LOG_ERROR, "iscsi_create_packet: Out of memory while allocating BHS iSCSI packet data" );
+/// iSCSI global vector. MUST be initialized with iscsi_create before any iSCSI functions are used.
+iscsi_globals *iscsi_globvec = NULL;
- return bhs_pkt;
- }
+/// Read/write lock for iSCSI global vector. MUST be initialized with iscsi_create before any iSCSI functions are used.
+pthread_rwlock_t iscsi_globvec_rwlock;
- bhs_pkt->opcode = 0; // Initialize everything to zero
- bhs_pkt->opcode_fields[0] = 0;
- bhs_pkt->opcode_fields[1] = 0;
- bhs_pkt->opcode_fields[2] = 0;
- bhs_pkt->total_ahs_len = 0;
- bhs_pkt->ds_len[0] = 0;
- bhs_pkt->ds_len[1] = 0;
- bhs_pkt->ds_len[2] = 0;
- bhs_pkt->lun_opcode.lun = 0ULL;
- bhs_pkt->init_task_tag = 0UL;
- memset( bhs_pkt->opcode_spec_fields, 0, sizeof(bhs_pkt->opcode_spec_fields) );
+/// iSCSI connection negotation key and value pair lookup table.
+static const iscsi_key_value_pair_lut_entry iscsi_connection_key_value_pair_lut[] = {
+ { ISCSI_LOGIN_AUTH_SESSION_TEXT_KEY_HEADER_DIGEST, (uint8_t *) "None", (uint8_t *) "CRC32C\0None\0", ISCSI_TEXT_KEY_VALUE_PAIR_TYPE_LIST, 0 },
+ { ISCSI_LOGIN_AUTH_SESSION_TEXT_KEY_DATA_DIGEST, (uint8_t *) "None", (uint8_t *) "CRC32C\0None\0", ISCSI_TEXT_KEY_VALUE_PAIR_TYPE_LIST, 0 },
+ { ISCSI_LOGIN_AUTH_SESSION_TEXT_KEY_MAX_RECV_DS_LEN, (uint8_t *) "8192", (uint8_t *) "512\016777215\0", ISCSI_TEXT_KEY_VALUE_PAIR_TYPE_NUM_DECLARATIVE, ISCSI_TEXT_KEY_VALUE_PAIR_FLAGS_MULTI_NEGOTIATION | ISCSI_TEXT_KEY_VALUE_PAIR_FLAGS_OVERRIDE_DEFAULT },
+ { ISCSI_LOGIN_AUTH_SESSION_TEXT_KEY_OF_MARKER, (uint8_t *) "No", (uint8_t *) "Yes\0No\0", ISCSI_TEXT_KEY_VALUE_PAIR_TYPE_BOOL_AND, 0 },
+ { ISCSI_LOGIN_AUTH_SESSION_TEXT_KEY_IF_MARKER, (uint8_t *) "No", (uint8_t *) "Yes\0No\0", ISCSI_TEXT_KEY_VALUE_PAIR_TYPE_BOOL_AND, 0 },
+ { ISCSI_LOGIN_AUTH_SESSION_TEXT_KEY_OF_MARK_INT, (uint8_t *) "1", (uint8_t *) "1\0""65535\0", ISCSI_TEXT_KEY_VALUE_PAIR_TYPE_NUM_MIN, 0 },
+ { ISCSI_LOGIN_AUTH_SESSION_TEXT_KEY_IF_MARK_INT, (uint8_t *) "1", (uint8_t *) "1\0""65535\0", ISCSI_TEXT_KEY_VALUE_PAIR_TYPE_NUM_MIN, 0 },
+ { ISCSI_LOGIN_AUTH_SECURITY_TEXT_KEY_AUTH_METHOD, (uint8_t *) "None", (uint8_t *) "CHAP\0None\0", ISCSI_TEXT_KEY_VALUE_PAIR_TYPE_LIST, 0 },
+ { ISCSI_LOGIN_AUTH_SECURITY_TEXT_KEY_AUTH_METHOD_CHAP_CHAP_A, (uint8_t *) "5", (uint8_t *) "5\0", ISCSI_TEXT_KEY_VALUE_PAIR_TYPE_LIST, ISCSI_TEXT_KEY_VALUE_PAIR_FLAGS_CHAP_TYPE },
+ { ISCSI_LOGIN_AUTH_SECURITY_TEXT_KEY_AUTH_METHOD_CHAP_CHAP_N, (uint8_t *) "", (uint8_t *) "\0", ISCSI_TEXT_KEY_VALUE_PAIR_TYPE_DECLARATIVE, ISCSI_TEXT_KEY_VALUE_PAIR_FLAGS_CHAP_TYPE },
+ { ISCSI_LOGIN_AUTH_SECURITY_TEXT_KEY_AUTH_METHOD_CHAP_CHAP_R, (uint8_t *) "", (uint8_t *) "\0", ISCSI_TEXT_KEY_VALUE_PAIR_TYPE_DECLARATIVE, ISCSI_TEXT_KEY_VALUE_PAIR_FLAGS_CHAP_TYPE },
+ { ISCSI_LOGIN_AUTH_SECURITY_TEXT_KEY_AUTH_METHOD_CHAP_CHAP_I, (uint8_t *) "", (uint8_t *) "\0", ISCSI_TEXT_KEY_VALUE_PAIR_TYPE_DECLARATIVE, ISCSI_TEXT_KEY_VALUE_PAIR_FLAGS_CHAP_TYPE },
+ { ISCSI_LOGIN_AUTH_SECURITY_TEXT_KEY_AUTH_METHOD_CHAP_CHAP_C, (uint8_t *) "", (uint8_t *) "\0", ISCSI_TEXT_KEY_VALUE_PAIR_TYPE_DECLARATIVE, ISCSI_TEXT_KEY_VALUE_PAIR_FLAGS_CHAP_TYPE },
+ { NULL, NULL, NULL, ISCSI_TEXT_KEY_VALUE_PAIR_TYPE_INVALID, 0 }
+};
- return bhs_pkt;
-}
+/// iSCSI session negotation key and value pair lookup table.
+static const iscsi_key_value_pair_lut_entry iscsi_session_key_value_pair_lut[] = {
+ { ISCSI_LOGIN_AUTH_SESSION_TEXT_KEY_MAX_CONNECTIONS, (uint8_t *) "1", (uint8_t *) "1\0""65535\0", ISCSI_TEXT_KEY_VALUE_PAIR_TYPE_NUM_MIN, ISCSI_TEXT_KEY_VALUE_PAIR_FLAGS_DISCOVERY_IGNORE },
+ { ISCSI_LOGIN_AUTH_SESSION_TEXT_KEY_SEND_TARGETS, (uint8_t *) "", (uint8_t *) "\0", ISCSI_TEXT_KEY_VALUE_PAIR_TYPE_DECLARATIVE, ISCSI_TEXT_KEY_VALUE_PAIR_FLAGS_SPECIAL_HANDLING },
+ { ISCSI_LOGIN_AUTH_SECURITY_TEXT_KEY_TARGET_NAME, (uint8_t *) "", (uint8_t *) "\0", ISCSI_TEXT_KEY_VALUE_PAIR_TYPE_DECLARATIVE, ISCSI_TEXT_KEY_VALUE_PAIR_FLAGS_TARGET_DECLARATIVE },
+ { ISCSI_LOGIN_AUTH_SECURITY_TEXT_KEY_INITIATOR_NAME, (uint8_t *) "", (uint8_t *) "\0", ISCSI_TEXT_KEY_VALUE_PAIR_TYPE_DECLARATIVE, 0 },
+ { ISCSI_LOGIN_AUTH_SECURITY_TEXT_KEY_TARGET_ALIAS, (uint8_t *) "", (uint8_t *) "\0", ISCSI_TEXT_KEY_VALUE_PAIR_TYPE_DECLARATIVE, 0 },
+ { ISCSI_LOGIN_AUTH_SECURITY_TEXT_KEY_INITIATOR_ALIAS, (uint8_t *) "", (uint8_t *) "\0", ISCSI_TEXT_KEY_VALUE_PAIR_TYPE_DECLARATIVE, 0 },
+ { ISCSI_LOGIN_AUTH_SECURITY_TEXT_KEY_TARGET_ADDRESS, (uint8_t *) "", (uint8_t *) "\0", ISCSI_TEXT_KEY_VALUE_PAIR_TYPE_DECLARATIVE, ISCSI_TEXT_KEY_VALUE_PAIR_FLAGS_TARGET_DECLARATIVE },
+ { ISCSI_LOGIN_AUTH_SECURITY_TEXT_KEY_TARGET_PORTAL_GROUP_TAG, (uint8_t *) "1", (uint8_t *) "1\0""65535\0", ISCSI_TEXT_KEY_VALUE_PAIR_TYPE_NUM_DECLARATIVE, ISCSI_TEXT_KEY_VALUE_PAIR_FLAGS_TARGET_DECLARATIVE },
+ { ISCSI_LOGIN_AUTH_SESSION_TEXT_KEY_INITIAL_R2T, (uint8_t *) "Yes", (uint8_t *) "Yes\0No\0", ISCSI_TEXT_KEY_VALUE_PAIR_TYPE_BOOL_OR, ISCSI_TEXT_KEY_VALUE_PAIR_FLAGS_DISCOVERY_IGNORE },
+ { ISCSI_LOGIN_AUTH_SESSION_TEXT_KEY_IMMEDIATE_DATA, (uint8_t *) "Yes", (uint8_t *) "Yes\0No\0", ISCSI_TEXT_KEY_VALUE_PAIR_TYPE_BOOL_AND, ISCSI_TEXT_KEY_VALUE_PAIR_FLAGS_DISCOVERY_IGNORE },
+ { ISCSI_LOGIN_AUTH_SESSION_TEXT_KEY_MAX_BURST_LEN, (uint8_t *) "262144", (uint8_t *) "512\0""16777215\0", ISCSI_TEXT_KEY_VALUE_PAIR_TYPE_NUM_MIN, ISCSI_TEXT_KEY_VALUE_PAIR_FLAGS_DISCOVERY_IGNORE | ISCSI_TEXT_KEY_VALUE_PAIR_FLAGS_USE_OTHER_MAX_VALUE },
+ { ISCSI_LOGIN_AUTH_SESSION_TEXT_KEY_FIRST_BURST_LEN, (uint8_t *) "65536", (uint8_t *) "512\0""16777215\0", ISCSI_TEXT_KEY_VALUE_PAIR_TYPE_NUM_MIN, ISCSI_TEXT_KEY_VALUE_PAIR_FLAGS_DISCOVERY_IGNORE | ISCSI_TEXT_KEY_VALUE_PAIR_FLAGS_USE_PREVIOUS_VALUE },
+ { ISCSI_LOGIN_AUTH_SESSION_TEXT_KEY_DEFAULT_TIME_WAIT, (uint8_t *) "2", (uint8_t *) "0\0""3600\0", ISCSI_TEXT_KEY_VALUE_PAIR_TYPE_NUM_MAX, 0 },
+ { ISCSI_LOGIN_AUTH_SESSION_TEXT_KEY_DEFAULT_TIME_RETAIN, (uint8_t *) "20", (uint8_t *) "0\0""3600\0", ISCSI_TEXT_KEY_VALUE_PAIR_TYPE_NUM_MIN, 0 },
+ { ISCSI_LOGIN_AUTH_SESSION_TEXT_KEY_MAX_OUTSTANDING_R2T, (uint8_t *) "1", (uint8_t *) "1\0""65536\0", ISCSI_TEXT_KEY_VALUE_PAIR_TYPE_NUM_MIN, ISCSI_TEXT_KEY_VALUE_PAIR_FLAGS_DISCOVERY_IGNORE },
+ { ISCSI_LOGIN_AUTH_SESSION_TEXT_KEY_DATA_PDU_IN_ORDER, (uint8_t *) "Yes", (uint8_t *) "Yes\0No\0", ISCSI_TEXT_KEY_VALUE_PAIR_TYPE_BOOL_OR, ISCSI_TEXT_KEY_VALUE_PAIR_FLAGS_DISCOVERY_IGNORE },
+ { ISCSI_LOGIN_AUTH_SESSION_TEXT_KEY_DATA_SEQ_IN_ORDER, (uint8_t *) "Yes", (uint8_t *) "Yes\0No\0", ISCSI_TEXT_KEY_VALUE_PAIR_TYPE_BOOL_OR, ISCSI_TEXT_KEY_VALUE_PAIR_FLAGS_DISCOVERY_IGNORE },
+ { ISCSI_LOGIN_AUTH_SESSION_TEXT_KEY_ERR_RECOVERY_LEVEL, (uint8_t *) "0", (uint8_t *) "0\0""2\0", ISCSI_TEXT_KEY_VALUE_PAIR_TYPE_NUM_MIN, 0 },
+ { ISCSI_LOGIN_AUTH_SECURITY_TEXT_KEY_SESSION_TYPE, (uint8_t *) "Normal", (uint8_t *) "Normal\0Discovery\0", ISCSI_TEXT_KEY_VALUE_PAIR_TYPE_DECLARATIVE, 0 },
+ { NULL, NULL, NULL, ISCSI_TEXT_KEY_VALUE_PAIR_TYPE_INVALID, 0 }
+};
/**
- * @brief Free resources allocated by iscsi_create_packet.
+ * @brief Initializes a global key and value pair with type and list / range informations for fast access.
*
- * Deallocates all aquired resources by iscsi_create_packet.
+ * This function is used to initialize the iSCSI
+ * global key and value pair list containing
+ * the key types and allowed values.
*
- * @param[in] packet_data Pointer to packet data to deallocate. If this is
- * NULL, this function does nothing.
+ * @param[in] key_value_pairs Pointer to key and value pair hash map
+ * which should store the global key and value
+ * informations, may NOT be NULL, so take caution.
+ * @param[in] lut Lookup table to use for initialization.
+ * NULL is not allowed here, so be careful.
+ * @return 0 on success, a negative error code otherwise.
*/
-void iscsi_destroy_packet(iscsi_bhs_packet *packet_data)
+static int iscsi_global_key_value_pair_init(iscsi_hashmap *key_value_pairs, const iscsi_key_value_pair_lut_entry *lut)
{
- if (packet_data != NULL)
- free( packet_data );
+ for ( uint i = 0U, j = 1U; lut[i].key != NULL; i++, j += j ) {
+ iscsi_key_value_pair *key_value_pair = (iscsi_key_value_pair *) malloc( sizeof(struct iscsi_key_value_pair) );
+
+ if ( key_value_pair == NULL ) {
+ logadd( LOG_ERROR, "iscsi_global_key_value_pair_init: Out of memory allocating key value pair" );
+
+ return -1;
+ }
+
+ const uint key_len = (uint) (strlen( (char *) lut[i].key ) + 1U);
+
+ key_value_pair->value = lut[i].value;
+ key_value_pair->list_range = lut[i].list_range;
+ key_value_pair->type = lut[i].type;
+ key_value_pair->flags = lut[i].flags;
+ key_value_pair->state_mask = j;
+
+ const int rc = iscsi_hashmap_put( key_value_pairs, (uint8_t *) lut[i].key, key_len, (uint8_t *) key_value_pair );
+
+ if ( rc < 0 ) {
+ free( key_value_pair );
+
+ return rc;
+ }
+ }
+
+ return 0;
}
/**
- * @brief Allocate and initialize an iSCSI AHS packet and append to existing data stream.
+ * @brief Allocates and initializes the iSCSI global vector structure.
*
- * Constructs and appends an Additional Header Segment (AHS) to already allocated
- * packet data. There is no guarantee that the pointer stays the same. Any references
- * to the old structure need to be updated!\n
- * This function currently throws away any data beyond AHS.
+ * This function MUST be called before any iSCSI
+ * related functions are used.\n
+ * It is safe to call this function if the iSCSI
+ * global vector already has been initialized
+ * in which case this function does nothing.
*
- * @param[in] packet_data Pointer to packet data to append to. If NULL, a Basic
- * Header Segment (BHS) will be created and initialized before adding a first
- * AHS.
- * @param[in] ahs_len Length of AHS packet data to be appended.
- * @return New pointer to BHS structure with additional AHS attached or NULL in case
- * of an reallocation error or total AHS length exceeds 255 DWORD's.
+ * @return 0 if the iSCSI global vector has been initialized
+ * successfully and is ready to use, a negative
+ * error code otherwise (memory exhausted).
*/
-iscsi_bhs_packet *iscsi_append_ahs_packet(iscsi_bhs_packet *packet_data, const uint32_t ahs_len)
+int iscsi_create()
{
- if ( packet_data == NULL ) {
- packet_data = iscsi_create_packet();
+ pthread_rwlock_wrlock( &iscsi_globvec_rwlock );
- if ( packet_data == NULL )
- return packet_data;
- }
+ if ( iscsi_globvec == NULL ) {
+ iscsi_globals *globvec = (iscsi_globals *) malloc( sizeof(struct iscsi_globals) );
- const uint32_t old_pkt_size = (const uint32_t) sizeof(struct iscsi_bhs_packet) + (packet_data->total_ahs_len << 2UL);
- const uint32_t new_pkt_size = (uint32_t) (old_pkt_size + iscsi_align(ahs_len, ISCSI_ALIGN_SIZE));
+ if ( globvec == NULL ) {
+ logadd( LOG_ERROR, "iscsi_create: Out of memory while allocating iSCSI global vector" );
- if ( new_pkt_size > (sizeof(struct iscsi_bhs_packet) + ISCSI_MAX_AHS_SIZE) ) {
- logadd( LOG_ERROR, "iscsi_append_ahs_packet: Total numer of AHS packet size exceeds 255 DWORDs" );
+ pthread_rwlock_unlock( &iscsi_globvec_rwlock );
- return NULL;
- }
+ return -1;
+ }
- packet_data = (iscsi_bhs_packet *) realloc( packet_data, new_pkt_size );
+ globvec->devices = iscsi_hashmap_create( _maxImages );
- if ( packet_data == NULL ) {
- logadd( LOG_ERROR, "iscsi_append_ahs_packet: Out of memory while allocating iSCSI AHS packet data for appending" );
+ if ( globvec->devices == NULL ) {
+ logadd( LOG_ERROR, "iscsi_create: Out of memory while allocating iSCSI global vector devices hash map" );
+
+ free( globvec );
+ pthread_rwlock_unlock( &iscsi_globvec_rwlock );
+
+ return -1;
+ }
+
+ if ( pthread_rwlock_init( &globvec->devices_rwlock, NULL ) != 0 ) {
+ logadd( LOG_ERROR, "iscsi_create: Out of memory while initializing read/write lock for iSCSI global vector devices hash map" );
+
+ iscsi_hashmap_destroy( globvec->devices );
+ free( globvec );
+ pthread_rwlock_unlock( &iscsi_globvec_rwlock );
+
+ return -1;
+ }
+
+ globvec->portal_groups = iscsi_hashmap_create( 1U );
+
+ if ( globvec->portal_groups == NULL ) {
+ logadd( LOG_ERROR, "iscsi_create: Out of memory while allocating iSCSI global vector portal groups hash map" );
+
+ pthread_rwlock_destroy( &globvec->devices_rwlock );
+ iscsi_hashmap_destroy( globvec->devices );
+ free( globvec );
+ pthread_rwlock_unlock( &iscsi_globvec_rwlock );
+
+ return -1;
+ }
+
+ if ( pthread_rwlock_init( &globvec->portal_groups_rwlock, NULL ) != 0 ) {
+ logadd( LOG_ERROR, "iscsi_create: Out of memory while initializing read/write lock for iSCSI global vector portal groups hash map" );
+
+ iscsi_hashmap_destroy( globvec->portal_groups );
+ pthread_rwlock_destroy( &globvec->devices_rwlock );
+ iscsi_hashmap_destroy( globvec->devices );
+ free( globvec );
+ pthread_rwlock_unlock( &iscsi_globvec_rwlock );
+
+ return -1;
+ }
+
+ globvec->target_nodes = iscsi_hashmap_create( _maxImages );
+
+ if ( globvec->target_nodes == NULL ) {
+ logadd( LOG_ERROR, "iscsi_create: Out of memory while allocating iSCSI global vector target nodes hash map" );
+
+ pthread_rwlock_destroy( &globvec->portal_groups_rwlock );
+ iscsi_hashmap_destroy( globvec->portal_groups );
+ pthread_rwlock_destroy( &globvec->devices_rwlock );
+ iscsi_hashmap_destroy( globvec->devices );
+ free( globvec );
+ pthread_rwlock_unlock( &iscsi_globvec_rwlock );
+
+ return -1;
+ }
+
+ if ( pthread_rwlock_init( &globvec->target_nodes_rwlock, NULL ) != 0 ) {
+ logadd( LOG_ERROR, "iscsi_create: Out of memory while initializing read/write lock for iSCSI global vector target nodes hash map" );
+
+ iscsi_hashmap_destroy( globvec->target_nodes );
+ pthread_rwlock_destroy( &globvec->portal_groups_rwlock );
+ iscsi_hashmap_destroy( globvec->portal_groups );
+ pthread_rwlock_destroy( &globvec->devices_rwlock );
+ iscsi_hashmap_destroy( globvec->devices );
+ free( globvec );
+ pthread_rwlock_unlock( &iscsi_globvec_rwlock );
+
+ return -1;
+ }
+
+ globvec->sessions = iscsi_hashmap_create( _maxClients );
+
+ if ( globvec->sessions == NULL ) {
+ logadd( LOG_ERROR, "iscsi_create: Out of memory while allocating iSCSI global vector sessions hash map" );
+
+ pthread_rwlock_destroy( &globvec->target_nodes_rwlock );
+ iscsi_hashmap_destroy( globvec->target_nodes );
+ pthread_rwlock_destroy( &globvec->portal_groups_rwlock );
+ iscsi_hashmap_destroy( globvec->portal_groups );
+ pthread_rwlock_destroy( &globvec->devices_rwlock );
+ iscsi_hashmap_destroy( globvec->devices );
+ free( globvec );
+ pthread_rwlock_unlock( &iscsi_globvec_rwlock );
+
+ return -1;
+ }
+
+ if ( pthread_rwlock_init( &globvec->sessions_rwlock, NULL ) != 0 ) {
+ logadd( LOG_ERROR, "iscsi_create: Out of memory while initializing read/write lock for iSCSI global vector sessions hash map map" );
+
+ iscsi_hashmap_destroy( globvec->sessions );
+ pthread_rwlock_destroy( &globvec->target_nodes_rwlock );
+ iscsi_hashmap_destroy( globvec->target_nodes );
+ pthread_rwlock_destroy( &globvec->portal_groups_rwlock );
+ iscsi_hashmap_destroy( globvec->portal_groups );
+ pthread_rwlock_destroy( &globvec->devices_rwlock );
+ iscsi_hashmap_destroy( globvec->devices );
+ free( globvec );
+ pthread_rwlock_unlock( &iscsi_globvec_rwlock );
+
+ return -1;
+ }
+
+ globvec->session_key_value_pairs = iscsi_hashmap_create( ((sizeof(iscsi_session_key_value_pair_lut) / sizeof(struct iscsi_key_value_pair_lut_entry)) - 1) );
+
+ if ( globvec->session_key_value_pairs == NULL ) {
+ logadd( LOG_ERROR, "iscsi_create: Out of memory while allocating iSCSI global vector session key and value pairs hash map" );
+
+ pthread_rwlock_destroy( &globvec->sessions_rwlock );
+ iscsi_hashmap_destroy( globvec->sessions );
+ pthread_rwlock_destroy( &globvec->target_nodes_rwlock );
+ iscsi_hashmap_destroy( globvec->target_nodes );
+ pthread_rwlock_destroy( &globvec->portal_groups_rwlock );
+ iscsi_hashmap_destroy( globvec->portal_groups );
+ pthread_rwlock_destroy( &globvec->devices_rwlock );
+ iscsi_hashmap_destroy( globvec->devices );
+ free( globvec );
+ pthread_rwlock_unlock( &iscsi_globvec_rwlock );
- return packet_data;
+ return -1;
+ }
+
+ int rc = iscsi_global_key_value_pair_init( globvec->session_key_value_pairs, &iscsi_session_key_value_pair_lut[0] );
+
+ if ( rc < 0 ) {
+ logadd( LOG_ERROR, "iscsi_create: Out of memory while initializing iSCSI global vector session key and value pairs hash map" );
+
+ iscsi_hashmap_iterate( globvec->session_key_value_pairs, iscsi_hashmap_destroy_value_callback, NULL );
+ iscsi_hashmap_destroy( globvec->session_key_value_pairs );
+ pthread_rwlock_destroy( &globvec->sessions_rwlock );
+ iscsi_hashmap_destroy( globvec->sessions );
+ pthread_rwlock_destroy( &globvec->target_nodes_rwlock );
+ iscsi_hashmap_destroy( globvec->target_nodes );
+ pthread_rwlock_destroy( &globvec->portal_groups_rwlock );
+ iscsi_hashmap_destroy( globvec->portal_groups );
+ pthread_rwlock_destroy( &globvec->devices_rwlock );
+ iscsi_hashmap_destroy( globvec->devices );
+ free( globvec );
+ pthread_rwlock_unlock( &iscsi_globvec_rwlock );
+
+ return -1;
+ }
+
+ globvec->connection_key_value_pairs = iscsi_hashmap_create( ((sizeof(iscsi_connection_key_value_pair_lut) / sizeof(struct iscsi_key_value_pair_lut_entry)) - 1) );
+
+ if ( globvec->connection_key_value_pairs == NULL ) {
+ logadd( LOG_ERROR, "iscsi_create: Out of memory while allocating iSCSI global vector connection key and value pairs hash map" );
+
+ iscsi_hashmap_iterate( globvec->session_key_value_pairs, iscsi_hashmap_destroy_value_callback, NULL );
+ iscsi_hashmap_destroy( globvec->session_key_value_pairs );
+ pthread_rwlock_destroy( &globvec->sessions_rwlock );
+ iscsi_hashmap_destroy( globvec->sessions );
+ pthread_rwlock_destroy( &globvec->target_nodes_rwlock );
+ iscsi_hashmap_destroy( globvec->target_nodes );
+ pthread_rwlock_destroy( &globvec->portal_groups_rwlock );
+ iscsi_hashmap_destroy( globvec->portal_groups );
+ pthread_rwlock_destroy( &globvec->devices_rwlock );
+ iscsi_hashmap_destroy( globvec->devices );
+ free( globvec );
+ pthread_rwlock_unlock( &iscsi_globvec_rwlock );
+
+ return -1;
+ }
+
+ rc = iscsi_global_key_value_pair_init( globvec->connection_key_value_pairs, &iscsi_connection_key_value_pair_lut[0] );
+
+ if ( rc < 0 ) {
+ logadd( LOG_ERROR, "iscsi_create: Out of memory while initializing iSCSI global vector connection key and value pairs hash map" );
+
+ iscsi_hashmap_iterate( globvec->connection_key_value_pairs, iscsi_hashmap_destroy_value_callback, NULL );
+ iscsi_hashmap_destroy( globvec->connection_key_value_pairs );
+ iscsi_hashmap_iterate( globvec->session_key_value_pairs, iscsi_hashmap_destroy_value_callback, NULL );
+ iscsi_hashmap_destroy( globvec->session_key_value_pairs );
+ pthread_rwlock_destroy( &globvec->sessions_rwlock );
+ iscsi_hashmap_destroy( globvec->sessions );
+ pthread_rwlock_destroy( &globvec->target_nodes_rwlock );
+ iscsi_hashmap_destroy( globvec->target_nodes );
+ pthread_rwlock_destroy( &globvec->portal_groups_rwlock );
+ iscsi_hashmap_destroy( globvec->portal_groups );
+ pthread_rwlock_destroy( &globvec->devices_rwlock );
+ iscsi_hashmap_destroy( globvec->devices );
+ free( globvec );
+ pthread_rwlock_unlock( &iscsi_globvec_rwlock );
+
+ return -1;
+ }
+
+ globvec->scsi_device_config = iscsi_hashmap_create( 0U );
+
+ if ( globvec->scsi_device_config == NULL ) {
+ logadd( LOG_ERROR, "iscsi_create: Out of memory while allocating iSCSI global vector SCSI device configuration" );
+
+ iscsi_hashmap_iterate( globvec->connection_key_value_pairs, iscsi_hashmap_destroy_value_callback, NULL );
+ iscsi_hashmap_destroy( globvec->connection_key_value_pairs );
+ iscsi_hashmap_iterate( globvec->session_key_value_pairs, iscsi_hashmap_destroy_value_callback, NULL );
+ iscsi_hashmap_destroy( globvec->session_key_value_pairs );
+ pthread_rwlock_destroy( &globvec->sessions_rwlock );
+ iscsi_hashmap_destroy( globvec->sessions );
+ pthread_rwlock_destroy( &globvec->target_nodes_rwlock );
+ iscsi_hashmap_destroy( globvec->target_nodes );
+ pthread_rwlock_destroy( &globvec->portal_groups_rwlock );
+ iscsi_hashmap_destroy( globvec->portal_groups );
+ pthread_rwlock_destroy( &globvec->devices_rwlock );
+ iscsi_hashmap_destroy( globvec->devices );
+ free( globvec );
+ pthread_rwlock_unlock( &iscsi_globvec_rwlock );
+
+ return -1;
+ }
+
+ if ( pthread_mutex_init( &globvec->scsi_device_config_mutex, NULL ) != 0 ) {
+ logadd( LOG_ERROR, "iscsi_create: Out of memory while initializing mutex for iSCSI global vector SCSI device configuration" );
+
+ iscsi_hashmap_destroy( globvec->scsi_device_config );
+ iscsi_hashmap_iterate( globvec->connection_key_value_pairs, iscsi_hashmap_key_destroy_value_callback, NULL );
+ iscsi_hashmap_destroy( globvec->connection_key_value_pairs );
+ iscsi_hashmap_iterate( globvec->session_key_value_pairs, iscsi_hashmap_destroy_value_callback, NULL );
+ iscsi_hashmap_destroy( globvec->session_key_value_pairs );
+ pthread_rwlock_destroy( &globvec->sessions_rwlock );
+ iscsi_hashmap_destroy( globvec->sessions );
+ pthread_rwlock_destroy( &globvec->target_nodes_rwlock );
+ iscsi_hashmap_destroy( globvec->target_nodes );
+ pthread_rwlock_destroy( &globvec->portal_groups_rwlock );
+ iscsi_hashmap_destroy( globvec->portal_groups );
+ pthread_rwlock_destroy( &globvec->devices_rwlock );
+ iscsi_hashmap_destroy( globvec->devices );
+ free( globvec );
+ pthread_rwlock_unlock( &iscsi_globvec_rwlock );
+
+ return -1;
+ }
+
+ globvec->flags = (ISCSI_GLOBALS_FLAGS_INIT_R2T | ISCSI_GLOBALS_FLAGS_IMMEDIATE_DATA | ISCSI_GLOBALS_FLAGS_DATA_PDU_IN_ORDER | ISCSI_GLOBALS_FLAGS_DATA_SEQ_IN_ORDER | ISCSI_GLOBALS_FLAGS_SCSI_IO_REMOVABLE | ISCSI_GLOBALS_FLAGS_SCSI_IO_WRITE_PROTECT);
+ globvec->target_name_check = ISCSI_GLOBALS_TARGET_NAME_CHECK_FULL;
+ globvec->max_sessions = 0U;
+ globvec->header_digest = 0;
+ globvec->data_digest = 0;
+ globvec->scsi_device_type = ISCSI_SCSI_BASIC_INQUIRY_DATA_PERIPHERAL_TYPE_DIRECT;
+ globvec->max_recv_ds_len = ISCSI_DEFAULT_RECV_DS_LEN;
+ globvec->max_session_conns = ISCSI_GLOBALS_DEFAULT_MAX_CONNECTIONS;
+ globvec->max_outstanding_r2t = ISCSI_GLOBALS_DEFAULT_MAX_OUTSTANDING_R2T;
+ globvec->default_time_to_wait = ISCSI_GLOBALS_DEFAULT_TIME_TO_WAIT;
+ globvec->default_time_to_retain = ISCSI_GLOBALS_DEFAULT_TIME_TO_RETAIN;
+ globvec->first_burst_len = ISCSI_GLOBALS_DEFAULT_FIRST_BURST_LEN;
+ globvec->max_burst_len = ISCSI_GLOBALS_DEFAULT_MAX_BURST_LEN;
+ globvec->err_recovery_level = ISCSI_GLOBALS_DEFAULT_ERR_RECOVERY_LEVEL;
+ globvec->chap_group = 0L;
+ globvec->scsi_physical_block_size = ISCSI_SCSI_EMU_PHYSICAL_BLOCK_SIZE;
+ globvec->scsi_physical_block_size_shift = iscsi_get_log2_of_pow2( globvec->scsi_physical_block_size );
+ globvec->scsi_logical_block_size = ISCSI_SCSI_EMU_BLOCK_SIZE;
+ globvec->scsi_logical_block_size_shift = iscsi_get_log2_of_pow2( globvec->scsi_logical_block_size );
+
+ iscsi_config_load( globvec );
+
+ iscsi_globvec = globvec;
}
- iscsi_ahs_packet *ahs_pkt = (iscsi_ahs_packet *) ((uint8_t *) packet_data + old_pkt_size);
- ahs_pkt->len = iscsi_get_be16((uint16_t) ahs_len);
- ahs_pkt->type = 0;
- ahs_pkt->specific = 0;
- memset( ahs_pkt->data, 0, (new_pkt_size - old_pkt_size) - offsetof(struct iscsi_ahs_packet, data) );
- packet_data->total_ahs_len += (uint8_t) ((ahs_len + (ISCSI_ALIGN_SIZE - 1)) >> 2UL);
+ pthread_rwlock_unlock( &iscsi_globvec_rwlock );
- return packet_data;
+ return 0;
}
/**
- * @brief Counts number of AHS packets in an iSCSI data packet stream.
- *
- * Gets the total number of AHS packets.
+ * @brief Deallocates all resources acquired by iscsi_create.
*
- * @param[in] packet_data Pointer to packet data of which the
- * number of AHS packets should be counted.
- * @return The number of AHS packets or zero in case none exist or
- * -1 in case of error.
+ * This function MUST be called before program termination
+ * for ensuring proper clean up.\n
+ * After this function returns, calling any iSCSI related
+ * function except iscsi_create is strictly forbidden.\n
+ * It is safe to call this function if the iSCSI global
+ * vector already has been destroyed in which case this
+ * function does nothing.
*/
-int iscsi_get_ahs_packets(const iscsi_bhs_packet *packet_data)
+void iscsi_destroy()
{
- if ( packet_data == NULL )
- return -1;
- else if ( packet_data->total_ahs_len == 0 )
- return 0;
+ pthread_rwlock_wrlock( &iscsi_globvec_rwlock );
- iscsi_ahs_packet *ahs_pkt = (iscsi_ahs_packet *) ((iscsi_bhs_packet *) packet_data + 1); // First AHS packet
- int count = 0L;
- uint32_t ahs_len = (uint32_t) packet_data->total_ahs_len << 2UL;
+ iscsi_globals *globvec = iscsi_globvec;
- while ( (int32_t) ahs_len > 0L ) {
- uint32_t len = iscsi_get_be16(ahs_pkt->len) + offsetof(struct iscsi_ahs_packet, data); // Total length of current AHS packet
- len = iscsi_align(len, ISCSI_ALIGN_SIZE);
+ if ( globvec != NULL ) {
+ iscsi_globvec = NULL;
- ahs_len -= len;
- ahs_pkt = (iscsi_ahs_packet *) (((uint8_t *) ahs_pkt) + (len - offsetof(struct iscsi_ahs_packet, data))); // Advance pointer to next AHS packet
- count++;
+ pthread_mutex_destroy( &globvec->scsi_device_config_mutex );
+ iscsi_hashmap_iterate( globvec->scsi_device_config, iscsi_hashmap_key_destroy_value_callback, NULL );
+ iscsi_hashmap_destroy( globvec->scsi_device_config );
+ globvec->scsi_device_config = NULL;
+
+ iscsi_hashmap_iterate( globvec->connection_key_value_pairs, iscsi_hashmap_destroy_value_callback, NULL );
+ iscsi_hashmap_destroy( globvec->connection_key_value_pairs );
+ globvec->connection_key_value_pairs = NULL;
+
+ iscsi_hashmap_iterate( globvec->session_key_value_pairs, iscsi_hashmap_destroy_value_callback, NULL );
+ iscsi_hashmap_destroy( globvec->session_key_value_pairs );
+ globvec->session_key_value_pairs = NULL;
+
+ pthread_rwlock_destroy( &globvec->sessions_rwlock );
+ iscsi_hashmap_iterate( globvec->sessions, iscsi_session_destroy_callback, NULL );
+ iscsi_hashmap_destroy( globvec->sessions );
+ globvec->sessions = NULL;
+
+ pthread_rwlock_destroy( &globvec->target_nodes_rwlock );
+ iscsi_hashmap_iterate( globvec->target_nodes, iscsi_target_node_destroy_callback, NULL );
+ iscsi_hashmap_destroy( globvec->target_nodes );
+ globvec->target_nodes = NULL;
+
+ pthread_rwlock_destroy( &globvec->portal_groups_rwlock );
+ iscsi_hashmap_iterate( globvec->portal_groups, iscsi_portal_group_destroy_callback, NULL );
+ iscsi_hashmap_destroy( globvec->portal_groups );
+ globvec->portal_groups = NULL;
+
+ pthread_rwlock_destroy( &globvec->devices_rwlock );
+ iscsi_hashmap_iterate( globvec->devices, iscsi_device_destroy_callback, NULL );
+ iscsi_hashmap_destroy( globvec->devices );
+ globvec->devices = NULL;
+
+ free( globvec );
}
- return count;
+ pthread_rwlock_unlock( &iscsi_globvec_rwlock );
}
/**
- * @brief Retrieves the pointer to an specific AHS packet by index.
+ * @brief Parses an INI configuration value and returns an integer representation of string with special boolean and suffixes handling.
*
- * Gets the pointer of an AHS packet by specified index.
+ * This function also handles boolean values 'true',
+ * 'yes', 'on', 'enabled' and 'activated', returning 1
+ * for them, as well as 'false', 'no', 'off', 'disabled'
+ * and 'deactivated' having a return value of 0.\n
+ * Also the suffixes 'm' for minutes, 'h' for hours and
+ * 'd' for days are understood as time units.\n
+ * SI units 'K', 'M', 'G', 'T', 'P' and 'E' are
+ * understood as well.\n
+ * If a 'b' or 'B' follows, 1000 will be used as a
+ * multiplier instead of 1024.\n
+ * Parsing will be internally done in 64 bits and the
+ * final result is clamped between -2147483647 and
+ * 2147483647.
*
- * @param[in] packet_data Pointer to packet data of which the
- * AHS packet should be retrieved.
- * @param[in] index Zero-based index number of AHS packet to
- * be received.
- * @return The pointer to the AHS packet at specified index on
- * success or NULL in case of an error or if the specific index
- * is out of range.
+ * @param[in] value Pointer to string for parsing. May
+ * NOT be NULL, so be careful.
+ * @return Parsed integer value or -2147483648
+ * in case of an error.
*/
-iscsi_ahs_packet *iscsi_get_ahs_packet(const iscsi_bhs_packet *packet_data, const int index)
+static int32_t iscsi_config_parse_int(const uint8_t *value)
{
- if ( packet_data == NULL || (packet_data->total_ahs_len == 0) )
- return NULL;
+ if ( *value == '\0' )
+ return -2147483648L;
- iscsi_ahs_packet *ahs_pkt = (iscsi_ahs_packet *) ((iscsi_bhs_packet *) packet_data + 1); // First AHS packet
- int count = index;
- uint32_t ahs_len = (uint32_t) packet_data->total_ahs_len << 2UL;
+ if ( (strcasecmp( (char *) value, "true" ) == 0) || (strcasecmp( (char *) value, "yes" ) == 0) || (strcasecmp( (char *) value, "on" ) == 0) || (strcasecmp( (char *) value, "enabled" ) == 0) || (strcasecmp( (char *) value, "activated" ) == 0) )
+ return 1L;
+ else if ( (strcasecmp( (char *) value, "false" ) == 0) || (strcasecmp( (char *) value, "no" ) == 0) || (strcasecmp( (char *) value, "off" ) == 0) || (strcasecmp( (char *) value, "disabled" ) == 0) || (strcasecmp( (char *) value, "deactivated" ) == 0) )
+ return 0L;
- while ( (int32_t) ahs_len > 0L ) {
- if ( count-- < 0L )
- return ahs_pkt;
+ uint8_t *val_end;
+ int64_t rc = (int64_t) strtoll( (char *) value, (char **) &val_end, 10 );
- uint32_t len = iscsi_get_be16(ahs_pkt->len) + offsetof(struct iscsi_ahs_packet, data); // Total length of current AHS packet
- len = iscsi_align(len, ISCSI_ALIGN_SIZE);
+ if ( value == val_end )
+ return -2147483648L;
- ahs_len -= len;
- ahs_pkt = (iscsi_ahs_packet *) (((uint8_t *) ahs_pkt) + (len - offsetof(struct iscsi_ahs_packet, data))); // Advance pointer to next AHS packet
+ while ( (*val_end == '\t') || (*val_end == ' ') ) {
+ val_end++;
}
- logadd( LOG_ERROR, "iscsi_get_ahs_packet: Specified index for AHS packet does not exist" );
+ switch ( *val_end ) {
+ case '\0' : {
+ break;
+ }
+ case 'm' : {
+ rc *= 60LL;
+ val_end++;
- return NULL;
-}
+ break;
+ }
+ case 'h' : {
+ rc *= 3600LL;
+ val_end++;
-/**
- * @brief Allocate and initialize an iSCSI header digest (CRC32C) and appends it to existing data stream.
- *
- * Constructs and appends an header digest (CRC32C) to already allocated
- * packet data. There is no guarantee that the pointer stays the same.
- * Any references to the old structure need to be updated!\n
- * This function currently throws away any data beyond AHS.
- *
- * @param[in] packet_data Pointer to packet data to append to. If NULL, a Basic
- * Header Segment (BHS) will be created and initialized before adding the
- * header digest.
- * @param[in] header_digest_size Length of header digest. Currently, only
- * 0, in which case the header digest will be removed, or 4 for CRC32C
- * are allowed.
- * @return New pointer to BHS structure with additional header digest attached
- * or NULL in case of an reallocation error or header digest is neither 0 nor 4.
- */
-iscsi_bhs_packet *iscsi_append_header_digest_packet(iscsi_bhs_packet *packet_data, const int header_digest_size)
-{
- if ( packet_data == NULL ) {
- packet_data = iscsi_create_packet();
+ break;
+ }
+ case 'd' : {
+ rc *= 86400LL;
+ val_end++;
- if ( packet_data == NULL )
- return packet_data;
- }
+ break;
+ }
+ default : {
+ const uint8_t c = (*val_end++ | ('a' - 'A'));
+ const bool ten = ((*val_end | ('a' - 'A')) == 'b');
- if ( (header_digest_size != 0) || (header_digest_size != ISCSI_DIGEST_SIZE) ) {
- logadd( LOG_ERROR, "iscsi_append_header_digest_packet: Header digest size MUST be either 0 or 4 bytes" );
+ switch ( c ) {
+ case 'k' : {
+ rc = (ten ? (rc * 1000LL) : (rc << 10LL));
- return NULL;
- }
+ break;
+ }
+ case 'm' : {
+ rc = (ten ? (rc * 1000000LL) : (rc << 20LL));
- const uint32_t old_pkt_size = (const uint32_t) sizeof(struct iscsi_bhs_packet) + (packet_data->total_ahs_len << 2UL);
- const uint32_t new_pkt_size = old_pkt_size + header_digest_size;
+ break;
+ }
+ case 'g' : {
+ rc = (ten ? (rc * 1000000000LL) : (rc << 30LL));
- packet_data = (iscsi_bhs_packet *) realloc( packet_data, new_pkt_size );
+ break;
+ }
+ case 't' : {
+ rc = (ten ? (rc * 1000000000000LL) : (rc << 40LL));
- if ( packet_data == NULL ) {
- logadd( LOG_ERROR, "iscsi_append_header_digest_packet: Out of memory while allocating iSCSI header digest packet data for appending" );
+ break;
+ }
+ case 'p' : {
+ rc = (ten ? (rc * 1000000000000000LL) : (rc << 50LL));
+
+ break;
+ }
+ case 'e' : {
+ rc = (ten ? (rc * 1000000000000000000LL) : (rc << 60LL));
- return packet_data;
+ break;
+ }
+ default : {
+ return -2147483648L;
+
+ break;
+ }
+ }
+
+ if ( ten )
+ val_end++;
+
+ break;
+ }
}
- memset( (((uint8_t *) packet_data) + old_pkt_size), 0, header_digest_size );
+ if ( *val_end != '\0' )
+ return -2147483648L;
- return packet_data;
+ if ( rc < -2147483647LL )
+ rc = -2147483647LL;
+ else if ( rc > 2147483647LL )
+ rc = 2147483647LL;
+
+ return (int32_t) rc;
}
/**
- * @brief Allocate and initialize an iSCSI DS packet and append to existing data stream.
+ * @brief Callback function from DNBD3 INI parser invoked for handling a specific key=value pair in a specified INI section.
*
- * Constructs and appends DataSegment (DS) to already allocated packet data.\n
- * There is no guarantee that the pointer stays the same. Any references
- * to the old structure need to be updated!\n
- * This function currently erases an already available DataSegment and
- * throws away any data beyond DS.
+ * This function checks whether the INI
+ * section belongs to the iSCSI server
+ * configuration part.\n
+ * Currently, the sections 'iscsi' and
+ * 'scsi' for the SCSI emulation are
+ * processed.
*
- * @param[in] packet_data Pointer to BHS packet data to append to. If NULL, a Basic
- * Header Segment (BHS) will be created and initialized before adding the DataSegment.
- * @param[in] header_digest_size Length of optional header digest (0 or 4 for now) to
- * add.
- * @param[in] ds_len Length of DataSegment packet data to be appended. May
- * not exceed 16MiB - 1 (16777215 bytes).
- * @param[in] data_digest_size Length of optional data digest (0 or 4 for now) to
- * add.
- * @return New pointer to BHS structure with additional DataSegment attached or
- * NULL in case of an reallocation error, either header or data digest size does not
- * confirm to the iSCSI standard or DS length exceeds 16777215 bytes.
- */
-iscsi_bhs_packet *iscsi_append_ds_packet(iscsi_bhs_packet *packet_data, const int header_digest_size, const uint32_t ds_len, const int data_digest_size)
-{
- if ( ((header_digest_size != 0) && header_digest_size != ISCSI_DIGEST_SIZE) || ((data_digest_size != 0) && data_digest_size != ISCSI_DIGEST_SIZE) || (ds_len >= 16777216UL) )
- return NULL;
+ * @param[in] user_data Pointer to iSCSI global vector where
+ * to store the configuration data. May
+ * NOT be NULL, so be careful.
+ * @param[in] section Pointer to currently processing
+ * INI section. NULL is NOT allowed here,
+ * take caution.
+ * @param[in] key Pointer to currently processing INI
+ * key. May NOT be NULL, so be careful.
+ * @param[in] value Pointer to currently processing INI
+ * value. NULL is prohibited, so take
+ * caution.
+ * @retval 1 INI parsing was successful.
+ * @retval 0 An error occured during INI parsing.
+ */
+static int iscsi_config_load_from_ini(void *user_data, const char *section, const char *key, const char *value)
+{
+ iscsi_globals *globvec = (iscsi_globals *) user_data;
+
+ if ( strcasecmp( section, ISCSI_GLOBALS_SECTION_ISCSI ) == 0 ) {
+ const int32_t num_value = iscsi_config_parse_int( (uint8_t *) value );
+
+ if ( strcasecmp( key, ISCSI_GLOBALS_SECTION_ISCSI_KEY_TARGET_NAME_CHECK ) == 0 ) {
+ if ( strcasecmp( value, "None" ) == 0 )
+ globvec->target_name_check = ISCSI_GLOBALS_TARGET_NAME_CHECK_NONE;
+ else if ( strcasecmp( value, "Relaxed" ) == 0 )
+ globvec->target_name_check = ISCSI_GLOBALS_TARGET_NAME_CHECK_RELAXED;
+ else
+ globvec->target_name_check = ISCSI_GLOBALS_TARGET_NAME_CHECK_FULL;
+ } else if ( strcasecmp( key, ISCSI_GLOBALS_SECTION_ISCSI_KEY_MAX_SESSIONS ) == 0 ) {
+ globvec->max_sessions = (uint) num_value;
+ } else if ( strcasecmp( key, (char *) ISCSI_LOGIN_AUTH_SESSION_TEXT_KEY_HEADER_DIGEST ) == 0 ) {
+ globvec->header_digest = ((strcasecmp( value, "CRC32C" ) == 0) ? ISCSI_DIGEST_SIZE : 0);
+ } else if ( strcasecmp( key, (char *) ISCSI_LOGIN_AUTH_SESSION_TEXT_KEY_DATA_DIGEST ) == 0 ) {
+ globvec->data_digest = ((strcasecmp( value, "CRC32C" ) == 0) ? ISCSI_DIGEST_SIZE : 0);
+ } else if ( strcasecmp( key, (char *) ISCSI_LOGIN_AUTH_SESSION_TEXT_KEY_MAX_RECV_DS_LEN ) == 0 ) {
+ if ( (num_value >= 512L) && (num_value <= (int32_t) ISCSI_MAX_DS_SIZE) )
+ globvec->max_recv_ds_len = num_value;
+ } else if ( strcasecmp( key, ISCSI_GLOBALS_SECTION_ISCSI_MAX_CONNECTIONS_PER_SESSIONS ) == 0 ) {
+ if ( (num_value > 0L) && (num_value <= 65535L) )
+ globvec->max_session_conns = num_value;
+ } else if ( strcasecmp( key, (char *) ISCSI_LOGIN_AUTH_SESSION_TEXT_KEY_MAX_OUTSTANDING_R2T ) == 0 ) {
+ if ( (num_value > 0L) && (num_value <= 65536L) )
+ globvec->max_outstanding_r2t = num_value;
+ } else if ( strcasecmp( key, (char *) ISCSI_LOGIN_AUTH_SESSION_TEXT_KEY_DEFAULT_TIME_WAIT ) == 0 ) {
+ if ( (uint32_t) num_value <= 3600UL )
+ globvec->default_time_to_wait = num_value;
+ } else if ( strcasecmp( key, (char *) ISCSI_LOGIN_AUTH_SESSION_TEXT_KEY_DEFAULT_TIME_RETAIN ) == 0 ) {
+ if ( (uint32_t) num_value <= 3600UL )
+ globvec->default_time_to_retain = num_value;
+ } else if ( strcasecmp( key, (char *) ISCSI_LOGIN_AUTH_SESSION_TEXT_KEY_FIRST_BURST_LEN ) == 0 ) {
+ if ( (num_value >= 512L) && (num_value <= (int32_t) ISCSI_MAX_DS_SIZE) )
+ globvec->first_burst_len = num_value;
+ } else if ( strcasecmp( key, (char *) ISCSI_LOGIN_AUTH_SESSION_TEXT_KEY_MAX_BURST_LEN ) == 0 ) {
+ if ( (num_value >= 512L) && (num_value <= (int32_t) ISCSI_MAX_DS_SIZE) )
+ globvec->max_burst_len = num_value;
+ } else if ( strcasecmp( key, (char *) ISCSI_LOGIN_AUTH_SESSION_TEXT_KEY_INITIAL_R2T ) == 0 ) {
+ if ( num_value != -2147483648L ) {
+ if ( num_value != 0L )
+ globvec->flags |= ISCSI_GLOBALS_FLAGS_INIT_R2T;
+ else
+ globvec->flags &= ~ISCSI_GLOBALS_FLAGS_INIT_R2T;
+ }
+ } else if ( strcasecmp( key, (char *) ISCSI_LOGIN_AUTH_SESSION_TEXT_KEY_IMMEDIATE_DATA ) == 0 ) {
+ if ( num_value != -2147483648L ) {
+ if ( num_value != 0L )
+ globvec->flags |= ISCSI_GLOBALS_FLAGS_IMMEDIATE_DATA;
+ else
+ globvec->flags &= ~ISCSI_GLOBALS_FLAGS_IMMEDIATE_DATA;
+ }
+ } else if ( strcasecmp( key, (char *) ISCSI_LOGIN_AUTH_SESSION_TEXT_KEY_DATA_PDU_IN_ORDER ) == 0 ) {
+ if ( num_value != -2147483648L ) {
+ if ( num_value != 0L )
+ globvec->flags |= ISCSI_GLOBALS_FLAGS_DATA_PDU_IN_ORDER;
+ else
+ globvec->flags &= ~ISCSI_GLOBALS_FLAGS_DATA_PDU_IN_ORDER;
+ }
+ } else if ( strcasecmp( key, (char *) ISCSI_LOGIN_AUTH_SESSION_TEXT_KEY_DATA_SEQ_IN_ORDER ) == 0 ) {
+ if ( num_value != -2147483648L ) {
+ if ( num_value != 0L )
+ globvec->flags |= ISCSI_GLOBALS_FLAGS_DATA_SEQ_IN_ORDER;
+ else
+ globvec->flags &= ~ISCSI_GLOBALS_FLAGS_DATA_SEQ_IN_ORDER;
+ }
+ } else if ( strcasecmp( key, (char *) ISCSI_LOGIN_AUTH_SESSION_TEXT_KEY_ERR_RECOVERY_LEVEL ) == 0 ) {
+ if ( (uint32_t) num_value <= 2UL )
+ globvec->err_recovery_level = num_value;
+ }
+ } else if ( strcasecmp( section, ISCSI_GLOBALS_SECTION_SCSI ) == 0 ) {
+ int32_t num_value = iscsi_config_parse_int( (uint8_t *) value );
+
+ if ( strcasecmp( key, ISCSI_GLOBALS_SECTION_SCSI_KEY_DEVICE_TYPE ) == 0 ) {
+ if ( strcasecmp( value, "Sequential" ) == 0 )
+ globvec->scsi_device_type = ISCSI_SCSI_BASIC_INQUIRY_DATA_PERIPHERAL_TYPE_SEQ;
+ else if ( strcasecmp( value, "WriteOnce" ) == 0 )
+ globvec->scsi_device_type = ISCSI_SCSI_BASIC_INQUIRY_DATA_PERIPHERAL_TYPE_WORM;
+ else if ( strcasecmp( value, "ReadOnlyDirect" ) == 0 )
+ globvec->scsi_device_type = ISCSI_SCSI_BASIC_INQUIRY_DATA_PERIPHERAL_TYPE_RO_DIRECT;
+ else if ( strcasecmp( value, "OpticalMemory" ) == 0 )
+ globvec->scsi_device_type = ISCSI_SCSI_BASIC_INQUIRY_DATA_PERIPHERAL_TYPE_OPTICAL;
+ else if ( strcasecmp( value, "MediaChanger" ) == 0 )
+ globvec->scsi_device_type = ISCSI_SCSI_BASIC_INQUIRY_DATA_PERIPHERAL_TYPE_CHANGER;
+ else
+ globvec->scsi_device_type = ISCSI_SCSI_BASIC_INQUIRY_DATA_PERIPHERAL_TYPE_DIRECT;
+ } else if ( strcasecmp( key, ISCSI_GLOBALS_SECTION_SCSI_KEY_PHYSICAL_BLOCK_SIZE ) == 0 ) {
+ num_value = iscsi_align_pow2_ceil( num_value );
+
+ if ( (num_value >= 256L) && (num_value <= 32768L) ) {
+ globvec->scsi_physical_block_size = num_value;
+ globvec->scsi_physical_block_size_shift = iscsi_get_log2_of_pow2( num_value );
+ }
+ } else if ( strcasecmp( key, ISCSI_GLOBALS_SECTION_SCSI_KEY_LOGICAL_BLOCK_SIZE ) == 0 ) {
+ num_value = iscsi_align_pow2_ceil( num_value );
- if ( packet_data == NULL ) {
- packet_data = iscsi_create_packet();
+ if ( (num_value >= 256L) && (num_value <= 32768L) ) {
+ globvec->scsi_logical_block_size = num_value;
+ globvec->scsi_logical_block_size_shift = iscsi_get_log2_of_pow2( num_value );
+ }
+ } else if ( strcasecmp( key, ISCSI_GLOBALS_SECTION_SCSI_KEY_REMOVABLE ) == 0 ) {
+ if ( num_value != -2147483648L ) {
+ if ( num_value != 0L )
+ globvec->flags |= ISCSI_GLOBALS_FLAGS_SCSI_IO_REMOVABLE;
+ else
+ globvec->flags &= ~ISCSI_GLOBALS_FLAGS_SCSI_IO_REMOVABLE;
+ }
+ } else if ( strcasecmp( key, ISCSI_GLOBALS_SECTION_SCSI_KEY_UNMAP ) == 0 ) {
+ if ( num_value != -2147483648L ) {
+ if ( num_value != 0L )
+ globvec->flags |= ISCSI_GLOBALS_FLAGS_SCSI_IO_UNMAP;
+ else
+ globvec->flags &= ~ISCSI_GLOBALS_FLAGS_SCSI_IO_UNMAP;
+ }
+ } else if ( strcasecmp( key, ISCSI_GLOBALS_SECTION_SCSI_KEY_NO_ROTATION ) == 0 ) {
+ if ( num_value != -2147483648L ) {
+ if ( num_value != 0L )
+ globvec->flags |= ISCSI_GLOBALS_FLAGS_SCSI_IO_NO_ROTATION;
+ else
+ globvec->flags &= ~ISCSI_GLOBALS_FLAGS_SCSI_IO_NO_ROTATION;
+ }
+ } else if ( strcasecmp( key, ISCSI_GLOBALS_SECTION_SCSI_KEY_PHYSICAL_READ_ONLY ) == 0 ) {
+ if ( num_value != -2147483648L ) {
+ if ( num_value != 0L )
+ globvec->flags |= ISCSI_GLOBALS_FLAGS_SCSI_IO_PHYSICAL_READ_ONLY;
+ else
+ globvec->flags &= ~ISCSI_GLOBALS_FLAGS_SCSI_IO_PHYSICAL_READ_ONLY;
+ }
+ } else if ( strcasecmp( key, ISCSI_GLOBALS_SECTION_SCSI_KEY_WRITE_PROTECT ) == 0 ) {
+ if ( num_value != -2147483648L ) {
+ if ( num_value != 0L )
+ globvec->flags |= ISCSI_GLOBALS_FLAGS_SCSI_IO_WRITE_PROTECT;
+ else
+ globvec->flags &= ~ISCSI_GLOBALS_FLAGS_SCSI_IO_WRITE_PROTECT;
+ }
+ } else if ( strcasecmp( key, ISCSI_GLOBALS_SECTION_SCSI_KEY_WRITE_CACHE ) == 0 ) {
+ if ( num_value != -2147483648L ) {
+ if ( num_value != 0L )
+ globvec->flags |= ISCSI_GLOBALS_FLAGS_SCSI_IO_WRITE_CACHE;
+ else
+ globvec->flags &= ~ISCSI_GLOBALS_FLAGS_SCSI_IO_WRITE_CACHE;
+ }
+ }
+ } else if ( strncasecmp( section, ISCSI_GLOBALS_SECTION_SCSI_DEVICE_PREFIX, ISCSI_STRLEN(ISCSI_GLOBALS_SECTION_SCSI_DEVICE_PREFIX) ) == 0 ) {
+ uint8_t *pattern = (((uint8_t *) section) + ISCSI_STRLEN(ISCSI_GLOBALS_SECTION_SCSI_DEVICE_PREFIX));
+ const uint key_len = (uint) (strlen( (char *) pattern ) + 1U);
- if ( packet_data == NULL )
- return packet_data;
- }
+ if ( key_len == 0U )
+ return 0;
- const uint32_t old_pkt_size = (const uint32_t) sizeof(struct iscsi_bhs_packet) + ((uint32_t) packet_data->total_ahs_len << 2UL);
- const uint32_t new_pkt_size = (uint32_t) (old_pkt_size + header_digest_size + iscsi_align(ds_len, ISCSI_ALIGN_SIZE) + data_digest_size);
+ uint8_t *hash_key = iscsi_hashmap_key_create( pattern, key_len );
- packet_data = (iscsi_bhs_packet *) realloc( packet_data, new_pkt_size );
+ if ( hash_key == NULL ) {
+ logadd( LOG_ERROR, "iscsi_config_load_from_ini: Out of memory allocating memory for iSCSI SCSI device INI configuration section key" );
- if ( packet_data == NULL ) {
- logadd( LOG_ERROR, "iscsi_append_ds_packet: Out of memory while allocating iSCSI DS packet data for appending" );
+ return 0;
+ }
- return packet_data;
- }
+ iscsi_scsi_device_config *scsi_device_config = NULL;
+ int rc = iscsi_hashmap_get( globvec->scsi_device_config, hash_key, key_len, (uint8_t **) &scsi_device_config );
- iscsi_put_be24( (uint8_t *) &packet_data->ds_len, ds_len );
- memset( ((uint8_t *) packet_data) + old_pkt_size, 0, (new_pkt_size - old_pkt_size) );
+ if ( scsi_device_config == NULL ) {
+ scsi_device_config = (iscsi_scsi_device_config *) malloc( sizeof(struct iscsi_scsi_device_config) );
- return packet_data;
-}
+ if ( scsi_device_config == NULL ) {
+ logadd( LOG_ERROR, "iscsi_config_load_from_ini: Out of memory allocating memory for iSCSI SCSI device configuration" );
-/// CRC32C lookup table. Created with a polynomial reflect value of 0x82F63B78.
-static const uint32_t crc32c_lut[] = {
- 0x00000000, 0xF26B8303, 0xE13B70F7, 0x1350F3F4, 0xC79A971F, 0x35F1141C, 0x26A1E7E8, 0xD4CA64EB,
- 0x8AD958CF, 0x78B2DBCC, 0x6BE22838, 0x9989AB3B, 0x4D43CFD0, 0xBF284CD3, 0xAC78BF27, 0x5E133C24,
- 0x105EC76F, 0xE235446C, 0xF165B798, 0x030E349B, 0xD7C45070, 0x25AFD373, 0x36FF2087, 0xC494A384,
- 0x9A879FA0, 0x68EC1CA3, 0x7BBCEF57, 0x89D76C54, 0x5D1D08BF, 0xAF768BBC, 0xBC267848, 0x4E4DFB4B,
- 0x20BD8EDE, 0xD2D60DDD, 0xC186FE29, 0x33ED7D2A, 0xE72719C1, 0x154C9AC2, 0x061C6936, 0xF477EA35,
- 0xAA64D611, 0x580F5512, 0x4B5FA6E6, 0xB93425E5, 0x6DFE410E, 0x9F95C20D, 0x8CC531F9, 0x7EAEB2FA,
- 0x30E349B1, 0xC288CAB2, 0xD1D83946, 0x23B3BA45, 0xF779DEAE, 0x05125DAD, 0x1642AE59, 0xE4292D5A,
- 0xBA3A117E, 0x4851927D, 0x5B016189, 0xA96AE28A, 0x7DA08661, 0x8FCB0562, 0x9C9BF696, 0x6EF07595,
- 0x417B1DBC, 0xB3109EBF, 0xA0406D4B, 0x522BEE48, 0x86E18AA3, 0x748A09A0, 0x67DAFA54, 0x95B17957,
- 0xCBA24573, 0x39C9C670, 0x2A993584, 0xD8F2B687, 0x0C38D26C, 0xFE53516F, 0xED03A29B, 0x1F682198,
- 0x5125DAD3, 0xA34E59D0, 0xB01EAA24, 0x42752927, 0x96BF4DCC, 0x64D4CECF, 0x77843D3B, 0x85EFBE38,
- 0xDBFC821C, 0x2997011F, 0x3AC7F2EB, 0xC8AC71E8, 0x1C661503, 0xEE0D9600, 0xFD5D65F4, 0x0F36E6F7,
- 0x61C69362, 0x93AD1061, 0x80FDE395, 0x72966096, 0xA65C047D, 0x5437877E, 0x4767748A, 0xB50CF789,
- 0xEB1FCBAD, 0x197448AE, 0x0A24BB5A, 0xF84F3859, 0x2C855CB2, 0xDEEEDFB1, 0xCDBE2C45, 0x3FD5AF46,
- 0x7198540D, 0x83F3D70E, 0x90A324FA, 0x62C8A7F9, 0xB602C312, 0x44694011, 0x5739B3E5, 0xA55230E6,
- 0xFB410CC2, 0x092A8FC1, 0x1A7A7C35, 0xE811FF36, 0x3CDB9BDD, 0xCEB018DE, 0xDDE0EB2A, 0x2F8B6829,
- 0x82F63B78, 0x709DB87B, 0x63CD4B8F, 0x91A6C88C, 0x456CAC67, 0xB7072F64, 0xA457DC90, 0x563C5F93,
- 0x082F63B7, 0xFA44E0B4, 0xE9141340, 0x1B7F9043, 0xCFB5F4A8, 0x3DDE77AB, 0x2E8E845F, 0xDCE5075C,
- 0x92A8FC17, 0x60C37F14, 0x73938CE0, 0x81F80FE3, 0x55326B08, 0xA759E80B, 0xB4091BFF, 0x466298FC,
- 0x1871A4D8, 0xEA1A27DB, 0xF94AD42F, 0x0B21572C, 0xDFEB33C7, 0x2D80B0C4, 0x3ED04330, 0xCCBBC033,
- 0xA24BB5A6, 0x502036A5, 0x4370C551, 0xB11B4652, 0x65D122B9, 0x97BAA1BA, 0x84EA524E, 0x7681D14D,
- 0x2892ED69, 0xDAF96E6A, 0xC9A99D9E, 0x3BC21E9D, 0xEF087A76, 0x1D63F975, 0x0E330A81, 0xFC588982,
- 0xB21572C9, 0x407EF1CA, 0x532E023E, 0xA145813D, 0x758FE5D6, 0x87E466D5, 0x94B49521, 0x66DF1622,
- 0x38CC2A06, 0xCAA7A905, 0xD9F75AF1, 0x2B9CD9F2, 0xFF56BD19, 0x0D3D3E1A, 0x1E6DCDEE, 0xEC064EED,
- 0xC38D26C4, 0x31E6A5C7, 0x22B65633, 0xD0DDD530, 0x0417B1DB, 0xF67C32D8, 0xE52CC12C, 0x1747422F,
- 0x49547E0B, 0xBB3FFD08, 0xA86F0EFC, 0x5A048DFF, 0x8ECEE914, 0x7CA56A17, 0x6FF599E3, 0x9D9E1AE0,
- 0xD3D3E1AB, 0x21B862A8, 0x32E8915C, 0xC083125F, 0x144976B4, 0xE622F5B7, 0xF5720643, 0x07198540,
- 0x590AB964, 0xAB613A67, 0xB831C993, 0x4A5A4A90, 0x9E902E7B, 0x6CFBAD78, 0x7FAB5E8C, 0x8DC0DD8F,
- 0xE330A81A, 0x115B2B19, 0x020BD8ED, 0xF0605BEE, 0x24AA3F05, 0xD6C1BC06, 0xC5914FF2, 0x37FACCF1,
- 0x69E9F0D5, 0x9B8273D6, 0x88D28022, 0x7AB90321, 0xAE7367CA, 0x5C18E4C9, 0x4F48173D, 0xBD23943E,
- 0xF36E6F75, 0x0105EC76, 0x12551F82, 0xE03E9C81, 0x34F4F86A, 0xC69F7B69, 0xD5CF889D, 0x27A40B9E,
- 0x79B737BA, 0x8BDCB4B9, 0x988C474D, 0x6AE7C44E, 0xBE2DA0A5, 0x4C4623A6, 0x5F16D052, 0xAD7D5351};
+ iscsi_hashmap_key_destroy( hash_key );
-/**
- * @brief Calculates digest (CRC32C).
- *
- * Calculates CRC32C with 0x82F63B78 polynomial reflect according to iSCSI specs.\n
- * TODO: Implement optimized SSE4.2 and ARM versions
- *
- * @param[in] data Pointer to data to calculate CRC32C for.
- * @param[in] len Length of data to be calculated. Must be
- * divisable by 4 which is guaranteed by iSCSI standard.
- * @param[in] crc32c Previous CRC32C in case of multiple passes.
- * @return CRC32C value. THis function cannot fail.
- */
-static inline uint32_t iscsi_crc32c_update(const uint8_t *data, const uint len, uint32_t crc32c)
-{
- for ( uint i = 0; i < len; i += 4 ) {
- crc32c = (crc32c >> 8UL) ^ crc32c_lut[(crc32c ^ data[i]) & 0xFF];
- crc32c = (crc32c >> 8UL) ^ crc32c_lut[(crc32c ^ data[i + 1]) & 0xFF];
- crc32c = (crc32c >> 8UL) ^ crc32c_lut[(crc32c ^ data[i + 2]) & 0xFF];
- crc32c = (crc32c >> 8UL) ^ crc32c_lut[(crc32c ^ data[i + 3]) & 0xFF];
- }
+ return 0;
+ }
- return crc32c;
-}
+ scsi_device_config->flags = 0;
-/**
- * @brief Calculate and store iSCSI header digest (CRC32C).
- *
- * Calculates header digest (CRC32C) with 0x82F63B78 polynomial reflect
- * according to iSCSI specs and stores the result in the iSCSI packet
- * data. This function cannot fail.
- *
- * @param[in] packet_data Pointer to ISCSI BHS packet to calculate CRC32C for.
- */
-void iscsi_calc_header_digest(const iscsi_bhs_packet *packet_data)
-{
- const uint32_t len = sizeof(struct iscsi_bhs_packet) + ((const uint32_t) packet_data->total_ahs_len << 2UL);
- uint8_t *hdr_digest = ((uint8_t *) packet_data) + len;
- const uint32_t crc32c = iscsi_crc32c_update( (const uint8_t *) packet_data, iscsi_align(len, 4), ISCSI_CRC32C_INITIAL ) ^ ISCSI_CRC32C_XOR;
+ if ( (globvec->flags & ISCSI_GLOBALS_FLAGS_INIT_R2T) != 0 )
+ scsi_device_config->flags |= ISCSI_GLOBALS_SCSI_DEVICE_CONFIG_FLAGS_INIT_R2T;
- iscsi_put_be32( hdr_digest, crc32c );
-}
+ if ( (globvec->flags & ISCSI_GLOBALS_FLAGS_IMMEDIATE_DATA) != 0 )
+ scsi_device_config->flags |= ISCSI_GLOBALS_SCSI_DEVICE_CONFIG_FLAGS_IMMEDIATE_DATA;
-/**
- * @brief Validates a stored iSCSI header digest (CRC32C) with actual header data.
- *
- * Verifies header digest (CRC32C) with 0x82F63B78 polynomial reflect
- * according to iSCSI specs. This function cannot fail.
- *
- * @param[in] packet_data Pointer to ISCSI BHS packet to validate CRC32C for.
- * @return True if CRC32C matches the stored value, false otherwise.
- */
-int iscsi_validate_header_digest(const iscsi_bhs_packet *packet_data)
-{
- const uint32_t len = sizeof(struct iscsi_bhs_packet) + ((const uint32_t) packet_data->total_ahs_len << 2UL);
- const uint8_t *hdr_digest = ((uint8_t *) packet_data) + len;
- const uint32_t pkt_crc32c = *(uint32_t *) hdr_digest;
- const uint32_t crc32c = iscsi_crc32c_update( (const uint8_t *) packet_data, len, ISCSI_CRC32C_INITIAL ) ^ ISCSI_CRC32C_XOR;
+ if ( (globvec->flags & ISCSI_GLOBALS_FLAGS_DATA_PDU_IN_ORDER) != 0 )
+ scsi_device_config->flags |= ISCSI_GLOBALS_SCSI_DEVICE_CONFIG_FLAGS_DATA_PDU_IN_ORDER;
- return iscsi_get_be32(pkt_crc32c) == crc32c;
-}
+ if ( (globvec->flags & ISCSI_GLOBALS_FLAGS_DATA_SEQ_IN_ORDER) != 0 )
+ scsi_device_config->flags |= ISCSI_GLOBALS_SCSI_DEVICE_CONFIG_FLAGS_DATA_SEQ_IN_ORDER;
-/**
- * @brief Calculate iSCSI data digest (CRC32C).
- *
- * Calculates data digest (CRC32) with 0x82F63B78 polynomial reflect
- * of a whole DataSegment (CRC32C) according to the iSCSI specs.\n
- * The resulting CRC32C will be stored in the iSCSI packet.
- *
- * @param[in] packet_data Pointer to ISCSI DS packet to calculate CRC32C for.
- * @param[in] header_digest_size Length of optional header digest (0 or 4 for now) in
- * order to calculate correct DataSegment index. The header digest size IS NOT checked
- * for conforming to iSCSI specs, so be careful.
- */
-void iscsi_calc_data_digest(const iscsi_bhs_packet *packet_data, const int header_digest_size)
-{
- const uint32_t ds_idx = (const uint32_t) sizeof(struct iscsi_bhs_packet) + ((const uint32_t) packet_data->total_ahs_len << 2UL) + header_digest_size;
- const uint8_t *data = ((uint8_t *) packet_data) + ds_idx;
- const uint32_t ds_len = iscsi_get_be24(packet_data->ds_len);
- const uint32_t len = iscsi_align(ds_len, 4);
- uint8_t *data_digest = ((uint8_t *) packet_data) + ds_idx + len;
- const uint32_t crc32c = iscsi_crc32c_update( data, len, ISCSI_CRC32C_INITIAL ) ^ ISCSI_CRC32C_XOR;
+ if ( (globvec->flags & ISCSI_GLOBALS_FLAGS_SCSI_IO_REMOVABLE) != 0 )
+ scsi_device_config->flags |= ISCSI_GLOBALS_SCSI_DEVICE_CONFIG_FLAGS_SCSI_IO_REMOVABLE;
- iscsi_put_be32( data_digest, crc32c );
-}
+ if ( (globvec->flags & ISCSI_GLOBALS_FLAGS_SCSI_IO_UNMAP) != 0 )
+ scsi_device_config->flags |= ISCSI_GLOBALS_SCSI_DEVICE_CONFIG_FLAGS_SCSI_IO_UNMAP;
-/**
- * @brief Validates a stored iSCSI data digest (CRC32C) with actual DataSegment.
- *
- * Verifies data digest (CRC32C) with 0x82F63B78 polynomial reflect
- * according to iSCSI specs. This function cannot fail.
- *
- * @param[in] packet_data Pointer to ISCSI BHS packet to calculate CRC32C for.
- * @param[in] header_digest_size Length of optional header digest (0 or 4 for now) in
- * order to calculate correct DataSegment index. The header digest size IS NOT checked
- * for conforming to iSCSI specs, so be careful.
- * @return True if CRC32C matches the stored value, false otherwise.
- */
-int iscsi_validate_data_digest(const iscsi_bhs_packet *packet_data, const int header_digest_size)
-{
- const uint32_t ds_idx = (const uint32_t) sizeof(struct iscsi_bhs_packet) + ((const uint32_t) packet_data->total_ahs_len << 2UL) + header_digest_size;
- const uint8_t *data = ((uint8_t *) packet_data) + ds_idx;
- const uint32_t ds_len = iscsi_get_be24(packet_data->ds_len);
- const uint32_t len = iscsi_align(ds_len, 4);
- const uint8_t *data_digest = data + len;
- const uint32_t pkt_crc32c = *(uint32_t *) data_digest;
- const uint32_t crc32c = iscsi_crc32c_update( (const uint8_t *) data, len, ISCSI_CRC32C_INITIAL ) ^ ISCSI_CRC32C_XOR;
+ if ( (globvec->flags & ISCSI_GLOBALS_FLAGS_SCSI_IO_NO_ROTATION) != 0 )
+ scsi_device_config->flags |= ISCSI_GLOBALS_SCSI_DEVICE_CONFIG_FLAGS_SCSI_IO_NO_ROTATION;
+
+ if ( (globvec->flags & ISCSI_GLOBALS_FLAGS_SCSI_IO_PHYSICAL_READ_ONLY) != 0 )
+ scsi_device_config->flags |= ISCSI_GLOBALS_SCSI_DEVICE_CONFIG_FLAGS_SCSI_IO_PHYSICAL_READ_ONLY;
+
+ if ( (globvec->flags & ISCSI_GLOBALS_FLAGS_SCSI_IO_WRITE_PROTECT) != 0 )
+ scsi_device_config->flags |= ISCSI_GLOBALS_SCSI_DEVICE_CONFIG_FLAGS_SCSI_IO_WRITE_PROTECT;
+
+ if ( (globvec->flags & ISCSI_GLOBALS_FLAGS_SCSI_IO_WRITE_CACHE) != 0 )
+ scsi_device_config->flags |= ISCSI_GLOBALS_SCSI_DEVICE_CONFIG_FLAGS_SCSI_IO_WRITE_CACHE;
+
+ scsi_device_config->header_digest = globvec->header_digest;
+ scsi_device_config->data_digest = globvec->data_digest;
+ scsi_device_config->scsi_device_type = globvec->scsi_device_type;
+ scsi_device_config->max_recv_ds_len = globvec->max_recv_ds_len;
+ scsi_device_config->max_session_conns = globvec->max_session_conns;
+ scsi_device_config->max_outstanding_r2t = globvec->max_outstanding_r2t;
+ scsi_device_config->default_time_to_wait = globvec->default_time_to_wait;
+ scsi_device_config->default_time_to_retain = globvec->default_time_to_retain;
+ scsi_device_config->first_burst_len = globvec->first_burst_len;
+ scsi_device_config->max_burst_len = globvec->max_burst_len;
+ scsi_device_config->err_recovery_level = globvec->err_recovery_level;
+ scsi_device_config->scsi_physical_block_size = globvec->scsi_physical_block_size;
+ scsi_device_config->scsi_physical_block_size_shift = globvec->scsi_physical_block_size_shift;
+ scsi_device_config->scsi_logical_block_size = globvec->scsi_logical_block_size;
+ scsi_device_config->scsi_logical_block_size_shift = globvec->scsi_logical_block_size_shift;
+
+ rc = iscsi_hashmap_put( globvec->scsi_device_config, hash_key, key_len, (uint8_t *) scsi_device_config );
+
+ if ( rc < 0 ) {
+ free( scsi_device_config );
+ iscsi_hashmap_key_destroy( hash_key );
+
+ return 0;
+ }
+ } else {
+ iscsi_hashmap_key_destroy( hash_key );
+ }
+
+ int32_t num_value = iscsi_config_parse_int( (uint8_t *) value );
+
+ if ( strcasecmp( key, (char *) ISCSI_LOGIN_AUTH_SESSION_TEXT_KEY_HEADER_DIGEST ) == 0 ) {
+ scsi_device_config->header_digest = ((strcasecmp( value, "CRC32C" ) == 0) ? ISCSI_DIGEST_SIZE : 0);
+ } else if ( strcasecmp( key, (char *) ISCSI_LOGIN_AUTH_SESSION_TEXT_KEY_DATA_DIGEST ) == 0 ) {
+ scsi_device_config->data_digest = ((strcasecmp( value, "CRC32C" ) == 0) ? ISCSI_DIGEST_SIZE : 0);
+ } else if ( strcasecmp( key, (char *) ISCSI_LOGIN_AUTH_SESSION_TEXT_KEY_MAX_RECV_DS_LEN ) == 0 ) {
+ if ( (num_value >= 512L) && (num_value <= (int32_t) ISCSI_MAX_DS_SIZE) )
+ scsi_device_config->max_recv_ds_len = num_value;
+ } else if ( strcasecmp( key, ISCSI_GLOBALS_SECTION_ISCSI_MAX_CONNECTIONS_PER_SESSIONS ) == 0 ) {
+ if ( (num_value > 0L) && (num_value <= 65535L) )
+ scsi_device_config->max_session_conns = num_value;
+ } else if ( strcasecmp( key, (char *) ISCSI_LOGIN_AUTH_SESSION_TEXT_KEY_MAX_OUTSTANDING_R2T ) == 0 ) {
+ if ( (num_value > 0L) && (num_value <= 65536L) )
+ scsi_device_config->max_outstanding_r2t = num_value;
+ } else if ( strcasecmp( key, (char *) ISCSI_LOGIN_AUTH_SESSION_TEXT_KEY_DEFAULT_TIME_WAIT ) == 0 ) {
+ if ( (uint32_t) num_value <= 3600UL )
+ scsi_device_config->default_time_to_wait = num_value;
+ } else if ( strcasecmp( key, (char *) ISCSI_LOGIN_AUTH_SESSION_TEXT_KEY_DEFAULT_TIME_RETAIN ) == 0 ) {
+ if ( (uint32_t) num_value <= 3600UL )
+ scsi_device_config->default_time_to_retain = num_value;
+ } else if ( strcasecmp( key, (char *) ISCSI_LOGIN_AUTH_SESSION_TEXT_KEY_FIRST_BURST_LEN ) == 0 ) {
+ if ( (num_value >= 512L) && (num_value <= (int32_t) ISCSI_MAX_DS_SIZE) )
+ scsi_device_config->first_burst_len = num_value;
+ } else if ( strcasecmp( key, (char *) ISCSI_LOGIN_AUTH_SESSION_TEXT_KEY_MAX_BURST_LEN ) == 0 ) {
+ if ( (num_value >= 512L) && (num_value <= (int32_t) ISCSI_MAX_DS_SIZE) )
+ scsi_device_config->max_burst_len = num_value;
+ } else if ( strcasecmp( key, (char *) ISCSI_LOGIN_AUTH_SESSION_TEXT_KEY_INITIAL_R2T ) == 0 ) {
+ if ( num_value != -2147483648L ) {
+ if ( num_value != 0L )
+ scsi_device_config->flags |= ISCSI_GLOBALS_FLAGS_INIT_R2T;
+ else
+ scsi_device_config->flags &= ~ISCSI_GLOBALS_FLAGS_INIT_R2T;
+ }
+ } else if ( strcasecmp( key, (char *) ISCSI_LOGIN_AUTH_SESSION_TEXT_KEY_IMMEDIATE_DATA ) == 0 ) {
+ if ( num_value != -2147483648L ) {
+ if ( num_value != 0L )
+ scsi_device_config->flags |= ISCSI_GLOBALS_FLAGS_IMMEDIATE_DATA;
+ else
+ scsi_device_config->flags &= ~ISCSI_GLOBALS_FLAGS_IMMEDIATE_DATA;
+ }
+ } else if ( strcasecmp( key, (char *) ISCSI_LOGIN_AUTH_SESSION_TEXT_KEY_DATA_PDU_IN_ORDER ) == 0 ) {
+ if ( num_value != -2147483648L ) {
+ if ( num_value != 0L )
+ scsi_device_config->flags |= ISCSI_GLOBALS_FLAGS_DATA_PDU_IN_ORDER;
+ else
+ scsi_device_config->flags &= ~ISCSI_GLOBALS_FLAGS_DATA_PDU_IN_ORDER;
+ }
+ } else if ( strcasecmp( key, (char *) ISCSI_LOGIN_AUTH_SESSION_TEXT_KEY_DATA_SEQ_IN_ORDER ) == 0 ) {
+ if ( num_value != -2147483648L ) {
+ if ( num_value != 0L )
+ scsi_device_config->flags |= ISCSI_GLOBALS_FLAGS_DATA_SEQ_IN_ORDER;
+ else
+ scsi_device_config->flags &= ~ISCSI_GLOBALS_FLAGS_DATA_SEQ_IN_ORDER;
+ }
+ } else if ( strcasecmp( key, (char *) ISCSI_LOGIN_AUTH_SESSION_TEXT_KEY_ERR_RECOVERY_LEVEL ) == 0 ) {
+ if ( (uint32_t) num_value <= 2UL )
+ scsi_device_config->err_recovery_level = num_value;
+ } else if ( strcasecmp( key, ISCSI_GLOBALS_SECTION_SCSI_KEY_DEVICE_TYPE ) == 0 ) {
+ if ( strcasecmp( value, "Sequential" ) == 0 )
+ scsi_device_config->scsi_device_type = ISCSI_SCSI_BASIC_INQUIRY_DATA_PERIPHERAL_TYPE_SEQ;
+ else if ( strcasecmp( value, "WriteOnce" ) == 0 )
+ scsi_device_config->scsi_device_type = ISCSI_SCSI_BASIC_INQUIRY_DATA_PERIPHERAL_TYPE_WORM;
+ else if ( strcasecmp( value, "ReadOnlyDirect" ) == 0 )
+ scsi_device_config->scsi_device_type = ISCSI_SCSI_BASIC_INQUIRY_DATA_PERIPHERAL_TYPE_RO_DIRECT;
+ else if ( strcasecmp( value, "OpticalMemory" ) == 0 )
+ scsi_device_config->scsi_device_type = ISCSI_SCSI_BASIC_INQUIRY_DATA_PERIPHERAL_TYPE_OPTICAL;
+ else if ( strcasecmp( value, "MediaChanger" ) == 0 )
+ scsi_device_config->scsi_device_type = ISCSI_SCSI_BASIC_INQUIRY_DATA_PERIPHERAL_TYPE_CHANGER;
+ else
+ scsi_device_config->scsi_device_type = ISCSI_SCSI_BASIC_INQUIRY_DATA_PERIPHERAL_TYPE_DIRECT;
+ } else if ( strcasecmp( key, ISCSI_GLOBALS_SECTION_SCSI_KEY_PHYSICAL_BLOCK_SIZE ) == 0 ) {
+ num_value = iscsi_align_pow2_ceil( num_value );
+
+ if ( (num_value >= 256L) && (num_value <= 32768L) ) {
+ scsi_device_config->scsi_physical_block_size = num_value;
+ scsi_device_config->scsi_physical_block_size_shift = iscsi_get_log2_of_pow2( num_value );
+ }
+ } else if ( strcasecmp( key, ISCSI_GLOBALS_SECTION_SCSI_KEY_LOGICAL_BLOCK_SIZE ) == 0 ) {
+ num_value = iscsi_align_pow2_ceil( num_value );
- return iscsi_get_be32(pkt_crc32c) == crc32c;
+ if ( (num_value >= 256L) && (num_value <= 32768L) ) {
+ scsi_device_config->scsi_logical_block_size = num_value;
+ scsi_device_config->scsi_logical_block_size_shift = iscsi_get_log2_of_pow2( num_value );
+ }
+ } else if ( strcasecmp( key, ISCSI_GLOBALS_SECTION_SCSI_KEY_REMOVABLE ) == 0 ) {
+ if ( num_value != -2147483648L ) {
+ if ( num_value != 0L )
+ scsi_device_config->flags |= ISCSI_GLOBALS_SCSI_DEVICE_CONFIG_FLAGS_SCSI_IO_REMOVABLE;
+ else
+ scsi_device_config->flags &= ~ISCSI_GLOBALS_SCSI_DEVICE_CONFIG_FLAGS_SCSI_IO_REMOVABLE;
+ }
+ } else if ( strcasecmp( key, ISCSI_GLOBALS_SECTION_SCSI_KEY_UNMAP ) == 0 ) {
+ if ( num_value != -2147483648L ) {
+ if ( num_value != 0L )
+ scsi_device_config->flags |= ISCSI_GLOBALS_SCSI_DEVICE_CONFIG_FLAGS_SCSI_IO_UNMAP;
+ else
+ scsi_device_config->flags &= ~ISCSI_GLOBALS_SCSI_DEVICE_CONFIG_FLAGS_SCSI_IO_UNMAP;
+ }
+ } else if ( strcasecmp( key, ISCSI_GLOBALS_SECTION_SCSI_KEY_NO_ROTATION ) == 0 ) {
+ if ( num_value != -2147483648L ) {
+ if ( num_value != 0L )
+ scsi_device_config->flags |= ISCSI_GLOBALS_SCSI_DEVICE_CONFIG_FLAGS_SCSI_IO_NO_ROTATION;
+ else
+ scsi_device_config->flags &= ~ISCSI_GLOBALS_SCSI_DEVICE_CONFIG_FLAGS_SCSI_IO_NO_ROTATION;
+ }
+ } else if ( strcasecmp( key, ISCSI_GLOBALS_SECTION_SCSI_KEY_PHYSICAL_READ_ONLY ) == 0 ) {
+ if ( num_value != -2147483648L ) {
+ if ( num_value != 0L )
+ scsi_device_config->flags |= ISCSI_GLOBALS_SCSI_DEVICE_CONFIG_FLAGS_SCSI_IO_PHYSICAL_READ_ONLY;
+ else
+ scsi_device_config->flags &= ~ISCSI_GLOBALS_SCSI_DEVICE_CONFIG_FLAGS_SCSI_IO_PHYSICAL_READ_ONLY;
+ }
+ } else if ( strcasecmp( key, ISCSI_GLOBALS_SECTION_SCSI_KEY_WRITE_PROTECT ) == 0 ) {
+ if ( num_value != -2147483648L ) {
+ if ( num_value != 0L )
+ scsi_device_config->flags |= ISCSI_GLOBALS_SCSI_DEVICE_CONFIG_FLAGS_SCSI_IO_WRITE_PROTECT;
+ else
+ scsi_device_config->flags &= ~ISCSI_GLOBALS_SCSI_DEVICE_CONFIG_FLAGS_SCSI_IO_WRITE_PROTECT;
+ }
+ } else if ( strcasecmp( key, ISCSI_GLOBALS_SECTION_SCSI_KEY_WRITE_CACHE ) == 0 ) {
+ if ( num_value != -2147483648L ) {
+ if ( num_value != 0L )
+ scsi_device_config->flags |= ISCSI_GLOBALS_SCSI_DEVICE_CONFIG_FLAGS_SCSI_IO_WRITE_CACHE;
+ else
+ scsi_device_config->flags &= ~ISCSI_GLOBALS_SCSI_DEVICE_CONFIG_FLAGS_SCSI_IO_WRITE_CACHE;
+ }
+ }
+ }
+
+ return 1;
}
/**
- * @brief Validates a single text key / value pair according to iSCSI specs.
+ * @brief Loads iSCSI server configuration from INI file.
*
- * Validates an iSCSI protocol key and value pair for compliance
- * with the iSCSI specs.
+ * This function parses the INI configuration file
+ * and assigns it to the config section of the iSCSI
+ * global vector.
*
- * @param[in] packet_data Pointer to key / value pair to be
- * validated. NULL is an illegal value, so be careful.
- * @param[in] len Length of the remaining packet data.
- * @return Number of bytes used by the key / vair pair or
- * a negative value in case of an error. This can be used for
- * incrementing the offset to the next key / value pair.
+ * @param[in] globvec Pointer to iSCSI global vector where to store
+ * the parsed and processed results. May NOT be
+ * NULL, so be careful.
+ *
+ * @return Number of configuration keys parsed or
+ * a neagtive error code otherwise.
*/
-static int iscsi_validate_text_key_value_pair(const uint8_t *packet_data, const uint32_t len)
+int iscsi_config_load(iscsi_globals *globvec)
{
- const uint key_val_len = (uint) strnlen( (char *) packet_data, len );
- const uint8_t *key_end = memchr( packet_data, '=', key_val_len );
+ char *name = (char *) iscsi_sprintf_alloc( "%s/%s", _configDir, CONFIG_FILENAME );
- if ( key_end == NULL )
- return ISCSI_VALIDATE_PACKET_RESULT_ERROR_PROTOCOL_SPECS; // Missing separator '=' for key / value pair -> invalid iSCSI packet data
+ if ( name == NULL )
+ return -1;
- const uint key_len = (uint) (key_end - packet_data);
+ if ( !file_isReadable( name ) ) {
+ free( name );
- if ( key_len == 0 )
- return ISCSI_VALIDATE_PACKET_RESULT_ERROR_PROTOCOL_SPECS; // Zero length is not allowed -> invalid iSCSI packet data
+ return 0;
+ }
- if ( key_len > ISCSI_TEXT_KEY_MAX_LEN )
- return ISCSI_VALIDATE_PACKET_RESULT_ERROR_PROTOCOL_SPECS;
+ pthread_mutex_lock( &globvec->scsi_device_config_mutex );
+ iscsi_hashmap_iterate( globvec->scsi_device_config, iscsi_hashmap_key_destroy_value_callback, NULL );
+ ini_parse( name, iscsi_config_load_from_ini, (void *) globvec );
+ free( name );
- const uint val_len = (uint) strnlen( (char *) (key_end + 1UL), key_val_len - key_len - 1UL );
- const uint max_len = (memcmp( packet_data, "CHAP_C=", (key_len + 1UL) ) == 0) || (memcmp( packet_data, "CHAP_R=", (key_len + 1UL) ) == 0) ? ISCSI_TEXT_VALUE_MAX_LEN : ISCSI_TEXT_VALUE_MAX_SIMPLE_LEN;
+ name = (char *) iscsi_sprintf_alloc( "%s/%s", _configDir, ISCSI_GLOBALS_CONFIG_FILENAME );
- if ( val_len > max_len )
- return ISCSI_VALIDATE_PACKET_RESULT_ERROR_PROTOCOL_SPECS; // Value exceeds maximum length -> invalid iSCSI packet data
+ if ( name == NULL ) {
+ pthread_mutex_unlock( &globvec->scsi_device_config_mutex );
- return (int) (key_len + 1UL + val_len + 1UL); // Number of bytes for processed key / value pair (+1 for '=' and NUL terminator)
+ return -1;
+ }
+
+ if ( !file_isReadable( name ) ) {
+ pthread_mutex_unlock( &globvec->scsi_device_config_mutex );
+ free( name );
+
+ return 0;
+ }
+
+ ini_parse( name, iscsi_config_load_from_ini, (void *) globvec );
+ pthread_mutex_unlock( &globvec->scsi_device_config_mutex );
+ free( name );
+
+ return 1;
}
/**
- * @brief Validates all text key / value pairs according to iSCSI specs.
+ * @brief Finds an iSCSI SCSI device configuration by name using pattern matching.
*
- * Validates all iSCSI protocol key and value pairs for
- * compliance with the iSCSI specs.
+ * Callback function for each element while iterating
+ * through the iSCSI SCSI device configuration hash
+ * map.
*
- * @param[in] packet_data Pointer to first key and value pair to
- * be validated. NULL is an illegal value here, so be careful.
- * @param[in] len Length of the remaining packet data.
- * @return 0 if validation for each text key and value pair was
- * successful, a negative error code in case iSCSI specs
- * are violated.
+ * @param[in] key Pointer to zero padded key. NULL is
+ * an invalid pointer here, so be careful.
+ * @param[in] key_size Number of bytes for the key.
+ * @param[in] value Value of the key, NULL creates an
+ * empty key assignment.
+ * @param[in,out] user_data Pointer to a data structure
+ * containing the iSCSI SCSI device configuration and
+ * the name to be searched for and may NOT be NULL,
+ * so be careful.
+ * @retval -1 The SCSI device configuration has been found and
+ * stored in the result structure. Therefore, no
+ * further searching is needed.
+ * @retval -2 An error occured during matching the
+ * name.
+ * @retval 0 The SCSI device configuration has not been found
+ * yet.
*/
-static int iscsi_validate_key_value_pairs(const uint8_t *packet_data, uint len)
+int iscsi_config_get_callback(uint8_t *key, const size_t key_size, uint8_t *value, uint8_t *user_data)
{
- if ( len == 0 )
- return ISCSI_VALIDATE_PACKET_RESULT_ERROR_PROTOCOL_SPECS; // Zero length is not allowed -> invalid iSCSI packet data
-
- int offset = 0L;
+ iscsi_scsi_device_config_find *scsi_device_config_find = (iscsi_scsi_device_config_find *) user_data;
+ const int rc = fnmatch( (char *) key, (char *) scsi_device_config_find->name, (FNM_PATHNAME | FNM_PERIOD) );
- while ( ((uint) offset < len) && (packet_data[offset] != '\0') ) {
- const int rc = iscsi_validate_text_key_value_pair( (packet_data + offset), (len - offset) );
+ if ( rc == FNM_NOMATCH )
+ return 0;
- if ( rc < ISCSI_VALIDATE_PACKET_RESULT_OK )
- return rc;
+ if ( rc != 0 )
+ return -2;
- offset += rc;
- }
+ scsi_device_config_find->scsi_device_config = (iscsi_scsi_device_config *) value;
- return (iscsi_align(offset, ISCSI_ALIGN_SIZE) != iscsi_align(len, ISCSI_ALIGN_SIZE)) ? ISCSI_VALIDATE_PACKET_RESULT_ERROR_PROTOCOL_SPECS : ISCSI_VALIDATE_PACKET_RESULT_OK;
+ return -1;
}
/**
- * @brief Check if valid iSCSI packet and validate if necessarily.
+ * @brief Retrieves a configuration value either from the iSCSI global vector or for a specified SCSI device name.
*
- * Checks whether packet data is an iSCSI packet or not.\n
- * Since iSCSI doesn't have a magic identifier for its packets, a
- * partial heuristic approach is needed for it. There is not always
- * a guarantee that a packet clearly belongs to iSCSI. If header
- * and/or data digests are present, their respective CRC32C's are
- * validated, too. Note that this function does NOT check if the
- * iSCSI command fits in, e.g. a SCSI data in/out command without
- * prior authentication, which is NOT allowed by the iSCSI standard,
- * will NOT be considered an error by this function as it has no
- * knowledge of prior commands sent.
+ * This function uses wildcard matching
+ * only if the SCSI device name does NOT
+ * have a direct match.
*
- * @param[in] packet_data Pointer to packet data to check for iSCSI, may
- * not be NULL.
- * @param[in] len Length of packet data to be checked for iSCSI, must be
- * at least 48 bytes (minimum BHS header size).
- * @param[in] header_digest_size Length of optional header digest (0 or 4 for now) in
- * order to validate header digest. The header digest size IS NOT checked for
- * conforming to iSCSI specs, so be careful.
- * @param[in] data_digest_size Length of optional data digest (0 or 4 for now) in
- * order to validate data digest size. The data digest size IS NOT checked for
- * conforming to iSCSI specs, so take caution.
- * @return 0 if it's clearly a iSCSI packet (detected without
- * heuristics) or a positive integfer value up to 65536, if heuristics
- * were necessary. A negative value is returned in case of an error or
- * if it's clearly not an iSCSI packet.
+ * @param[in] name Pointer to SCSI configuration name to
+ * be retrieved or NULL if the iSCSI
+ * global vector configuration should
+ * be accessed instead.
+ * @param[in] type Type of configuration to be
+ * retrieved.
+ * @return The requested configuration value or a
+ * negative error code otherwise.
*/
-int iscsi_validate_packet(const struct iscsi_bhs_packet *packet_data, const uint32_t len, const int header_digest_size, const int data_digest_size)
+int32_t iscsi_config_get(uint8_t *name, const int type)
{
- if ( packet_data == NULL )
- return ISCSI_VALIDATE_PACKET_RESULT_ERROR_NO_DATA;
- else if ( len < sizeof (struct iscsi_bhs_packet) )
- return ISCSI_VALIDATE_PACKET_RESULT_ERROR_SIZE_TOO_SMALL;
+ if ( name != NULL ) {
+ const uint key_len = (uint) (strlen( (char *) name ) + 1U);
+ uint8_t *hash_key = iscsi_hashmap_key_create( name, key_len );
- const uint32_t ahs_len = (uint32_t) packet_data->total_ahs_len << 2UL; // AHS length is in DWORD's
- const uint32_t ds_len = (uint32_t) iscsi_get_be24(packet_data->ds_len);
+ if ( hash_key == NULL ) {
+ logadd( LOG_ERROR, "iscsi_config_get: Out of memory allocating memory for iSCSI SCSI device configuration key" );
- if ( len != (sizeof (struct iscsi_bhs_packet) + ahs_len + header_digest_size + iscsi_align(ds_len, ISCSI_ALIGN_SIZE) + data_digest_size) )
- return ISCSI_VALIDATE_PACKET_RESULT_ERROR_SIZE_MISMATCH;
+ return -1L;
+ }
- const uint8_t opcode = ISCSI_GET_OPCODE(packet_data->opcode);
+ pthread_mutex_lock( &iscsi_globvec->scsi_device_config_mutex );
- switch ( opcode ) {
- case ISCSI_CLIENT_NOP_OUT : {
- if ( (int8_t) packet_data->opcode < 0 )
- return ISCSI_VALIDATE_PACKET_RESULT_ERROR_PROTOCOL_SPECS; // MSB always MUST be cleared for this opcode -> invalid iSCSI packet data
+ iscsi_scsi_device_config *scsi_device_config = NULL;
+ int rc = iscsi_hashmap_get( iscsi_globvec->scsi_device_config, hash_key, key_len, (uint8_t **) &scsi_device_config );
- const iscsi_nop_out_packet *nop_out_pkt = (const iscsi_nop_out_packet *) packet_data;
+ if ( rc < 0 ) {
+ iscsi_scsi_device_config_find scsi_device_config_find = {NULL, name};
- if ( (nop_out_pkt->flags != -0x80) || (nop_out_pkt->reserved != 0) || (nop_out_pkt->reserved2[0] != 0) || (nop_out_pkt->reserved2[1] != 0) )
- return ISCSI_VALIDATE_PACKET_RESULT_ERROR_PROTOCOL_SPECS; // Flags always MUST be 0x80 for now and remaining reserved fields need all to be zero, but are NOT -> invalid iSCSI packet data
+ rc = iscsi_hashmap_iterate(iscsi_globvec->scsi_device_config, iscsi_config_get_callback, (uint8_t *) &scsi_device_config_find );
- break;
- }
- case ISCSI_CLIENT_SCSI_CMD : {
- if ( (int8_t) packet_data->opcode < 0 )
- return ISCSI_VALIDATE_PACKET_RESULT_ERROR_PROTOCOL_SPECS; // MSB always MUST be cleared for this opcode -> invalid iSCSI packet data
+ scsi_device_config = scsi_device_config_find.scsi_device_config;
- const iscsi_scsi_cmd_packet *scsi_cmd_pkt = (const iscsi_scsi_cmd_packet *) packet_data;
+ if ( scsi_device_config != NULL ) {
+ iscsi_scsi_device_config *new_scsi_device_config = (iscsi_scsi_device_config *) malloc( sizeof(struct iscsi_scsi_device_config) );
- if ( scsi_cmd_pkt->reserved != 0 )
- return ISCSI_VALIDATE_PACKET_RESULT_ERROR_PROTOCOL_SPECS; // Reserved field needs to be zero, but is NOT -> invalid iSCSI packet data
+ if ( new_scsi_device_config == NULL ) {
+ logadd( LOG_ERROR, "iscsi_config_get: Out of memory allocating memory for new iSCSI SCSI device configuration" );
- break;
- }
- case ISCSI_CLIENT_TASK_FUNC_REQ : {
- if ( ((int8_t) packet_data->opcode < 0) || (ahs_len != 0) || (ds_len != 0) )
- return ISCSI_VALIDATE_PACKET_RESULT_ERROR_PROTOCOL_SPECS; // MSB MUST always be cleared, AHS and DataSegment MUST be zero according to specs -> invalid iSCSI packet data
+ pthread_mutex_unlock( &iscsi_globvec->scsi_device_config_mutex );
+ iscsi_hashmap_key_destroy( hash_key );
- const iscsi_task_mgmt_func_req_packet *task_mgmt_func_req_pkt = (const iscsi_task_mgmt_func_req_packet *) packet_data;
+ return -1L;
+ }
- if ( (task_mgmt_func_req_pkt->func >= 0) || (task_mgmt_func_req_pkt->reserved != 0) || (task_mgmt_func_req_pkt->reserved2 != 0) )
- return ISCSI_VALIDATE_PACKET_RESULT_ERROR_PROTOCOL_SPECS; // Function bit 7 always MUST be set and reserved fields need all to be zero, but are NOT -> invalid iSCSI packet data
+ memcpy( new_scsi_device_config, scsi_device_config, sizeof(struct iscsi_scsi_device_config) );
+ rc = iscsi_hashmap_put( iscsi_globvec->scsi_device_config, hash_key, key_len, (uint8_t *) new_scsi_device_config );
- break;
+ if ( rc < 0 ) {
+ pthread_mutex_unlock( &iscsi_globvec->scsi_device_config_mutex );
+ free( new_scsi_device_config );
+ iscsi_hashmap_key_destroy( hash_key );
+
+ return -1L;
+ }
+
+ scsi_device_config = new_scsi_device_config;
+ hash_key = NULL;
+ }
}
- case ISCSI_CLIENT_LOGIN_REQ : {
- if ( (packet_data->opcode != (opcode | 0x40)) || (ahs_len != 0) )
- return ISCSI_VALIDATE_PACKET_RESULT_ERROR_PROTOCOL_SPECS; // Bit 6 always MUST be set and AHS MUST be zero according to specs -> invalid iSCSI packet data
- const iscsi_login_req_packet *login_req_pkt = (const iscsi_login_req_packet *) packet_data;
+ pthread_mutex_unlock( &iscsi_globvec->scsi_device_config_mutex );
- if ( (ISCSI_VERSION_MIN < login_req_pkt->version_min) || (ISCSI_VERSION_MAX > login_req_pkt->version_max) )
- return ISCSI_VALIDATE_PACKET_RESULT_ERROR_UNSUPPORTED_VERSION;
+ if ( hash_key != NULL )
+ iscsi_hashmap_key_destroy( hash_key );
- if ( (ISCSI_LOGIN_REQ_FLAGS_GET_CURRENT_STAGE(login_req_pkt->flags) == ISCSI_LOGIN_REQ_FLAGS_CURRENT_STAGE_RESERVED) || ((ISCSI_LOGIN_REQ_FLAGS_GET_NEXT_STAGE(login_req_pkt->flags) == ISCSI_LOGIN_REQ_FLAGS_CURRENT_STAGE_RESERVED) && ((login_req_pkt->flags & ISCSI_LOGIN_REQ_FLAGS_TRANSIT) != 0)) || ((login_req_pkt->flags < 0) && ((login_req_pkt->flags & ISCSI_LOGIN_REQ_FLAGS_CONTINUE) != 0)) || (login_req_pkt->flags & ~(ISCSI_LOGIN_REQ_FLAGS_CONTINUE | ISCSI_LOGIN_REQ_FLAGS_TRANSIT) != 0) || (login_req_pkt->reserved != 0) || (login_req_pkt->reserved2[0] != 0) || (login_req_pkt->reserved2[1] != 0) )
- return ISCSI_VALIDATE_PACKET_RESULT_ERROR_PROTOCOL_SPECS; // Current Stage (CSG) is set to reserved and Next Stage (NSG) is reserved and T bit is set, if C bit is set, T MUST be cleared and reserved fields need all to be zero, but are NOT -> invalid iSCSI packet data
+ if ( scsi_device_config != NULL ) {
+ switch ( type ) {
+ case ISCSI_GLOBALS_CONFIG_TYPE_HEADER_DIGEST : {
+ return scsi_device_config->header_digest;
- return iscsi_validate_key_value_pairs( ((const uint8_t *) login_req_pkt) + iscsi_align(ds_len, ISCSI_ALIGN_SIZE), ds_len );
+ break;
+ }
+ case ISCSI_GLOBALS_CONFIG_TYPE_DATA_DIGEST : {
+ return scsi_device_config->data_digest;
- break;
- }
- case ISCSI_CLIENT_TEXT_REQ : {
- if ( (int8_t) packet_data->opcode < 0 )
- return ISCSI_VALIDATE_PACKET_RESULT_ERROR_PROTOCOL_SPECS; // MSB always MUST be cleared for this opcode -> invalid iSCSI packet data
+ break;
+ }
+ case ISCSI_GLOBALS_CONFIG_TYPE_MAX_RECV_DS_LEN : {
+ return scsi_device_config->max_recv_ds_len;
- const iscsi_text_req_packet *text_req_pkt = (const iscsi_text_req_packet *) packet_data;
+ break;
+ }
+ case ISCSI_GLOBALS_CONFIG_TYPE_MAX_SESSION_CONNS : {
+ return scsi_device_config->max_session_conns;
- if ( ((text_req_pkt->flags < 0) && ((text_req_pkt->flags & ISCSI_TEXT_REQ_FLAGS_CONTINUE) != 0)) || (text_req_pkt->flags & ~(ISCSI_TEXT_REQ_FLAGS_CONTINUE | ISCSI_TEXT_REQ_FLAGS_FINAL) != 0) || (text_req_pkt->reserved != 0) || (text_req_pkt->reserved2[0] != 0) || (text_req_pkt->reserved2[1] != 0) )
- return ISCSI_VALIDATE_PACKET_RESULT_ERROR_PROTOCOL_SPECS; // If C bit is set, F MUST be cleared and reserved fields need all to be zero, but are NOT -> invalid iSCSI packet data
+ break;
+ }
+ case ISCSI_GLOBALS_CONFIG_TYPE_MAX_OUTSTANDING_R2T : {
+ return scsi_device_config->max_outstanding_r2t;
- return iscsi_validate_key_value_pairs( ((const uint8_t *) text_req_pkt) + iscsi_align(ds_len, ISCSI_ALIGN_SIZE), ds_len );
+ break;
+ }
+ case ISCSI_GLOBALS_CONFIG_TYPE_DEFAULT_TIME_TO_WAIT : {
+ return scsi_device_config->default_time_to_wait;
- break;
- }
- case ISCSI_CLIENT_SCSI_DATA_OUT : {
- if ( packet_data->opcode != opcode )
- return ISCSI_VALIDATE_PACKET_RESULT_ERROR_PROTOCOL_SPECS; // Bits 6 and 7 always MUST be cleared for this opcode -> invalid iSCSI packet data
+ break;
+ }
+ case ISCSI_GLOBALS_CONFIG_TYPE_DEFAULT_TIME_TO_RETAIN : {
+ return scsi_device_config->default_time_to_retain;
- const iscsi_scsi_data_out_req_packet *scsi_data_out_req_pkt = (const iscsi_scsi_data_out_req_packet *) packet_data;
+ break;
+ }
+ case ISCSI_GLOBALS_CONFIG_TYPE_FIRST_BURST_LEN : {
+ return scsi_device_config->first_burst_len;
- if ( (scsi_data_out_req_pkt->reserved != 0) || (scsi_data_out_req_pkt->reserved2 != 0) || (scsi_data_out_req_pkt->reserved3 != 0) || (scsi_data_out_req_pkt->reserved4 != 0) )
- return ISCSI_VALIDATE_PACKET_RESULT_ERROR_PROTOCOL_SPECS; // Reserved fields need all to be zero, but are NOT -> invalid iSCSI packet data
+ break;
+ }
+ case ISCSI_GLOBALS_CONFIG_TYPE_MAX_BURST_LEN : {
+ return scsi_device_config->max_burst_len;
- break;
- }
- case ISCSI_CLIENT_LOGOUT_REQ : {
- if ( (int8_t) packet_data->opcode < 0 )
- return ISCSI_VALIDATE_PACKET_RESULT_ERROR_PROTOCOL_SPECS; // MSB always MUST be cleared for this opcode -> invalid iSCSI packet data
+ break;
+ }
+ case ISCSI_GLOBALS_CONFIG_TYPE_ERR_RECOVERY_LEVEL : {
+ return scsi_device_config->err_recovery_level;
- const iscsi_logout_req_packet *logout_req_pkt = (const iscsi_logout_req_packet *) packet_data;
+ break;
+ }
+ case ISCSI_GLOBALS_CONFIG_TYPE_SCSI_PHYSICAL_BLOCK_SIZE : {
+ return scsi_device_config->scsi_physical_block_size;
- if ( (logout_req_pkt->reason_code >= 0) || (logout_req_pkt->reserved != 0) || (logout_req_pkt->reserved2 != 0) || (logout_req_pkt->reserved3 != 0) || (logout_req_pkt->reserved4[0] != 0) || (logout_req_pkt->reserved4[1] != 0) )
- return ISCSI_VALIDATE_PACKET_RESULT_ERROR_PROTOCOL_SPECS; // Reason code always MUST have bit 7 set and Reserved fields need all to be zero, but are NOT -> invalid iSCSI packet data
+ break;
+ }
+ case ISCSI_GLOBALS_CONFIG_TYPE_SCSI_DEVICE_TYPE : {
+ return scsi_device_config->scsi_device_type;
- break;
- }
- case ISCSI_CLIENT_SNACK_REQ : {
- if ( packet_data->opcode != opcode )
- return ISCSI_VALIDATE_PACKET_RESULT_ERROR_PROTOCOL_SPECS; // Bits 6 and 7 always MUST be cleared for this opcode -> invalid iSCSI packet data
+ break;
+ }
+ case ISCSI_GLOBALS_CONFIG_TYPE_SCSI_PHYSICAL_BLOCK_SIZE_SHIFT : {
+ return scsi_device_config->scsi_physical_block_size_shift;
- const iscsi_snack_req_packet *snack_req_pkt = (const iscsi_snack_req_packet *) packet_data;
+ break;
+ }
+ case ISCSI_GLOBALS_CONFIG_TYPE_SCSI_LOGICAL_BLOCK_SIZE : {
+ return scsi_device_config->scsi_logical_block_size;
- if ( (snack_req_pkt->type >= 0) || (snack_req_pkt->reserved != 0) || (snack_req_pkt->reserved2 != 0) || (snack_req_pkt->reserved3 != 0) )
- return ISCSI_VALIDATE_PACKET_RESULT_ERROR_PROTOCOL_SPECS; // Bit 7 of type always MUST be set and reserved fields need all to be zero, but are NOT -> invalid iSCSI packet data
+ break;
+ }
+ case ISCSI_GLOBALS_CONFIG_TYPE_SCSI_LOGICAL_BLOCK_SIZE_SHIFT : {
+ return scsi_device_config->scsi_logical_block_size_shift;
- break;
- }
- case ISCSI_CLIENT_VENDOR_CODE1 :
- case ISCSI_CLIENT_VENDOR_CODE2 :
- case ISCSI_CLIENT_VENDOR_CODE3 : {
- break;
- }
- case ISCSI_SERVER_NOP_IN : {
- if ( packet_data->opcode != opcode )
- return ISCSI_VALIDATE_PACKET_RESULT_ERROR_PROTOCOL_SPECS; // Bits 6 and 7 always MUST be cleared for this opcode -> invalid iSCSI packet data
+ break;
+ }
+ case ISCSI_GLOBALS_CONFIG_TYPE_FLAGS_INIT_R2T : {
+ return (((scsi_device_config->flags & ISCSI_GLOBALS_SCSI_DEVICE_CONFIG_FLAGS_INIT_R2T) != 0) ? 1L : 0L);
- const iscsi_nop_in_packet *nop_in_pkt = (const iscsi_nop_in_packet *) packet_data;
+ break;
+ }
+ case ISCSI_GLOBALS_CONFIG_TYPE_FLAGS_IMMEDIATE_DATA : {
+ return (((scsi_device_config->flags & ISCSI_GLOBALS_SCSI_DEVICE_CONFIG_FLAGS_IMMEDIATE_DATA) != 0) ? 1L : 0L);
- if ( (nop_in_pkt->flags != -0x80) || (nop_in_pkt->reserved != 0) || (nop_in_pkt->reserved2[0] != 0) || (nop_in_pkt->reserved2[1] != 0) )
- return ISCSI_VALIDATE_PACKET_RESULT_ERROR_PROTOCOL_SPECS; // Flags always MUST be 0x80 for now and remaining reserved fields need all to be zero, but are NOT -> invalid iSCSI packet data
+ break;
+ }
+ case ISCSI_GLOBALS_CONFIG_TYPE_FLAGS_DATA_PDU_IN_ORDER : {
+ return (((scsi_device_config->flags & ISCSI_GLOBALS_SCSI_DEVICE_CONFIG_FLAGS_DATA_PDU_IN_ORDER) != 0) ? 1L : 0L);
- break;
- }
- case ISCSI_SERVER_SCSI_RESPONSE : {
- if ( packet_data->opcode != opcode )
- return ISCSI_VALIDATE_PACKET_RESULT_ERROR_PROTOCOL_SPECS; // Bits 6 and 7 always MUST be cleared for this opcode -> invalid iSCSI packet data
+ break;
+ }
+ case ISCSI_GLOBALS_CONFIG_TYPE_FLAGS_DATA_SEQ_IN_ORDER : {
+ return (((scsi_device_config->flags & ISCSI_GLOBALS_SCSI_DEVICE_CONFIG_FLAGS_DATA_SEQ_IN_ORDER) != 0) ? 1L : 0L);
- const iscsi_scsi_response_packet *scsi_response_pkt = (iscsi_scsi_response_packet *) packet_data;
+ break;
+ }
+ case ISCSI_GLOBALS_CONFIG_TYPE_FLAGS_SCSI_IO_REMOVABLE : {
+ return (((scsi_device_config->flags & ISCSI_GLOBALS_SCSI_DEVICE_CONFIG_FLAGS_SCSI_IO_REMOVABLE) != 0) ? 1L : 0L);
- if ( scsi_response_pkt->flags >= 0 )
- return ISCSI_VALIDATE_PACKET_RESULT_ERROR_PROTOCOL_SPECS; // Flags always MUST have bit 7 set -> invalid iSCSI packet data
+ break;
+ }
+ case ISCSI_GLOBALS_CONFIG_TYPE_FLAGS_SCSI_IO_UNMAP : {
+ return (((scsi_device_config->flags & ISCSI_GLOBALS_SCSI_DEVICE_CONFIG_FLAGS_SCSI_IO_UNMAP) != 0) ? 1L : 0L);
- break;
- }
- case ISCSI_SERVER_TASK_FUNC_RES : {
- if ( (packet_data->opcode != opcode) || (ahs_len != 0) || (ds_len != 0) )
- return ISCSI_VALIDATE_PACKET_RESULT_ERROR_PROTOCOL_SPECS; // Bits 6 and 7 always MUST be cleared, AHS and DataSegment MUST be zero according to specs -> invalid iSCSI packet data
+ break;
+ }
+ case ISCSI_GLOBALS_CONFIG_TYPE_FLAGS_SCSI_IO_NO_ROTATION : {
+ return (((scsi_device_config->flags & ISCSI_GLOBALS_SCSI_DEVICE_CONFIG_FLAGS_SCSI_IO_NO_ROTATION) != 0) ? 1L : 0L);
+
+ break;
+ }
+ case ISCSI_GLOBALS_CONFIG_TYPE_FLAGS_SCSI_IO_PHYSICAL_READ_ONLY : {
+ return (((scsi_device_config->flags & ISCSI_GLOBALS_SCSI_DEVICE_CONFIG_FLAGS_SCSI_IO_PHYSICAL_READ_ONLY) != 0) ? 1L : 0L);
- const iscsi_task_mgmt_func_response_packet *task_mgmt_func_response_pkt = (const iscsi_task_mgmt_func_response_packet *) packet_data;
+ break;
+ }
+ case ISCSI_GLOBALS_CONFIG_TYPE_FLAGS_SCSI_IO_WRITE_PROTECT : {
+ return (((scsi_device_config->flags & ISCSI_GLOBALS_SCSI_DEVICE_CONFIG_FLAGS_SCSI_IO_WRITE_PROTECT) != 0) ? 1L : 0L);
- if ( (task_mgmt_func_response_pkt->flags != -0x80) || (task_mgmt_func_response_pkt->reserved != 0) || (task_mgmt_func_response_pkt->reserved2 != 0) || (task_mgmt_func_response_pkt->reserved3 != 0) || (task_mgmt_func_response_pkt->reserved4 != 0) || (task_mgmt_func_response_pkt->reserved5 != 0) )
- return ISCSI_VALIDATE_PACKET_RESULT_ERROR_PROTOCOL_SPECS; // Flags must be always 0x80 for now and remaining reserved fields need all to be zero, but are NOT -> invalid iSCSI packet data
+ break;
+ }
+ case ISCSI_GLOBALS_CONFIG_TYPE_FLAGS_SCSI_IO_WRITE_CACHE : {
+ return (((scsi_device_config->flags & ISCSI_GLOBALS_SCSI_DEVICE_CONFIG_FLAGS_SCSI_IO_WRITE_CACHE) != 0) ? 1L : 0L);
- break;
+ break;
+ }
+ default : {
+ return -1L;
+
+ break;
+ }
+ }
}
- case ISCSI_SERVER_LOGIN_RES : {
- if ( packet_data->opcode != opcode )
- return ISCSI_VALIDATE_PACKET_RESULT_ERROR_PROTOCOL_SPECS; // Bits 6 and 7 always MUST be cleared for this opcode -> invalid iSCSI packet data is mandatory
+ }
- const iscsi_login_response_packet *login_response_pkt = (const iscsi_login_response_packet *) packet_data;
+ switch ( type ) {
+ case ISCSI_GLOBALS_CONFIG_TYPE_HEADER_DIGEST : {
+ return iscsi_globvec->header_digest;
- if ( (ISCSI_VERSION_MIN < login_response_pkt->version_max) || (ISCSI_VERSION_MAX > login_response_pkt->version_max) || (ISCSI_VERSION_MIN < login_response_pkt->version_active) || (ISCSI_VERSION_MAX > login_response_pkt->version_active) )
- return ISCSI_VALIDATE_PACKET_RESULT_ERROR_UNSUPPORTED_VERSION;
+ break;
+ }
+ case ISCSI_GLOBALS_CONFIG_TYPE_DATA_DIGEST : {
+ return iscsi_globvec->data_digest;
- if ( (ISCSI_LOGIN_RESPONSE_FLAGS_GET_CURRENT_STAGE(login_response_pkt->flags) == ISCSI_LOGIN_RESPONSE_FLAGS_CURRENT_STAGE_RESERVED) || ((ISCSI_LOGIN_RESPONSE_FLAGS_GET_NEXT_STAGE(login_response_pkt->flags) == ISCSI_LOGIN_RESPONSE_FLAGS_CURRENT_STAGE_RESERVED) && ((login_response_pkt->flags & ISCSI_LOGIN_RESPONSE_FLAGS_TRANSIT) != 0)) || ((login_response_pkt->flags < 0) && ((login_response_pkt->flags & ISCSI_LOGIN_RESPONSE_FLAGS_CONTINUE) != 0)) || (login_response_pkt->flags & ~(ISCSI_LOGIN_RESPONSE_FLAGS_CONTINUE | ISCSI_LOGIN_RESPONSE_FLAGS_TRANSIT) != 0) || (login_response_pkt->reserved != 0) || (login_response_pkt->reserved2 != 0) || (login_response_pkt->reserved3 != 0) )
- return ISCSI_VALIDATE_PACKET_RESULT_ERROR_PROTOCOL_SPECS; // Current Stage (CSG) is set to reserved and Next Stage (NSG) is reserved and T bit is set, if C bit is set, T MUST be cleared and reserved fields need all to be zero, but are NOT -> invalid iSCSI packet data
+ break;
+ }
+ case ISCSI_GLOBALS_CONFIG_TYPE_MAX_RECV_DS_LEN : {
+ return iscsi_globvec->max_recv_ds_len;
- return iscsi_validate_key_value_pairs( ((const uint8_t *) login_response_pkt) + iscsi_align(ds_len, ISCSI_ALIGN_SIZE), ds_len );
+ break;
+ }
+ case ISCSI_GLOBALS_CONFIG_TYPE_MAX_SESSION_CONNS : {
+ return iscsi_globvec->max_session_conns;
break;
}
- case ISCSI_SERVER_TEXT_RES : {
- if ( packet_data->opcode != opcode )
- return ISCSI_VALIDATE_PACKET_RESULT_ERROR_PROTOCOL_SPECS; // Bits 6 and 7 always MUST be cleared for this opcode -> invalid iSCSI packet data
+ case ISCSI_GLOBALS_CONFIG_TYPE_MAX_OUTSTANDING_R2T : {
+ return iscsi_globvec->max_outstanding_r2t;
- const iscsi_text_response_packet *text_response_pkt = (const iscsi_text_response_packet *) packet_data;
+ break;
+ }
+ case ISCSI_GLOBALS_CONFIG_TYPE_DEFAULT_TIME_TO_WAIT : {
+ return iscsi_globvec->default_time_to_wait;
- if ( ((text_response_pkt->flags < 0) && ((text_response_pkt->flags & ISCSI_TEXT_RESPONSE_FLAGS_CONTINUE) != 0)) || (text_response_pkt->flags & ~(ISCSI_TEXT_RESPONSE_FLAGS_CONTINUE | ISCSI_TEXT_RESPONSE_FLAGS_FINAL) != 0) || (text_response_pkt->reserved != 0) || (text_response_pkt->reserved2[0] != 0) || (text_response_pkt->reserved2[1] != 0) )
- return ISCSI_VALIDATE_PACKET_RESULT_ERROR_PROTOCOL_SPECS; // If C bit is set, F MUST be cleared and reserved fields need all to be zero, but are NOT -> invalid iSCSI packet data
+ break;
+ }
+ case ISCSI_GLOBALS_CONFIG_TYPE_DEFAULT_TIME_TO_RETAIN : {
+ return iscsi_globvec->default_time_to_retain;
- return iscsi_validate_key_value_pairs( ((const uint8_t *) text_response_pkt) + iscsi_align(ds_len, ISCSI_ALIGN_SIZE), ds_len );
+ break;
+ }
+ case ISCSI_GLOBALS_CONFIG_TYPE_FIRST_BURST_LEN : {
+ return iscsi_globvec->first_burst_len;
break;
}
- case ISCSI_SERVER_SCSI_DATA_IN : {
- if ( packet_data->opcode != opcode )
- return ISCSI_VALIDATE_PACKET_RESULT_ERROR_PROTOCOL_SPECS; // Bits 6 and 7 always MUST be cleared for this opcode -> invalid iSCSI packet data
+ case ISCSI_GLOBALS_CONFIG_TYPE_MAX_BURST_LEN : {
+ return iscsi_globvec->max_burst_len;
- const iscsi_scsi_data_in_response_packet *scsi_data_in_pkt = (const iscsi_scsi_data_in_response_packet *) packet_data;
+ break;
+ }
+ case ISCSI_GLOBALS_CONFIG_TYPE_ERR_RECOVERY_LEVEL : {
+ return iscsi_globvec->err_recovery_level;
- if ( scsi_data_in_pkt->reserved != 0 )
- return ISCSI_VALIDATE_PACKET_RESULT_ERROR_PROTOCOL_SPECS; // Reserved field needs to be zero, but is NOT -> invalid iSCSI packet data
+ break;
+ }
+ case ISCSI_GLOBALS_CONFIG_TYPE_SCSI_DEVICE_TYPE : {
+ return iscsi_globvec->scsi_device_type;
break;
}
- case ISCSI_SERVER_LOGOUT_RES : {
- if ( (packet_data->opcode != opcode) || (ahs_len != 0) || (ds_len != 0) )
- return ISCSI_VALIDATE_PACKET_RESULT_ERROR_PROTOCOL_SPECS; // Bits 6 and 7 always MUST be cleared, AHS and DataSegment MUST be zero according to specs -> invalid iSCSI packet data
+ case ISCSI_GLOBALS_CONFIG_TYPE_SCSI_PHYSICAL_BLOCK_SIZE : {
+ return iscsi_globvec->scsi_physical_block_size;
- const iscsi_logout_response_packet *logout_response_pkt = (const iscsi_logout_response_packet *) packet_data;
+ break;
+ }
+ case ISCSI_GLOBALS_CONFIG_TYPE_SCSI_PHYSICAL_BLOCK_SIZE_SHIFT : {
+ return iscsi_globvec->scsi_physical_block_size_shift;
- if ( (logout_response_pkt->flags != -0x80) || (logout_response_pkt->reserved != 0) || (logout_response_pkt->reserved2 != 0) || (logout_response_pkt->reserved3 != 0) || (logout_response_pkt->reserved4 != 0) || (logout_response_pkt->reserved5 != 0) )
- return ISCSI_VALIDATE_PACKET_RESULT_ERROR_PROTOCOL_SPECS; // Flags must be always 0x80 for now and remaining reserved fields need all to be zero, but are NOT -> invalid iSCSI packet data
+ break;
+ }
+ case ISCSI_GLOBALS_CONFIG_TYPE_SCSI_LOGICAL_BLOCK_SIZE : {
+ return iscsi_globvec->scsi_logical_block_size;
break;
}
- case ISCSI_SERVER_READY_XFER : {
- if ( (packet_data->opcode != opcode) || (ahs_len != 0) || (ds_len != 0) )
- return ISCSI_VALIDATE_PACKET_RESULT_ERROR_PROTOCOL_SPECS; // AHS and DataSegment MUST be zero according to specs -> invalid iSCSI packet data
+ case ISCSI_GLOBALS_CONFIG_TYPE_SCSI_LOGICAL_BLOCK_SIZE_SHIFT : {
+ return iscsi_globvec->scsi_logical_block_size_shift;
- const iscsi_r2t_packet *r2t_pkt = (const iscsi_r2t_packet *) packet_data;
+ break;
+ }
+ case ISCSI_GLOBALS_CONFIG_TYPE_FLAGS_INIT_R2T : {
+ return (((iscsi_globvec->flags & ISCSI_GLOBALS_FLAGS_INIT_R2T) != 0) ? 1L : 0L);
- if ( (r2t_pkt->flags != -0x80) || (r2t_pkt->reserved != 0) )
- return ISCSI_VALIDATE_PACKET_RESULT_ERROR_PROTOCOL_SPECS; // Flags must be always 0x80 for now and remaining reserved field needs to be zero, but is NOT -> invalid iSCSI packet data
+ break;
+ }
+ case ISCSI_GLOBALS_CONFIG_TYPE_FLAGS_IMMEDIATE_DATA : {
+ return (((iscsi_globvec->flags & ISCSI_GLOBALS_FLAGS_IMMEDIATE_DATA) != 0) ? 1L : 0L);
+
+ break;
+ }
+ case ISCSI_GLOBALS_CONFIG_TYPE_FLAGS_DATA_PDU_IN_ORDER : {
+ return (((iscsi_globvec->flags & ISCSI_GLOBALS_FLAGS_DATA_PDU_IN_ORDER) != 0) ? 1L : 0L);
break;
}
- case ISCSI_SERVER_ASYNC_MSG : {
- if ( (packet_data->opcode != opcode) || (ds_len == 0) )
- return ISCSI_VALIDATE_PACKET_RESULT_ERROR_PROTOCOL_SPECS; // Bits 6 and 7 always MUST be cleared for this opcode and DataSegment is mandatory -> invalid iSCSI packet data
+ case ISCSI_GLOBALS_CONFIG_TYPE_FLAGS_DATA_SEQ_IN_ORDER : {
+ return (((iscsi_globvec->flags & ISCSI_GLOBALS_FLAGS_DATA_SEQ_IN_ORDER) != 0) ? 1L : 0L);
- const iscsi_async_msg_packet *async_msg_pkt = (const iscsi_async_msg_packet *) packet_data;
+ break;
+ }
+ case ISCSI_GLOBALS_CONFIG_TYPE_FLAGS_SCSI_IO_REMOVABLE : {
+ return (((iscsi_globvec->flags & ISCSI_GLOBALS_FLAGS_SCSI_IO_REMOVABLE) != 0) ? 1L : 0L);
- if ( (async_msg_pkt->flags != -0x80) || (async_msg_pkt->tag != 0xFFFFFFFFUL) || (async_msg_pkt->reserved != 0) || (async_msg_pkt->reserved2 != 0) || (async_msg_pkt->reserved3 != 0) )
- return ISCSI_VALIDATE_PACKET_RESULT_ERROR_PROTOCOL_SPECS; // Flags must be always 0x80 for now, remaining reserved fields need all to be zero, but are NOT and tag always MUST be 0xFFFFFFFF -> invalid iSCSI packet data
+ break;
+ }
+ case ISCSI_GLOBALS_CONFIG_TYPE_FLAGS_SCSI_IO_UNMAP : {
+ return (((iscsi_globvec->flags & ISCSI_GLOBALS_FLAGS_SCSI_IO_UNMAP) != 0) ? 1L : 0L);
break;
}
- case ISCSI_SERVER_VENDOR_CODE1 :
- case ISCSI_SERVER_VENDOR_CODE2 :
- case ISCSI_SERVER_VENDOR_CODE3 : {
+ case ISCSI_GLOBALS_CONFIG_TYPE_FLAGS_SCSI_IO_NO_ROTATION : {
+ return (((iscsi_globvec->flags & ISCSI_GLOBALS_FLAGS_SCSI_IO_NO_ROTATION) != 0) ? 1L : 0L);
+
break;
}
- case ISCSI_SERVER_REJECT : {
- if ( packet_data->opcode != opcode )
- return ISCSI_VALIDATE_PACKET_RESULT_ERROR_PROTOCOL_SPECS; // Bits 6 and 7 always MUST be cleared according to specs -> invalid iSCSI packet data
+ case ISCSI_GLOBALS_CONFIG_TYPE_FLAGS_SCSI_IO_PHYSICAL_READ_ONLY : {
+ return (((iscsi_globvec->flags & ISCSI_GLOBALS_FLAGS_SCSI_IO_PHYSICAL_READ_ONLY) != 0) ? 1L : 0L);
- const iscsi_reject_packet *reject_pkt = (const iscsi_reject_packet *) packet_data;
+ break;
+ }
+ case ISCSI_GLOBALS_CONFIG_TYPE_FLAGS_SCSI_IO_WRITE_PROTECT : {
+ return (((iscsi_globvec->flags & ISCSI_GLOBALS_FLAGS_SCSI_IO_WRITE_PROTECT) != 0) ? 1L : 0L);
- if ( (reject_pkt->flags != -0x80) || (reject_pkt->tag != 0xFFFFFFFFUL) || (reject_pkt->reserved != 0) || (reject_pkt->reserved2 != 0) || (reject_pkt->reserved3 != 0) || (reject_pkt->reserved4[0] != 0) || (reject_pkt->reserved4[1] != 0) )
- return ISCSI_VALIDATE_PACKET_RESULT_ERROR_PROTOCOL_SPECS; // Flags must be always 0x80 for now, remaining reserved fields need all to be zero, but are NOT and tag always MUST be 0xFFFFFFFF -> invalid iSCSI packet data
+ break;
+ }
+ case ISCSI_GLOBALS_CONFIG_TYPE_FLAGS_SCSI_IO_WRITE_CACHE : {
+ return (((iscsi_globvec->flags & ISCSI_GLOBALS_FLAGS_SCSI_IO_WRITE_CACHE) != 0) ? 1L : 0L);
break;
}
default : {
- return ISCSI_VALIDATE_PACKET_RESULT_ERROR_INVALID_OPCODE;
+ return -1L;
break;
}
}
- if ( (header_digest_size != 0) && (iscsi_validate_header_digest( packet_data ) == 0) )
- return ISCSI_VALIDATE_PACKET_RESULT_ERROR_CRC32C_HDR_DIGEST;
-
- if ( (data_digest_size != 0) && (iscsi_validate_data_digest( packet_data, header_digest_size ) == 0) )
- return ISCSI_VALIDATE_PACKET_RESULT_ERROR_CRC32C_DATA_DIGEST;
-
- return ISCSI_VALIDATE_PACKET_RESULT_OK; // All tests for iSCSI passed, return 0
+ return -1L;
}
/**
@@ -1877,7 +2360,7 @@ int iscsi_validate_packet(const struct iscsi_bhs_packet *packet_data, const uint
* data stream amd puts the extracted data into a hash map to be used by
* the iSCSI implementation.
*
- * @param[in] pairs Pointer to hash map containing all related keys and pairs.
+ * @param[in] key_value_pairs Pointer to hash map containing all related keys and pairs.
* May NOT be NULL, so take caution.
* @param[in] packet_data Pointer to key / value pair to be parsed. NULL is
* an illegal value, so be careful.
@@ -1886,7 +2369,7 @@ int iscsi_validate_packet(const struct iscsi_bhs_packet *packet_data, const uint
* a negative value in case of an error. This can be used for
* incrementing the offset to the next key / value pair.
*/
-static int iscsi_parse_text_key_value_pair(iscsi_hashmap *pairs, const uint8_t *packet_data, const uint32_t len)
+static int iscsi_parse_text_key_value_pair(iscsi_hashmap *key_value_pairs, const uint8_t *packet_data, const uint32_t len)
{
const uint key_val_len = (uint) strnlen( (char *) packet_data, len );
const uint8_t *key_end = memchr( packet_data, '=', key_val_len );
@@ -1894,68 +2377,68 @@ static int iscsi_parse_text_key_value_pair(iscsi_hashmap *pairs, const uint8_t *
if ( key_end == NULL ) {
logadd( LOG_ERROR, "iscsi_parse_text_key_value_pair: Key / value separator '=' not found" );
- return -1L;
+ return -1;
}
const uint key_len = (uint) (key_end - packet_data);
- if ( key_len == 0 ) {
+ if ( key_len == 0U ) {
logadd( LOG_ERROR, "iscsi_parse_text_key_value_pair: Empty key found which is NOT allowed according to iSCSI specs" );
- return -1L;
+ return -1;
}
if ( key_len > ISCSI_TEXT_KEY_MAX_LEN ) {
logadd( LOG_ERROR, "iscsi_parse_text_key_value_pair: Key value is too large (max 63 bytes)" );
- return -1L;
+ return -1;
}
- const uint hash_key_len = (key_len + 1UL);
+ const uint hash_key_len = (key_len + 1U);
uint8_t *hash_key = iscsi_hashmap_key_create( packet_data, hash_key_len );
if ( hash_key == NULL )
- return -1L;
+ return -1;
hash_key[key_len] = '\0';
- if ( iscsi_hashmap_contains( pairs, hash_key, hash_key_len ) ) {
+ if ( iscsi_hashmap_contains( key_value_pairs, hash_key, hash_key_len ) ) {
logadd( LOG_ERROR, "iscsi_parse_text_key_value_pair: Forbidden duplicate key discovered" );
iscsi_hashmap_key_destroy( hash_key );
- return -1L;
+ return -1;
}
- const uint val_len = (uint) strnlen( (char *) (key_end + 1UL), key_val_len - key_len - 1UL );
- const uint max_len = (strcmp( (char *) hash_key, "CHAP_C" ) == 0) || (strcmp( (char *) hash_key, "CHAP_R" ) == 0) ? ISCSI_TEXT_VALUE_MAX_LEN : ISCSI_TEXT_VALUE_MAX_SIMPLE_LEN;
+ const uint val_len = (uint) (strnlen( (char *) (key_end + 1U), (key_val_len - key_len - 1U) ) + 1U);
+ const uint max_len = (((strcmp( (char *) hash_key, "CHAP_C" ) == 0) || (strcmp( (char *) hash_key, "CHAP_R" ) == 0)) ? ISCSI_TEXT_VALUE_MAX_LEN : ISCSI_TEXT_VALUE_MAX_SIMPLE_LEN);
if ( val_len > max_len ) {
logadd( LOG_ERROR, "iscsi_parse_text_key_value_pair: Value length larger than iSCSI specs allow" );
iscsi_hashmap_key_destroy( hash_key );
- return -1L;
+ return -1;
}
- uint8_t *hash_val = (uint8_t *) malloc( iscsi_align(val_len + 1UL, ISCSI_HASHMAP_VALUE_ALIGN) );
+ uint8_t *hash_val = (uint8_t *) malloc( ISCSI_ALIGN(val_len, ISCSI_TEXT_VALUE_ALIGN) );
if ( hash_val == NULL ) {
logadd( LOG_ERROR, "iscsi_parse_text_key_value_pair: Out of memory allocating memory for value string" );
iscsi_hashmap_key_destroy( hash_key );
- return -1L;
+ return -1;
}
- memcpy( hash_val, key_end + 1, val_len );
+ memcpy( hash_val, (key_end + 1), val_len );
- const int rc = iscsi_hashmap_put( pairs, hash_key, hash_key_len, hash_val );
+ const int rc = iscsi_hashmap_put( key_value_pairs, hash_key, hash_key_len, hash_val );
if ( rc < 0 )
- return -1L;
+ return -1;
- return (int) (hash_key_len + val_len + 1UL); // Number of bytes for processed key / value pair (+1 for '=' and NUL terminator)
+ return (int) (hash_key_len + val_len);
}
/**
@@ -1965,7 +2448,7 @@ static int iscsi_parse_text_key_value_pair(iscsi_hashmap *pairs, const uint8_t *
* data amd puts the extracted data into a hash map to be used by
* the iSCSI implementation.
*
- * @param[in] pairs Pointer to hash map that should contain all
+ * @param[in] key_value_pairs Pointer to hash map that should contain all
* extracted keys and pairs. May NOT be NULL, so take caution.
* @param[in] packet_data Pointer to first key and value pair to
* be parsed. NULL is an illegal value here, so be careful.
@@ -1977,10 +2460,10 @@ static int iscsi_parse_text_key_value_pair(iscsi_hashmap *pairs, const uint8_t *
* @retval 0 Key and value pair was parsed successfully and was added to
* hash map.
*/
-int iscsi_parse_key_value_pairs(iscsi_hashmap *pairs, const uint8_t *packet_data, uint len, int c_bit, uint8_t **partial_pairs)
+int iscsi_parse_key_value_pairs(iscsi_hashmap *key_value_pairs, const uint8_t *packet_data, uint len, int c_bit, uint8_t **partial_pairs)
{
- if ( len == 0 )
- return 0L; // iSCSI specs don't allow zero length
+ if ( len == 0U )
+ return 0; // iSCSI specs don't allow zero length
if ( (partial_pairs != NULL) && (*partial_pairs != NULL) ) { // Strip partial text parameters in case C bit was enabled previously
uint key_val_pair_len;
@@ -1991,13 +2474,13 @@ int iscsi_parse_key_value_pairs(iscsi_hashmap *pairs, const uint8_t *packet_data
uint8_t *tmp_partial_buf = iscsi_sprintf_alloc( "%s%s", *partial_pairs, (const char *) packet_data );
if ( tmp_partial_buf == NULL )
- return -1L;
+ return -1;
- const int rc = iscsi_parse_text_key_value_pair( pairs, tmp_partial_buf, (uint32_t) (key_val_pair_len + strlen( (char *) *partial_pairs )) );
+ const int rc = iscsi_parse_text_key_value_pair( key_value_pairs, tmp_partial_buf, (uint32_t) (key_val_pair_len + strlen( (char *) *partial_pairs )) );
free( tmp_partial_buf );
if ( rc < 0 )
- return -1L;
+ return -1;
free( *partial_pairs );
*partial_pairs = NULL;
@@ -2010,45 +2493,45 @@ int iscsi_parse_key_value_pairs(iscsi_hashmap *pairs, const uint8_t *packet_data
if ( partial_pairs == NULL ) {
logadd( LOG_ERROR, "iscsi_parse_key_value_pairs: C bit set but missing partial parameter" );
- return -1L;
+ return -1;
}
uint key_val_pair_len;
- for (key_val_pair_len = len - 1; (packet_data[key_val_pair_len] != '\0') && (key_val_pair_len > 0); key_val_pair_len--) {
+ for (key_val_pair_len = (len - 1U); (packet_data[key_val_pair_len] != '\0') && (key_val_pair_len > 0U); key_val_pair_len--) {
}
- if ( key_val_pair_len != 0 )
+ if ( key_val_pair_len != 0U )
key_val_pair_len++; // NUL char found, don't copy to target buffer'
- *partial_pairs = (uint8_t *) malloc ( (len - key_val_pair_len) + 1 );
+ *partial_pairs = (uint8_t *) malloc( ((len - key_val_pair_len) + 1U) );
if ( *partial_pairs == NULL ) {
logadd( LOG_ERROR, "iscsi_parse_key_value_pairs: Out of memory allocating partial parameter" );
- return -1L;
+ return -1;
}
memcpy( *partial_pairs, &packet_data[key_val_pair_len], (len - key_val_pair_len) );
- if ( key_val_pair_len != 0 )
- len = key_val_pair_len - 1;
+ if ( key_val_pair_len != 0U )
+ len = (key_val_pair_len - 1U);
else
- return 0L;
+ return 0;
}
- int offset = 0L;
+ int offset = 0;
while ( ((uint) offset < len) && (packet_data[offset] != '\0') ) {
- const int rc = iscsi_parse_text_key_value_pair( pairs, (packet_data + offset), (len - offset) );
+ const int rc = iscsi_parse_text_key_value_pair( key_value_pairs, (packet_data + offset), (len - offset) );
if ( rc < 0 )
- return -1L;
+ return -1;
offset += rc;
}
- return 0L;
+ return 0;
}
/**
@@ -2068,7 +2551,7 @@ int iscsi_parse_key_value_pairs(iscsi_hashmap *pairs, const uint8_t *packet_data
*/
static int iscsi_get_key_value_pair(iscsi_hashmap *key_value_pairs, const uint8_t *key, uint8_t **out_value)
{
- const uint key_len = (uint) strlen( (char *) key ) + 1;
+ const uint key_len = (uint) (strlen( (char *) key ) + 1U);
return iscsi_hashmap_get( key_value_pairs, key, key_len, out_value );
}
@@ -2090,24 +2573,24 @@ static int iscsi_get_key_value_pair(iscsi_hashmap *key_value_pairs, const uint8_
*/
static int iscsi_add_key_value_pair(iscsi_hashmap *key_value_pairs, const uint8_t *key, const uint8_t *value)
{
- const uint key_len = (uint) strlen( (char *) key ) + 1;
+ const uint key_len = (uint) (strlen( (char *) key ) + 1U);
uint8_t *hash_key = iscsi_hashmap_key_create( key, key_len );
if ( hash_key == NULL ) {
logadd( LOG_ERROR, "iscsi_add_key_value_pair: Out of memory allocating key" );
- return -1L;
+ return -1;
}
- const uint val_len = (uint) strlen( (char *) value ) + 1;
- uint8_t *hash_val = (uint8_t *) malloc( iscsi_align(val_len, ISCSI_HASHMAP_VALUE_ALIGN) );
+ const uint val_len = (uint) (strlen( (char *) value ) + 1U);
+ uint8_t *hash_val = (uint8_t *) malloc( ISCSI_ALIGN(val_len, ISCSI_TEXT_VALUE_ALIGN) );
if ( hash_val == NULL ) {
logadd( LOG_ERROR, "iscsi_add_key_value_pair: Out of memory allocating string value" );
iscsi_hashmap_key_destroy( hash_key );
- return -1L;
+ return -1;
}
memcpy( hash_val, value, val_len );
@@ -2134,24 +2617,24 @@ static int iscsi_add_key_value_pair(iscsi_hashmap *key_value_pairs, const uint8_
*/
static int iscsi_update_key_value_pair(iscsi_hashmap *key_value_pairs, const uint8_t *key, const uint8_t *value)
{
- const uint key_len = (uint) strlen( (char *) key ) + 1;
+ const uint key_len = (uint) (strlen( (char *) key ) + 1U);
uint8_t *hash_key = iscsi_hashmap_key_create( key, key_len );
if ( hash_key == NULL ) {
logadd( LOG_ERROR, "iscsi_update_key_value_pair: Out of memory allocating key" );
- return -1L;
+ return -1;
}
- const uint val_len = (uint) strlen( (char *) value ) + 1;
- uint8_t *hash_val = (uint8_t *) malloc( iscsi_align(val_len, ISCSI_HASHMAP_VALUE_ALIGN) );
+ const uint val_len = (uint) (strlen( (char *) value ) + 1U);
+ uint8_t *hash_val = (uint8_t *) malloc( ISCSI_ALIGN(val_len, ISCSI_TEXT_VALUE_ALIGN) );
if ( hash_val == NULL ) {
logadd( LOG_ERROR, "iscsi_update_key_value_pair: Out of memory allocating string value" );
iscsi_hashmap_key_destroy( hash_key );
- return -1L;
+ return -1;
}
memcpy( hash_val, value, val_len );
@@ -2200,14 +2683,14 @@ static int iscsi_get_int_key_value_pair(iscsi_hashmap *key_value_pairs, const ui
* @return 0 on successful operation, or a negative value on
* error (memory exhaustion).
*/
-static int iscsi_add_int_key_value_pair(iscsi_hashmap *key_value_pairs, const uint8_t *key, const int value)
+static int iscsi_add_int_key_value_pair(iscsi_hashmap *key_value_pairs, const uint8_t *key, const int32_t value)
{
- const uint8_t *hash_val = iscsi_sprintf_alloc( "%d", value );
+ const uint8_t *hash_val = iscsi_sprintf_alloc( "%" PRId32, value );
if ( hash_val == NULL ) {
logadd( LOG_ERROR, "iscsi_add_int_key_value_pair: Out of memory allocating integer value." );
- return -1L;
+ return -1;
}
return iscsi_add_key_value_pair( key_value_pairs, key, hash_val );
@@ -2230,17 +2713,21 @@ static int iscsi_add_int_key_value_pair(iscsi_hashmap *key_value_pairs, const ui
* @return 0 on successful operation, or a negative value on
* error (memory exhaustion).
*/
-static int iscsi_update_int_key_value_pair(iscsi_hashmap *key_value_pairs, const uint8_t *key, const int value)
+static int iscsi_update_int_key_value_pair(iscsi_hashmap *key_value_pairs, const uint8_t *key, const int32_t value)
{
- const uint8_t *hash_val = iscsi_sprintf_alloc( "%d", value );
+ uint8_t *hash_val = iscsi_sprintf_alloc( "%" PRId32, value );
if ( hash_val == NULL ) {
logadd( LOG_ERROR, "iscsi_update_int_key_value_pair: Out of memory allocating integer value." );
- return -1L;
+ return -1;
}
- return iscsi_update_key_value_pair( key_value_pairs, key, hash_val );
+ const int rc = iscsi_update_key_value_pair( key_value_pairs, key, hash_val );
+
+ free( hash_val );
+
+ return rc;
}
/**
@@ -2264,7 +2751,7 @@ static int iscsi_get_bool_key_value_pair(iscsi_hashmap *key_value_pairs, const u
int rc = iscsi_get_key_value_pair( key_value_pairs, key, &value );
if ( rc == 0 )
- *out_value = (strcasecmp( (char *) value, "Yes" ) == 0) ? true : false;
+ *out_value = (strcasecmp( (char *) value, "Yes" ) == 0);
return rc;
}
@@ -2322,6 +2809,746 @@ static int iscsi_update_bool_key_value_pair(iscsi_hashmap *key_value_pairs, cons
}
/**
+ * @brief Allocates and initializes an iSCSI task structure.
+ *
+ * This function also initializes the underlying
+ * SCSI task structure with the transfer complete
+ * callback function.\n
+ * If a parent task is specified, SCSI data
+ * is copied over from it.
+ *
+ * @param[in] conn Pointer to iSCSI connection to associate
+ * the task with. May NOT be NULL, so take
+ * caution.
+ * @param[in] parent Pointer to parent iSCSI task to copy
+ * over SCSI task data from.
+ * @param[in] callback Callback function to be invoked
+ * after data transfer has been completed and
+ * may be NULL in case no further action is
+ * required.
+ * @return Pointer to iSCSI task structure or NULL
+ * in case of an error (memory exhaustion).
+ */
+iscsi_task *iscsi_task_create(iscsi_connection *conn, iscsi_task *parent, iscsi_scsi_task_xfer_complete_callback callback)
+{
+ iscsi_task *task = (iscsi_task *) malloc( sizeof(struct iscsi_task) );
+
+ if ( task == NULL ) {
+ logadd( LOG_ERROR, "iscsi_task_create: Out of memory while allocating iSCSI task" );
+
+ return NULL;
+ }
+
+ task->node.succ = NULL;
+ task->node.pred = NULL;
+ task->parent = parent;
+ task->sub_tasks.head = NULL;
+ task->sub_tasks.tail = NULL;
+ task->sub_tasks.pred = NULL;
+ task->conn = conn;
+ task->pdu = NULL;
+ task->pos = 0UL;
+ task->len = 0UL;
+ task->id = 0ULL;
+ task->flags = 0;
+ task->lun_id = 0;
+ task->init_task_tag = 0UL;
+ task->target_xfer_tag = 0UL;
+ task->des_data_xfer_pos = 0UL;
+ task->des_data_xfer_len = 0UL;
+ task->data_sn = 0UL;
+ task->scsi_data_out_cnt = 0UL;
+ task->r2t_len = 0UL;
+ task->r2t_sn = 0UL;
+ task->r2t_next_exp_pos = 0UL;
+ task->r2t_data_sn = 0UL;
+ task->r2t_sn_ack = 0UL;
+ task->r2t_outstanding = 0UL;
+
+ conn->task_cnt++;
+
+ iscsi_scsi_task_create( &task->scsi_task, callback, iscsi_task_destroy_callback );
+
+ if ( parent != NULL ) {
+ parent->scsi_task.ref++;
+
+ task->init_task_tag = parent->init_task_tag;
+ task->lun_id = parent->lun_id;
+
+ task->scsi_task.flags = parent->scsi_task.flags;
+ task->scsi_task.xfer_len = parent->scsi_task.xfer_len;
+ task->scsi_task.lun = parent->scsi_task.lun;
+ task->scsi_task.cdb = parent->scsi_task.cdb;
+ task->scsi_task.target_port = parent->scsi_task.target_port;
+ task->scsi_task.init_port = parent->scsi_task.init_port;
+
+ if ( (task->scsi_task.flags & ISCSI_SCSI_TASK_FLAGS_XFER_READ) != 0 )
+ conn->scsi_data_in_cnt++;
+ }
+
+ return task;
+}
+
+/**
+ * @brief Deallocates all resources of the iSCSI task of an iSCSI SCSI task.
+ *
+ * This callback function is called when the
+ * iSCSI SCSI task itself is about to be
+ * destroyed in order to free the associated
+ * iSCSI task and PDU.
+ *
+ * @param[in] scsi_task Pointer to iSCSI SCSI task to deallocate
+ * its iSCSI task. May NOT be NULL, so ba
+ * careful.
+ */
+void iscsi_task_destroy_callback(iscsi_scsi_task *scsi_task)
+{
+ if ( scsi_task != NULL ) {
+ iscsi_task *task = ISCSI_CONTAINER(iscsi_task, scsi_task, scsi_task);
+
+ if ( task->parent != NULL ) {
+ if ( (task->scsi_task.flags & ISCSI_SCSI_TASK_FLAGS_XFER_READ) != 0 )
+ task->conn->scsi_data_in_cnt--;
+
+ iscsi_scsi_task_destroy( &task->parent->scsi_task );
+
+ task->parent = NULL;
+ }
+
+ if ( task->pdu != NULL ) {
+ iscsi_connection_pdu_destroy( task->pdu );
+
+ task->pdu = NULL;
+ }
+
+ task->conn->task_cnt--;
+
+ free( task );
+ }
+}
+
+/**
+ * @brief Deallocates resources acquired by iscsi_task_create.
+ *
+ * This function also frees the embedded SCSI task.
+ *
+ * @param[in] task Pointer to iSCSI task to deallocate. If
+ * set to NULL, this function does nothing.
+ */
+void iscsi_task_destroy(iscsi_task *task)
+{
+ if ( task != NULL )
+ iscsi_scsi_task_destroy( &task->scsi_task );
+}
+
+/**
+ * @brief Enqueues an iSCSI task.
+ *
+ * This function adds an iSCSI task to a
+ * SCSI queue.
+ *
+ * @param[in] conn Pointer to iSCSI connection to enqueue
+ * the task to and may NOT be NULL, so be
+ * careful.
+ * @param[in] task Pointer to iSCSI task to enqueue to the
+ * associated device. NULL is not allowed
+ * here, take caution.
+ */
+void iscsi_task_queue(iscsi_connection *conn, iscsi_task *task)
+{
+ task->flags |= ISCSI_TASK_FLAGS_QUEUED;
+
+ iscsi_device_scsi_task_queue( conn->device, &task->scsi_task );
+}
+
+/**
+ * @brief Searches an iSCSI task by Target Transfer Tag (TTT).
+ *
+ * This function searches for an iSCSI task by
+ * iterating through the iSCSI connection active
+ * Ready To Transfer tasks doubly linked list.
+ *
+ * @param[in] conn Pointer to iSCSI connection to
+ * search in the active Ready To Transfer tasks
+ * doubly linked list and may NOT be NULL, so
+ * be careful.
+ * @param[in] target_xfer_tag Target Transfer Tag (TTT)
+ * to be searched for.
+ * @return Pointer to found iSCSI task or NULL in
+ * case no iSCSI task has a matching Target
+ * Transfer Tag (TTT).
+ */
+static iscsi_task *iscsi_task_find(iscsi_connection *conn, const uint32_t target_xfer_tag)
+{
+ iscsi_task *task;
+
+ iscsi_list_foreach_node ( &conn->r2t_tasks_active, task ) {
+ if ( task->target_xfer_tag == target_xfer_tag )
+ return task;
+ }
+
+ return NULL;
+}
+
+/**
+ * @brief Removes all iSCSI SCSI sub tasks of a primary task which completed a read data transfer in case data sequence is in order.
+ *
+ * This function removes all sub tasks of an iSCSI
+ * primary task which have finished their transfers
+ * when the data sequence is in order.
+ *
+ * @param[in] conn Pointer to iSCSI connection of which
+ * the data transfer has been finished and
+ * may NOT be NULL, so be careful.
+ * @param[in] primary_task Pointer to iSCSI primary task
+ * of which to remove all sub tasks which have
+ * finished the data transfer. NULL is NOT allowed
+ * here, so take caution.
+ */
+static void iscsi_task_xfer_complete_process_read_sub_tasks(iscsi_connection *conn, iscsi_task *primary_task)
+{
+ iscsi_task *sub_task;
+ iscsi_task *tmp;
+
+ iscsi_list_foreach_safe_node ( &primary_task->sub_tasks, sub_task, tmp ) {
+ if ( primary_task->des_data_xfer_pos != sub_task->scsi_task.pos )
+ break;
+
+ iscsi_list_remove( &sub_task->node );
+
+ primary_task->des_data_xfer_pos += sub_task->scsi_task.len;
+
+ if ( primary_task->des_data_xfer_pos == primary_task->scsi_task.xfer_len )
+ iscsi_task_destroy( primary_task );
+
+ iscsi_task_response( conn, sub_task );
+ iscsi_task_destroy( sub_task );
+ }
+}
+
+/**
+ * @brief Processes an iSCSI SCSI task which completed a read data transfer.
+ *
+ * This function post-processes a task upon
+ * finish of a read data transfer.
+ *
+ * @param[in] conn Pointer to iSCSI connection of which
+ * the data transfer has been finished and
+ * may NOT be NULL, so be careful.
+ * @param[in] task Pointer to iSCSI task which finished
+ * the data transfer. NULL is NOT allowed
+ * here, so take caution.
+ * @param[in] primary_task Pointer to iSCSI primary task
+ * which finished the data transfer which
+ * may NOT be NULL, so be careful.
+ */
+void iscsi_task_xfer_complete_process_read(iscsi_connection *conn, iscsi_task *task, iscsi_task *primary_task)
+{
+ if ( task->scsi_task.status != ISCSI_SCSI_STATUS_GOOD ) {
+ if ( primary_task->scsi_task.status == ISCSI_SCSI_STATUS_GOOD ) {
+ iscsi_task *sub_task;
+
+ iscsi_list_foreach_node ( &primary_task->sub_tasks, sub_task ) {
+ iscsi_scsi_task_status_copy( &sub_task->scsi_task, &task->scsi_task );
+ }
+
+ iscsi_scsi_task_status_copy( &primary_task->scsi_task, &task->scsi_task );
+ }
+ } else if ( primary_task->scsi_task.status != ISCSI_SCSI_STATUS_GOOD ) {
+ iscsi_scsi_task_status_copy( &task->scsi_task, &primary_task->scsi_task );
+ }
+
+ if ( task == primary_task ) {
+ primary_task->des_data_xfer_pos = task->scsi_task.len;
+
+ iscsi_task_response( conn, task );
+ iscsi_task_destroy( task );
+ } else if ( (conn->session->flags & ISCSI_SESSION_FLAGS_DATA_SEQ_IN_ORDER) == 0 ) {
+ primary_task->des_data_xfer_pos += task->scsi_task.len;
+
+ if ( primary_task->des_data_xfer_pos == primary_task->scsi_task.xfer_len )
+ iscsi_task_destroy(primary_task );
+
+ iscsi_task_response( conn, task );
+ iscsi_task_destroy( task );
+ } else if ( task->scsi_task.pos != primary_task->des_data_xfer_pos ) {
+ iscsi_task *sub_task;
+
+ iscsi_list_foreach_node ( &primary_task->sub_tasks, sub_task ) {
+ if ( task->scsi_task.pos < sub_task->scsi_task.pos ) {
+ iscsi_list_insert( &primary_task->sub_tasks, &sub_task->node, task->node.pred );
+
+ return;
+ }
+ }
+
+ iscsi_list_enqueue( &primary_task->sub_tasks, &task->node );
+ } else {
+ iscsi_list_push( &primary_task->sub_tasks, &task->node );
+
+ iscsi_task_xfer_complete_process_read_sub_tasks( conn, primary_task );
+ }
+}
+
+/**
+ * @brief Adds an iSCSI transfer task to either pending (if maximum is exceeded) or active tasks doubly linked list.
+ *
+ * This function also sends Ready To Transfer
+ * (R2T) packet data to the initiator.
+ *
+ * @param[in] conn Pointer to iSCSI connection to add the
+ * transfer task to. May NOT be NULL, so be
+ * careful.
+ * @param[in] task Pointer to iSCSI task to add to
+ * active or pending doubly linked list.
+ * NULL is NOT allowed here, take caution.
+ * @return 0 on successful operation, a negative
+ * error code otherwise.
+ */
+static int iscsi_task_xfer_add(iscsi_connection *conn, iscsi_task *task)
+{
+ const uint32_t xfer_len = task->scsi_task.xfer_len;
+ uint32_t ds_len = task->pdu->ds_len;
+ const uint32_t seg_len = ISCSI_DEFAULT_MAX_RECV_DS_LEN;
+ const uint32_t data_out_req = (uint32_t) (iscsi_is_pow2( seg_len ) ? (((xfer_len - ds_len - 1UL) >> iscsi_get_log2_of_pow2( seg_len )) + 1UL) : (((xfer_len - ds_len - 1UL) / seg_len) + 1UL));
+
+ task->scsi_data_out_cnt = data_out_req;
+
+ if ( conn->r2t_pending >= ISCSI_DEFAULT_MAX_R2T_PER_CONNECTION ) {
+ iscsi_list_enqueue( &conn->r2t_tasks_queue, &task->node );
+
+ return ISCSI_CONNECT_PDU_READ_OK;
+ }
+
+ conn->scsi_data_out_cnt += data_out_req;
+ conn->r2t_pending++;
+
+ task->r2t_next_exp_pos = ds_len;
+ task->r2t_len = 0UL;
+ task->r2t_sn = 0UL;
+
+ if ( ++conn->target_xfer_tag == 0xFFFFFFFFUL )
+ conn->target_xfer_tag = 0UL;
+
+ task->target_xfer_tag = conn->target_xfer_tag;
+
+ const uint32_t max_burst_len = conn->session->max_burst_len;
+
+ while ( ds_len != xfer_len ) {
+ uint32_t len = (xfer_len - ds_len);
+
+ if ( len > max_burst_len )
+ len = max_burst_len;
+
+ const int rc = iscsi_r2t_send( conn, task, &task->r2t_sn, ds_len, len, task->target_xfer_tag );
+
+ if ( rc < 0 )
+ return rc;
+
+ ds_len += len;
+
+ task->r2t_next_exp_pos = ds_len;
+
+ if ( conn->session->max_outstanding_r2t == ++task->r2t_outstanding )
+ break;
+ }
+
+ iscsi_list_enqueue( &conn->r2t_tasks_active, &task->node );
+
+ task->flags |= ISCSI_TASK_FLAGS_R2T_ACTIVE;
+
+ return ISCSI_CONNECT_PDU_READ_OK;
+}
+
+/**
+ * @brief Starts queued iSCSI Ready To Transfer (R2T) tasks by moving them from queued doubly linked list to active doubly linked list.
+ *
+ * This function iterates through all enqueued
+ * transfer tasks of an ISCSI connection and moves
+ * them into the active transfer tasks doubly
+ * linked list until the maximum number of active
+ * transfer tasks has been reached.
+ *
+ * @param[in] conn Pointer to iSCSI connection from where to
+ * move the enqueued iSCSI tasks to the active task
+ * doubly linked list. May NOT be NULL, so be
+ * careful.
+ */
+static void iscsi_task_xfer_queued_tasks_start(iscsi_connection *conn)
+{
+ iscsi_task *task;
+ iscsi_task *tmp;
+
+ iscsi_list_foreach_safe_node ( &conn->r2t_tasks_queue, task, tmp ) {
+ if ( conn->r2t_pending >= ISCSI_DEFAULT_MAX_R2T_PER_CONNECTION )
+ return;
+
+ iscsi_list_remove( &task->node );
+ iscsi_task_xfer_add( conn, task );
+ }
+}
+
+/**
+ * @brief Deletes an iSCSI task from the active Ready To Transfer (R2T) doubly linked list by Target Transfer Tag (TTT).
+ *
+ * This function traverses through an iSCSI task's
+ * active Ready To Transfer (R2T) doubly linked
+ * list in order to find the Target Transfer Tag
+ * (TTT) to be deleted.
+ *
+ * @param[in] conn Pointer to iSCSI connection to
+ * search in the active Ready To Transfer
+ * (R2T) doubly linked list.
+ * @param[in] target_xfer_tag Target Transfer Tag (TTT) to
+ * delete the ISCSI task of.
+ * @retval true The iSCSI task has been found and
+ * deleted successfully.
+ * @retval false The iSCSI task does NOT exist and
+ * therefore could NOT be deleted.
+ */
+bool iscsi_task_xfer_del(iscsi_connection *conn, const uint32_t target_xfer_tag)
+{
+ iscsi_task *task;
+ iscsi_task *tmp;
+
+ iscsi_list_foreach_safe_node ( &conn->r2t_tasks_active, task, tmp ) {
+ if ( task->target_xfer_tag != target_xfer_tag )
+ continue;
+
+ conn->scsi_data_out_cnt -= task->scsi_data_out_cnt;
+ conn->r2t_pending--;
+
+ iscsi_list_remove( &task->node );
+
+ task->flags &= ~ISCSI_TASK_FLAGS_R2T_ACTIVE;
+
+ iscsi_task_destroy( task );
+ iscsi_task_xfer_queued_tasks_start( conn );
+
+ return true;
+ }
+
+ return false;
+}
+
+/**
+ * @brief Processes an iSCSI SCSI task which completed a non-read data transfer.
+ *
+ * This function post-processes a task upon
+ * finish of a non-read data transfer.
+ *
+ * @param[in] conn Pointer to iSCSI connection of which
+ * the data transfer has been finished and
+ * may NOT be NULL, so be careful.
+ * @param[in] task Pointer to iSCSI task which finished
+ * the data transfer. NULL is NOT allowed
+ * here, so take caution.
+ * @param[in] primary_task Pointer to iSCSI primary task
+ * which finished the data transfer which
+ * may NOT be NULL, so be careful.
+ */
+void iscsi_task_xfer_complete_process_other(iscsi_connection *conn, iscsi_task *task, iscsi_task *primary_task)
+{
+ primary_task->des_data_xfer_pos += task->scsi_task.len;
+
+ if ( task == primary_task ) {
+ iscsi_task_response( conn, task );
+ iscsi_task_destroy( task );
+
+ return;
+ }
+
+ if ( task->scsi_task.status == ISCSI_SCSI_STATUS_GOOD )
+ primary_task->scsi_task.xfer_pos += task->scsi_task.xfer_pos;
+ else if ( primary_task->scsi_task.status == ISCSI_SCSI_STATUS_GOOD )
+ iscsi_scsi_task_status_copy( &primary_task->scsi_task, &task->scsi_task );
+
+ if ( primary_task->des_data_xfer_pos == primary_task->scsi_task.xfer_len ) {
+ if ( (primary_task->flags & ISCSI_TASK_FLAGS_R2T_ACTIVE) != 0 ) {
+ iscsi_task_response( conn, primary_task );
+ iscsi_task_xfer_del( conn, primary_task->target_xfer_tag );
+ } else {
+ iscsi_task_response( conn, task );
+ }
+ }
+
+ iscsi_task_destroy( task );
+}
+
+/**
+ * @brief Callback function after iSCSI SCSI Data In response has been sent.
+ *
+ * This function is invoked after the iSCSI
+ * SCSI Data In response has been sent to
+ * the client via TCP/IP.
+ *
+ * @param[in] user_data Pointer to iSCSI connection which
+ * was used for sending the response.
+ */
+static void iscsi_connection_pdu_scsi_data_in_complete(uint8_t *user_data)
+{
+ iscsi_connection *conn = (iscsi_connection *) user_data;
+
+ iscsi_connection_handle_scsi_data_in_queued_tasks( conn );
+}
+
+/**
+ * @brief Sends a single iSCSI SCSI Data In packet to the client.
+ *
+ * This function reads the data from the
+ * associated DNBD3 image as well and sends
+ * it to the initiator.
+ *
+ * @pararm[in] conn Pointer to iSCSI connection for which the
+ * packet should be sent for. May NOT be
+ * NULL, so be careful.
+ * @pararm[in] task Pointer to iSCSI task which handles the
+ * actual SCSI packet data. NULL is NOT
+ * allowed here, so take caution.
+ * @pararm[in] pos Offset of data to be sent in bytes.
+ * @pararm[in] len Length of data to be sent in bytes
+ * @pararm[in] res_snt Residual Count.
+ * @pararm[in] data_sn Data Sequence Number (DataSN).
+ * @pararm[in] flags Flags for this data packet.
+ * @return Next Data Sequence Number (DataSN) on success,
+ * the same DataSN as passed on error.
+ */
+static uint32_t iscsi_scsi_data_in_send(iscsi_connection *conn, iscsi_task *task, const uint32_t pos, const uint32_t len, const uint32_t res_cnt, const uint32_t data_sn, const int8_t flags)
+{
+ iscsi_pdu *response_pdu = iscsi_connection_pdu_create( conn, 0U, conn->header_digest, len, conn->data_digest );
+
+ if ( response_pdu == NULL ) {
+ logadd( LOG_ERROR, "iscsi_scsi_data_in_send: Out of memory while allocating iSCSI SCSI Data In response PDU" );
+
+ return data_sn;
+ }
+
+ response_pdu->task = task;
+ task->scsi_task.ref++;
+
+ iscsi_scsi_data_in_response_packet *scsi_data_in_pkt = (iscsi_scsi_data_in_response_packet *) response_pdu->bhs_pkt;
+
+ scsi_data_in_pkt->opcode = ISCSI_OPCODE_SERVER_SCSI_DATA_IN;
+ scsi_data_in_pkt->flags = (flags & ~(ISCSI_SCSI_DATA_IN_RESPONSE_FLAGS_RES_UNDERFLOW | ISCSI_SCSI_DATA_IN_RESPONSE_FLAGS_RES_OVERFLOW));
+ scsi_data_in_pkt->reserved = 0U;
+
+ iscsi_task *primary_task = ((task->parent != NULL) ? task->parent : task);
+
+ if ( (flags & ISCSI_SCSI_DATA_IN_RESPONSE_FLAGS_STATUS) != 0 ) {
+ if ( (flags & ISCSI_SCSI_DATA_IN_RESPONSE_FLAGS_FINAL) != 0 ) {
+ scsi_data_in_pkt->flags |= (flags & (ISCSI_SCSI_DATA_IN_RESPONSE_FLAGS_RES_UNDERFLOW | ISCSI_SCSI_DATA_IN_RESPONSE_FLAGS_RES_OVERFLOW));
+
+ if ( (primary_task->pdu->bhs_pkt->opcode & ISCSI_OPCODE_FLAGS_IMMEDIATE) == 0 )
+ conn->session->max_cmd_sn++;
+
+ iscsi_put_be32( (uint8_t *) &scsi_data_in_pkt->res_cnt, res_cnt );
+ } else {
+ scsi_data_in_pkt->res_cnt = 0UL;
+ }
+
+ scsi_data_in_pkt->status = task->scsi_task.status;
+ iscsi_put_be32( (uint8_t *) &scsi_data_in_pkt->stat_sn, conn->stat_sn++ );
+ } else {
+ scsi_data_in_pkt->status = 0U;
+ scsi_data_in_pkt->stat_sn = 0UL;
+ scsi_data_in_pkt->res_cnt = 0UL;
+ }
+
+ iscsi_put_be32( (uint8_t *) &scsi_data_in_pkt->total_ahs_len, len ); // TotalAHSLength is always 0 and DataSegmentLength is 24-bit, so write in one step.
+ scsi_data_in_pkt->lun = 0ULL;
+ iscsi_put_be32( (uint8_t *) &scsi_data_in_pkt->init_task_tag, task->init_task_tag );
+ scsi_data_in_pkt->target_xfer_tag = 0xFFFFFFFFUL; // Minus one does not require endianess conversion
+ iscsi_put_be32( (uint8_t *) &scsi_data_in_pkt->exp_cmd_sn, conn->session->exp_cmd_sn );
+ iscsi_put_be32( (uint8_t *) &scsi_data_in_pkt->max_cmd_sn, conn->session->max_cmd_sn );
+ iscsi_put_be32( (uint8_t *) &scsi_data_in_pkt->data_sn, data_sn );
+
+ if ( conn->session->err_recovery_level > 0UL )
+ primary_task->data_sn = data_sn;
+
+ const uint32_t offset = (task->scsi_task.pos + pos);
+ iscsi_put_be32( (uint8_t *) &scsi_data_in_pkt->buf_offset, offset );
+
+ memcpy( response_pdu->ds_cmd_data, (task->scsi_task.buf + pos), len );
+
+ iscsi_connection_pdu_write( conn, response_pdu, iscsi_connection_pdu_scsi_data_in_complete, (uint8_t *) conn );
+
+ return (data_sn + 1UL);
+}
+
+/**
+ * @brief Handles iSCSI task read (incoming) data.
+ *
+ * This function handles iSCSI incoming data
+ * read buffer for both processed and
+ * unprocessed tasks.
+ *
+ * @param[in] conn Pointer to iSCSI connection of which the
+ * incoming data should be handled, may NOT be
+ * NULL, so be careful.
+ * @param[in] task Pointer to iSCSI task for handling
+ * the incoming data. NULL is NOT allowed here,
+ * take caution.
+ * @return 0 on successful incoming transfer handling,
+ * a negative error code otherwise.
+ */
+static int iscsi_task_xfer_scsi_data_in(iscsi_connection *conn, iscsi_task *task)
+{
+ if ( task->scsi_task.status != ISCSI_SCSI_STATUS_GOOD )
+ return 0;
+
+ const uint32_t pos = task->scsi_task.xfer_pos;
+ uint32_t xfer_len = task->scsi_task.len;
+ const uint32_t seg_len = conn->max_recv_ds_len;
+ uint32_t res_cnt = 0UL;
+ int8_t flags = 0;
+
+ if ( pos < xfer_len ) {
+ res_cnt = (xfer_len - pos);
+ xfer_len = pos;
+ flags |= ISCSI_SCSI_DATA_IN_RESPONSE_FLAGS_RES_UNDERFLOW;
+ } else if ( pos > xfer_len ) {
+ res_cnt = (pos - xfer_len);
+ flags |= ISCSI_SCSI_DATA_IN_RESPONSE_FLAGS_RES_OVERFLOW;
+ }
+
+ iscsi_task *primary_task = ((task->parent != NULL) ? task->parent : task);
+ uint32_t data_sn = primary_task->data_sn;
+ uint32_t max_burst_offset = 0UL;
+ const uint32_t max_burst_len = conn->session->max_burst_len;
+ const uint32_t data_in_seq_count = (uint32_t) (iscsi_is_pow2( max_burst_len ) ? (((xfer_len - 1UL) >> iscsi_get_log2_of_pow2( max_burst_len )) + 1UL) : (((xfer_len - 1UL) / max_burst_len) + 1UL));
+ int8_t status = 0;
+
+ for ( uint32_t i = 0UL; i < data_in_seq_count; i++ ) {
+ uint32_t seq_end = (max_burst_offset + max_burst_len);
+
+ if ( seq_end > xfer_len )
+ seq_end = xfer_len;
+
+ for ( uint32_t offset = max_burst_offset; offset < seq_end; offset += seg_len ) {
+ uint32_t len = (seq_end - offset);
+
+ if ( len > seg_len )
+ len = seg_len;
+
+ flags &= (int8_t) ~(ISCSI_SCSI_DATA_IN_RESPONSE_FLAGS_STATUS | ISCSI_SCSI_DATA_IN_RESPONSE_FLAGS_FINAL);
+
+ if ( (offset + len) == seq_end ) {
+ flags |= (int8_t) ISCSI_SCSI_DATA_IN_RESPONSE_FLAGS_FINAL;
+
+ if ( (task->scsi_task.sense_data_len == 0U) && ((offset + len) == xfer_len) && (primary_task->des_data_xfer_pos == primary_task->scsi_task.xfer_len) ) {
+ flags |= (int8_t) ISCSI_SCSI_DATA_IN_RESPONSE_FLAGS_STATUS;
+ status |= flags;
+ }
+ }
+
+ data_sn = iscsi_scsi_data_in_send( conn, task, offset, len, res_cnt, data_sn, flags );
+ }
+
+ max_burst_offset += max_burst_len;
+ }
+
+ if ( primary_task != task )
+ primary_task->scsi_task.xfer_pos += task->scsi_task.xfer_pos;
+
+ primary_task->data_sn = data_sn;
+
+ return (status & ISCSI_SCSI_DATA_IN_RESPONSE_FLAGS_STATUS);
+}
+
+/**
+ * @brief Creates, initializes and sends an iSCSI task reponse PDU.
+ *
+ * This function also receives any remaining
+ * incoming data in case the task is reading.
+ *
+ * @param[in] conn Pointer to iSCSI connection to handle the
+ * task resnponse for and may NOT be NULL,
+ * so be careful.
+ * @param[in] task Pointer to iSSCI task to create the
+ * response PDU from. NULL is NOT allowed
+ * here, take caution.
+ */
+void iscsi_task_response(iscsi_connection *conn, iscsi_task *task)
+{
+ iscsi_task *primary_task = ((task->parent != NULL) ? task->parent : task);
+ iscsi_pdu *pdu = primary_task->pdu;
+ iscsi_scsi_cmd_packet *scsi_cmd_pkt = (iscsi_scsi_cmd_packet *) pdu->bhs_pkt;
+ const uint32_t xfer_len = primary_task->scsi_task.xfer_len;
+
+ if ( (scsi_cmd_pkt->flags_task & ISCSI_SCSI_CMD_FLAGS_TASK_READ) != 0 ) {
+ const int rc = iscsi_task_xfer_scsi_data_in( conn, task );
+
+ if ( (rc > 0) || (primary_task->des_data_xfer_pos != primary_task->scsi_task.xfer_len) )
+ return;
+ }
+
+ const uint32_t ds_len = ((task->scsi_task.sense_data_len != 0U) ? (task->scsi_task.sense_data_len + offsetof(struct iscsi_scsi_ds_cmd_data, sense_data)) : 0UL);
+ iscsi_pdu *response_pdu = iscsi_connection_pdu_create( conn, 0U, conn->header_digest, ds_len, conn->data_digest );
+
+ if ( response_pdu == NULL ) {
+ logadd( LOG_ERROR, "iscsi_task_response: Out of memory while allocating iSCSI SCSI response PDU" );
+
+ return;
+ }
+
+ iscsi_scsi_response_packet *scsi_response_pkt = (iscsi_scsi_response_packet *) response_pdu->bhs_pkt;
+
+ if ( task->scsi_task.sense_data_len != 0U ) {
+ iscsi_scsi_ds_cmd_data *ds_cmd_data_pkt = response_pdu->ds_cmd_data;
+
+ iscsi_put_be16( (uint8_t *) &ds_cmd_data_pkt->len, task->scsi_task.sense_data_len );
+ memcpy( ds_cmd_data_pkt->sense_data, task->scsi_task.sense_data, task->scsi_task.sense_data_len );
+
+ iscsi_put_be32( (uint8_t *) &scsi_response_pkt->total_ahs_len, ds_len ); // TotalAHSLength is always 0 and DataSegmentLength is 24-bit, so write in one step.
+ } else {
+ *(uint32_t *) &scsi_response_pkt->total_ahs_len = 0UL; // TotalAHSLength and DataSegmentLength are always 0, so write in one step.
+ }
+
+ response_pdu->task = task;
+ task->scsi_task.ref++;
+
+ scsi_response_pkt->opcode = ISCSI_OPCODE_SERVER_SCSI_RESPONSE;
+ scsi_response_pkt->flags = -0x80;
+ scsi_response_pkt->response = ISCSI_SCSI_RESPONSE_CODE_OK;
+
+ const uint32_t pos = primary_task->scsi_task.xfer_pos;
+
+ if ( (xfer_len != 0UL) && (task->scsi_task.status == ISCSI_SCSI_STATUS_GOOD) ) {
+ if ( pos < xfer_len ) {
+ const uint32_t res_cnt = (xfer_len - pos);
+
+ scsi_response_pkt->flags |= ISCSI_SCSI_RESPONSE_FLAGS_RES_UNDERFLOW;
+ iscsi_put_be32( (uint8_t *) &scsi_response_pkt->res_cnt, res_cnt );
+ } else if ( pos > xfer_len ) {
+ const uint32_t res_cnt = (pos - xfer_len);
+
+ scsi_response_pkt->flags |= ISCSI_SCSI_RESPONSE_FLAGS_RES_OVERFLOW;
+ iscsi_put_be32( (uint8_t *) &scsi_response_pkt->res_cnt, res_cnt );
+ } else {
+ scsi_response_pkt->res_cnt = 0UL;
+ }
+ } else {
+ scsi_response_pkt->res_cnt = 0UL;
+ }
+
+ scsi_response_pkt->status = task->scsi_task.status;
+ scsi_response_pkt->reserved = 0ULL;
+ iscsi_put_be32( (uint8_t *) &scsi_response_pkt->init_task_tag, task->init_task_tag );
+ scsi_response_pkt->snack_tag = 0UL;
+ iscsi_put_be32( (uint8_t *) &scsi_response_pkt->stat_sn, conn->stat_sn++ );
+
+ if ( (scsi_cmd_pkt->opcode & ISCSI_OPCODE_FLAGS_IMMEDIATE) == 0 )
+ conn->session->max_cmd_sn++;
+
+ iscsi_put_be32( (uint8_t *) &scsi_response_pkt->exp_cmd_sn, conn->session->exp_cmd_sn );
+ iscsi_put_be32( (uint8_t *) &scsi_response_pkt->max_cmd_sn, conn->session->max_cmd_sn );
+ scsi_response_pkt->exp_data_sn = 0UL;
+ scsi_response_pkt->bidi_read_res_cnt = 0UL;
+
+ iscsi_connection_pdu_write( conn, response_pdu, NULL, NULL );
+}
+
+/**
* @brief Creates and initializes an iSCSI portal group.
*
* Specified tag and flags are used for portal group
@@ -2331,7 +3558,7 @@ static int iscsi_update_bool_key_value_pair(iscsi_hashmap *key_value_pairs, cons
* @return Pointer to allocated and initialized portal group
* or NULL in case of memory
*/
-iscsi_portal_group *iscsi_portal_group_create(const int tag, const int flags)
+iscsi_portal_group *iscsi_portal_group_create(const uint64_t tag, const int flags)
{
iscsi_portal_group *portal_group = (iscsi_portal_group *) malloc( sizeof(struct iscsi_portal_group) );
@@ -2341,7 +3568,7 @@ iscsi_portal_group *iscsi_portal_group_create(const int tag, const int flags)
return NULL;
}
- portal_group->portals = iscsi_hashmap_create( 0UL );
+ portal_group->portals = iscsi_hashmap_create( 0U );
if ( portal_group->portals == NULL ) {
logadd( LOG_ERROR, "iscsi_portal_group_create: Out of memory allocating iSCSI portal hash map" );
@@ -2351,7 +3578,7 @@ iscsi_portal_group *iscsi_portal_group_create(const int tag, const int flags)
return NULL;
}
- portal_group->ref_count = 0L;
+ portal_group->ref_count = 0;
portal_group->tag = tag;
portal_group->flags = flags;
portal_group->chap_group = 0L;
@@ -2360,28 +3587,26 @@ iscsi_portal_group *iscsi_portal_group_create(const int tag, const int flags)
}
/**
- * @brief iSCSI portal destructor callback for hash map.
+ * @brief iSCSI portal group destructor callback for hash map.
*
* Callback function for deallocation of an iSCSI
- * portal stored in the iSCSI portal group hash map.
+ * portal group stored in the hash map managing all
+ * iSCSI portal groups.
*
* @param[in] key Pointer to zero padded key. NULL is
* an invalid pointer here, so be careful.
- * @param[in] key_size Number of bytes for the key, MUST
- * be a multiple of 8 bytes which is NOT checked, so
- * be careful.
+ * @param[in] key_size Number of bytes for the key.
* @param[in] value Value of the key, NULL is allowed.
* @param[in,out] user_data This argument is not used by
* this function and should be always NULL for now, as
* there is a possibility for future usage.
* @return Always returns 0 as this function cannot fail.
*/
-int iscsi_portal_destroy_callback(uint8_t *key, const size_t key_size, uint8_t *value, uint8_t *user_data)
+int iscsi_portal_group_destroy_callback(uint8_t *key, const size_t key_size, uint8_t *value, uint8_t *user_data)
{
- iscsi_portal_destroy( (iscsi_portal *) value );
- iscsi_hashmap_key_destroy( key );
+ iscsi_portal_group_destroy( (iscsi_portal_group *) value );
- return 0L;
+ return 0;
}
/**
@@ -2389,6 +3614,7 @@ int iscsi_portal_destroy_callback(uint8_t *key, const size_t key_size, uint8_t *
*
* This function frees the associated hash map containing the
* poptals and the structure itself.
+ *
* @param[in] portal_group Pointer to iSCSI portal group to deallocate.
* May be NULL in which case this function does nothing.
*/
@@ -2412,9 +3638,9 @@ void iscsi_portal_group_destroy(iscsi_portal_group *portal_group)
* This function allocates host:port of iSCSI portal for use
* as key and sets the portal group in the portal.
*
- * @param[in] iSCSI portal group to add portal to. May NOT be NULL,
+ * @param[in] portal_group iSCSI portal group to add portal to. May NOT be NULL,
* so take caution.
- * @param[in] iSCSI portal to add to portal group. NULL is NOT
+ * @param[in] portal iSCSI portal to add to portal group. NULL is NOT
* allowed here, so be careful.
* @retval -1 An error occured during adding the portal,
* usually caused by memory exhaustion
@@ -2425,18 +3651,21 @@ int iscsi_portal_group_add_portal(iscsi_portal_group *portal_group, iscsi_portal
{
uint8_t *tmp_buf = iscsi_sprintf_alloc( "%s:%s", portal->host, portal->port );
- if ( tmp_buf == NULL )
- return -1L;
+ if ( tmp_buf == NULL ) {
+ logadd( LOG_ERROR, "iscsi_portal_group_add_portal: Out of memory allocating temporarily key buffer for iSCSI portal" );
+
+ return -1;
+ }
- const uint key_len = (uint) strlen( (char *) tmp_buf ) + 1;
- uint8_t *key = iscsi_hashmap_key_create( tmp_buf, key_len );
+ const uint key_len = (uint) (strlen( (char *) tmp_buf ) + 1U);
+ uint8_t *key = iscsi_hashmap_key_create( tmp_buf, key_len );
free( tmp_buf );
if ( key == NULL ) {
logadd( LOG_ERROR, "iscsi_portal_group_add_portal: Out of memory allocating key for iSCSI portal" );
- return -1L;
+ return -1;
}
int rc = iscsi_hashmap_put( portal_group->portals, key, key_len, (uint8_t *) portal );
@@ -2451,7 +3680,49 @@ int iscsi_portal_group_add_portal(iscsi_portal_group *portal_group, iscsi_portal
portal->group = portal_group;
- return 0L;
+ return 0;
+}
+
+/**
+ * @brief Removes an iSCSI portal from the iSCSI portal group hash map.
+ *
+ * This function deallocates the hash key used
+ * for storing the portal in the portal group
+ * as well.
+ *
+ * @param[in] portal_group iSCSI portal group to remove portal from. May
+ * NOT be NULL, so take caution.
+ * @param[in] portal iSCSI portal to remove from the portal group.
+ * NULL is NOT allowed here, so be careful.
+ */
+void iscsi_portal_group_del_portal(iscsi_portal_group *portal_group, iscsi_portal *portal)
+{
+ uint8_t *tmp_buf = iscsi_sprintf_alloc( "%s:%s", portal->host, portal->port );
+
+ if ( tmp_buf == NULL ) {
+ logadd( LOG_ERROR, "iscsi_portal_group_del_portal: Out of memory allocating temporarily key buffer for iSCSI portal" );
+
+ return;
+ }
+
+ const uint key_len = (uint) (strlen( (char *) tmp_buf ) + 1U);
+ uint8_t *key = iscsi_hashmap_key_create( tmp_buf, key_len );
+
+ free( tmp_buf );
+
+ if ( key == NULL ) {
+ logadd( LOG_ERROR, "iscsi_portal_group_del_portal: Out of memory allocating key for iSCSI portal" );
+
+ return;
+ }
+
+ if ( iscsi_hashmap_contains( portal_group->portals, key, key_len ) ) {
+ portal->group = NULL;
+
+ iscsi_hashmap_remove_free( portal_group->portals, key, key_len, iscsi_hashmap_key_destroy_callback, NULL );
+ }
+
+ iscsi_hashmap_key_destroy( key );
}
/**
@@ -2477,7 +3748,7 @@ iscsi_portal *iscsi_portal_create(const uint8_t *host, const uint8_t *port)
portal->group = NULL;
- const uint host_len = (uint) strlen( (char *) host ) + 1;
+ const uint host_len = (uint) (strlen( (char *) host ) + 1U);
portal->host = (uint8_t *) malloc( host_len );
@@ -2489,7 +3760,7 @@ iscsi_portal *iscsi_portal_create(const uint8_t *host, const uint8_t *port)
memcpy( portal->host, host, host_len );
- const uint port_len = (uint) strlen( (char *) port ) + 1;
+ const uint port_len = (uint) (strlen( (char *) port ) + 1U);
portal->port = (uint8_t *) malloc( port_len );
@@ -2501,12 +3772,35 @@ iscsi_portal *iscsi_portal_create(const uint8_t *host, const uint8_t *port)
memcpy( portal->port, port, port_len );
- portal->sock = -1L;
+ portal->sock = -1;
return portal;
}
/**
+ * @brief iSCSI portal destructor callback for hash map.
+ *
+ * Callback function for deallocation of an iSCSI
+ * portal stored in the iSCSI portal group hash map.
+ *
+ * @param[in] key Pointer to zero padded key. NULL is
+ * an invalid pointer here, so be careful.
+ * @param[in] key_size Number of bytes for the key.
+ * @param[in] value Value of the key, NULL is allowed.
+ * @param[in,out] user_data This argument is not used by
+ * this function and should be always NULL for now, as
+ * there is a possibility for future usage.
+ * @return Always returns 0 as this function cannot fail.
+ */
+int iscsi_portal_destroy_callback(uint8_t *key, const size_t key_size, uint8_t *value, uint8_t *user_data)
+{
+ iscsi_portal_destroy( (iscsi_portal *) value );
+ iscsi_hashmap_key_destroy( key );
+
+ return 0;
+}
+
+/**
* @brief Deallocates all resources acquired by iscsi_portal_create.
*
* This function frees the memory acquired for host / IP address
@@ -2535,6 +3829,3858 @@ void iscsi_portal_destroy(iscsi_portal *portal)
}
/**
+ * @brief Allocates and initializes a SCSI task.
+ *
+ * THis function assocates the callback
+ * functions to the SCSI task and sets
+ * the reference count to 1.
+ *
+ * @param[in] scsi_task Pointer to SCSI task. This
+ * may NOT be NULL, so be careful.
+ * @param[in] xfer_complete_callback Pointer to transfer completed callback
+ * function.
+ * @param[in] destroy_callback Pointer to SCSI task destruction
+ * callback function.
+ */
+void iscsi_scsi_task_create(iscsi_scsi_task *scsi_task, iscsi_scsi_task_xfer_complete_callback xfer_complete_callback, iscsi_scsi_task_destroy_callback destroy_callback)
+{
+ scsi_task->node.succ = NULL;
+ scsi_task->node.pred = NULL;
+ scsi_task->lun = NULL;
+ scsi_task->target_port = NULL;
+ scsi_task->init_port = NULL;
+ scsi_task->cdb = NULL;
+ scsi_task->xfer_complete_callback = xfer_complete_callback;
+ scsi_task->destroy_callback = destroy_callback;
+ scsi_task->io_complete_callback = NULL;
+ scsi_task->io_wait.image = NULL;
+ scsi_task->io_wait.callback = NULL;
+ scsi_task->io_wait.user_data = NULL;
+ scsi_task->sense_data = NULL;
+ scsi_task->buf = NULL;
+ scsi_task->pos = 0UL;
+ scsi_task->len = 0UL;
+ scsi_task->id = 0ULL;
+ scsi_task->flags = 0;
+ scsi_task->ref = 1UL;
+ scsi_task->xfer_pos = 0UL;
+ scsi_task->xfer_len = 0UL;
+ scsi_task->sense_data_len = 0U;
+ scsi_task->status = ISCSI_SCSI_STATUS_GOOD;
+ scsi_task->task_mgmt_func = ISCSI_TASK_MGMT_FUNC_REQ_FUNC_ABORT_TASK;
+ scsi_task->task_mgmt_response = ISCSI_TASK_MGMT_FUNC_RESPONSE_FUNC_COMPLETE;
+}
+
+/**
+ * @brief Deallocates all resources acquired iscsi_scsi_task_create.
+ *
+ * This function also calls the task destruction
+ * callback function if the reference count
+ * becomes zero.
+ *
+ * @param[in] scsi_task Pointer to iSCSI SCSI task to deallocate.
+ * This may be NULL in which case nothing
+ * happens.
+ */
+void iscsi_scsi_task_destroy(iscsi_scsi_task *scsi_task)
+{
+ if ( (scsi_task != NULL) && (--scsi_task->ref == 0UL) ) {
+ if ( scsi_task->buf != NULL ) {
+ if ( (scsi_task->flags & ISCSI_SCSI_TASK_FLAGS_XFER_WRITE) == 0 )
+ free( scsi_task->buf );
+
+ scsi_task->buf = NULL;
+ }
+
+ scsi_task->destroy_callback( scsi_task );
+ }
+}
+
+/**
+ * @brief Callback function when an iSCSI SCSI task completed the data transfer.
+ *
+ * This function post-processes a task upon
+ * finish of data transfer.
+ *
+ * @param[in] scsi_task Pointer to iSCSI SCSI task which finished
+ * the data transfer and may NOT be NULL,
+ * so be careful.
+ */
+void iscsi_scsi_task_xfer_complete(iscsi_scsi_task *scsi_task)
+{
+ iscsi_task *task = ISCSI_CONTAINER(iscsi_task, scsi_task, scsi_task);
+
+ task->flags &= ~ISCSI_TASK_FLAGS_QUEUED;
+
+ iscsi_task *primary_task = ((task->parent != NULL) ? task->parent : task);
+ iscsi_connection *conn = task->conn;
+
+ if ( (((iscsi_scsi_cmd_packet *) primary_task->pdu->bhs_pkt)->flags_task & ISCSI_SCSI_CMD_FLAGS_TASK_READ) != 0 )
+ iscsi_task_xfer_complete_process_read( conn, task, primary_task );
+ else
+ iscsi_task_xfer_complete_process_other( conn, task, primary_task );
+}
+
+/**
+ * @brief Allocates, if necessary and initializes SCSI sense data for check condition status code.
+ *
+ * This function is invoked whenever additional
+ * SCSI sense data for check condition status
+ * code is required for sending to the
+ * initiator.
+ *
+ * @param[in] scsi_task Pointer to iSCSI SCSI task to allocate
+ * and assign the SCSI check condition status
+ * code sense data for. May NOT be NULL, so
+ * be careful.
+ * @param[in] sense_key Sense Key (SK).
+ * @param[in] asc Additional Sense Code (ASC).
+ * @param[in] ascq Additional Sense Code Qualifier (ASCQ).
+ */
+void iscsi_scsi_task_sense_data_build(iscsi_scsi_task *scsi_task, const uint8_t sense_key, const uint8_t asc, const uint8_t ascq)
+{
+ iscsi_scsi_sense_data_check_cond_packet *sense_data = (iscsi_scsi_sense_data_check_cond_packet *) scsi_task->sense_data;
+
+ if ( sense_data == NULL ) {
+ sense_data = malloc( sizeof(struct iscsi_scsi_sense_data_check_cond_packet) );
+
+ if ( sense_data == NULL ) {
+ logadd( LOG_ERROR, "iscsi_scsi_task_sense_data_build: Out of memory allocating iSCSI SCSI conidtion check status code sense data" );
+
+ return;
+ }
+
+ scsi_task->sense_data = (iscsi_scsi_sense_data_packet *) sense_data;
+ }
+
+ sense_data->sense_data.response_code = (int8_t) (ISCSI_SCSI_SENSE_DATA_PUT_RESPONSE_CODE(ISCSI_SCSI_SENSE_DATA_RESPONSE_CODE_CURRENT_FMT) | ISCSI_SCSI_SENSE_DATA_RESPONSE_CODE_VALID);
+ sense_data->sense_data.reserved = 0U;
+ sense_data->sense_data.sense_key_flags = ISCSI_SCSI_SENSE_DATA_PUT_SENSE_KEY(sense_key);
+ sense_data->sense_data.info = 0UL; // Zero does not require endianess conversion
+ sense_data->sense_data.add_len = (sizeof(struct iscsi_scsi_sense_data_check_cond_packet) - sizeof(struct iscsi_scsi_sense_data_packet));
+
+ sense_data->cmd_spec_info = 0UL; // Zero does not require endianess conversion
+ sense_data->asc = asc;
+ sense_data->ascq = ascq;
+ sense_data->field_rep_unit_code = 0U;
+ sense_data->sense_key_spec_flags = 0U;
+ sense_data->sense_key_spec = 0U; // Zero does not require endianess conversion
+
+ scsi_task->sense_data_len = sizeof(struct iscsi_scsi_sense_data_check_cond_packet);
+}
+
+/**
+ * @brief Sets an iSCSI SCSI task status code with optional additional details.
+ *
+ * Sense Key (SK), Additional Sense Code (ASC)
+ * and Additional Sense Code Qualifier (ASCQ)
+ * are only generated on check condition SCSI
+ * status code.
+ *
+ * @param[in] scsi_task Pointer to iSCSI SCSI task to set the
+ * SCSI status and additional details for. May
+ * NOT be NULL, so be careful.
+ * @param[in] status SCSI status code to be set.
+ * @param[in] sense_key Sense Key (SK).
+ * @param[in] asc Additional Sense Code (ASC).
+ * @param[in] ascq Additional Sense Code Qualifier (ASCQ).
+ */
+static void iscsi_scsi_task_status_set(iscsi_scsi_task *scsi_task, const uint8_t status, const uint8_t sense_key, const uint8_t asc, const uint8_t ascq)
+{
+ if ( status == ISCSI_SCSI_STATUS_CHECK_COND )
+ iscsi_scsi_task_sense_data_build( scsi_task, sense_key, asc, ascq );
+
+ scsi_task->status = status;
+}
+
+/**
+ * @brief Copies iSCSI SCSI task sense data and status code.
+ *
+ * This function allocates, if necessary, a
+ * SCSI sense data buffer and copies it over
+ * from source or deallocates the sense data
+ * buffer in case the source has no sense
+ * data.
+ *
+ * @param[in] dst_scsi_task Pointer to iSCSI SCSI task to copy to.
+ * May NOT be NULL, so be careful.
+ * @param[in] src_scsi_task Pointer to iSCSI SCSI task to copy from.
+ * NULL is NOT allowed here, take caution.
+ * @return 0 on successful copy operation, a negative
+ * error code otherwise.
+ */
+int iscsi_scsi_task_status_copy(iscsi_scsi_task *dst_scsi_task, const iscsi_scsi_task *src_scsi_task)
+{
+ if ( dst_scsi_task->sense_data != NULL )
+ free( dst_scsi_task->sense_data );
+
+ if ( src_scsi_task->sense_data != NULL ) {
+ dst_scsi_task->sense_data = malloc( src_scsi_task->sense_data_len );
+
+ if ( dst_scsi_task == NULL )
+ return -1;
+
+ memcpy( dst_scsi_task->sense_data, src_scsi_task->sense_data, src_scsi_task->sense_data_len );
+ } else {
+ dst_scsi_task->sense_data = NULL;
+ }
+
+ dst_scsi_task->sense_data_len = src_scsi_task->sense_data_len;
+ dst_scsi_task->status = src_scsi_task->status;
+
+ return 0;
+}
+
+/**
+ * @brief Processes a iSCSI SCSI task with no LUN identifier.
+ *
+ * This function only generates a SCSI response
+ * if the SCSI command is INQUIRY, otherwise
+ * a SCSI error will be generated as specified
+ * by the SCSI standard.
+ *
+ * @param[in] scsi_task Pointer to iSCSI SCSI task to process
+ * the task with no LUN identifier for. May NOT
+ * be NULL, so be careful.
+ */
+void iscsi_scsi_task_lun_process_none(iscsi_scsi_task *scsi_task)
+{
+ iscsi_scsi_std_inquiry_data_packet std_inquiry_data_pkt;
+ iscsi_scsi_cdb_inquiry *cdb = (iscsi_scsi_cdb_inquiry *) scsi_task->cdb;
+
+ scsi_task->len = scsi_task->xfer_len;
+
+ if ( cdb->cdb.opcode == ISCSI_SCSI_OPCODE_INQUIRY ) {
+ uint len = sizeof(struct iscsi_scsi_std_inquiry_data_packet);
+
+ memset( &std_inquiry_data_pkt, 0, len );
+
+ std_inquiry_data_pkt.basic_inquiry.peripheral_type_id = (ISCSI_SCSI_BASIC_INQUIRY_DATA_PUT_PERIPHERAL_TYPE(ISCSI_SCSI_BASIC_INQUIRY_DATA_PERIPHERAL_TYPE_UNKNOWN) | ISCSI_SCSI_BASIC_INQUIRY_DATA_PUT_PERIPHERAL_ID(ISCSI_SCSI_BASIC_INQUIRY_DATA_PERIPHERAL_ID_NEVER));
+ std_inquiry_data_pkt.basic_inquiry.add_len = (uint8_t) (len - sizeof(struct iscsi_scsi_basic_inquiry_data_packet));
+
+ const uint alloc_len = iscsi_get_be16(cdb->alloc_len);
+
+ if ( len > alloc_len )
+ len = alloc_len;
+
+ memcpy( scsi_task->buf, &std_inquiry_data_pkt, len );
+
+ scsi_task->xfer_pos = len;
+ scsi_task->status = ISCSI_SCSI_STATUS_GOOD;
+ } else {
+ iscsi_scsi_task_status_set( scsi_task, ISCSI_SCSI_STATUS_CHECK_COND, ISCSI_SCSI_SENSE_KEY_ILLEGAL_REQ, ISCSI_SCSI_ASC_LU_NOT_SUPPORTED, ISCSI_SCSI_ASCQ_CAUSE_NOT_REPORTABLE );
+
+ scsi_task->xfer_pos = 0UL;
+ }
+}
+
+/**
+ * @brief Processes a iSCSI SCSI aborted task.
+ *
+ * This function will generate a SCSI error as
+ * specified by the SCSI standard.
+ *
+ * @param[in] scsi_task Pointer to iSCSI SCSI task to process
+ * the task to be aborted. May NOT be NULL, so
+ * be careful.
+ */
+void iscsi_scsi_task_lun_process_abort(iscsi_scsi_task *scsi_task)
+{
+ iscsi_scsi_task_status_set( scsi_task, ISCSI_SCSI_STATUS_CHECK_COND, ISCSI_SCSI_SENSE_KEY_ABORTED_COMMAND, ISCSI_SCSI_ASC_NO_ADDITIONAL_SENSE, ISCSI_SCSI_ASCQ_CAUSE_NOT_REPORTABLE );
+}
+
+/**
+ * @brief Allocates and initializes an iSCSI LUN structure for linkage with a DNBD3 image.
+ *
+ * This function does not set the DNBD3
+ * image itself.
+ *
+ * @param[in] id LUN identifier.
+ * @return Pointer to ISCSI device LUN or NULL in case
+ * of an error (memory exhaustion).
+ */
+iscsi_scsi_lun *iscsi_scsi_lun_create(const int lun_id)
+{
+ iscsi_scsi_lun *lun = (iscsi_scsi_lun *) malloc( sizeof(struct iscsi_scsi_lun) );
+
+ if ( lun == NULL ) {
+ logadd( LOG_ERROR, "iscsi_scsi_lun_create: Out of memory allocating iSCSI device LUN" );
+
+ return NULL;
+ }
+
+ iscsi_list_create( &lun->tasks );
+
+ if ( pthread_mutex_init( &lun->tasks_mutex, NULL ) != 0 ) {
+ logadd( LOG_ERROR, "iscsi_scsi_lun_create: Error while initializing tasks mutex for iSCSI device LUN" );
+
+ return NULL;
+ }
+
+ iscsi_list_create( &lun->tasks_pending );
+
+ if ( pthread_mutex_init( &lun->tasks_pending_mutex, NULL ) != 0 ) {
+ logadd( LOG_ERROR, "iscsi_scsi_lun_create: Error while initializing pendings tasks mutex for iSCSI device LUN" );
+
+ pthread_mutex_destroy( &lun->tasks_mutex );
+
+ return NULL;
+ }
+
+ iscsi_list_create( &lun->tasks_mgmt );
+
+ if ( pthread_mutex_init( &lun->tasks_mgmt_mutex, NULL ) != 0 ) {
+ logadd( LOG_ERROR, "iscsi_scsi_lun_create: Error while initializing management tasks mutex for iSCSI device LUN" );
+
+ pthread_mutex_destroy( &lun->tasks_pending_mutex );
+ pthread_mutex_destroy( &lun->tasks_mutex );
+
+ return NULL;
+ }
+
+ iscsi_list_create( &lun->tasks_mgmt_pending );
+
+ if ( pthread_mutex_init( &lun->tasks_mgmt_pending_mutex, NULL ) != 0 ) {
+ logadd( LOG_ERROR, "iscsi_scsi_lun_create: Error while initializing management pending tasks mutex for iSCSI device LUN" );
+
+ pthread_mutex_destroy( &lun->tasks_mgmt_mutex );
+ pthread_mutex_destroy( &lun->tasks_pending_mutex );
+ pthread_mutex_destroy( &lun->tasks_mutex );
+
+ return NULL;
+ }
+
+ lun->pr_regs = iscsi_hashmap_create( 0U );
+
+ if ( lun->pr_regs == NULL ) {
+ logadd( LOG_ERROR, "iscsi_scsi_lun_create: Out of memory allocating iSCSI device LUN Persistent Reservation (PR) registrant for I_T nexus hash map" );
+
+ pthread_mutex_destroy( &lun->tasks_mgmt_pending_mutex );
+ pthread_mutex_destroy( &lun->tasks_mgmt_mutex );
+ pthread_mutex_destroy( &lun->tasks_pending_mutex );
+ pthread_mutex_destroy( &lun->tasks_mutex );
+ free( lun );
+
+ return NULL;
+ }
+
+ lun->pr_reservation.holder = NULL;
+ lun->pr_reservation.cr_key = 0ULL;
+ lun->pr_reservation.type = 0;
+ lun->pr_reservation.flags = 0L;
+
+ lun->pr_scsi2_holder.target_port = NULL;
+ lun->pr_scsi2_holder.target_name = NULL;
+ lun->pr_scsi2_holder.init_port = NULL;
+ lun->pr_scsi2_holder.init_name = NULL;
+ lun->pr_scsi2_holder.transport_id = NULL;
+ lun->pr_scsi2_holder.r_key = 0ULL;
+ lun->pr_scsi2_holder.rel_target_port_id = 0U;
+ lun->pr_scsi2_holder.transport_id_len = 0U;
+
+ lun->device = NULL;
+ lun->image = NULL;
+ lun->id = lun_id;
+ lun->flags = 0;
+ lun->pr_gen = 0UL;
+
+ return lun;
+}
+
+/**
+ * @brief iSCSI SCSI LUN destructor callback for hash map.
+ *
+ * Callback function for deallocation of an iSCSI
+ * SCSI LUN stored in the iSCSI device hash map.
+ *
+ * @param[in] key Pointer to zero padded key. NULL is
+ * an invalid pointer here, so be careful.
+ * @param[in] key_size Number of bytes for the key.
+ * @param[in] value Value of the key, NULL is allowed.
+ * @param[in,out] user_data This argument is not used by
+ * this function and should be always NULL for now, as
+ * there is a possibility for future usage.
+ * @return Always returns 0 as this function cannot fail.
+ */
+int iscsi_scsi_lun_destroy_callback(uint8_t *key, const size_t key_size, uint8_t *value, uint8_t *user_data)
+{
+ iscsi_scsi_lun_destroy( (iscsi_scsi_lun *) value );
+ iscsi_hashmap_key_destroy( key );
+
+ return 0;
+}
+
+/**
+ * @brief Deallocates all resources acquired by iscsi_scsi_lun_create.
+ *
+ * This function does not deallocate the
+ * associated DNBD3 image and therefore
+ * just deallocates the associated SCSI
+ * tasks.
+ *
+ * @param[in] lun Pointer to iSCSI device LUN to be freed.
+ * May be NULL in which case this function
+ * does nothing at all.
+ */
+void iscsi_scsi_lun_destroy(iscsi_scsi_lun *lun)
+{
+ if ( lun != NULL ) {
+ if ( lun->pr_regs != NULL ) {
+ // iscsi_hashmap_iterate( lun->pr_regs, iscsi_scsi_pr_registrant_destroy_callback, NULL );
+ iscsi_hashmap_destroy( lun->pr_regs );
+
+ lun->pr_regs = NULL;
+ }
+
+ pthread_mutex_destroy( &lun->tasks_mgmt_pending_mutex );
+ pthread_mutex_destroy( &lun->tasks_mgmt_mutex );
+ pthread_mutex_destroy( &lun->tasks_pending_mutex );
+ pthread_mutex_destroy( &lun->tasks_mutex );
+ free( lun );
+ }
+}
+
+/**
+ * @brief Converts an internal representation of a LUN identifier to an iSCSI LUN required for packet data.
+ *
+ * This function needs to be called prior
+ * storing the internal SCSI identifier
+ * representation in the iSCSI packet.
+ *
+ * @param[in] lun_id Internal SCSI presentation of LUN
+ * identifier to be converted to iSCSI packet data
+ * representation.
+ * @return iSCSI packet data representation of LUN or
+ * 0 in case of an invalid LUN.
+ */
+uint64_t iscsi_scsi_lun_get_from_scsi(const int lun_id)
+{
+ uint64_t iscsi_scsi_lun;
+
+ if ( lun_id < 0x100 )
+ iscsi_scsi_lun = (uint64_t) (lun_id & 0xFF) << 48ULL;
+ else if ( lun_id < 0x4000 )
+ iscsi_scsi_lun = (1ULL << 62ULL) | (uint64_t) (lun_id & 0x3FFF) << 48ULL;
+ else
+ iscsi_scsi_lun = 0ULL;
+
+ return iscsi_scsi_lun;
+}
+
+/**
+ * @brief Converts an iSCSI LUN from packet data to internal SCSI LUN identifier.
+ *
+ * This function needs to be called prior
+ * storing the iSCSI packet data
+ * representation in the structures
+ * requiring an internal SCSI identifier.
+ *
+ * @param[in] lun iSCSI packet data LUN to be converted
+ * to the internal SCSI LUN identifier
+ * representation.
+ * @return SCSI identifier representation of iSCSI
+ * packet data LUN or 0xFFFF in case of
+ * an error.
+ */
+int iscsi_scsi_lun_get_from_iscsi(const uint64_t lun)
+{
+ int lun_id = (int) (lun >> 62ULL) & 0x03;
+
+ if ( lun_id == 0x00 )
+ lun_id = (int) (lun >> 48ULL) & 0xFF;
+ else if ( lun_id == 0x01 )
+ lun_id = (int) (lun >> 48ULL) & 0x3FFF;
+ else
+ lun_id = 0xFFFF;
+
+ return lun_id;
+}
+
+/**
+ * @brief Appends an iSCSI SCSI task to a iSCSI SCSI LUN pending tasks doubly linked list.
+ *
+ * This function cannot fail.
+ *
+ * @param[in] lun Pointer to iSCSI SCSI LUN to append the
+ * task to, may NOT be NULL, so be careful.
+ * @param[in] scsi_task Pointer to iSCSI SCSI task to be
+ * appended. NULL is NOT an allowed value, so take
+ * caution.
+ */
+void iscsi_scsi_lun_task_append(iscsi_scsi_lun *lun, iscsi_scsi_task *scsi_task)
+{
+ iscsi_list_enqueue( &lun->tasks_pending, &scsi_task->node );
+}
+
+/**
+ * @brief Executes all iSCSI SCSI pending tasks assigned to a iSCSI SCSI LUN.
+ *
+ * This function also removes the pending tasks
+ * from the hash map of the SCSI LUN.
+ *
+ * @param[in] lun Pointer to ISCSI SCSI LUN of which the
+ * pending tasks should be executed and may NOT
+ * be NULL, so be careful.
+ */
+void iscsi_scsi_lun_tasks_exec(iscsi_scsi_lun *lun)
+{
+ while ( !iscsi_list_empty( &lun->tasks_pending ) ) {
+ iscsi_scsi_task *scsi_task = (iscsi_scsi_task *) iscsi_list_peek( &lun->tasks_pending );
+
+ iscsi_list_remove( &scsi_task->node );
+ pthread_mutex_unlock( &lun->tasks_pending_mutex );
+ iscsi_scsi_lun_task_run( lun, scsi_task );
+ pthread_mutex_lock( &lun->tasks_pending_mutex );
+ }
+}
+
+/**
+ * @brief Checks whether the iSCSI SCSI task requires unit attention.
+ *
+ * This function parses the SCSI opcode of the
+ * SCSI Command Descriptor Block (CDB).
+ *
+ * @param[in] scsi_task Pointer to iSCSI SCSI task to check
+ * unit attention for which may NOT be NULL,
+ * so be careful.
+ * @retval true Unit attention is required.
+ * @retval false Unit attention is NOT required.
+ */
+static bool iscsi_scsi_lun_handle_unit_attention(iscsi_scsi_task *scsi_task)
+{
+ switch ( scsi_task->cdb->opcode ) {
+ case ISCSI_SCSI_OPCODE_INQUIRY :
+ case ISCSI_SCSI_OPCODE_REPORTLUNS :
+ case ISCSI_SCSI_OPCODE_REQUESTSENSE : {
+ return false;
+
+ break;
+ }
+ default : {
+ return true;
+
+ break;
+ }
+
+ }
+}
+
+/**
+ * @brief Runs an iSCSI SCSI task for a specified iSCSI SCSI LUN.
+ *
+ * This function moves the task back to the
+ * iSCSI SCSI LUN tasks hash map prior
+ * execution.\n
+ * Errors are nandled according to the SCSI
+ * standard.
+ *
+ * @param[in] lun Pointer to iSCSI SCSI LUN of which the
+ * task should be run and may NOT be NULL,
+ * so be careful.
+ * @param[in] scsi_task Pointer to iSCSI SCSI task to be run.
+ * NULL is NOT valid here, take caution.
+ */
+void iscsi_scsi_lun_task_run(iscsi_scsi_lun *lun, iscsi_scsi_task *scsi_task)
+{
+ int rc;
+
+ pthread_mutex_lock( &lun->tasks_mutex );
+ iscsi_list_enqueue( &lun->tasks, &scsi_task->node );
+ pthread_mutex_unlock( &lun->tasks_mutex );
+
+ scsi_task->status = ISCSI_SCSI_STATUS_GOOD;
+
+ if ( (lun->flags & ISCSI_SCSI_LUN_FLAGS_REMOVED) != 0 ) {
+ iscsi_scsi_task_lun_process_abort( scsi_task );
+
+ rc = ISCSI_SCSI_TASK_RUN_COMPLETE;
+ } else if ( ((lun->flags & ISCSI_SCSI_LUN_FLAGS_RESIZING) != 0) && iscsi_scsi_lun_handle_unit_attention( scsi_task ) ) {
+ iscsi_scsi_task_status_set( scsi_task, ISCSI_SCSI_STATUS_CHECK_COND, ISCSI_SCSI_SENSE_KEY_UNIT_ATTENTION, ISCSI_SCSI_ASC_CAPACITY_DATA_HAS_CHANGED, ISCSI_SCSI_ASCQ_CAPACITY_DATA_HAS_CHANGED );
+
+ lun->flags &= ~ISCSI_SCSI_LUN_FLAGS_RESIZING;
+
+ rc = ISCSI_SCSI_TASK_RUN_COMPLETE;
+ } else {
+ if ( (lun->pr_reservation.flags & ISCSI_SCSI_PR_RESERVATION_FLAGS_SPC2_RESERVE) != 0 )
+ rc = iscsi_scsi_pr_check_scsi2( scsi_task );
+ else
+ rc = iscsi_scsi_pr_check( scsi_task );
+
+ if ( rc < 0 )
+ rc = ISCSI_SCSI_TASK_RUN_COMPLETE;
+ else
+ rc = iscsi_scsi_emu_exec( scsi_task );
+ }
+
+ if ( rc == ISCSI_SCSI_TASK_RUN_COMPLETE )
+ iscsi_scsi_lun_task_complete( lun, scsi_task );
+}
+
+/**
+ * @brief Handles iSCSI SCSI task completition.
+ *
+ * This function removes the completed task from
+ * the iSCSI SCSI LUN task doubly linked list
+ * and calls the transfer finished callback
+ * function.
+ *
+ * @param[in] lun Pointer to iSCSI SCSI LUN to remove the task
+ * from.
+ * @param[in] scsi_task Pointer to iSCSI SCSI task to be removed
+ * and to invoke the transfer finished callback
+ * of and may NOT be NULL, so be careful.
+ */
+void iscsi_scsi_lun_task_complete(iscsi_scsi_lun *lun, iscsi_scsi_task *scsi_task)
+{
+ if ( lun != NULL )
+ iscsi_list_remove( &scsi_task->node );
+
+ scsi_task->xfer_complete_callback( scsi_task );
+}
+
+/**
+ * @brief Appends iSCSI SCSI task to pending tasks doubly linked list and / or runs it directly.
+ *
+ * This function checks whether there are pending
+ * task management pending tasks to be executed
+ * first.\n
+ * If there are pending tasks enqueued, they will
+ * be executed prior this new task.\n
+ * If this is the only one task, it will be
+ * executed immediately.
+ *
+ * @param[in] lun Pointer to iSCSI SCSI LUN which should be
+ * checked for pending tasks prior execution. May
+ * NOT be NULL, so be careful.
+ * @param[in] scsi_task Pointer to iSCSI SCSI task to either be
+ * enqueued and run or to be run directly.
+ */
+void iscsi_scsi_lun_task_exec(iscsi_scsi_lun *lun, iscsi_scsi_task *scsi_task)
+{
+ pthread_mutex_lock( &lun->tasks_mgmt_pending_mutex );
+
+ if ( !iscsi_list_empty( &lun->tasks_mgmt_pending ) ) {
+ pthread_mutex_unlock( &lun->tasks_mgmt_pending_mutex );
+ pthread_mutex_lock( &lun->tasks_pending_mutex );
+ iscsi_scsi_lun_task_append( lun, scsi_task );
+ pthread_mutex_unlock( &lun->tasks_pending_mutex );
+
+ return;
+ }
+
+ pthread_mutex_unlock( &lun->tasks_mgmt_pending_mutex );
+ pthread_mutex_lock( &lun->tasks_pending_mutex );
+
+ if ( !iscsi_list_empty( &lun->tasks_pending ) ) {
+ iscsi_scsi_lun_task_append( lun, scsi_task );
+ iscsi_scsi_lun_tasks_exec( lun );
+ pthread_mutex_unlock( &lun->tasks_pending_mutex );
+
+ return;
+ }
+
+ pthread_mutex_unlock( &lun->tasks_pending_mutex );
+
+ iscsi_scsi_lun_task_run( lun, scsi_task );
+}
+
+/**
+ * @brief Checks if iSCSI SCSI Persistent Reservation (PR) SCSI-2 I_T nexus is holder.
+ *
+ * This function compares the target and
+ * initiator name with the registrant.
+ *
+ * @param[in] lun Pointer to iSCSI SCSI LUN to be
+ * checked, may NOT be NULL, so be careful.
+ * @param[in] target_port Pointer to iSCSI target port to
+ * check for.
+ * @param[in] init_port Pointer to iSCSI initiator port to
+ * check for.
+ * @retval true The iSCSI SCSI Persistent Reservation
+ * (PR) SCSI-2 I_T nexus is actually the holder.
+ * @retval false The iSCSI SCSI Persistent Reservation
+ * (PR) SCSI-2 I_T nexus is NOT the holder.
+ */
+static inline bool iscsi_scsi_pr_check_scsi2_it_nexus_is_holder(const iscsi_scsi_lun *lun, const iscsi_port *target_port, const iscsi_port *init_port)
+{
+ const iscsi_scsi_pr_registrant *reg = lun->pr_reservation.holder;
+
+ return ((reg->target_port == target_port) && (reg->init_port == init_port));
+}
+
+/**
+ * @brief Checks the iSCSI SCSI Persistent Reservation (PR) SCSI-2 reserve of an iSCSI SCSI task.
+ *
+ * This function also sets the SCSI error
+ * code if the check fails.
+ *
+ * @param[in] scsi_task Pointer to iSCSI SCSI task to be
+ * checked for and may NOT be NULL, so
+ * be careful.
+ * @return 0 on successful check, a negative
+ * error code otherwise.
+ */
+int iscsi_scsi_pr_check_scsi2(iscsi_scsi_task *scsi_task)
+{
+ const iscsi_scsi_lun *lun = scsi_task->lun;
+
+ switch ( scsi_task->cdb->opcode ) {
+ case ISCSI_SCSI_OPCODE_INQUIRY :
+ case ISCSI_SCSI_OPCODE_RELEASE6 :
+ case ISCSI_SCSI_OPCODE_RELEASE10 : {
+ return ISCSI_SCSI_TASK_RUN_COMPLETE;
+
+ break;
+ }
+ default : {
+ break;
+ }
+ }
+
+ if ( (lun->pr_reservation.holder == NULL) || iscsi_scsi_pr_check_scsi2_it_nexus_is_holder( lun, scsi_task->target_port, scsi_task->init_port ) )
+ return ISCSI_SCSI_TASK_RUN_COMPLETE;
+
+ iscsi_scsi_task_status_set( scsi_task, ISCSI_SCSI_STATUS_RESERVATION_CONFLICT, ISCSI_SCSI_SENSE_KEY_NO_SENSE, ISCSI_SCSI_ASC_NO_ADDITIONAL_SENSE, ISCSI_SCSI_ASCQ_CAUSE_NOT_REPORTABLE );
+
+ return ISCSI_SCSI_TASK_RUN_UNKNOWN;
+}
+
+/**
+ * @brief Finds an iSCSI SCSI Persistent Reservation (PR) registrant by target and initiator port.
+ *
+ * Callback function for each element while iterating
+ * through the iSCSI SCSI LUN Persistent Reservation
+ * (PR) registrants hash map.
+ *
+ * @param[in] key Pointer to zero padded key. NULL is
+ * an invalid pointer here, so be careful.
+ * @param[in] key_size Number of bytes for the key.
+ * @param[in] value Value of the key, NULL creates an
+ * empty key assignment.
+ * @param[in,out] user_data Pointer to a data structure
+ * containing the iSCSI SCSI Persistent Reservation
+ * (PR) registrant and the target, as well as the
+ * initiator port to be searched for and may NOT be
+ * NULL, so be careful.
+ * @retval -1 The registrant has been found and stored
+ * in the result structure. Therefore, no further
+ * searching is needed.
+ * @retval 0 The registrant has not been found yet.
+ */
+int iscsi_scsi_pr_registrant_get_callback(uint8_t *key, const size_t key_size, uint8_t *value, uint8_t *user_data)
+{
+ iscsi_scsi_pr_registrant_get_reg *reg_find = (iscsi_scsi_pr_registrant_get_reg *) user_data;
+ iscsi_scsi_pr_registrant *reg = (iscsi_scsi_pr_registrant *) value;
+
+ if ( (reg_find->target_port != reg->target_port) || (reg_find->init_port != reg->init_port) )
+ return 0;
+
+ reg_find->reg = reg;
+
+ return -1;
+}
+
+/**
+ * @brief Searches an iSCSI SCSI Persistent Reservation (PR) registrant by target and initiator port.
+ *
+ * This function searches for an iSCSI SCSI Persistent
+ * Reservation (PR) registrant by iterating through
+ * the iSCSI SCSI LUN Persistent Reservation (PR)
+ * registrants hash map.
+ *
+ * @param[in] lun Pointer to iSCSI SCSI LUN to
+ * search in the Persistent Reservation (PR)
+ * registrants hash map. May NOT be NULL, so be
+ * careful.
+ * @param[in] target_port Pointer to iSCSI target port to
+ * search for.
+ * @param[in] init_port Pointer to iSCSI initiator port to
+ * search for.
+ * @return Pointer to found iSCSI SCSI Persistent
+ * Reservation (PR) registrant or NULL in case no
+ * registrant has a matching target and Initiator
+ * port.
+ */
+static iscsi_scsi_pr_registrant *iscsi_scsi_pr_registrant_get(const iscsi_scsi_lun *lun, iscsi_port *target_port, iscsi_port *init_port)
+{
+ iscsi_scsi_pr_registrant_get_reg reg_find = {NULL, target_port, init_port};
+
+ iscsi_hashmap_iterate( lun->pr_regs, iscsi_scsi_pr_registrant_get_callback, (uint8_t *) &reg_find );
+
+ return reg_find.reg;
+}
+
+/**
+ * @brief Checks whether iSCSI SCSI Persistent Reservation (PR) reservation type is all registrants or not.
+ *
+ * This function checks both if write exclusive and
+ * exclusive access types.
+ *
+ * @param[in] lun Pointer to iSCSI SCSI LUN to
+ * check the Persistent Reservation (PR)
+ * reservation's type. May NOT be NULL, so be
+ * careful.
+ * @retval true The iSCSI SCSI Persistent Reservation (PR)
+ * reservation type is set to all registrants.
+ * @retval false The iSCSI SCSI Persistent Reservation (PR)
+ * reservation type is NOT set to all registrants.
+ */
+static inline bool iscsi_scsi_pr_check_is_all_type(const iscsi_scsi_lun *lun)
+{
+ return ((lun->pr_reservation.type == ISCSI_SCSI_PR_RESERVATION_TYPE_WRITE_EXCLUSIVE_ALL_REGS) || (lun->pr_reservation.type == ISCSI_SCSI_PR_RESERVATION_TYPE_EXCLUSIVE_ACCESS_ALL_REGS));
+}
+
+/**
+ * @brief Checks whether iSCSI SCSI Persistent Reservation (PR) reservation holder is the specified registrant or not.
+ *
+ * This function also checks if reservation type is
+ * all registrants or not.
+ *
+ * @param[in] lun Pointer to iSCSI SCSI LUN to
+ * check the Persistent Reservation (PR)
+ * reservation holder for. May NOT be NULL, so be
+ * careful.
+ * @param[in] reg Pointer to iSCSI SCSI Persistent
+ * Reservation (PR) registrant to check for.
+ * @retval true The iSCSI SCSI Persistent Reservation (PR)
+ * reservation holder matches the registrant.
+ * @retval false The iSCSI SCSI Persistent Reservation (PR)
+ * reservation holder does NOT match the registrant.
+ */
+static inline bool iscsi_scsi_pr_check_registrant_is_holder(const iscsi_scsi_lun *lun, const iscsi_scsi_pr_registrant *reg)
+{
+ return (((reg != NULL) && iscsi_scsi_pr_check_is_all_type( lun )) || (lun->pr_reservation.holder == reg));
+}
+
+/**
+ * @brief Checks the iSCSI SCSI Persistent Reservation (PR) of an iSCSI SCSI task.
+ *
+ * This function also sets the SCSI error
+ * code if the check fails.
+ *
+ * @param[in] scsi_task Pointer to iSCSI SCSI task to be
+ * checked for and may NOT be NULL, so
+ * be careful.
+ * @return 0 on successful check, a negative
+ * error code otherwise.
+ */
+int iscsi_scsi_pr_check(iscsi_scsi_task *scsi_task)
+{
+ const iscsi_scsi_lun *lun = scsi_task->lun;
+ const iscsi_scsi_pr_registrant *reg = iscsi_scsi_pr_registrant_get( lun, scsi_task->target_port, scsi_task->init_port );
+
+ if ( (reg == NULL) || ((reg->target_port == scsi_task->target_port) && (reg->init_port == scsi_task->init_port)) )
+ return ISCSI_SCSI_TASK_RUN_COMPLETE;
+
+ const iscsi_scsi_cdb *cdb = (iscsi_scsi_cdb *) scsi_task->cdb;
+ bool dma_to_device = false;
+
+ switch ( cdb->opcode ) {
+ case ISCSI_SCSI_OPCODE_INQUIRY :
+ case ISCSI_SCSI_OPCODE_REPORTLUNS :
+ case ISCSI_SCSI_OPCODE_REQUESTSENSE :
+ case ISCSI_SCSI_OPCODE_LOGSENSE :
+ case ISCSI_SCSI_OPCODE_TESTUNITREADY :
+ case ISCSI_SCSI_OPCODE_STARTSTOPUNIT :
+ case ISCSI_SCSI_OPCODE_READCAPACITY10 :
+ case ISCSI_SCSI_OPCODE_PERSISTENT_RESERVE_IN :
+ case ISCSI_SCSI_OPCODE_SERVICE_ACTION_IN_16 :
+ case ISCSI_SCSI_OPCODE_RESERVE6 :
+ case ISCSI_SCSI_OPCODE_RESERVE10 :
+ case ISCSI_SCSI_OPCODE_RELEASE6 :
+ case ISCSI_SCSI_OPCODE_RELEASE10 : {
+ return ISCSI_SCSI_TASK_RUN_COMPLETE;
+
+ break;
+ }
+ case ISCSI_SCSI_OPCODE_MODESELECT6 :
+ case ISCSI_SCSI_OPCODE_MODESELECT10 :
+ case ISCSI_SCSI_OPCODE_MODESENSE6 :
+ case ISCSI_SCSI_OPCODE_MODESENSE10 :
+ case ISCSI_SCSI_OPCODE_LOGSELECT : {
+ if ( reg == NULL ) {
+ iscsi_scsi_task_status_set( scsi_task, ISCSI_SCSI_STATUS_RESERVATION_CONFLICT, ISCSI_SCSI_SENSE_KEY_NO_SENSE, ISCSI_SCSI_ASC_NO_ADDITIONAL_SENSE, ISCSI_SCSI_ASCQ_CAUSE_NOT_REPORTABLE );
+
+ return ISCSI_SCSI_TASK_RUN_UNKNOWN;
+ }
+
+ return ISCSI_SCSI_TASK_RUN_COMPLETE;
+
+ break;
+ }
+ case ISCSI_SCSI_OPCODE_PERSISTENT_RESERVE_OUT : {
+ const iscsi_scsi_cdb_pr_reserve_out *cdb_pr_reserve_out = (iscsi_scsi_cdb_pr_reserve_out *) cdb;
+ const uint8_t action = ISCSI_SCSI_CDB_PR_RESERVE_OUT_GET_ACTION(cdb_pr_reserve_out->action);
+
+ switch ( action ) {
+ case ISCSI_SCSI_CDB_PR_RESERVE_OUT_ACTION_REGISTER :
+ case ISCSI_SCSI_CDB_PR_RESERVE_OUT_ACTION_REGISTER_IGNORE_EXIST_KEY : {
+ return ISCSI_SCSI_TASK_RUN_COMPLETE;
+
+ break;
+ }
+ case ISCSI_SCSI_CDB_PR_RESERVE_OUT_ACTION_RELEASE :
+ case ISCSI_SCSI_CDB_PR_RESERVE_OUT_ACTION_CLEAR :
+ case ISCSI_SCSI_CDB_PR_RESERVE_OUT_ACTION_PREEMPT :
+ case ISCSI_SCSI_CDB_PR_RESERVE_OUT_ACTION_PREEMPT_ABORT : {
+ if ( reg == NULL ) {
+ iscsi_scsi_task_status_set( scsi_task, ISCSI_SCSI_STATUS_RESERVATION_CONFLICT, ISCSI_SCSI_SENSE_KEY_NO_SENSE, ISCSI_SCSI_ASC_NO_ADDITIONAL_SENSE, ISCSI_SCSI_ASCQ_CAUSE_NOT_REPORTABLE );
+
+ return ISCSI_SCSI_TASK_RUN_UNKNOWN;
+ }
+
+ return ISCSI_SCSI_TASK_RUN_COMPLETE;
+
+ break;
+ }
+ default : {
+ iscsi_scsi_task_status_set( scsi_task, ISCSI_SCSI_STATUS_RESERVATION_CONFLICT, ISCSI_SCSI_SENSE_KEY_NO_SENSE, ISCSI_SCSI_ASC_NO_ADDITIONAL_SENSE, ISCSI_SCSI_ASCQ_CAUSE_NOT_REPORTABLE );
+
+ return ISCSI_SCSI_TASK_RUN_UNKNOWN;
+
+ break;
+
+ }
+ }
+
+ break;
+ }
+ case ISCSI_SCSI_OPCODE_READ6 :
+ case ISCSI_SCSI_OPCODE_READ10 :
+ case ISCSI_SCSI_OPCODE_READ12 :
+ case ISCSI_SCSI_OPCODE_READ16 : {
+ break;
+ }
+ case ISCSI_SCSI_OPCODE_WRITE6 :
+ case ISCSI_SCSI_OPCODE_WRITE10 :
+ case ISCSI_SCSI_OPCODE_WRITE12 :
+ case ISCSI_SCSI_OPCODE_WRITE16 :
+ case ISCSI_SCSI_OPCODE_UNMAP :
+ case ISCSI_SCSI_OPCODE_SYNCHRONIZECACHE10 :
+ case ISCSI_SCSI_OPCODE_SYNCHRONIZECACHE16 : {
+ dma_to_device = true;
+
+ break;
+ }
+ default : {
+ iscsi_scsi_task_status_set( scsi_task, ISCSI_SCSI_STATUS_RESERVATION_CONFLICT, ISCSI_SCSI_SENSE_KEY_NO_SENSE, ISCSI_SCSI_ASC_NO_ADDITIONAL_SENSE, ISCSI_SCSI_ASCQ_CAUSE_NOT_REPORTABLE );
+
+ return ISCSI_SCSI_TASK_RUN_UNKNOWN;
+
+ break;
+ }
+ }
+
+ switch ( lun->pr_reservation.type ) {
+ case ISCSI_SCSI_PR_RESERVATION_TYPE_WRITE_EXCLUSIVE : {
+ if ( dma_to_device ) {
+ iscsi_scsi_task_status_set( scsi_task, ISCSI_SCSI_STATUS_RESERVATION_CONFLICT, ISCSI_SCSI_SENSE_KEY_NO_SENSE, ISCSI_SCSI_ASC_NO_ADDITIONAL_SENSE, ISCSI_SCSI_ASCQ_CAUSE_NOT_REPORTABLE );
+
+ return ISCSI_SCSI_TASK_RUN_UNKNOWN;
+ }
+
+ break;
+ }
+ case ISCSI_SCSI_PR_RESERVATION_TYPE_EXCLUSIVE_ACCESS : {
+ iscsi_scsi_task_status_set( scsi_task, ISCSI_SCSI_STATUS_RESERVATION_CONFLICT, ISCSI_SCSI_SENSE_KEY_NO_SENSE, ISCSI_SCSI_ASC_NO_ADDITIONAL_SENSE, ISCSI_SCSI_ASCQ_CAUSE_NOT_REPORTABLE );
+
+ return ISCSI_SCSI_TASK_RUN_UNKNOWN;
+
+ break;
+ }
+ case ISCSI_SCSI_PR_RESERVATION_TYPE_WRITE_EXCLUSIVE_REGS_ONLY :
+ case ISCSI_SCSI_PR_RESERVATION_TYPE_WRITE_EXCLUSIVE_ALL_REGS : {
+ if ( (reg == NULL) && dma_to_device ) {
+ iscsi_scsi_task_status_set( scsi_task, ISCSI_SCSI_STATUS_RESERVATION_CONFLICT, ISCSI_SCSI_SENSE_KEY_NO_SENSE, ISCSI_SCSI_ASC_NO_ADDITIONAL_SENSE, ISCSI_SCSI_ASCQ_CAUSE_NOT_REPORTABLE );
+
+ return ISCSI_SCSI_TASK_RUN_UNKNOWN;
+ }
+
+ break;
+ }
+ case ISCSI_SCSI_PR_RESERVATION_TYPE_EXCLUSIVE_ACCESS_REGS_ONLY :
+ case ISCSI_SCSI_PR_RESERVATION_TYPE_EXCLUSIVE_ACCESS_ALL_REGS : {
+ if ( reg == NULL ) {
+ iscsi_scsi_task_status_set( scsi_task, ISCSI_SCSI_STATUS_RESERVATION_CONFLICT, ISCSI_SCSI_SENSE_KEY_NO_SENSE, ISCSI_SCSI_ASC_NO_ADDITIONAL_SENSE, ISCSI_SCSI_ASCQ_CAUSE_NOT_REPORTABLE );
+
+ return ISCSI_SCSI_TASK_RUN_UNKNOWN;
+ }
+
+ break;
+ }
+ default : {
+ break;
+ }
+ }
+
+ return ISCSI_SCSI_TASK_RUN_COMPLETE;
+}
+
+/**
+ * @brief Constructs an iSCSI SCSI Persistent Reservation (PR) out parameter list of an iSCSI SCSI task.
+ *
+ * This function also sets the SCSI
+ * status result code accordingly.
+ *
+ * @param[in] scsi_task Pointer to iSCSI SCSI task to
+ * construct Persistent Reservation (PR)
+ * out parameter list for. May NOT be NULL,
+ * so be careful.
+ * @param[in] pr_reserve_out_parameter_list Pointer to iSCSI SCSI Persistent
+ * Reservation (PR) out parameter list. NULL
+ * is NOT allowed here, take caution.
+ * @param[in] cdb_pr_reserve_out Pointer to iSCSI SCSI Command
+ * Descriptor Block (CDB) to construct the
+ * out data from and may NOT be NULL, so be
+ * careful.
+ * @param[in] len Length of parameter list in bytes.
+ * @return 0 on successful operation, a negative
+ * error code otherwise.
+ */
+int iscsi_scsi_pr_out(iscsi_scsi_task *scsi_task, iscsi_scsi_pr_reserve_out_parameter_list_packet *pr_reserve_out_parameter_list, const iscsi_scsi_cdb_pr_reserve_out *cdb_pr_reserve_out, const uint len)
+{
+ // TODO: Implement function.
+
+ return 0;
+}
+
+/**
+ * @brief Constructs iSCSI SCSI Persistent Reservation (PR) in parameter data of an iSCSI SCSI task.
+ *
+ * This function also sets the SCSI
+ * status result code accordingly.
+ *
+ * @param[in] scsi_task Pointer to iSCSI SCSI task to
+ * construct Persistent Reservation (PR)
+ * in parameter data for. May NOT be NULL,
+ * so be careful.
+ * @param[in] pr_reserve_in_parameter_data Pointer to iSCSI SCSI Persistent
+ * Reservation (PR) in parameter data. NULL
+ * is NOT allowed here, take caution.
+ * @param[in] cdb_pr_reserve_in Pointer to iSCSI SCSI Command
+ * Descriptor Block (CDB) to construct the
+ * in data from and may NOT be NULL, so be
+ * careful.
+ * @param[in] len Length of parameter data in bytes.
+ * @return 0 on successful operation, a negative
+ * error code otherwise.
+ */
+int iscsi_scsi_pr_in(iscsi_scsi_task *scsi_task, iscsi_scsi_pr_reserve_in_parameter_data_packet *pr_reserve_in_parameter_data, const iscsi_scsi_cdb_pr_reserve_in *cdb_pr_reserve_in, const uint len)
+{
+ // TODO: Implement function.
+
+ return 0;
+}
+
+/**
+ * @brief Reserves an iSCSI SCSI Persistent Reservation (PR) of an iSCSI SCSI task.
+ *
+ * This function also sets the SCSI
+ * status result code accordingly.
+ *
+ * @param[in] scsi_task Pointer to iSCSI SCSI task to
+ * reserve the Persistent Reservation
+ * (PR) for. May NOT be NULL, so be
+ * careful.
+ * @param[in] cdb_pr_reserve_6 Pointer to iSCSI SCSI Command
+ * Descriptor Block (CDB) to reserve the
+ * data from. NULL is NOT allowed here,
+ * take caution.
+ * @return 0 on successful operation, a negative
+ * error code otherwise.
+ */
+int iscsi_scsi_pr_reserve_scsi2(iscsi_scsi_task *scsi_task, const iscsi_scsi_cdb_pr_reserve_6 *cdb_pr_reserve_6)
+{
+ // TODO: Implement function.
+
+ return 0;
+}
+
+/**
+ * @brief Releases an iSCSI SCSI Persistent Reservation (PR) of an iSCSI SCSI task.
+ *
+ * This function also sets the SCSI
+ * status result code accordingly.
+ *
+ * @param[in] scsi_task Pointer to iSCSI SCSI task to
+ * release the Persistent Reservation
+ * (PR) for. May NOT be NULL, so be
+ * careful.
+ * @return 0 on successful operation, a negative
+ * error code otherwise.
+ */
+int iscsi_scsi_pr_release_scsi2(iscsi_scsi_task *scsi_task)
+{
+ // TODO: Implement function.
+
+ return 0;
+}
+
+/**
+ * @brief Checks whether an I/O feature is supported by a DNBD3 image.
+ *
+ * This function depends on DNBD3 image
+ * properties and queries only one I/O
+ * feature at once.
+ *
+ * @param[in] image Pointer to DNBD3 image to check I/O
+ * attributes for. May NOT be NULL, so be
+ * careful.
+ * @param[in] type I/O type to be checked for.
+ * @retval true The DNBD3 image supports the I/O feature.
+ * @retval false The I/O feature is NOT supported for the
+ * DNBD3 image.
+ */
+static inline bool iscsi_scsi_emu_io_type_is_supported(const dnbd3_image_t *image, const int type)
+{
+ // TODO: Actually implement this function.
+
+ int32_t flags;
+
+ switch ( type ) {
+ case ISCSI_SCSI_EMU_IO_TYPE_REMOVABLE : {
+ flags = iscsi_config_get( (uint8_t *) image->name, ISCSI_GLOBALS_CONFIG_TYPE_FLAGS_SCSI_IO_REMOVABLE );
+
+ if ( flags < 0L )
+ flags = iscsi_config_get( NULL, ISCSI_GLOBALS_CONFIG_TYPE_FLAGS_SCSI_IO_REMOVABLE );
+
+ return (bool) flags;
+
+ break;
+ }
+ case ISCSI_SCSI_EMU_IO_TYPE_UNMAP : {
+ flags = iscsi_config_get( (uint8_t *) image->name, ISCSI_GLOBALS_CONFIG_TYPE_FLAGS_SCSI_IO_UNMAP );
+
+ if ( flags < 0L )
+ flags = iscsi_config_get( NULL, ISCSI_GLOBALS_CONFIG_TYPE_FLAGS_SCSI_IO_UNMAP );
+
+ return (bool) flags;
+
+ break;
+ }
+ case ISCSI_SCSI_EMU_IO_TYPE_NO_ROTATION : {
+ flags = iscsi_config_get( (uint8_t *) image->name, ISCSI_GLOBALS_CONFIG_TYPE_FLAGS_SCSI_IO_NO_ROTATION );
+
+ if ( flags < 0L )
+ flags = iscsi_config_get( NULL, ISCSI_GLOBALS_CONFIG_TYPE_FLAGS_SCSI_IO_NO_ROTATION );
+
+ return (bool) flags;
+
+ break;
+ }
+ case ISCSI_SCSI_EMU_IO_TYPE_PHYSICAL_READ_ONLY : {
+ flags = iscsi_config_get( (uint8_t *) image->name, ISCSI_GLOBALS_CONFIG_TYPE_FLAGS_SCSI_IO_PHYSICAL_READ_ONLY );
+
+ if ( flags < 0L )
+ flags = iscsi_config_get( NULL, ISCSI_GLOBALS_CONFIG_TYPE_FLAGS_SCSI_IO_PHYSICAL_READ_ONLY );
+
+ return (bool) flags;
+
+ break;
+ }
+ case ISCSI_SCSI_EMU_IO_TYPE_WRITE_PROTECT : {
+ flags = iscsi_config_get( (uint8_t *) image->name, ISCSI_GLOBALS_CONFIG_TYPE_FLAGS_SCSI_IO_WRITE_PROTECT );
+
+ if ( flags < 0L )
+ flags = iscsi_config_get( NULL, ISCSI_GLOBALS_CONFIG_TYPE_FLAGS_SCSI_IO_WRITE_PROTECT );
+
+ return (bool) flags;
+
+ break;
+ }
+ case ISCSI_SCSI_EMU_IO_TYPE_WRITE_CACHE : {
+ flags = iscsi_config_get( (uint8_t *) image->name, ISCSI_GLOBALS_CONFIG_TYPE_FLAGS_SCSI_IO_WRITE_CACHE );
+
+ if ( flags < 0L )
+ flags = iscsi_config_get( NULL, ISCSI_GLOBALS_CONFIG_TYPE_FLAGS_SCSI_IO_WRITE_CACHE );
+
+ return (bool) flags;
+
+ break;
+ }
+ default : {
+ return false;
+
+ break;
+ }
+ }
+
+ return false;
+}
+
+/**
+ * @brief Retrieves the number of total physical blocks for a DNBD3 image.
+ *
+ * This function depends on DNBD3 image
+ * properties.
+ *
+ * @param[in] image Pointer to DNBD3 image to retrieve
+ * the physical size from. May NOT be NULL,
+ * so be careful.
+ * @return The number of total physical blocks.
+ */
+static inline uint64_t iscsi_scsi_emu_physical_block_get_count(const dnbd3_image_t *image)
+{
+ int32_t block_size_shift = iscsi_config_get( (uint8_t *) image->name, ISCSI_GLOBALS_CONFIG_TYPE_SCSI_PHYSICAL_BLOCK_SIZE_SHIFT );
+
+ if ( block_size_shift < 0L )
+ block_size_shift = iscsi_config_get( NULL, ISCSI_GLOBALS_CONFIG_TYPE_SCSI_PHYSICAL_BLOCK_SIZE_SHIFT );
+
+ return (image->virtualFilesize >> (uint32_t) block_size_shift);
+}
+
+/**
+ * @brief Retrieves the bit shift of a physical block in bytes for a DNBD3 image.
+ *
+ * This function depends on DNBD3 image
+ * properties.
+ *
+ * @param[in] image Pointer to DNBD3 image to retrieve
+ * the physical bit shift size. May NOT
+ * be NULL, so be careful.
+ * @return The physical block size in bytes as a
+ * bit shift count.
+ */
+static inline uint32_t iscsi_scsi_emu_physical_block_get_size_shift(const dnbd3_image_t *image)
+{
+ int32_t block_size_shift = iscsi_config_get( (uint8_t *) image->name, ISCSI_GLOBALS_CONFIG_TYPE_SCSI_PHYSICAL_BLOCK_SIZE_SHIFT );
+
+ if ( block_size_shift < 0L )
+ block_size_shift = iscsi_config_get( NULL, ISCSI_GLOBALS_CONFIG_TYPE_SCSI_PHYSICAL_BLOCK_SIZE_SHIFT );
+
+ return block_size_shift;
+}
+
+/**
+ * @brief Retrieves the size of a physical block in bytes for a DNBD3 image.
+ *
+ * This function depends on DNBD3 image
+ * properties.
+ *
+ * @param[in] image Pointer to DNBD3 image to retrieve
+ * the physical block size. May NOT be NULL,
+ * so be careful.
+ * @return The physical block size in bytes.
+ */
+static inline uint32_t iscsi_scsi_emu_physical_block_get_size(const dnbd3_image_t *image)
+{
+ int32_t block_size = iscsi_config_get( (uint8_t *) image->name, ISCSI_GLOBALS_CONFIG_TYPE_SCSI_PHYSICAL_BLOCK_SIZE );
+
+ if ( block_size < 0L )
+ block_size = iscsi_config_get( NULL, ISCSI_GLOBALS_CONFIG_TYPE_SCSI_PHYSICAL_BLOCK_SIZE );
+
+ return block_size;
+}
+
+/**
+ * @brief Retrieves the number of total logical blocks for a DNBD3 image.
+ *
+ * This function depends on DNBD3 image
+ * properties.
+ *
+ * @param[in] image Pointer to DNBD3 image to retrieve
+ * the logical size from. May NOT be NULL,
+ * so be careful.
+ * @return The number of total logical blocks.
+ */
+static inline uint64_t iscsi_scsi_emu_block_get_count(const dnbd3_image_t *image)
+{
+ int32_t block_size_shift = iscsi_config_get( (uint8_t *) image->name, ISCSI_GLOBALS_CONFIG_TYPE_SCSI_LOGICAL_BLOCK_SIZE_SHIFT );
+
+ if ( block_size_shift < 0L )
+ block_size_shift = iscsi_config_get( NULL, ISCSI_GLOBALS_CONFIG_TYPE_SCSI_LOGICAL_BLOCK_SIZE_SHIFT );
+
+ return (image->virtualFilesize >> (uint32_t) block_size_shift);
+}
+
+/**
+ * @brief Retrieves the bit shift of a logical block in bytes for a DNBD3 image.
+ *
+ * This function depends on DNBD3 image
+ * properties.
+ *
+ * @param[in] image Pointer to DNBD3 image to retrieve
+ * the logical block bit shift size.
+ * May NOT be NULL, so be careful.
+ * @return The logical block size in bytes as a
+ * bit shift count.
+ */
+static inline uint32_t iscsi_scsi_emu_block_get_size_shift(const dnbd3_image_t *image)
+{
+ int32_t block_size_shift = iscsi_config_get( (uint8_t *) image->name, ISCSI_GLOBALS_CONFIG_TYPE_SCSI_LOGICAL_BLOCK_SIZE_SHIFT );
+
+ if ( block_size_shift < 0L )
+ block_size_shift = iscsi_config_get( NULL, ISCSI_GLOBALS_CONFIG_TYPE_SCSI_LOGICAL_BLOCK_SIZE_SHIFT );
+
+ return block_size_shift;
+}
+
+/**
+ * @brief Retrieves the size of a logical block in bytes for a DNBD3 image.
+ *
+ * This function depends on DNBD3 image
+ * properties.
+ *
+ * @param[in] image Pointer to DNBD3 image to retrieve
+ * the logical block size. May NOT be NULL,
+ * so be careful.
+ * @return The logical block size in bytes.
+ */
+static inline uint32_t iscsi_scsi_emu_block_get_size(const dnbd3_image_t *image)
+{
+ int32_t block_size = iscsi_config_get( (uint8_t *) image->name, ISCSI_GLOBALS_CONFIG_TYPE_SCSI_LOGICAL_BLOCK_SIZE );
+
+ if ( block_size < 0L )
+ block_size = iscsi_config_get( NULL, ISCSI_GLOBALS_CONFIG_TYPE_SCSI_LOGICAL_BLOCK_SIZE );
+
+ return block_size;
+}
+
+/**
+ * @brief Retrieves the bit shift ratio between logical and physical block size for a DNBD3 image.
+ *
+ * This function depends on DNBD3 image
+ * properties.
+ *
+ * @param[in] image Pointer to DNBD3 image to retrieve
+ * the ratio between the logical and
+ * physical block size. May NOT be
+ * NULL, so be careful.
+ * @return The ratio between logical and physical
+ * block size as a logical bit shift
+ * count.
+ */
+static inline uint32_t iscsi_scsi_emu_block_get_ratio_shift(const dnbd3_image_t *image)
+{
+ return (iscsi_scsi_emu_physical_block_get_size_shift( image ) - iscsi_scsi_emu_block_get_size_shift( image ));
+}
+
+/**
+ * @brief Retrieves the ratio between logical and physical block size for a DNBD3 image.
+ *
+ * This function depends on DNBD3 image
+ * properties.
+ *
+ * @param[in] image Pointer to DNBD3 image to retrieve
+ * the ratio between the logical and
+ * physical block size. May NOT be
+ * NULL, so be careful.
+ * @return The ratio between logical logical and physical
+ * block size.
+ */
+static inline uint32_t iscsi_scsi_emu_block_get_ratio(const dnbd3_image_t *image)
+{
+ return (1UL << iscsi_scsi_emu_block_get_ratio_shift( image ));
+}
+
+/**
+ * @brief Converts offset and length in bytes to block number and length specified by a block size.
+ *
+ * This function uses bit shifting if
+ * the block size is a power of two.
+ *
+ * @param[out] offset_blocks Pointer where to store the block
+ * number. May NOT be NULL, so be
+ * careful.
+ * @param[out] num_blocks Pointer where to store the number of
+ * blocks. NULL is NOT allowed here,
+ * so take caution.
+ * @param[in] offset_bytes Offset in bytes.
+ * @param[in] num_bytes Number of bytes.
+ * @param[in] block_size Block size in bytes.
+ * @return 0 if specified offset and number of
+ * bytes is aligned to block size or a
+ * positive value if unaligned.
+ */
+static uint64_t iscsi_scsi_emu_bytes_to_blocks(uint64_t *offset_blocks, uint64_t *num_blocks, const uint64_t offset_bytes, const uint64_t num_bytes, const uint32_t block_size)
+{
+ if ( iscsi_is_pow2( block_size ) ) {
+ const uint32_t shift = iscsi_get_log2_of_pow2( block_size );
+
+ *offset_blocks = (offset_bytes >> shift);
+ *num_blocks = (num_bytes >> shift);
+
+ return ((offset_bytes - (*offset_blocks << shift)) | (num_bytes - (*num_blocks << shift)));
+ }
+
+ *offset_blocks = (offset_bytes / block_size);
+ *num_blocks = (num_bytes / block_size);
+
+ return ((offset_bytes % block_size) | (num_bytes % block_size));
+}
+
+/**
+ * @brief Enqueues an I/O task in the waiting queue.
+ *
+ * This function invokes a callback function
+ * with optional user data.
+ *
+ * @param[in] scsi_task Pointer to iSCSI SCSI task associated
+ * to the I/O process in the waiting queue.
+ * May NOT be NULL, so be careful.
+ * @param[in] callback Pointer to an I/O wait callback
+ * function which executes the pending I/O
+ * operation. NULL is NOT allowed here, so
+ * take caution.
+ * @param[in] user_data Pointer to optional user data for
+ * the callback function.
+ * @retval -1 The I/O task could not be
+ * run in the waiting queue.
+ * @retval 0 The I/O task has been added
+ * successfully in the I/O task waiting
+ * queue.
+ */
+static int iscsi_scsi_emu_queue_io_wait(iscsi_scsi_task *scsi_task, iscsi_scsi_emu_io_wait_callback callback, uint8_t *user_data)
+{
+ scsi_task->io_wait.image = scsi_task->lun->image;
+ scsi_task->io_wait.callback = callback;
+ scsi_task->io_wait.user_data = user_data;
+
+ return iscsi_scsi_emu_io_queue( &scsi_task->io_wait );
+}
+
+/**
+ * @brief Converts offset and length specified by a block size to offset and length in bytes.
+ *
+ * This function uses bit shifting if
+ * the block size is a power of two.
+ *
+ * @param[out] offset_bytes Pointer where to store the block
+ * in bytes. May NOT be NULL, so be
+ * careful.
+ * @param[in] offset_blocks Offset in blocks.
+ * @param[in] num_blocks Number of blocks.
+ * @param[in] block_size Block size in bytes.
+ * @return Number of blocks in bytes.
+ */
+static uint64_t iscsi_scsi_emu_blocks_to_bytes(uint64_t *offset_bytes, const uint64_t offset_blocks, const uint64_t num_blocks, const uint32_t block_size)
+{
+ if ( iscsi_is_pow2( block_size ) ) {
+ const uint32_t shift = iscsi_get_log2_of_pow2( block_size );
+
+ *offset_bytes = (offset_blocks << shift);
+
+ return (num_blocks << shift);
+ }
+
+ *offset_bytes = (offset_blocks * block_size);
+
+ return (num_blocks * block_size);
+}
+
+/**
+ * @brief Reads a number of blocks from a block offset of a DNBD3 image to a specified buffer.
+ *
+ * This function enqueues the I/O read
+ * process which invokes a callback
+ * function when the read operation has
+ * been finished.
+ *
+ * @param[in] scsi_task Pointer to iSCSI SCSI task which
+ * executes the I/O read operation, may
+ * NOT be NULL, so be careful.
+ * @param[in] buf Pointer to buffer where to store
+ * the read data. NULL is NOT allowed
+ * here, take caution.
+ * @param[in] image Pointer to DNBD3 image to read
+ * data from and may NOT be NULL, so
+ * be careful.
+ * @param[in] offset_blocks Offset in blocks to start reading from.
+ * @param[in] num_blocks Number of blocks to read.
+ * @param[in] block_size Block size in bytes.
+ * @param[in] callback Pointer to callback function to invoke
+ * after I/O read operation has been
+ * finished. NULL is a prohibited
+ * value, so be careful.
+ * @param[in] user_data Pointer to user data passed to the
+ * callback function.
+ * @return 0 on successful operation, a negative
+ * error code otherwise.
+ */
+int iscsi_scsi_emu_io_block_read(iscsi_scsi_task *scsi_task, uint8_t *buf, dnbd3_image_t *image, const uint64_t offset_blocks, const uint64_t num_blocks, const uint32_t block_size, iscsi_scsi_emu_io_complete_callback callback, uint8_t *user_data)
+{
+ uint64_t offset_bytes;
+ const uint64_t num_bytes = iscsi_scsi_emu_blocks_to_bytes( &offset_bytes, offset_blocks, num_blocks, block_size );
+ const int64_t len = pread( image->readFd, buf, (size_t) num_bytes, offset_bytes );
+ const bool success = ((uint64_t) len == num_bytes);
+ iscsi_connection_exec_queue *exec_queue = (iscsi_connection_exec_queue *) malloc( sizeof(struct iscsi_connection_exec_queue) );
+
+ if ( exec_queue == NULL ) {
+ logadd( LOG_ERROR, "iscsi_scsi_emu_io_block_read: Out of memory while allocating execution queue for async I/O" );
+
+ return -ENOMEM;
+ }
+
+ exec_queue->data.io.callback = callback;
+ exec_queue->data.io.image = image;
+ exec_queue->data.io.user_data = user_data;
+ exec_queue->data.io.success = success;
+ exec_queue->type = ISCSI_CONNECT_EXEC_QUEUE_TYPE_SCSI_EMU_IO;
+
+ iscsi_task *task = ISCSI_CONTAINER(iscsi_task, scsi_task, scsi_task);
+
+ iscsi_list_enqueue( &task->conn->exec_queue, &exec_queue->node );
+
+ return (success ? 0 : -EIO);
+}
+
+/**
+ * @brief Completes an iSCSI SCSI task after a finished I/O read operation.
+ *
+ * THis function also sets the SCSI status
+ * and error code as required.
+ *
+ * @param[in] image Pointer to DNBD3 image where
+ * the I/O read operation occured and
+ * may NOT be NULL, so be careful.
+ * @param[in] user_data Pointer to the iSCSI SCSI task
+ * responsible for this I/O operation.
+ * NULL is NOT allowed here, so take
+ * caution.
+ * @param[in] success true if the I/O operation has been
+ * completed successfully, false otherwise.
+ * @return Pointer to passed user data.
+ */
+uint8_t *iscsi_scsi_emu_block_read_complete_callback(dnbd3_image_t *image, uint8_t *user_data, const bool success)
+{
+ iscsi_scsi_task *scsi_task = (iscsi_scsi_task *) user_data;
+
+ if ( success )
+ scsi_task->status = ISCSI_SCSI_STATUS_GOOD;
+ else
+ iscsi_scsi_task_status_set( scsi_task, ISCSI_SCSI_STATUS_CHECK_COND, ISCSI_SCSI_SENSE_KEY_MEDIUM_ERR, ISCSI_SCSI_ASC_UNRECOVERED_READ_ERR, ISCSI_SCSI_ASCQ_CAUSE_NOT_REPORTABLE );
+
+ iscsi_scsi_lun_task_complete( scsi_task->lun, scsi_task );
+
+ return user_data;
+}
+
+/**
+ * @brief Compares and writes a number of blocks starting from a block offset in a DNBD3 image with specified buffers.
+ *
+ * This function enqueues the I/O compare
+ * and write process which invokes a
+ * callback function when the compare and
+ * write operation has been finished.
+ *
+ * @param[in] scsi_task Pointer to iSCSI SCSI task which
+ * executes the I/O compare and write
+ * operation, may NOT be NULL, so be
+ * careful.
+ * @param[in] buf Pointer to buffer which contains
+ * the data to be written. NULL is NOT
+ * allowed here, take caution.
+ * @param[in] cmp_buf Pointer to buffer which contains
+ * the data to be compared and may NOT
+ * be NULL, so be careful.
+ * @param[in] image Pointer to DNBD3 image to write
+ * data to. NULL is an illegal value,
+ * take caution.
+ * @param[in] offset_blocks Offset in blocks to start writing to.
+ * @param[in] num_blocks Number of blocks to write.
+ * @param[in] block_size Block size in bytes.
+ * @param[in] callback Pointer to callback function to invoke
+ * after I/O compare and write operation
+ * has been finished. NULL is a
+ * prohibited value, so be careful.
+ * @param[in] user_data Pointer to user data passed to the
+ * callback function.
+ * @return 0 on successful operation, a negative
+ * error code otherwise.
+ */
+int iscsi_scsi_emu_io_block_cmp_write(iscsi_scsi_task *scsi_task, uint8_t *buf, uint8_t *cmp_buf, dnbd3_image_t *image, const uint64_t offset_blocks, const uint64_t num_blocks, const uint32_t block_size, iscsi_scsi_emu_io_complete_callback callback, uint8_t *user_data)
+{
+ // TODO: Implement compare and write I/O.
+
+ return ISCSI_SCSI_TASK_RUN_COMPLETE;
+}
+
+/**
+ * @brief Completes an iSCSI SCSI task after a finished I/O write operation.
+ *
+ * THis function also sets the SCSI status
+ * and error code as required.
+ *
+ * @param[in] image Pointer to DNBD3 image where
+ * the I/O write operation occured and
+ * may NOT be NULL, so be careful.
+ * @param[in] user_data Pointer to the iSCSI SCSI task
+ * responsible for this I/O operation.
+ * NULL is NOT allowed here, so take
+ * caution.
+ * @param[in] success true if the I/O operation has been
+ * completed successfully, false otherwise.
+ * @return Pointer to passed user data.
+ */
+uint8_t *iscsi_scsi_emu_block_write_complete_callback(dnbd3_image_t *image, uint8_t *user_data, const bool success)
+{
+ iscsi_scsi_task *scsi_task = (iscsi_scsi_task *) user_data;
+
+ free( scsi_task->buf );
+ scsi_task->buf = NULL;
+
+ if ( success )
+ scsi_task->status = ISCSI_SCSI_STATUS_GOOD;
+ else
+ iscsi_scsi_task_status_set( scsi_task, ISCSI_SCSI_STATUS_CHECK_COND, ISCSI_SCSI_SENSE_KEY_MEDIUM_ERR, ISCSI_SCSI_ASC_WRITE_ERR, ISCSI_SCSI_ASCQ_CAUSE_NOT_REPORTABLE );
+
+ iscsi_scsi_lun_task_complete( scsi_task->lun, scsi_task );
+
+ return user_data;
+}
+
+/**
+ * @brief Writes a number of blocks from a block offset to a DNBD3 image of a specified buffer.
+ *
+ * This function enqueues the I/O write
+ * process which invokes a callback
+ * function when the write operation
+ * has been finished.
+ *
+ * @param[in] scsi_task Pointer to iSCSI SCSI task which
+ * executes the I/O write operation, may
+ * NOT be NULL, so be careful.
+ * @param[in] buf Pointer to buffer which contains
+ * the data to be written. NULL is NOT
+ * allowed here, take caution.
+ * @param[in] image Pointer to DNBD3 image to write
+ * data to and may NOT be NULL, so
+ * be careful.
+ * @param[in] offset_blocks Offset in blocks to start writing to.
+ * @param[in] num_blocks Number of blocks to write.
+ * @param[in] block_size Block size in bytes.
+ * @param[in] callback Pointer to callback function to invoke
+ * after I/O write operation has been
+ * finished. NULL is a prohibited
+ * value, so be careful.
+ * @param[in] user_data Pointer to user data passed to the
+ * callback function.
+ * @return 0 on successful operation, a negative
+ * error code otherwise.
+ */
+int iscsi_scsi_emu_io_block_write(iscsi_scsi_task *scsi_task, uint8_t *buf, dnbd3_image_t *image, const uint64_t offset_blocks, const uint64_t num_blocks, const uint32_t block_size, iscsi_scsi_emu_io_complete_callback callback, uint8_t *user_data)
+{
+ uint64_t offset_bytes;
+ const uint64_t num_bytes = iscsi_scsi_emu_blocks_to_bytes( &offset_bytes, offset_blocks, num_blocks, block_size );
+ const int64_t len = pwrite( image->readFd, buf, (size_t) num_bytes, offset_bytes );
+ const bool success = ((uint64_t) len == num_bytes);
+ iscsi_connection_exec_queue *exec_queue = (iscsi_connection_exec_queue *) malloc( sizeof(struct iscsi_connection_exec_queue) );
+
+ if ( exec_queue == NULL ) {
+ logadd( LOG_ERROR, "iscsi_scsi_emu_io_block_read: Out of memory while allocating execution queue for async I/O" );
+
+ return -ENOMEM;
+ }
+
+ exec_queue->data.io.callback = callback;
+ exec_queue->data.io.image = image;
+ exec_queue->data.io.user_data = user_data;
+ exec_queue->data.io.success = success;
+ exec_queue->type = ISCSI_CONNECT_EXEC_QUEUE_TYPE_SCSI_EMU_IO;
+
+ iscsi_task *task = ISCSI_CONTAINER(iscsi_task, scsi_task, scsi_task);
+
+ iscsi_list_enqueue( &task->conn->exec_queue, &exec_queue->node );
+
+ return (success ? 0 : -EIO);
+}
+
+/**
+ * @brief Executes a read or write operation on a DNBD3 image.
+ *
+ * This function also sets the SCSI
+ * status result code accordingly.
+ *
+ * @param[in] image Pointer to DNBD3 image to read from
+ * or to write to. May NOT be NULL, so
+ * be careful.
+ * @param[in] scsi_task Pointer to iSCSI SCSI task
+ * responsible for this read or write
+ * task. NULL is NOT allowed here, take
+ * caution.
+ * @param[in] lba Logical Block Address (LBA) to start
+ * reading from or writing to.
+ * @param[in] xfer_len Transfer length in logical blocks.
+ * @param[in] flags Flags indicating if a read or write
+ * operation is in progress. For a
+ * write operation an optional verify
+ * can be requested.
+ * @return 0 on successful operation, a negative
+ * error code otherwise.
+ */
+static int iscsi_scsi_emu_block_read_write(dnbd3_image_t *image, iscsi_scsi_task *scsi_task, const uint64_t lba, const uint32_t xfer_len, const int flags)
+{
+ scsi_task->xfer_pos = 0UL;
+
+ if ( (scsi_task->flags & (ISCSI_SCSI_TASK_FLAGS_XFER_READ | ISCSI_SCSI_TASK_FLAGS_XFER_WRITE)) == (ISCSI_SCSI_TASK_FLAGS_XFER_READ | ISCSI_SCSI_TASK_FLAGS_XFER_WRITE) ) {
+ iscsi_scsi_task_status_set( scsi_task, ISCSI_SCSI_STATUS_CHECK_COND, ISCSI_SCSI_SENSE_KEY_NO_SENSE, ISCSI_SCSI_ASC_NO_ADDITIONAL_SENSE, ISCSI_SCSI_ASCQ_CAUSE_NOT_REPORTABLE );
+
+ return ISCSI_SCSI_TASK_RUN_COMPLETE;
+ }
+
+ const uint64_t block_count = iscsi_scsi_emu_block_get_count( image );
+
+ if ( (block_count <= lba) || ((block_count - lba) < xfer_len) ) {
+ iscsi_scsi_task_status_set( scsi_task, ISCSI_SCSI_STATUS_CHECK_COND, ISCSI_SCSI_SENSE_KEY_ILLEGAL_REQ, ISCSI_SCSI_ASC_LOGICAL_BLOCK_ADDRESS_OUT_OF_RANGE, ISCSI_SCSI_ASCQ_CAUSE_NOT_REPORTABLE );
+
+ return ISCSI_SCSI_TASK_RUN_COMPLETE;
+ }
+
+ if ( xfer_len == 0UL ) {
+ scsi_task->status = ISCSI_SCSI_STATUS_GOOD;
+
+ return ISCSI_SCSI_TASK_RUN_COMPLETE;
+ }
+
+ const uint32_t block_size = iscsi_scsi_emu_block_get_size( image );
+ const bool block_size_pow2 = iscsi_is_pow2( block_size );
+ uint32_t block_size_shift;
+
+ if ( block_size_pow2 )
+ block_size_shift = iscsi_scsi_emu_block_get_size_shift( image );
+
+ const uint32_t max_xfer_len = (block_size_pow2 ? (ISCSI_SCSI_EMU_MAX_XFER_LEN >> block_size_shift) : (ISCSI_SCSI_EMU_MAX_XFER_LEN / block_size));
+
+ if ( xfer_len > max_xfer_len ) {
+ iscsi_scsi_task_status_set( scsi_task, ISCSI_SCSI_STATUS_CHECK_COND, ISCSI_SCSI_SENSE_KEY_ILLEGAL_REQ, ISCSI_SCSI_ASC_INVALID_FIELD_IN_CDB, ISCSI_SCSI_ASCQ_CAUSE_NOT_REPORTABLE );
+
+ return ISCSI_SCSI_TASK_RUN_COMPLETE;
+ }
+
+ if ( ((flags & ISCSI_SCSI_EMU_BLOCK_FLAGS_WRITE) != 0) && ((block_size_pow2 ? (xfer_len << block_size_shift) : (xfer_len * block_size)) > scsi_task->xfer_len) ) {
+ iscsi_scsi_task_status_set( scsi_task, ISCSI_SCSI_STATUS_CHECK_COND, ISCSI_SCSI_SENSE_KEY_NO_SENSE, ISCSI_SCSI_ASC_NO_ADDITIONAL_SENSE, ISCSI_SCSI_ASCQ_CAUSE_NOT_REPORTABLE );
+
+ return ISCSI_SCSI_TASK_RUN_COMPLETE;
+ }
+
+ uint64_t offset_blocks;
+ uint64_t num_blocks;
+
+ if ( iscsi_scsi_emu_bytes_to_blocks( &offset_blocks, &num_blocks, scsi_task->pos, scsi_task->len, block_size ) != 0ULL ) {
+ iscsi_scsi_task_status_set( scsi_task, ISCSI_SCSI_STATUS_CHECK_COND, ISCSI_SCSI_SENSE_KEY_NO_SENSE, ISCSI_SCSI_ASC_NO_ADDITIONAL_SENSE, ISCSI_SCSI_ASCQ_CAUSE_NOT_REPORTABLE );
+
+ return ISCSI_SCSI_TASK_RUN_COMPLETE;
+ }
+
+ offset_blocks += lba;
+
+ int rc;
+
+ if ( (flags & ISCSI_SCSI_EMU_BLOCK_FLAGS_WRITE) == 0 ) {
+ scsi_task->buf = (uint8_t *) malloc( scsi_task->len );
+
+ if ( scsi_task->buf == NULL ) {
+ iscsi_scsi_emu_queue_io_wait( scsi_task, iscsi_scsi_emu_block_resubmit_process_callback, (uint8_t *) scsi_task );
+
+ return ISCSI_SCSI_TASK_RUN_PENDING;
+ }
+
+ rc = iscsi_scsi_emu_io_block_read( scsi_task, scsi_task->buf, image, offset_blocks, num_blocks, block_size, iscsi_scsi_emu_block_read_complete_callback, (uint8_t *) scsi_task );
+ } else if ( iscsi_scsi_emu_io_type_is_supported( image, ISCSI_SCSI_EMU_IO_TYPE_PHYSICAL_READ_ONLY ) || iscsi_scsi_emu_io_type_is_supported( image, ISCSI_SCSI_EMU_IO_TYPE_WRITE_PROTECT ) ) {
+ iscsi_scsi_task_status_set( scsi_task, ISCSI_SCSI_STATUS_CHECK_COND, ISCSI_SCSI_SENSE_KEY_DATA_PROTECT, ISCSI_SCSI_ASC_WRITE_PROTECTED, ISCSI_SCSI_ASCQ_CAUSE_NOT_REPORTABLE );
+
+ return ISCSI_SCSI_TASK_RUN_COMPLETE;
+ } else if ( (flags & ISCSI_SCSI_EMU_BLOCK_FLAGS_VERIFY) != 0 ) {
+ if ( scsi_task->len != (block_size + block_size) ) {
+ iscsi_scsi_task_status_set( scsi_task, ISCSI_SCSI_STATUS_CHECK_COND, ISCSI_SCSI_SENSE_KEY_ILLEGAL_REQ, ISCSI_SCSI_ASC_INVALID_FIELD_IN_CDB, ISCSI_SCSI_ASCQ_CAUSE_NOT_REPORTABLE );
+
+ return ISCSI_SCSI_TASK_RUN_COMPLETE;
+ }
+
+ uint8_t *cmp_buf = (scsi_task->buf + block_size);
+
+ rc = iscsi_scsi_emu_io_block_cmp_write( scsi_task, scsi_task->buf, cmp_buf, image, offset_blocks, 1ULL, block_size, iscsi_scsi_emu_block_write_complete_callback, (uint8_t *) scsi_task );
+ } else {
+ rc = iscsi_scsi_emu_io_block_write( scsi_task, scsi_task->buf, image, offset_blocks, num_blocks, block_size, iscsi_scsi_emu_block_write_complete_callback, (uint8_t *) scsi_task );
+ }
+
+ if ( rc < 0 ) {
+ if ( rc == -ENOMEM ) {
+ iscsi_scsi_emu_queue_io_wait( scsi_task, iscsi_scsi_emu_block_resubmit_process_callback, (uint8_t *) scsi_task );
+
+ return ISCSI_SCSI_TASK_RUN_PENDING;
+ }
+
+ iscsi_scsi_task_status_set( scsi_task, ISCSI_SCSI_STATUS_CHECK_COND, ISCSI_SCSI_SENSE_KEY_NO_SENSE, ISCSI_SCSI_ASC_NO_ADDITIONAL_SENSE, ISCSI_SCSI_ASCQ_CAUSE_NOT_REPORTABLE );
+
+ return ISCSI_SCSI_TASK_RUN_COMPLETE;
+ }
+
+ scsi_task->xfer_pos = scsi_task->len;
+
+ return ISCSI_SCSI_TASK_RUN_PENDING;
+}
+
+/**
+ * @brief Enqueues an I/O wait in the thread pool to execute.
+ *
+ * This function uses the DNBD3 image
+ * name in order to identify the
+ * newly created thread.
+ *
+ * @param[in] io_wait Pointer to I/O wait structure
+ * containing the image name, the
+ * callback function and optional
+ * user data passed to callback. May
+ * NOT be NULL, so be careful.
+ * @retval -1 An error occured during the
+ * thread enqeue operation.
+ * @retval 0 The thread has been enqueued
+ * successfully.
+ */
+int iscsi_scsi_emu_io_queue(iscsi_scsi_emu_io_wait *io_wait)
+{
+ return (threadpool_run( (void *(*)(void *)) io_wait->callback, (void *) io_wait->user_data, io_wait->image->name ) ? 0 : -1);
+}
+
+/**
+ * @brief Executes a cache synchronization operation on a DNBD3 image.
+ *
+ * This function also sets the SCSI
+ * status result code accordingly.
+ *
+ * @param[in] image Pointer to DNBD3 image to
+ * synchronize the cache of. May NOT
+ * be NULL, so be careful.
+ * @param[in] scsi_task Pointer to iSCSI SCSI task
+ * responsible for this cache
+ * synchronization. NULL is NOT
+ * allowed here, take caution.
+ * @param[in] lba Logical Block Address (LBA) to start
+ * cache synchronization with.
+ * @param[in] xfer_len Synchronization length in logical blocks.
+ * @return 0 on successful operation, a negative
+ * error code otherwise.
+ */
+static int iscsi_scsi_emu_block_sync(dnbd3_image_t *image, iscsi_scsi_task *scsi_task, const uint64_t lba, const uint32_t xfer_len)
+{
+ // TODO: Implement SCSI emulation for DNBD3 image.
+
+ return 0;
+}
+
+/**
+ * @brief Executes a unmap operation on a DNBD3 image.
+ *
+ * This function also sets the SCSI
+ * status result code accordingly.
+ *
+ * @param[in] image Pointer to DNBD3 image to
+ * unmap. May NOT be NULL, so be
+ * careful.
+ * @param[in] scsi_task Pointer to iSCSI SCSI task
+ * responsible for this unmap
+ * operation. NULL is NOT allowed
+ * here, take caution.
+ * @return 0 on successful operation, a negative
+ * error code otherwise.
+ */
+static int iscsi_scsi_emu_block_unmap(dnbd3_image_t *image, iscsi_scsi_task *scsi_task)
+{
+ // TODO: Implement SCSI emulation for DNBD3 image.
+
+ return 0;
+}
+
+/**
+ * @brief Executes a write same operation on a DNBD3 image.
+ *
+ * This function also sets the SCSI
+ * status result code accordingly.
+ *
+ * @param[in] image Pointer to DNBD3 image to write
+ * to. May NOT be NULL, so be
+ * careful.
+ * @param[in] scsi_task Pointer to iSCSI SCSI task
+ * responsible for this write task.
+ * NULL is NOT allowed here, take
+ * caution.
+ * @param[in] lba Logical Block Address (LBA) to start
+ * writing to.
+ * @param[in] xfer_len Transfer length in logical blocks.
+ * @param[in] flags SCSI (Command Descriptor Block) CDB flags.
+ * @return 0 on successful operation, a negative
+ * error code otherwise.
+ */
+static int iscsi_scsi_emu_block_write_same(dnbd3_image_t *image, iscsi_scsi_task *scsi_task, const uint64_t lba, const uint32_t xfer_len, const int flags)
+{
+ // TODO: Implement SCSI emulation for DNBD3 image.
+
+ return 0;
+}
+
+/**
+ * @brief Initializes a DNBD3 image for an iSCSI SCSI LUN retrieved from its iSCSI SCSI task and optionally check for read access.
+ *
+ * This function also sets the SCSI
+ * status result code accordingly.
+ *
+ * @param[in] scsi_task Pointer to iSCSI SCSI task
+ * to retrieve the iSCSI SCSI LUN
+ * from in order to initialize the
+ * DNBD3 image and also set the SCSI
+ * error code. May NOT be NULL, so
+ * be careful.
+ * @param[in] access Check if read access for DNBD3
+ * image is working.
+ * @retval true The DNBD3 image has been initialized
+ * successfully and is readable.
+ * @retval false The DNBD3 image has NOT been
+ * successfully and is read is not possible.
+ */
+static bool iscsi_scsi_emu_image_init(iscsi_scsi_task *scsi_task, const bool access)
+{
+ // TODO: Handle server and proxy stuff.
+
+ iscsi_scsi_lun *lun = scsi_task->lun;
+
+ if ( lun->image == NULL ) {
+ lun->image = image_getOrLoad( (char *) lun->device->name, (uint16_t) lun->id );
+
+ if ( lun->image == NULL ) {
+ iscsi_scsi_task_status_set( scsi_task, ISCSI_SCSI_STATUS_CHECK_COND, ISCSI_SCSI_SENSE_KEY_NOT_READY, ISCSI_SCSI_ASC_LOGICAL_UNIT_NOT_READY, ISCSI_SCSI_ASCQ_MANUAL_INTERVENTION_REQUIRED );
+
+ return false;
+ }
+ }
+
+ if ( access && (!image_ensureOpen( lun->image ) || lun->image->problem.read || (lun->image->virtualFilesize == 0ULL)) ) {
+ iscsi_scsi_task_status_set( scsi_task, ISCSI_SCSI_STATUS_CHECK_COND, ISCSI_SCSI_SENSE_KEY_NOT_READY, ISCSI_SCSI_ASC_LOGICAL_UNIT_NOT_READY, ISCSI_SCSI_ASCQ_MANUAL_INTERVENTION_REQUIRED );
+
+ return false;
+ }
+
+ return true;
+}
+
+/**
+ * @brief Executes SCSI block emulation on a DNBD3 image.
+ *
+ * This function determines the block
+ * based SCSI opcode and executes it.
+ *
+ * @param[in] scsi_task Pointer to iSCSI SCSI task
+ * to process the SCSI block operation
+ * for and may NOT be NULL, be careful.
+ * @return 0 on successful operation, a negative
+ * error code otherwise.
+ */
+static int iscsi_scsi_emu_block_process(iscsi_scsi_task *scsi_task)
+{
+ iscsi_scsi_lun *lun = scsi_task->lun;
+ uint64_t lba;
+ uint32_t xfer_len;
+
+ switch ( scsi_task->cdb->opcode ) {
+ case ISCSI_SCSI_OPCODE_READ6 : {
+ const iscsi_scsi_cdb_read_write_6 *cdb_read_write_6 = (iscsi_scsi_cdb_read_write_6 *) scsi_task->cdb;
+
+ if ( !iscsi_scsi_emu_image_init( scsi_task, true ) )
+ break;
+
+ lba = iscsi_get_be24(cdb_read_write_6->lba);
+ xfer_len = cdb_read_write_6->xfer_len;
+
+ if ( xfer_len == 0UL )
+ xfer_len = 256UL;
+
+ return iscsi_scsi_emu_block_read_write( lun->image, scsi_task, lba, xfer_len, 0 );
+
+ break;
+ }
+ case ISCSI_SCSI_OPCODE_WRITE6 : {
+ const iscsi_scsi_cdb_read_write_6 *cdb_read_write_6 = (iscsi_scsi_cdb_read_write_6 *) scsi_task->cdb;
+
+ if ( !iscsi_scsi_emu_image_init( scsi_task, true ) )
+ break;
+
+ lba = iscsi_get_be24(cdb_read_write_6->lba);
+ xfer_len = cdb_read_write_6->xfer_len;
+
+ if ( xfer_len == 0UL )
+ xfer_len = 256UL;
+
+ return iscsi_scsi_emu_block_read_write( lun->image, scsi_task, lba, xfer_len, ISCSI_SCSI_EMU_BLOCK_FLAGS_WRITE );
+
+ break;
+ }
+ case ISCSI_SCSI_OPCODE_READ10 : {
+ const iscsi_scsi_cdb_read_write_10 *cdb_read_write_10 = (iscsi_scsi_cdb_read_write_10 *) scsi_task->cdb;
+
+ if ( !iscsi_scsi_emu_image_init( scsi_task, true ) )
+ break;
+
+ lba = iscsi_get_be32(cdb_read_write_10->lba);
+ xfer_len = iscsi_get_be16(cdb_read_write_10->xfer_len);
+
+ return iscsi_scsi_emu_block_read_write( lun->image, scsi_task, lba, xfer_len, 0 );
+
+ break;
+ }
+ case ISCSI_SCSI_OPCODE_WRITE10 : {
+ const iscsi_scsi_cdb_read_write_10 *cdb_read_write_10 = (iscsi_scsi_cdb_read_write_10 *) scsi_task->cdb;
+
+ if ( !iscsi_scsi_emu_image_init( scsi_task, true ) )
+ break;
+
+ lba = iscsi_get_be32(cdb_read_write_10->lba);
+ xfer_len = iscsi_get_be16(cdb_read_write_10->xfer_len);
+
+ return iscsi_scsi_emu_block_read_write( lun->image, scsi_task, lba, xfer_len, ISCSI_SCSI_EMU_BLOCK_FLAGS_WRITE );
+
+ break;
+ }
+ case ISCSI_SCSI_OPCODE_READ12 : {
+ const iscsi_scsi_cdb_read_write_12 *cdb_read_write_12 = (iscsi_scsi_cdb_read_write_12 *) scsi_task->cdb;
+
+ if ( !iscsi_scsi_emu_image_init( scsi_task, true ) )
+ break;
+
+ lba = iscsi_get_be32(cdb_read_write_12->lba);
+ xfer_len = iscsi_get_be32(cdb_read_write_12->xfer_len);
+
+ return iscsi_scsi_emu_block_read_write( lun->image, scsi_task, lba, xfer_len, 0 );
+
+ break;
+ }
+ case ISCSI_SCSI_OPCODE_WRITE12 : {
+ const iscsi_scsi_cdb_read_write_12 *cdb_read_write_12 = (iscsi_scsi_cdb_read_write_12 *) scsi_task->cdb;
+
+ if ( !iscsi_scsi_emu_image_init( scsi_task, true ) )
+ break;
+
+ lba = iscsi_get_be32(cdb_read_write_12->lba);
+ xfer_len = iscsi_get_be32(cdb_read_write_12->xfer_len);
+
+ return iscsi_scsi_emu_block_read_write( lun->image, scsi_task, lba, xfer_len, ISCSI_SCSI_EMU_BLOCK_FLAGS_WRITE );
+
+ break;
+ }
+ case ISCSI_SCSI_OPCODE_READ16 : {
+ const iscsi_scsi_cdb_read_write_16 *cdb_read_write_16 = (iscsi_scsi_cdb_read_write_16 *) scsi_task->cdb;
+
+ if ( !iscsi_scsi_emu_image_init( scsi_task, true ) )
+ break;
+
+ lba = iscsi_get_be64(cdb_read_write_16->lba);
+ xfer_len = iscsi_get_be32(cdb_read_write_16->xfer_len);
+
+ return iscsi_scsi_emu_block_read_write( lun->image, scsi_task, lba, xfer_len, 0 );
+
+ break;
+ }
+ case ISCSI_SCSI_OPCODE_WRITE16 : {
+ const iscsi_scsi_cdb_read_write_16 *cdb_read_write_16 = (iscsi_scsi_cdb_read_write_16 *) scsi_task->cdb;
+
+ if ( !iscsi_scsi_emu_image_init( scsi_task, true ) )
+ break;
+
+ lba = iscsi_get_be64(cdb_read_write_16->lba);
+ xfer_len = iscsi_get_be32(cdb_read_write_16->xfer_len);
+
+ return iscsi_scsi_emu_block_read_write( lun->image, scsi_task, lba, xfer_len, ISCSI_SCSI_EMU_BLOCK_FLAGS_WRITE );
+
+ break;
+ }
+ case ISCSI_SCSI_OPCODE_COMPARE_AND_WRITE : {
+ const iscsi_scsi_cdb_cmp_write *cdb_cmp_write = (iscsi_scsi_cdb_cmp_write *) scsi_task->cdb;
+
+ lba = iscsi_get_be64(cdb_cmp_write->lba);
+ xfer_len = cdb_cmp_write->num_blocks;
+
+ if ( ((cdb_cmp_write->flags & (ISCSI_SCSI_CDB_CMP_WRITE_FLAGS_FUA | ISCSI_SCSI_CDB_CMP_WRITE_FLAGS_DPO)) != 0) || ISCSI_SCSI_CDB_CMP_WRITE_FLAGS_GET_WRPROTECT(cdb_cmp_write->flags) ) {
+ iscsi_scsi_task_status_set( scsi_task, ISCSI_SCSI_STATUS_CHECK_COND, ISCSI_SCSI_SENSE_KEY_ILLEGAL_REQ, ISCSI_SCSI_ASC_INVALID_FIELD_IN_CDB, ISCSI_SCSI_ASCQ_CAUSE_NOT_REPORTABLE );
+
+ return ISCSI_SCSI_TASK_RUN_COMPLETE;
+ }
+
+ if ( xfer_len != 1UL ) {
+ iscsi_scsi_task_status_set( scsi_task, ISCSI_SCSI_STATUS_CHECK_COND, ISCSI_SCSI_SENSE_KEY_ILLEGAL_REQ, ISCSI_SCSI_ASC_INVALID_FIELD_IN_CDB, ISCSI_SCSI_ASCQ_CAUSE_NOT_REPORTABLE );
+
+ return ISCSI_SCSI_TASK_RUN_COMPLETE;
+ }
+
+ if ( !iscsi_scsi_emu_image_init( scsi_task, true ) )
+ break;
+
+ return iscsi_scsi_emu_block_read_write( lun->image, scsi_task, lba, xfer_len, (ISCSI_SCSI_EMU_BLOCK_FLAGS_WRITE | ISCSI_SCSI_EMU_BLOCK_FLAGS_VERIFY) );
+
+ break;
+ }
+ case ISCSI_SCSI_OPCODE_READCAPACITY10 : {
+ if ( !iscsi_scsi_emu_image_init( scsi_task, true ) )
+ break;
+
+ iscsi_scsi_read_capacity_10_parameter_data_packet *buf = (iscsi_scsi_read_capacity_10_parameter_data_packet *) malloc( sizeof(struct iscsi_scsi_read_capacity_10_parameter_data_packet) );
+
+ if ( buf == NULL ) {
+ iscsi_scsi_task_status_set( scsi_task, ISCSI_SCSI_STATUS_CHECK_COND, ISCSI_SCSI_SENSE_KEY_NOT_READY, ISCSI_SCSI_ASC_LOGICAL_UNIT_NOT_READY, ISCSI_SCSI_ASCQ_BECOMING_READY );
+
+ return ISCSI_SCSI_TASK_RUN_COMPLETE;
+ }
+
+ lba = iscsi_scsi_emu_block_get_count( lun->image ) - 1ULL;
+
+ if ( lba > 0xFFFFFFFFULL )
+ buf->lba = 0xFFFFFFFFUL; // Minus one does not require endianess conversion
+ else
+ iscsi_put_be32( (uint8_t *) &buf->lba, (uint32_t) lba );
+
+ xfer_len = iscsi_scsi_emu_block_get_size( lun->image );
+
+ iscsi_put_be32( (uint8_t *) &buf->block_len, xfer_len );
+
+ uint len = scsi_task->len;
+
+ if ( len > sizeof(struct iscsi_scsi_read_capacity_10_parameter_data_packet) )
+ len = sizeof(struct iscsi_scsi_read_capacity_10_parameter_data_packet); // TODO: Check whether scatter data is required
+
+ scsi_task->buf = (uint8_t *) buf;
+ scsi_task->xfer_pos = len;
+ scsi_task->status = ISCSI_SCSI_STATUS_GOOD;
+
+ break;
+ }
+ case ISCSI_SCSI_OPCODE_SERVICE_ACTION_IN_16 : {
+ const iscsi_scsi_cdb_service_action_in_16 *cdb_servce_in_action_16 = (iscsi_scsi_cdb_service_action_in_16 *) scsi_task->cdb;
+
+ switch ( ISCSI_SCSI_CDB_SERVICE_ACTION_IN_16_GET_ACTION(cdb_servce_in_action_16->action) ) {
+ case ISCSI_SCSI_CDB_SERVICE_ACTION_IN_16_ACTION_READ_CAPACITY_16 : {
+ if ( !iscsi_scsi_emu_image_init( scsi_task, true ) )
+ break;
+
+ iscsi_scsi_service_action_in_16_parameter_data_packet *buf = (iscsi_scsi_service_action_in_16_parameter_data_packet *) malloc( sizeof(struct iscsi_scsi_service_action_in_16_parameter_data_packet) );
+
+ if ( buf == NULL ) {
+ iscsi_scsi_task_status_set( scsi_task, ISCSI_SCSI_STATUS_CHECK_COND, ISCSI_SCSI_SENSE_KEY_NOT_READY, ISCSI_SCSI_ASC_LOGICAL_UNIT_NOT_READY, ISCSI_SCSI_ASCQ_BECOMING_READY );
+
+ return ISCSI_SCSI_TASK_RUN_COMPLETE;
+ }
+
+ lba = iscsi_scsi_emu_block_get_count( lun->image ) - 1ULL;
+ xfer_len = iscsi_scsi_emu_block_get_size( lun->image );
+
+ iscsi_put_be64( (uint8_t *) &buf->lba, lba );
+ iscsi_put_be32( (uint8_t *) &buf->block_len, xfer_len );
+
+ buf->flags = 0;
+
+ const uint8_t exponent = (uint8_t) iscsi_scsi_emu_block_get_ratio_shift( lun->image );
+
+ buf->exponents = ISCSI_SCSI_SERVICE_ACTION_IN_16_PARAM_DATA_PUT_LBPPB_EXPONENT((exponent <= ISCSI_SCSI_SERVICE_ACTION_IN_16_PARAM_DATA_LBPPB_EXPONENT_MASK) ? exponent : 0U);
+
+ if ( iscsi_scsi_emu_io_type_is_supported( lun->image, ISCSI_SCSI_EMU_IO_TYPE_UNMAP ) )
+ iscsi_put_be16( (uint8_t *) &buf->lbp_lalba, ISCSI_SCSI_SERVICE_ACTION_IN_16_PARAM_DATA_LBPME );
+ else
+ buf->lbp_lalba = 0U;
+
+ buf->reserved[0] = 0ULL;
+ buf->reserved[1] = 0ULL;
+
+ uint len = cdb_servce_in_action_16->alloc_len;
+
+ if ( len > sizeof(struct iscsi_scsi_service_action_in_16_parameter_data_packet) )
+ len = sizeof(struct iscsi_scsi_service_action_in_16_parameter_data_packet); // TODO: Check whether scatter data is required
+
+ scsi_task->buf = (uint8_t *) buf;
+ scsi_task->xfer_pos = len;
+ scsi_task->status = ISCSI_SCSI_STATUS_GOOD;
+
+ break;
+ }
+ default : {
+ return ISCSI_SCSI_TASK_RUN_UNKNOWN;
+
+ break;
+ }
+ }
+
+ break;
+ }
+ case ISCSI_SCSI_OPCODE_SYNCHRONIZECACHE10 : {
+ const iscsi_scsi_cdb_sync_cache_10 *cdb_sync_cache_10 = (iscsi_scsi_cdb_sync_cache_10 *) scsi_task->cdb;
+
+ if ( !iscsi_scsi_emu_image_init( scsi_task, true ) )
+ break;
+
+ lba = iscsi_get_be32(cdb_sync_cache_10->lba);
+ xfer_len = iscsi_get_be16(cdb_sync_cache_10->xfer_len);
+
+ if ( xfer_len == 0UL )
+ xfer_len = (uint32_t) (iscsi_scsi_emu_block_get_count( lun->image ) - lba);
+
+ return iscsi_scsi_emu_block_sync( lun->image, scsi_task, lba, xfer_len );
+
+ break;
+ }
+ case ISCSI_SCSI_OPCODE_SYNCHRONIZECACHE16 : {
+ const iscsi_scsi_cdb_sync_cache_16 *cdb_sync_cache_16 = (iscsi_scsi_cdb_sync_cache_16 *) scsi_task->cdb;
+
+ if ( !iscsi_scsi_emu_image_init( scsi_task, true ) )
+ break;
+
+ lba = iscsi_get_be64(cdb_sync_cache_16->lba);
+ xfer_len = iscsi_get_be32(cdb_sync_cache_16->xfer_len);
+
+ if ( xfer_len == 0UL )
+ xfer_len = (uint32_t) (iscsi_scsi_emu_block_get_count( lun->image ) - lba);
+
+ return iscsi_scsi_emu_block_sync( lun->image, scsi_task, lba, xfer_len );
+
+ break;
+ }
+ case ISCSI_SCSI_OPCODE_UNMAP : {
+ if ( !iscsi_scsi_emu_image_init( scsi_task, true ) )
+ break;
+
+ return iscsi_scsi_emu_block_unmap( lun->image, scsi_task );
+
+ break;
+ }
+ case ISCSI_SCSI_OPCODE_WRITE_SAME10 : {
+ const iscsi_scsi_cdb_write_same_10 *cdb_write_same_10 = (iscsi_scsi_cdb_write_same_10 *) scsi_task->cdb;
+
+ if ( !iscsi_scsi_emu_image_init( scsi_task, true ) )
+ break;
+
+ lba = iscsi_get_be32(cdb_write_same_10->lba);
+ xfer_len = iscsi_get_be16(cdb_write_same_10->xfer_len);
+
+ return iscsi_scsi_emu_block_write_same( lun->image, scsi_task, lba, xfer_len, cdb_write_same_10->flags );
+
+ break;
+ }
+ case ISCSI_SCSI_OPCODE_WRITE_SAME16 : {
+ const iscsi_scsi_cdb_write_same_16 *cdb_write_same_16 = (iscsi_scsi_cdb_write_same_16 *) scsi_task->cdb;
+
+ if ( !iscsi_scsi_emu_image_init( scsi_task, true ) )
+ break;
+
+ lba = iscsi_get_be64(cdb_write_same_16->lba);
+ xfer_len = iscsi_get_be32(cdb_write_same_16->xfer_len);
+
+ return iscsi_scsi_emu_block_write_same( lun->image, scsi_task, lba, xfer_len, cdb_write_same_16->flags );
+
+ break;
+ }
+ default : {
+ return ISCSI_SCSI_TASK_RUN_UNKNOWN;
+
+ break;
+ }
+ }
+
+ return ISCSI_SCSI_TASK_RUN_COMPLETE;
+}
+
+/**
+ * @brief Resubmits an iSCSI SCSI task for execution.
+ *
+ * This function is invoked if an iSCSI
+ * SCSI task needs to be resubmitted in
+ * case if a prior execution failed and
+ * the failure is recoverable.
+ *
+ * @param[in] user_data Pointer to user_data which is
+ * the iSCSI SCSI task to be executed
+ * again. May NOT be NULL, so be
+ * careful.
+ * @return Pointer to passed user data.
+ */
+uint8_t *iscsi_scsi_emu_block_resubmit_process_callback(uint8_t *user_data)
+{
+ iscsi_scsi_task *scsi_task = (iscsi_scsi_task *) user_data;
+
+ iscsi_scsi_emu_block_process( scsi_task );
+
+ return user_data;
+}
+
+/**
+ * @brief Checks whether provided SCSI CDB allocation length is large enough.
+ *
+ * This function also sets the SCSI
+ * status result code if the allocation
+ * size is insufficent.
+ *
+ * @param[in] scsi_task Pointer to iSCSI SCSI task to set
+ * the iSCSI status result code for and
+ * may NOT be NULL, so be careful.
+ * @param[in] len Actual length in bytes passed to check.
+ * @param[in] min_len Minimum length in bytes required.
+ * @retval 0 Allocation length is sufficent.
+ * @retval -1 Allocation length is insufficent, SCSI status
+ * code set.
+ */
+static int iscsi_scsi_emu_check_len(iscsi_scsi_task *scsi_task, const uint len, const uint min_len)
+{
+ if ( len >= min_len )
+ return 0;
+
+ iscsi_scsi_task_status_set( scsi_task, ISCSI_SCSI_STATUS_CHECK_COND, ISCSI_SCSI_SENSE_KEY_ILLEGAL_REQ, ISCSI_SCSI_ASC_INVALID_FIELD_IN_CDB, ISCSI_SCSI_ASCQ_CAUSE_NOT_REPORTABLE );
+
+ return -1;
+}
+
+/**
+ * @brief Calculates the 64-bit IEEE Extended NAA for a name.
+ *
+ * @param[out] buf Pointer to 64-bit output buffer for
+ * storing the IEEE Extended NAA. May
+ * NOT be NULL, so be careful.
+ * @param[in] name Pointer to string containing the
+ * name to calculate the IEEE Extended
+ * NAA for. NULL is NOT allowed here, so
+ * take caution.
+ */
+static inline void iscsi_scsi_emu_naa_ieee_ext_set(uint64_t *buf, const uint8_t *name)
+{
+ const uint64_t wwn = iscsi_target_node_wwn_get( name );
+
+ iscsi_put_be64( (uint8_t *) buf, wwn );
+}
+
+/**
+ * @brief Copies a SCSI name string and zero pads until total string length is aligned to DWORD boundary.
+ *
+ * @param[out] buf Pointer to copy the aligned SCSI
+ * string to. May NOT be NULL, so be
+ * careful.
+ * @param[in] name Pointer to string containing the
+ * SCSI name to be copied. NULL is NOT
+ * allowed here, so take caution.
+ * @return The aligned string length in bytes.
+ */
+static size_t iscsi_scsi_emu_pad_scsi_name(uint8_t *buf, const uint8_t *name)
+{
+ size_t len = strlen( (char *) name );
+
+ memcpy( buf, name, len );
+
+ do {
+ buf[len++] = '\0';
+ } while ( (len & (ISCSI_ALIGN_SIZE - 1)) != 0 );
+
+ return len;
+}
+
+/**
+ * @brief Fills in a single Vital Product Data (VPD) SCSI Port Designation Descriptor entry of an INQUIRY operation.
+ *
+ * Callback function for each element while iterating
+ * through the iSCSI SCSI device ports hash map.\n
+ * The iteration process is aborted when the
+ * remaining allocation length is not enough
+ * to hold the current VPD SCSI Port Designation
+ * Descriptor.
+ *
+ * @param[in] key Pointer to zero padded key. NULL is
+ * an invalid pointer here, so be careful.
+ * @param[in] key_size Number of bytes for the key.
+ * @param[in] value Value of the key, NULL creates an
+ * empty key assignment.
+ * @param[in,out] user_data Pointer to a data structure
+ * containing the current Vital Product Data
+ * (VPD) SCSI Port Designation Descriptor
+ * entry, the total length of all VPD SCSI Port
+ * Designation Descriptor entries in bytes, the
+ * remaining allocation length in bytes. May
+ * NOT be NULL, so be careful.
+ * @retval -1 Operation failure, ran out of
+ * allocation space during traversal.
+ * @retval 0 Successful operation, there is enough
+ * allocation space to store this
+ * reported Vital Product Data (VPD) SCSI Port
+ * Designation Descriptor entry.
+ */
+int iscsi_scsi_emu_primary_inquiry_callback(uint8_t *key, const size_t key_size, uint8_t *value, uint8_t *user_data)
+{
+ iscsi_scsi_emu_primary_inquiry_ports_fill *port_report_fill = (iscsi_scsi_emu_primary_inquiry_ports_fill *) user_data;
+ iscsi_port *port = (iscsi_port *) value;
+
+ if ( (port->flags & ISCSI_PORT_FLAGS_IN_USE) == 0 )
+ return 0;
+
+ const uint port_name_len = (uint) (strlen( (char *) port->name ) + 1U);
+ const uint len = (uint) (sizeof(struct iscsi_scsi_vpd_scsi_port_design_dec_inquiry_data_packet) + sizeof(struct iscsi_scsi_vpd_scsi_target_port_design_dec_inquiry_data_packet) + ISCSI_ALIGN(port_name_len, ISCSI_ALIGN_SIZE));
+
+ port_report_fill->len -= len;
+
+ if ( (int) port_report_fill->len < 0 )
+ return -1;
+
+ iscsi_scsi_vpd_scsi_port_design_dec_inquiry_data_packet *vpd_scsi_port_design_desc_inquiry_data_pkt = port_report_fill->port_entry;
+
+ vpd_scsi_port_design_desc_inquiry_data_pkt->reserved = 0U;
+ iscsi_put_be16( (uint8_t *) &vpd_scsi_port_design_desc_inquiry_data_pkt->rel_port_id, port->index );
+ vpd_scsi_port_design_desc_inquiry_data_pkt->reserved2 = 0U;
+ vpd_scsi_port_design_desc_inquiry_data_pkt->init_port_len = 0U;
+ vpd_scsi_port_design_desc_inquiry_data_pkt->reserved3 = 0U;
+ iscsi_put_be16( (uint8_t *) &vpd_scsi_port_design_desc_inquiry_data_pkt->target_desc_len, (uint16_t) (len - sizeof(struct iscsi_scsi_vpd_scsi_port_design_dec_inquiry_data_packet)) );
+
+ iscsi_scsi_vpd_scsi_target_port_design_dec_inquiry_data_packet *vpd_scsi_target_port_design_desc_inquiry_data_pkt = vpd_scsi_port_design_desc_inquiry_data_pkt->target_desc;
+
+ vpd_scsi_target_port_design_desc_inquiry_data_pkt->protocol_id_code_set = ISCSI_SCSI_VPD_SCSI_TARGET_PORT_DESIGN_DESC_INQUIRY_DATA_PUT_PROTOCOL_ID(ISCSI_SCSI_VPD_SCSI_TARGET_PORT_DESIGN_DESC_INQUIRY_DATA_PROTOCOL_ID_ISCSI) | ISCSI_SCSI_VPD_SCSI_TARGET_PORT_DESIGN_DESC_INQUIRY_DATA_PUT_CODE_SET(ISCSI_SCSI_VPD_SCSI_TARGET_PORT_DESIGN_DESC_INQUIRY_DATA_CODE_SET_UTF8);
+ vpd_scsi_target_port_design_desc_inquiry_data_pkt->flags = (int8_t) (ISCSI_SCSI_VPD_SCSI_TARGET_PORT_DESIGN_DESC_INQUIRY_DATA_FLAGS_PUT_TYPE(ISCSI_SCSI_VPD_SCSI_TARGET_PORT_DESIGN_DESC_INQUIRY_DATA_FLAGS_TYPE_SCSI_NAME) | ISCSI_SCSI_VPD_SCSI_TARGET_PORT_DESIGN_DESC_INQUIRY_DATA_FLAGS_PUT_ASSOC(ISCSI_SCSI_VPD_SCSI_TARGET_PORT_DESIGN_DESC_INQUIRY_DATA_FLAGS_ASSOC_TARGET_PORT) | ISCSI_SCSI_VPD_SCSI_TARGET_PORT_DESIGN_DESC_INQUIRY_DATA_FLAGS_PIV);
+ vpd_scsi_target_port_design_desc_inquiry_data_pkt->reserved = 0U;
+ vpd_scsi_target_port_design_desc_inquiry_data_pkt->len = (uint8_t) iscsi_scsi_emu_pad_scsi_name( vpd_scsi_target_port_design_desc_inquiry_data_pkt->design, port->name );
+
+ port_report_fill->port_entry = (iscsi_scsi_vpd_scsi_port_design_dec_inquiry_data_packet *) (((uint8_t *) vpd_scsi_port_design_desc_inquiry_data_pkt) + len);
+ port_report_fill->alloc_len += len;
+
+ return 0;
+}
+
+/**
+ * @brief Executes an inquiry operation on a DNBD3 image.
+ *
+ * This function also sets the SCSI
+ * status result code accordingly.
+ *
+ * @param[in] image Pointer to DNBD3 image to get
+ * the inquiry data from. May NOT be
+ * NULL, so be careful.
+ * @param[in] scsi_task Pointer to iSCSI SCSI task
+ * responsible for this inqueiry
+ * request. NULL is NOT allowed here,
+ * take caution.
+ * @param[in] cdb_inquiry Pointer to Command Descriptor
+ * Block (CDB) and may NOT be NULL, be
+ * careful.
+ * @param[in] std_inquiry_data_pkt Pointer to standard inquiry
+ * data packet to fill the inquiry
+ * data with.
+ * @param[in] len Length of inquiry result buffer
+ * in bytes.
+ * @return 0 on successful operation, a negative
+ * error code otherwise.
+ */
+static int iscsi_scsi_emu_primary_inquiry(dnbd3_image_t *image, iscsi_scsi_task *scsi_task, const iscsi_scsi_cdb_inquiry *cdb_inquiry, iscsi_scsi_std_inquiry_data_packet *std_inquiry_data_pkt, const uint len)
+{
+ if ( len < sizeof(struct iscsi_scsi_std_inquiry_data_packet) ) {
+ scsi_task->xfer_pos = 0UL;
+
+ iscsi_scsi_task_status_set( scsi_task, ISCSI_SCSI_STATUS_CHECK_COND, ISCSI_SCSI_SENSE_KEY_NO_SENSE, ISCSI_SCSI_ASC_NO_ADDITIONAL_SENSE, ISCSI_SCSI_ASCQ_CAUSE_NOT_REPORTABLE );
+
+ return -1;
+ }
+
+ const int evpd = (cdb_inquiry->lun_flags & ISCSI_SCSI_CDB_INQUIRY_FLAGS_EVPD);
+ const uint pc = cdb_inquiry->page_code;
+
+ if ( (evpd == 0) && (pc != 0U) ) {
+ iscsi_scsi_task_status_set( scsi_task, ISCSI_SCSI_STATUS_CHECK_COND, ISCSI_SCSI_SENSE_KEY_ILLEGAL_REQ, ISCSI_SCSI_ASC_INVALID_FIELD_IN_CDB, ISCSI_SCSI_ASCQ_CAUSE_NOT_REPORTABLE );
+
+ return -1;
+ }
+
+ iscsi_scsi_lun *lun = scsi_task->lun;
+ iscsi_device *device = lun->device;
+ iscsi_port *port = scsi_task->target_port;
+
+ if ( evpd != 0 ) {
+ iscsi_scsi_vpd_page_inquiry_data_packet *vpd_page_inquiry_data_pkt = (iscsi_scsi_vpd_page_inquiry_data_packet *) std_inquiry_data_pkt;
+ int32_t scsi_device_type = iscsi_config_get( (uint8_t *) image->name, ISCSI_GLOBALS_CONFIG_TYPE_SCSI_DEVICE_TYPE );
+ uint alloc_len;
+
+ if ( scsi_device_type < 0L )
+ scsi_device_type = iscsi_config_get( NULL, ISCSI_GLOBALS_CONFIG_TYPE_SCSI_DEVICE_TYPE );
+
+ const uint8_t pti = ISCSI_SCSI_VPD_PAGE_INQUIRY_DATA_PUT_PERIPHERAL_TYPE(scsi_device_type) | ISCSI_SCSI_VPD_PAGE_INQUIRY_DATA_PUT_PERIPHERAL_ID(ISCSI_SCSI_VPD_PAGE_INQUIRY_DATA_PERIPHERAL_ID_POSSIBLE);
+
+ vpd_page_inquiry_data_pkt->peripheral_type_id = pti;
+ vpd_page_inquiry_data_pkt->page_code = (uint8_t) pc;
+
+ switch ( pc ) {
+ case ISCSI_SCSI_VPD_PAGE_INQUIRY_DATA_PAGE_CODE_SUPPORTED_VPD_PAGES : {
+ vpd_page_inquiry_data_pkt->params[0] = ISCSI_SCSI_VPD_PAGE_INQUIRY_DATA_PAGE_CODE_SUPPORTED_VPD_PAGES;
+ vpd_page_inquiry_data_pkt->params[1] = ISCSI_SCSI_VPD_PAGE_INQUIRY_DATA_PAGE_CODE_UNIT_SERIAL_NUMBER;
+ vpd_page_inquiry_data_pkt->params[2] = ISCSI_SCSI_VPD_PAGE_INQUIRY_DATA_PAGE_CODE_DEVICE_ID;
+ vpd_page_inquiry_data_pkt->params[3] = ISCSI_SCSI_VPD_PAGE_INQUIRY_DATA_PAGE_CODE_MANAGEMENT_NETWORK_ADDRS;
+ vpd_page_inquiry_data_pkt->params[4] = ISCSI_SCSI_VPD_PAGE_INQUIRY_DATA_PAGE_CODE_EXTENDED_INQUIRY_DATA;
+ vpd_page_inquiry_data_pkt->params[5] = ISCSI_SCSI_VPD_PAGE_INQUIRY_DATA_PAGE_CODE_MODE_PAGE_POLICY;
+ vpd_page_inquiry_data_pkt->params[6] = ISCSI_SCSI_VPD_PAGE_INQUIRY_DATA_PAGE_CODE_SCSI_PORTS;
+ vpd_page_inquiry_data_pkt->params[7] = ISCSI_SCSI_VPD_PAGE_INQUIRY_DATA_PAGE_CODE_BLOCK_LIMITS;
+ vpd_page_inquiry_data_pkt->params[8] = ISCSI_SCSI_VPD_PAGE_INQUIRY_DATA_PAGE_CODE_BLOCK_DEV_CHARS;
+
+ alloc_len = 9U;
+
+ if ( iscsi_scsi_emu_io_type_is_supported( image, ISCSI_SCSI_EMU_IO_TYPE_UNMAP ) ) {
+ vpd_page_inquiry_data_pkt->params[9] = ISCSI_SCSI_VPD_PAGE_INQUIRY_DATA_PAGE_CODE_THIN_PROVISION;
+
+ alloc_len++;
+ }
+
+ iscsi_put_be16( (uint8_t *) &vpd_page_inquiry_data_pkt->alloc_len, (uint16_t) alloc_len );
+
+ break;
+ }
+ case ISCSI_SCSI_VPD_PAGE_INQUIRY_DATA_PAGE_CODE_UNIT_SERIAL_NUMBER : {
+ const char *name = image->name;
+
+ alloc_len = (uint) strlen( name );
+
+ if ( alloc_len >= (len - sizeof(struct iscsi_scsi_vpd_page_inquiry_data_packet)) )
+ alloc_len = (uint) ((len - sizeof(struct iscsi_scsi_vpd_page_inquiry_data_packet)) - 1U);
+
+ memcpy( vpd_page_inquiry_data_pkt->params, name, alloc_len );
+ memset( (vpd_page_inquiry_data_pkt->params + alloc_len), '\0', (len - alloc_len - sizeof(struct iscsi_scsi_vpd_page_inquiry_data_packet)) );
+
+ alloc_len++;
+
+ iscsi_put_be16( (uint8_t *) &vpd_page_inquiry_data_pkt->alloc_len, (uint16_t) alloc_len );
+
+ break;
+ }
+ case ISCSI_SCSI_VPD_PAGE_INQUIRY_DATA_PAGE_CODE_DEVICE_ID : {
+ const uint dev_name_len = (uint) (strlen( (char *) device->name ) + 1U);
+ const uint port_name_len = (uint) (strlen( (char *) port->name ) + 1U);
+
+ alloc_len = (sizeof(struct iscsi_scsi_vpd_page_design_desc_inquiry_data_packet) + sizeof(struct iscsi_scsi_vpd_page_design_desc_ieee_naa_ext_inquiry_data_packet)); // 64-bit IEEE NAA Extended
+ alloc_len += (sizeof(struct iscsi_scsi_vpd_page_design_desc_inquiry_data_packet) + sizeof(struct iscsi_scsi_vpd_page_design_desc_t10_vendor_id_inquiry_data_packet)); // T10 Vendor ID
+ alloc_len += (uint) (sizeof(struct iscsi_scsi_vpd_page_design_desc_inquiry_data_packet) + ISCSI_ALIGN(dev_name_len, ISCSI_ALIGN_SIZE)); // SCSI Device Name
+ alloc_len += (uint) (sizeof(struct iscsi_scsi_vpd_page_design_desc_inquiry_data_packet) + ISCSI_ALIGN(port_name_len, ISCSI_ALIGN_SIZE)); // SCSI Target Port Name
+ alloc_len += (sizeof(struct iscsi_scsi_vpd_page_design_desc_inquiry_data_packet) + sizeof(struct iscsi_scsi_vpd_page_design_desc_rel_target_port_inquiry_data_packet)); // Relative Target Port
+ alloc_len += (sizeof(struct iscsi_scsi_vpd_page_design_desc_inquiry_data_packet) + sizeof(struct iscsi_scsi_vpd_page_design_desc_target_port_group_inquiry_data_packet)); // Target Port Group
+ alloc_len += (sizeof(struct iscsi_scsi_vpd_page_design_desc_inquiry_data_packet) + sizeof(struct iscsi_scsi_vpd_page_design_desc_logical_unit_group_inquiry_data_packet)); // Logical Unit Group
+
+ if ( len < (alloc_len + sizeof(struct iscsi_scsi_vpd_page_inquiry_data_packet)) ) {
+ iscsi_scsi_task_status_set( scsi_task, ISCSI_SCSI_STATUS_CHECK_COND, ISCSI_SCSI_SENSE_KEY_ILLEGAL_REQ, ISCSI_SCSI_ASC_INVALID_FIELD_IN_CDB, ISCSI_SCSI_ASCQ_CAUSE_NOT_REPORTABLE );
+
+ return -1;
+ }
+
+ iscsi_scsi_vpd_page_design_desc_inquiry_data_packet *vpd_page_design_desc_inquiry_data_pkt = (iscsi_scsi_vpd_page_design_desc_inquiry_data_packet *) vpd_page_inquiry_data_pkt->params;
+
+ vpd_page_design_desc_inquiry_data_pkt->protocol_id_code_set = ISCSI_SCSI_VPD_PAGE_DESIGN_DESC_INQUIRY_DATA_PUT_CODE_SET(ISCSI_SCSI_VPD_PAGE_DESIGN_DESC_INQUIRY_DATA_CODE_SET_BINARY) | ISCSI_SCSI_VPD_PAGE_DESIGN_DESC_INQUIRY_DATA_PUT_PROTOCOL_ID(device->protocol_id);
+ vpd_page_design_desc_inquiry_data_pkt->flags = (int8_t) (ISCSI_SCSI_VPD_PAGE_DESIGN_DESC_INQUIRY_DATA_FLAGS_PUT_TYPE(ISCSI_SCSI_VPD_PAGE_DESIGN_DESC_INQUIRY_DATA_FLAGS_TYPE_NAA) | ISCSI_SCSI_VPD_PAGE_DESIGN_DESC_INQUIRY_DATA_FLAGS_PUT_ASSOC(ISCSI_SCSI_VPD_PAGE_DESIGN_DESC_INQUIRY_DATA_FLAGS_ASSOC_LOGICAL_UNIT) | ISCSI_SCSI_VPD_PAGE_DESIGN_DESC_INQUIRY_DATA_FLAGS_PIV);
+ vpd_page_design_desc_inquiry_data_pkt->reserved = 0U;
+ vpd_page_design_desc_inquiry_data_pkt->len = sizeof(struct iscsi_scsi_vpd_page_design_desc_ieee_naa_ext_inquiry_data_packet);
+
+ iscsi_scsi_emu_naa_ieee_ext_set( (uint64_t *) vpd_page_design_desc_inquiry_data_pkt->desc, (uint8_t *) image->name );
+
+ alloc_len = (sizeof(struct iscsi_scsi_vpd_page_design_desc_inquiry_data_packet) + sizeof(struct iscsi_scsi_vpd_page_design_desc_ieee_naa_ext_inquiry_data_packet));
+
+ vpd_page_design_desc_inquiry_data_pkt = (iscsi_scsi_vpd_page_design_desc_inquiry_data_packet *) (((uint8_t *) vpd_page_design_desc_inquiry_data_pkt) + alloc_len);
+ vpd_page_design_desc_inquiry_data_pkt->protocol_id_code_set = ISCSI_SCSI_VPD_PAGE_DESIGN_DESC_INQUIRY_DATA_PUT_CODE_SET(ISCSI_SCSI_VPD_PAGE_DESIGN_DESC_INQUIRY_DATA_CODE_SET_ASCII) | ISCSI_SCSI_VPD_PAGE_DESIGN_DESC_INQUIRY_DATA_PUT_PROTOCOL_ID(device->protocol_id);
+ vpd_page_design_desc_inquiry_data_pkt->flags = (int8_t) (ISCSI_SCSI_VPD_PAGE_DESIGN_DESC_INQUIRY_DATA_FLAGS_PUT_TYPE(ISCSI_SCSI_VPD_PAGE_DESIGN_DESC_INQUIRY_DATA_FLAGS_TYPE_T10_VENDOR_ID) | ISCSI_SCSI_VPD_PAGE_DESIGN_DESC_INQUIRY_DATA_FLAGS_PUT_ASSOC(ISCSI_SCSI_VPD_PAGE_DESIGN_DESC_INQUIRY_DATA_FLAGS_ASSOC_LOGICAL_UNIT) | ISCSI_SCSI_VPD_PAGE_DESIGN_DESC_INQUIRY_DATA_FLAGS_PIV);
+ vpd_page_design_desc_inquiry_data_pkt->reserved = 0U;
+ vpd_page_design_desc_inquiry_data_pkt->len = sizeof(struct iscsi_scsi_vpd_page_design_desc_t10_vendor_id_inquiry_data_packet);
+
+ iscsi_scsi_vpd_page_design_desc_t10_vendor_id_inquiry_data_packet *vpd_page_design_desc_t10_vendor_id_inquiry_data_pkt = (iscsi_scsi_vpd_page_design_desc_t10_vendor_id_inquiry_data_packet *) vpd_page_design_desc_inquiry_data_pkt->desc;
+
+ iscsi_strcpy_pad( (char *) vpd_page_design_desc_t10_vendor_id_inquiry_data_pkt->vendor_id, ISCSI_SCSI_STD_INQUIRY_DATA_DISK_VENDOR_ID, sizeof(vpd_page_design_desc_t10_vendor_id_inquiry_data_pkt->vendor_id), ' ' );
+ iscsi_strcpy_pad( (char *) vpd_page_design_desc_t10_vendor_id_inquiry_data_pkt->product_id, image->name, sizeof(vpd_page_design_desc_t10_vendor_id_inquiry_data_pkt->product_id), ' ' );
+ iscsi_strcpy_pad( (char *) vpd_page_design_desc_t10_vendor_id_inquiry_data_pkt->unit_serial_num, image->path, sizeof(vpd_page_design_desc_t10_vendor_id_inquiry_data_pkt->unit_serial_num), ' ' );
+
+ alloc_len += (sizeof(struct iscsi_scsi_vpd_page_design_desc_inquiry_data_packet) + sizeof(struct iscsi_scsi_vpd_page_design_desc_t10_vendor_id_inquiry_data_packet));
+
+ vpd_page_design_desc_inquiry_data_pkt = (iscsi_scsi_vpd_page_design_desc_inquiry_data_packet *) (((uint8_t *) vpd_page_design_desc_inquiry_data_pkt) + (sizeof(struct iscsi_scsi_vpd_page_design_desc_inquiry_data_packet) + sizeof(struct iscsi_scsi_vpd_page_design_desc_t10_vendor_id_inquiry_data_packet)));
+ vpd_page_design_desc_inquiry_data_pkt->protocol_id_code_set = ISCSI_SCSI_VPD_PAGE_DESIGN_DESC_INQUIRY_DATA_PUT_CODE_SET(ISCSI_SCSI_VPD_PAGE_DESIGN_DESC_INQUIRY_DATA_CODE_SET_UTF8) | ISCSI_SCSI_VPD_PAGE_DESIGN_DESC_INQUIRY_DATA_PUT_PROTOCOL_ID(device->protocol_id);
+ vpd_page_design_desc_inquiry_data_pkt->flags = (int8_t) (ISCSI_SCSI_VPD_PAGE_DESIGN_DESC_INQUIRY_DATA_FLAGS_PUT_TYPE(ISCSI_SCSI_VPD_PAGE_DESIGN_DESC_INQUIRY_DATA_FLAGS_TYPE_SCSI_NAME) | ISCSI_SCSI_VPD_PAGE_DESIGN_DESC_INQUIRY_DATA_FLAGS_PUT_ASSOC(ISCSI_SCSI_VPD_PAGE_DESIGN_DESC_INQUIRY_DATA_FLAGS_ASSOC_TARGET_DEVICE) | ISCSI_SCSI_VPD_PAGE_DESIGN_DESC_INQUIRY_DATA_FLAGS_PIV);
+ vpd_page_design_desc_inquiry_data_pkt->reserved = 0U;
+ vpd_page_design_desc_inquiry_data_pkt->len = (uint8_t) iscsi_scsi_emu_pad_scsi_name( vpd_page_design_desc_inquiry_data_pkt->desc, device->name );
+
+ alloc_len += (uint) (sizeof(struct iscsi_scsi_vpd_page_design_desc_inquiry_data_packet) + vpd_page_design_desc_inquiry_data_pkt->len);
+
+ vpd_page_design_desc_inquiry_data_pkt = (iscsi_scsi_vpd_page_design_desc_inquiry_data_packet *) (((uint8_t *) vpd_page_design_desc_inquiry_data_pkt) + (sizeof(struct iscsi_scsi_vpd_page_design_desc_inquiry_data_packet) + vpd_page_design_desc_inquiry_data_pkt->len));
+ vpd_page_design_desc_inquiry_data_pkt->protocol_id_code_set = ISCSI_SCSI_VPD_PAGE_DESIGN_DESC_INQUIRY_DATA_PUT_CODE_SET(ISCSI_SCSI_VPD_PAGE_DESIGN_DESC_INQUIRY_DATA_CODE_SET_UTF8) | ISCSI_SCSI_VPD_PAGE_DESIGN_DESC_INQUIRY_DATA_PUT_PROTOCOL_ID(device->protocol_id);
+ vpd_page_design_desc_inquiry_data_pkt->flags = (int8_t) (ISCSI_SCSI_VPD_PAGE_DESIGN_DESC_INQUIRY_DATA_FLAGS_PUT_TYPE(ISCSI_SCSI_VPD_PAGE_DESIGN_DESC_INQUIRY_DATA_FLAGS_TYPE_SCSI_NAME) | ISCSI_SCSI_VPD_PAGE_DESIGN_DESC_INQUIRY_DATA_FLAGS_PUT_ASSOC(ISCSI_SCSI_VPD_PAGE_DESIGN_DESC_INQUIRY_DATA_FLAGS_ASSOC_TARGET_PORT) | ISCSI_SCSI_VPD_PAGE_DESIGN_DESC_INQUIRY_DATA_FLAGS_PIV);
+ vpd_page_design_desc_inquiry_data_pkt->reserved = 0U;
+ vpd_page_design_desc_inquiry_data_pkt->len = (uint8_t) iscsi_scsi_emu_pad_scsi_name( vpd_page_design_desc_inquiry_data_pkt->desc, port->name );
+
+ alloc_len += (uint) (sizeof(struct iscsi_scsi_vpd_page_design_desc_inquiry_data_packet) + vpd_page_design_desc_inquiry_data_pkt->len);
+
+ vpd_page_design_desc_inquiry_data_pkt = (iscsi_scsi_vpd_page_design_desc_inquiry_data_packet *) (((uint8_t *) vpd_page_design_desc_inquiry_data_pkt) + (sizeof(struct iscsi_scsi_vpd_page_design_desc_inquiry_data_packet) + vpd_page_design_desc_inquiry_data_pkt->len));
+ vpd_page_design_desc_inquiry_data_pkt->protocol_id_code_set = ISCSI_SCSI_VPD_PAGE_DESIGN_DESC_INQUIRY_DATA_PUT_CODE_SET(ISCSI_SCSI_VPD_PAGE_DESIGN_DESC_INQUIRY_DATA_CODE_SET_BINARY) | ISCSI_SCSI_VPD_PAGE_DESIGN_DESC_INQUIRY_DATA_PUT_PROTOCOL_ID(device->protocol_id);
+ vpd_page_design_desc_inquiry_data_pkt->flags = (int8_t) (ISCSI_SCSI_VPD_PAGE_DESIGN_DESC_INQUIRY_DATA_FLAGS_PUT_TYPE(ISCSI_SCSI_VPD_PAGE_DESIGN_DESC_INQUIRY_DATA_FLAGS_TYPE_REL_TARGET_PORT) | ISCSI_SCSI_VPD_PAGE_DESIGN_DESC_INQUIRY_DATA_FLAGS_PUT_ASSOC(ISCSI_SCSI_VPD_PAGE_DESIGN_DESC_INQUIRY_DATA_FLAGS_ASSOC_TARGET_PORT) | ISCSI_SCSI_VPD_PAGE_DESIGN_DESC_INQUIRY_DATA_FLAGS_PIV);
+ vpd_page_design_desc_inquiry_data_pkt->reserved = 0U;
+ vpd_page_design_desc_inquiry_data_pkt->len = sizeof(struct iscsi_scsi_vpd_page_design_desc_rel_target_port_inquiry_data_packet);
+
+ iscsi_scsi_vpd_page_design_desc_rel_target_port_inquiry_data_packet *vpd_page_design_desc_rel_target_port_inquiry_data_pkt = (iscsi_scsi_vpd_page_design_desc_rel_target_port_inquiry_data_packet *) vpd_page_design_desc_inquiry_data_pkt->desc;
+
+ vpd_page_design_desc_rel_target_port_inquiry_data_pkt->reserved = 0U;
+ iscsi_put_be16( (uint8_t *) &vpd_page_design_desc_rel_target_port_inquiry_data_pkt->index, port->index );
+
+ alloc_len += (sizeof(struct iscsi_scsi_vpd_page_design_desc_inquiry_data_packet) + sizeof(struct iscsi_scsi_vpd_page_design_desc_rel_target_port_inquiry_data_packet));
+
+ vpd_page_design_desc_inquiry_data_pkt = (iscsi_scsi_vpd_page_design_desc_inquiry_data_packet *) (((uint8_t *) vpd_page_design_desc_inquiry_data_pkt) + (sizeof(struct iscsi_scsi_vpd_page_design_desc_inquiry_data_packet) + sizeof(struct iscsi_scsi_vpd_page_design_desc_rel_target_port_inquiry_data_packet)));
+ vpd_page_design_desc_inquiry_data_pkt->protocol_id_code_set = ISCSI_SCSI_VPD_PAGE_DESIGN_DESC_INQUIRY_DATA_PUT_CODE_SET(ISCSI_SCSI_VPD_PAGE_DESIGN_DESC_INQUIRY_DATA_CODE_SET_BINARY) | ISCSI_SCSI_VPD_PAGE_DESIGN_DESC_INQUIRY_DATA_PUT_PROTOCOL_ID(device->protocol_id);
+ vpd_page_design_desc_inquiry_data_pkt->flags = (int8_t) (ISCSI_SCSI_VPD_PAGE_DESIGN_DESC_INQUIRY_DATA_FLAGS_PUT_TYPE(ISCSI_SCSI_VPD_PAGE_DESIGN_DESC_INQUIRY_DATA_FLAGS_TYPE_TARGET_PORT_GROUP) | ISCSI_SCSI_VPD_PAGE_DESIGN_DESC_INQUIRY_DATA_FLAGS_PUT_ASSOC(ISCSI_SCSI_VPD_PAGE_DESIGN_DESC_INQUIRY_DATA_FLAGS_ASSOC_TARGET_PORT) | ISCSI_SCSI_VPD_PAGE_DESIGN_DESC_INQUIRY_DATA_FLAGS_PIV);
+ vpd_page_design_desc_inquiry_data_pkt->reserved = 0U;
+ vpd_page_design_desc_inquiry_data_pkt->len = sizeof(struct iscsi_scsi_vpd_page_design_desc_target_port_group_inquiry_data_packet);
+
+ iscsi_scsi_vpd_page_design_desc_target_port_group_inquiry_data_packet *vpd_page_design_desc_target_port_group_inquiry_data_pkt = (iscsi_scsi_vpd_page_design_desc_target_port_group_inquiry_data_packet *) vpd_page_design_desc_inquiry_data_pkt->desc;
+
+ vpd_page_design_desc_target_port_group_inquiry_data_pkt->reserved = 0U;
+ vpd_page_design_desc_target_port_group_inquiry_data_pkt->index = 0U;
+
+ alloc_len += (sizeof(struct iscsi_scsi_vpd_page_design_desc_inquiry_data_packet) + sizeof(struct iscsi_scsi_vpd_page_design_desc_target_port_group_inquiry_data_packet));
+
+ vpd_page_design_desc_inquiry_data_pkt = (iscsi_scsi_vpd_page_design_desc_inquiry_data_packet *) (((uint8_t *) vpd_page_design_desc_inquiry_data_pkt) + (sizeof(struct iscsi_scsi_vpd_page_design_desc_inquiry_data_packet) + sizeof(struct iscsi_scsi_vpd_page_design_desc_target_port_group_inquiry_data_packet)));
+ vpd_page_design_desc_inquiry_data_pkt->protocol_id_code_set = ISCSI_SCSI_VPD_PAGE_DESIGN_DESC_INQUIRY_DATA_PUT_CODE_SET(ISCSI_SCSI_VPD_PAGE_DESIGN_DESC_INQUIRY_DATA_CODE_SET_BINARY) | ISCSI_SCSI_VPD_PAGE_DESIGN_DESC_INQUIRY_DATA_PUT_PROTOCOL_ID(device->protocol_id);
+ vpd_page_design_desc_inquiry_data_pkt->flags = (int8_t) (ISCSI_SCSI_VPD_PAGE_DESIGN_DESC_INQUIRY_DATA_FLAGS_PUT_TYPE(ISCSI_SCSI_VPD_PAGE_DESIGN_DESC_INQUIRY_DATA_FLAGS_TYPE_LOGICAL_UNIT_GROUP) | ISCSI_SCSI_VPD_PAGE_DESIGN_DESC_INQUIRY_DATA_FLAGS_PUT_ASSOC(ISCSI_SCSI_VPD_PAGE_DESIGN_DESC_INQUIRY_DATA_FLAGS_ASSOC_LOGICAL_UNIT) | ISCSI_SCSI_VPD_PAGE_DESIGN_DESC_INQUIRY_DATA_FLAGS_PIV);
+ vpd_page_design_desc_inquiry_data_pkt->reserved = 0U;
+ vpd_page_design_desc_inquiry_data_pkt->len = sizeof(struct iscsi_scsi_vpd_page_design_desc_logical_unit_group_inquiry_data_packet);
+
+ iscsi_scsi_vpd_page_design_desc_logical_unit_group_inquiry_data_packet *vpd_page_design_desc_logical_unit_group_inquiry_data_pkt = (iscsi_scsi_vpd_page_design_desc_logical_unit_group_inquiry_data_packet *) vpd_page_design_desc_inquiry_data_pkt->desc;
+
+ vpd_page_design_desc_logical_unit_group_inquiry_data_pkt->reserved = 0U;
+ iscsi_put_be16( (uint8_t *) &vpd_page_design_desc_logical_unit_group_inquiry_data_pkt->id, (uint16_t) device->id );
+
+ alloc_len += (sizeof(struct iscsi_scsi_vpd_page_design_desc_inquiry_data_packet) + sizeof(struct iscsi_scsi_vpd_page_design_desc_logical_unit_group_inquiry_data_packet));
+
+ iscsi_put_be16( (uint8_t *) &vpd_page_inquiry_data_pkt->alloc_len, (uint16_t) alloc_len );
+
+ break;
+ }
+ case ISCSI_SCSI_VPD_PAGE_INQUIRY_DATA_PAGE_CODE_EXTENDED_INQUIRY_DATA : {
+ iscsi_scsi_vpd_page_ext_inquiry_data_packet *vpd_page_ext_inquiry_data_pkt = (iscsi_scsi_vpd_page_ext_inquiry_data_packet *) vpd_page_inquiry_data_pkt;
+
+ alloc_len = (sizeof(iscsi_scsi_vpd_page_ext_inquiry_data_packet) - sizeof(iscsi_scsi_vpd_page_inquiry_data_packet));
+
+ if ( len < (alloc_len + sizeof(struct iscsi_scsi_vpd_page_inquiry_data_packet)) ) {
+ iscsi_scsi_task_status_set( scsi_task, ISCSI_SCSI_STATUS_CHECK_COND, ISCSI_SCSI_SENSE_KEY_ILLEGAL_REQ, ISCSI_SCSI_ASC_INVALID_FIELD_IN_CDB, ISCSI_SCSI_ASCQ_CAUSE_NOT_REPORTABLE );
+
+ return -1;
+ }
+
+ vpd_page_ext_inquiry_data_pkt->reserved = 0U;
+ vpd_page_ext_inquiry_data_pkt->page_len = (uint8_t) alloc_len;
+ vpd_page_ext_inquiry_data_pkt->check_flags = 0;
+ vpd_page_ext_inquiry_data_pkt->support_flags = (ISCSI_SCSI_VPD_PAGE_EXT_INQUIRY_DATA_SUPPORT_FLAGS_SIMPSUP | ISCSI_SCSI_VPD_PAGE_EXT_INQUIRY_DATA_SUPPORT_FLAGS_HEADSUP);
+ vpd_page_ext_inquiry_data_pkt->support_flags_2 = 0;
+ vpd_page_ext_inquiry_data_pkt->luiclr = 0U;
+ vpd_page_ext_inquiry_data_pkt->cbcs = 0U;
+ vpd_page_ext_inquiry_data_pkt->micro_dl = 0U;
+ vpd_page_ext_inquiry_data_pkt->reserved2[0] = 0ULL;
+ vpd_page_ext_inquiry_data_pkt->reserved2[1] = 0ULL;
+ vpd_page_ext_inquiry_data_pkt->reserved2[2] = 0ULL;
+ vpd_page_ext_inquiry_data_pkt->reserved2[3] = 0ULL;
+ vpd_page_ext_inquiry_data_pkt->reserved2[4] = 0ULL;
+ vpd_page_ext_inquiry_data_pkt->reserved2[5] = 0ULL;
+ vpd_page_ext_inquiry_data_pkt->reserved3 = 0UL;
+ vpd_page_ext_inquiry_data_pkt->reserved4 = 0U;
+
+ iscsi_put_be16( (uint8_t *) &vpd_page_inquiry_data_pkt->alloc_len, (uint16_t) alloc_len );
+
+ break;
+ }
+ case ISCSI_SCSI_VPD_PAGE_INQUIRY_DATA_PAGE_CODE_MANAGEMENT_NETWORK_ADDRS : {
+ alloc_len = 0U;
+
+ vpd_page_inquiry_data_pkt->alloc_len = 0U;
+
+ break;
+ }
+ case ISCSI_SCSI_VPD_PAGE_INQUIRY_DATA_PAGE_CODE_MODE_PAGE_POLICY : {
+ iscsi_scsi_vpd_mode_page_policy_desc_inquiry_data_packet *vpd_page_mode_page_policy_desc_inquiry_data_pkt = (iscsi_scsi_vpd_mode_page_policy_desc_inquiry_data_packet *) vpd_page_inquiry_data_pkt->params;
+
+ alloc_len = sizeof(struct iscsi_scsi_vpd_mode_page_policy_desc_inquiry_data_packet);
+
+ vpd_page_mode_page_policy_desc_inquiry_data_pkt->page_code = ISCSI_SCSI_VPD_MODE_PAGE_POLICY_DESC_INQUIRY_DATA_POLICY_PAGE_CODE_MASK;
+ vpd_page_mode_page_policy_desc_inquiry_data_pkt->sub_page_code = 0xFFU;
+ vpd_page_mode_page_policy_desc_inquiry_data_pkt->flags = 0U;
+ vpd_page_mode_page_policy_desc_inquiry_data_pkt->reserved = 0U;
+
+ iscsi_put_be16( (uint8_t *) &vpd_page_inquiry_data_pkt->alloc_len, (uint16_t) alloc_len );
+
+ break;
+ }
+ case ISCSI_SCSI_VPD_PAGE_INQUIRY_DATA_PAGE_CODE_SCSI_PORTS : {
+ iscsi_scsi_emu_primary_inquiry_ports_fill port_report_fill = {(iscsi_scsi_vpd_scsi_port_design_dec_inquiry_data_packet *) vpd_page_inquiry_data_pkt->params, 0U, (uint) (len - sizeof(struct iscsi_scsi_vpd_page_inquiry_data_packet))};
+ const int rc = iscsi_hashmap_iterate( device->ports, iscsi_scsi_emu_primary_inquiry_callback, (uint8_t *) &port_report_fill );
+
+ if ( rc < 0 ) {
+ iscsi_scsi_task_status_set( scsi_task, ISCSI_SCSI_STATUS_CHECK_COND, ISCSI_SCSI_SENSE_KEY_ILLEGAL_REQ, ISCSI_SCSI_ASC_INVALID_FIELD_IN_CDB, ISCSI_SCSI_ASCQ_CAUSE_NOT_REPORTABLE );
+
+ return -1;
+ }
+
+ alloc_len = port_report_fill.alloc_len;
+
+ iscsi_put_be16( (uint8_t *) &vpd_page_inquiry_data_pkt->alloc_len, (uint16_t) alloc_len );
+
+ break;
+ }
+ case ISCSI_SCSI_VPD_PAGE_INQUIRY_DATA_PAGE_CODE_BLOCK_LIMITS : {
+ iscsi_scsi_vpd_page_block_limits_inquiry_data_packet *vpd_page_block_limits_inquiry_data_pkt = (iscsi_scsi_vpd_page_block_limits_inquiry_data_packet *) vpd_page_inquiry_data_pkt->params;
+
+ if ( len < (sizeof(struct iscsi_scsi_vpd_page_inquiry_data_packet) + sizeof(struct iscsi_scsi_vpd_page_block_limits_inquiry_data_packet)) ) {
+ iscsi_scsi_task_status_set( scsi_task, ISCSI_SCSI_STATUS_CHECK_COND, ISCSI_SCSI_SENSE_KEY_ILLEGAL_REQ, ISCSI_SCSI_ASC_INVALID_FIELD_IN_CDB, ISCSI_SCSI_ASCQ_CAUSE_NOT_REPORTABLE );
+
+ return -1;
+ }
+
+ alloc_len = sizeof(struct iscsi_scsi_vpd_page_block_limits_inquiry_data_packet);
+
+ vpd_page_block_limits_inquiry_data_pkt->flags = 0;
+
+ uint32_t blocks = (ISCSI_SCSI_EMU_MAX_XFER_LEN >> iscsi_scsi_emu_block_get_size_shift( image ));
+
+ if ( blocks > 255UL )
+ blocks = 255UL;
+
+ vpd_page_block_limits_inquiry_data_pkt->max_cmp_write_len = (uint8_t) blocks;
+
+ uint32_t optimal_blocks = ISCSI_SCSI_EMU_BLOCK_SIZE >> iscsi_scsi_emu_block_get_size_shift( image );
+
+ if ( optimal_blocks == 0UL )
+ optimal_blocks = 1UL;
+
+ iscsi_put_be16( (uint8_t *) &vpd_page_block_limits_inquiry_data_pkt->optimal_granularity_xfer_len, (uint16_t) optimal_blocks );
+ iscsi_put_be32( (uint8_t *) &vpd_page_block_limits_inquiry_data_pkt->max_xfer_len, blocks );
+ iscsi_put_be32( (uint8_t *) &vpd_page_block_limits_inquiry_data_pkt->optimal_xfer_len, blocks );
+ vpd_page_block_limits_inquiry_data_pkt->max_prefetch_len = 0UL;
+
+ if ( iscsi_scsi_emu_io_type_is_supported( image, ISCSI_SCSI_EMU_IO_TYPE_UNMAP ) ) {
+ iscsi_put_be32( (uint8_t *) &vpd_page_block_limits_inquiry_data_pkt->max_unmap_lba_cnt, ISCSI_SCSI_EMU_MAX_UNMAP_LBA_COUNT );
+ iscsi_put_be32( (uint8_t *) &vpd_page_block_limits_inquiry_data_pkt->max_unmap_block_desc_cnt, ISCSI_SCSI_EMU_MAX_UNMAP_BLOCK_DESC_COUNT );
+ } else {
+ vpd_page_block_limits_inquiry_data_pkt->max_unmap_lba_cnt = 0UL;
+ vpd_page_block_limits_inquiry_data_pkt->max_unmap_block_desc_cnt = 0UL;
+ }
+
+ vpd_page_block_limits_inquiry_data_pkt->optimal_unmap_granularity = 0UL;
+ vpd_page_block_limits_inquiry_data_pkt->unmap_granularity_align_ugavalid = 0UL;
+ iscsi_put_be64( (uint8_t *) &vpd_page_block_limits_inquiry_data_pkt->max_write_same_len, blocks );
+ vpd_page_block_limits_inquiry_data_pkt->reserved[0] = 0ULL;
+ vpd_page_block_limits_inquiry_data_pkt->reserved[1] = 0ULL;
+ vpd_page_block_limits_inquiry_data_pkt->reserved2 = 0UL;
+
+ iscsi_put_be16( (uint8_t *) &vpd_page_inquiry_data_pkt->alloc_len, (uint16_t) alloc_len );
+
+ break;
+ }
+ case ISCSI_SCSI_VPD_PAGE_INQUIRY_DATA_PAGE_CODE_BLOCK_DEV_CHARS : {
+ iscsi_scsi_vpd_page_block_dev_chars_inquiry_data_packet *vpd_page_block_dev_chars_inquiry_data_pkt = (iscsi_scsi_vpd_page_block_dev_chars_inquiry_data_packet *) vpd_page_inquiry_data_pkt->params;
+
+ if ( len < (sizeof(struct iscsi_scsi_vpd_page_inquiry_data_packet) + sizeof(struct iscsi_scsi_vpd_page_block_dev_chars_inquiry_data_packet)) ) {
+ iscsi_scsi_task_status_set( scsi_task, ISCSI_SCSI_STATUS_CHECK_COND, ISCSI_SCSI_SENSE_KEY_ILLEGAL_REQ, ISCSI_SCSI_ASC_INVALID_FIELD_IN_CDB, ISCSI_SCSI_ASCQ_CAUSE_NOT_REPORTABLE );
+
+ return -1;
+ }
+
+ alloc_len = sizeof(struct iscsi_scsi_vpd_page_block_dev_chars_inquiry_data_packet);
+
+ vpd_page_block_dev_chars_inquiry_data_pkt->medium_rotation_rate = (iscsi_scsi_emu_io_type_is_supported( image, ISCSI_SCSI_EMU_IO_TYPE_NO_ROTATION ) ? ISCSI_SCSI_VPD_PAGE_BLOCK_DEV_CHARS_INQUIRY_DATA_MEDIUM_ROTATION_RATE_NONE : ISCSI_SCSI_VPD_PAGE_BLOCK_DEV_CHARS_INQUIRY_DATA_MEDIUM_ROTATION_RATE_NOT_REPORTED);
+ vpd_page_block_dev_chars_inquiry_data_pkt->product_type = ISCSI_SCSI_VPD_PAGE_BLOCK_DEV_CHARS_INQUIRY_DATA_PRODUCT_TYPE_NOT_INDICATED;
+ vpd_page_block_dev_chars_inquiry_data_pkt->flags = ISCSI_SCSI_VPD_PAGE_BLOCK_DEV_CHARS_INQUIRY_DATA_FLAGS_PUT_NOMINAL_FORM_FACTOR(ISCSI_SCSI_VPD_PAGE_BLOCK_DEV_CHARS_INQUIRY_DATA_FLAGS_NOMINAL_FORM_FACTOR_NOT_REPORTED);
+ vpd_page_block_dev_chars_inquiry_data_pkt->support_flags = 0U;
+ vpd_page_block_dev_chars_inquiry_data_pkt->reserved[0] = 0ULL;
+ vpd_page_block_dev_chars_inquiry_data_pkt->reserved[1] = 0ULL;
+ vpd_page_block_dev_chars_inquiry_data_pkt->reserved[2] = 0ULL;
+ vpd_page_block_dev_chars_inquiry_data_pkt->reserved[3] = 0ULL;
+ vpd_page_block_dev_chars_inquiry_data_pkt->reserved[4] = 0ULL;
+ vpd_page_block_dev_chars_inquiry_data_pkt->reserved[5] = 0ULL;
+ vpd_page_block_dev_chars_inquiry_data_pkt->reserved2 = 0UL;
+ vpd_page_block_dev_chars_inquiry_data_pkt->reserved3 = 0U;
+ vpd_page_block_dev_chars_inquiry_data_pkt->reserved4 = 0U;
+
+ iscsi_put_be16( (uint8_t *) &vpd_page_inquiry_data_pkt->alloc_len, (uint16_t) alloc_len );
+
+ break;
+ }
+ case ISCSI_SCSI_VPD_PAGE_INQUIRY_DATA_PAGE_CODE_THIN_PROVISION : {
+ if ( !iscsi_scsi_emu_io_type_is_supported( image, ISCSI_SCSI_EMU_IO_TYPE_UNMAP ) ) {
+ scsi_task->xfer_pos = 0UL;
+
+ iscsi_scsi_task_status_set( scsi_task, ISCSI_SCSI_STATUS_CHECK_COND, ISCSI_SCSI_SENSE_KEY_NO_SENSE, ISCSI_SCSI_ASC_NO_ADDITIONAL_SENSE, ISCSI_SCSI_ASCQ_CAUSE_NOT_REPORTABLE );
+
+ return -1;
+ }
+
+ iscsi_scsi_vpd_page_thin_provision_inquiry_data_packet *vpd_page_thin_provision_inquiry_data_pkt = (iscsi_scsi_vpd_page_thin_provision_inquiry_data_packet *) vpd_page_inquiry_data_pkt->params;
+
+ alloc_len = sizeof(struct iscsi_scsi_vpd_page_thin_provision_inquiry_data_packet);
+
+ vpd_page_thin_provision_inquiry_data_pkt->threshold_exponent = 0U;
+ vpd_page_thin_provision_inquiry_data_pkt->flags = (int8_t) ISCSI_SCSI_VPD_PAGE_THIN_PROVISION_INQUIRY_DATA_FLAGS_LBPU;
+ vpd_page_thin_provision_inquiry_data_pkt->provision_type = ISCSI_SCSI_VPD_PAGE_THIN_PROVISION_INQUIRY_DATA_PUT_PROVISION_TYPE(ISCSI_SCSI_VPD_PAGE_THIN_PROVISION_INQUIRY_DATA_PROVISION_TYPE_THIN_PROVISIONING);
+ vpd_page_thin_provision_inquiry_data_pkt->reserved = 0U;
+
+ iscsi_put_be16( (uint8_t *) &vpd_page_inquiry_data_pkt->alloc_len, (uint16_t) alloc_len );
+
+ break;
+ }
+ default : {
+ scsi_task->xfer_pos = 0UL;
+
+ iscsi_scsi_task_status_set( scsi_task, ISCSI_SCSI_STATUS_CHECK_COND, ISCSI_SCSI_SENSE_KEY_NO_SENSE, ISCSI_SCSI_ASC_NO_ADDITIONAL_SENSE, ISCSI_SCSI_ASCQ_CAUSE_NOT_REPORTABLE );
+
+ return -1;
+
+ break;
+ }
+ }
+
+ return (int) (alloc_len + sizeof(struct iscsi_scsi_vpd_page_inquiry_data_packet));
+ } else {
+ int32_t scsi_device_type = iscsi_config_get( (uint8_t *) image->name, ISCSI_GLOBALS_CONFIG_TYPE_SCSI_DEVICE_TYPE );
+ uint alloc_len;
+
+ if ( scsi_device_type < 0L )
+ scsi_device_type = iscsi_config_get( NULL, ISCSI_GLOBALS_CONFIG_TYPE_SCSI_DEVICE_TYPE );
+
+ const uint8_t pti = ISCSI_SCSI_BASIC_INQUIRY_DATA_PUT_PERIPHERAL_TYPE(scsi_device_type) | ISCSI_SCSI_BASIC_INQUIRY_DATA_PUT_PERIPHERAL_ID(ISCSI_SCSI_BASIC_INQUIRY_DATA_PERIPHERAL_ID_POSSIBLE);
+
+ std_inquiry_data_pkt->basic_inquiry.peripheral_type_id = pti;
+ std_inquiry_data_pkt->basic_inquiry.peripheral_type_mod_flags = (int8_t) (iscsi_scsi_emu_io_type_is_supported( image, ISCSI_SCSI_EMU_IO_TYPE_REMOVABLE ) ? ISCSI_SCSI_BASIC_INQUIRY_DATA_PERIPHERAL_TYPE_MOD_FLAGS_REMOVABLE_MEDIA : 0);
+ std_inquiry_data_pkt->basic_inquiry.version = ISCSI_SCSI_BASIC_INQUIRY_DATA_PUT_VERSION_ANSI(ISCSI_SCSI_BASIC_INQUIRY_DATA_VERSION_ANSI_SPC3);
+ std_inquiry_data_pkt->basic_inquiry.response_data_fmt_flags = ISCSI_SCSI_BASIC_INQUIRY_DATA_PUT_RESPONSE_DATA_FMT_FLAGS(ISCSI_SCSI_BASIC_INQUIRY_DATA_RESPONSE_DATA_FMT_FLAGS_SCSI_2) | ISCSI_SCSI_BASIC_INQUIRY_DATA_RESPONSE_DATA_FMT_FLAGS_HISUP;
+
+ std_inquiry_data_pkt->tpgs_flags = 0U;
+ std_inquiry_data_pkt->services_flags = ISCSI_SCSI_STD_INQUIRY_DATA_SERVICES_FLAGS_MULTIP;
+ std_inquiry_data_pkt->flags = ISCSI_SCSI_STD_INQUIRY_DATA_FLAGS_COMMAND_QUEUE;
+
+ iscsi_strcpy_pad( (char *) std_inquiry_data_pkt->vendor_id, ISCSI_SCSI_STD_INQUIRY_DATA_DISK_VENDOR_ID, sizeof(std_inquiry_data_pkt->vendor_id), ' ' );
+ iscsi_strcpy_pad( (char *) std_inquiry_data_pkt->product_id, image->name, sizeof(std_inquiry_data_pkt->product_id), ' ' );
+
+ char image_rev[sizeof(std_inquiry_data_pkt->product_rev_level) + 1];
+
+ sprintf( image_rev, "%04" PRIX16, image->rid );
+ iscsi_strcpy_pad( (char *) std_inquiry_data_pkt->product_rev_level, image_rev, sizeof(std_inquiry_data_pkt->product_rev_level), ' ' );
+
+ uint add_len = (sizeof(struct iscsi_scsi_std_inquiry_data_packet) - sizeof(struct iscsi_scsi_basic_inquiry_data_packet));
+ iscsi_scsi_ext_inquiry_data_packet *ext_inquiry_data_pkt = (iscsi_scsi_ext_inquiry_data_packet *) std_inquiry_data_pkt;
+
+ if ( len >= ISCSI_NEXT_OFFSET(iscsi_scsi_ext_inquiry_data_packet, vendor_spec) ) {
+ iscsi_strcpy_pad( (char *) ext_inquiry_data_pkt->vendor_spec, ISCSI_SCSI_EXT_INQUIRY_DATA_VENDOR_SPEC_ID, sizeof(ext_inquiry_data_pkt->vendor_spec), ' ' );
+
+ add_len += sizeof(ext_inquiry_data_pkt->vendor_spec);
+ }
+
+ if ( len >= ISCSI_NEXT_OFFSET(iscsi_scsi_ext_inquiry_data_packet, flags) ) {
+ ext_inquiry_data_pkt->flags = 0;
+
+ add_len += sizeof(ext_inquiry_data_pkt->flags);
+ }
+
+ if ( len >= ISCSI_NEXT_OFFSET(iscsi_scsi_ext_inquiry_data_packet, reserved) ) {
+ ext_inquiry_data_pkt->reserved = 0U;
+
+ add_len += sizeof(ext_inquiry_data_pkt->reserved);
+ }
+
+ if ( len >= ISCSI_NEXT_OFFSET(iscsi_scsi_ext_inquiry_data_packet, version_desc[0]) ) {
+ iscsi_put_be16( (uint8_t *) &ext_inquiry_data_pkt->version_desc[0], ISCSI_SCSI_EXT_INQUIRY_DATA_VERSION_DESC_ISCSI_NO_VERSION );
+
+ add_len += sizeof(ext_inquiry_data_pkt->version_desc[0]);
+ }
+
+ if ( len >= ISCSI_NEXT_OFFSET(iscsi_scsi_ext_inquiry_data_packet, version_desc[1]) ) {
+ iscsi_put_be16( (uint8_t *) &ext_inquiry_data_pkt->version_desc[1], ISCSI_SCSI_EXT_INQUIRY_DATA_VERSION_DESC_SPC3_NO_VERSION );
+
+ add_len += sizeof(ext_inquiry_data_pkt->version_desc[1]);
+ }
+
+ if ( len >= ISCSI_NEXT_OFFSET(iscsi_scsi_ext_inquiry_data_packet, version_desc[2]) ) {
+ iscsi_put_be16( (uint8_t *) &ext_inquiry_data_pkt->version_desc[2], ISCSI_SCSI_EXT_INQUIRY_DATA_VERSION_DESC_SBC2_NO_VERSION );
+
+ add_len += sizeof(ext_inquiry_data_pkt->version_desc[2]);
+ }
+
+ if ( len >= ISCSI_NEXT_OFFSET(iscsi_scsi_ext_inquiry_data_packet, version_desc[3]) ) {
+ iscsi_put_be16( (uint8_t *) &ext_inquiry_data_pkt->version_desc[3], ISCSI_SCSI_EXT_INQUIRY_DATA_VERSION_DESC_SAM2_NO_VERSION );
+
+ add_len += sizeof(ext_inquiry_data_pkt->version_desc[3]);
+ }
+
+ if ( len >= ISCSI_NEXT_OFFSET(iscsi_scsi_ext_inquiry_data_packet, version_desc[4]) ) {
+ uint alloc_len = (uint) (len - offsetof(iscsi_scsi_ext_inquiry_data_packet, version_desc[4]));
+
+ if ( alloc_len > (sizeof(struct iscsi_scsi_ext_inquiry_data_packet) - offsetof(iscsi_scsi_ext_inquiry_data_packet, version_desc[4])) )
+ alloc_len = (sizeof(struct iscsi_scsi_ext_inquiry_data_packet) - offsetof(iscsi_scsi_ext_inquiry_data_packet, version_desc[4]));
+
+ memset( &ext_inquiry_data_pkt->version_desc[4], 0, alloc_len );
+ add_len += alloc_len;
+ }
+
+ std_inquiry_data_pkt->basic_inquiry.add_len = (uint8_t) add_len;
+
+ return (int) (add_len + sizeof(struct iscsi_scsi_basic_inquiry_data_packet));
+ }
+}
+
+/**
+ * @brief Fills in a single LUN entry of a report LUNs operation on a DNBD3 image.
+ *
+ * Callback function for each element while iterating
+ * through the iSCSI SCSI LUNs hash map.\n
+ * The iteration process is aborted when the
+ * remaining allocation length is not enough
+ * to hold the current LUN.
+ *
+ * @param[in] key Pointer to zero padded key. NULL is
+ * an invalid pointer here, so be careful.
+ * @param[in] key_size Number of bytes for the key.
+ * @param[in] value Value of the key, NULL creates an
+ * empty key assignment.
+ * @param[in,out] user_data Pointer to a data structure
+ * containing the report LUN list, the
+ * current report LUN entry, the total
+ * length of all LUN entries in bytes, the
+ * remaining allocation length in bytes and
+ * the selected report. May NOT be NULL, so
+ * be careful.
+ * @retval -1 Operation failure, ran out of
+ * allocation space during traversal.
+ * @retval 0 Successful operation, there is enough
+ * allocation space to store this
+ * reported LUN entry.
+ */
+int iscsi_scsi_emu_primary_report_luns_callback(uint8_t *key, const size_t key_size, uint8_t *value, uint8_t *user_data)
+{
+ iscsi_scsi_emu_primary_report_luns_fill *lun_report_fill = (iscsi_scsi_emu_primary_report_luns_fill *) user_data;
+ iscsi_scsi_lun *scsi_lun = (iscsi_scsi_lun *) value;
+
+ lun_report_fill->alloc_len -= (uint) sizeof(struct iscsi_scsi_report_luns_parameter_data_lun_entry_packet);
+
+ if ( (int) lun_report_fill->alloc_len < 0 )
+ return -1;
+
+ lun_report_fill->len += (uint) sizeof(struct iscsi_scsi_report_luns_parameter_data_lun_entry_packet);
+
+ const uint64_t lun = iscsi_scsi_lun_get_from_scsi( scsi_lun->id );
+ iscsi_put_be64( (uint8_t *) &lun_report_fill->lun_entry->lun, lun );
+
+ lun_report_fill->lun_entry++;
+
+ return 0;
+}
+
+/**
+ * @brief Executes a report LUNs operation on a DNBD3 image.
+ *
+ * This function also sets the SCSI
+ * status result code accordingly.
+ *
+ * @param[in] lun Pointer to iSCSI SCSI LUN to
+ * report the LUNs for. May NOT be
+ * NULL, so be careful.
+ * @param[in] report_luns_parameter_data_pkt Pointer to report LUNS
+ * parameter data packet to fill the
+ * LUN data data with.
+ * @param[in] len Length of LUN reporting result buffer
+ * in bytes.
+ * @param[in] select_report Selected report.
+ * @return Total length of LUN data on successful
+ * operation, a negative error code
+ * otherwise.
+ */
+static int iscsi_scsi_emu_primary_report_luns(iscsi_scsi_lun *lun, iscsi_scsi_report_luns_parameter_data_lun_list_packet *report_luns_parameter_data_pkt, const uint len, const uint select_report)
+{
+ if ( len < sizeof(struct iscsi_scsi_report_luns_parameter_data_lun_list_packet) )
+ return -1;
+
+ switch ( select_report ) {
+ case ISCSI_SCSI_CDB_REPORT_LUNS_SELECT_REPORT_LU_ADDR_METHOD : {
+ break;
+ }
+ case ISCSI_SCSI_CDB_REPORT_LUNS_SELECT_REPORT_LU_KNOWN : {
+ break;
+ }
+ case ISCSI_SCSI_CDB_REPORT_LUNS_SELECT_REPORT_LU_ALL : {
+ break;
+ }
+ default : {
+ return -1;
+
+ break;
+ }
+ }
+
+ report_luns_parameter_data_pkt->lun_list_len = 0UL;
+ report_luns_parameter_data_pkt->reserved = 0UL;
+
+ iscsi_scsi_emu_primary_report_luns_fill lun_report_fill = {report_luns_parameter_data_pkt, (iscsi_scsi_report_luns_parameter_data_lun_entry_packet *) (report_luns_parameter_data_pkt + 1), 0U, (uint) (len - sizeof(struct iscsi_scsi_report_luns_parameter_data_lun_list_packet)), select_report };
+
+ pthread_rwlock_rdlock( &lun->device->luns_rwlock );
+
+ const int rc = iscsi_hashmap_iterate( lun->device->luns, iscsi_scsi_emu_primary_report_luns_callback, (uint8_t *) &lun_report_fill );
+
+ pthread_rwlock_unlock( &lun->device->luns_rwlock );
+
+ if ( rc < 0 )
+ return -1;
+
+ iscsi_put_be32( (uint8_t *) &report_luns_parameter_data_pkt->lun_list_len, lun_report_fill.len );
+
+ return (int) (lun_report_fill.len + sizeof(struct iscsi_scsi_report_luns_parameter_data_lun_list_packet));
+}
+
+/**
+ * @brief Initializes a mode sense page or sub page and zero fills the parameter data.
+ *
+ * This function also sets the correct
+ * page length and flags either for
+ * the page or sub page. If a sub page
+ * is initialized, the sub page code
+ * will also be set.
+ *
+ * @param[in] mode_sense_mode_page_pkt Pointer to mode sense parameter
+ * mode page or sub page data packet
+ * to initialize. If this is NULL,
+ * this function does nothing.
+ * @param[in] len Length in bytes to initialize with zeroes.
+ * @param[in] page Page code.
+ * @param[in] sub_page Sub page code.
+ */
+static void iscsi_scsi_emu_primary_mode_sense_page_init(iscsi_scsi_mode_sense_mode_page_data_packet *mode_sense_mode_page_pkt, const uint len, const uint page, const uint sub_page)
+{
+ if ( mode_sense_mode_page_pkt == NULL )
+ return;
+
+ if ( sub_page == 0U ) {
+ mode_sense_mode_page_pkt->page_code_flags = (uint8_t) ISCSI_SCSI_MODE_SENSE_MODE_PAGE_PUT_PAGE_CODE(page);
+ mode_sense_mode_page_pkt->page_len = (uint8_t) (len - sizeof(struct iscsi_scsi_mode_sense_mode_page_data_packet));
+
+ memset( mode_sense_mode_page_pkt->params, 0, (len - offsetof(struct iscsi_scsi_mode_sense_mode_page_data_packet, params)) );
+ } else {
+ iscsi_scsi_mode_sense_mode_sub_page_data_packet *mode_sense_mode_sub_page_pkt = (iscsi_scsi_mode_sense_mode_sub_page_data_packet *) mode_sense_mode_page_pkt;
+
+ mode_sense_mode_sub_page_pkt->page_code_flags = (uint8_t) (ISCSI_SCSI_MODE_SENSE_MODE_PAGE_PUT_PAGE_CODE(page) | ISCSI_SCSI_MODE_SENSE_MODE_PAGE_FLAGS_SPF);
+ mode_sense_mode_sub_page_pkt->sub_page_code = (uint8_t) sub_page;
+ iscsi_put_be16( (uint8_t *) &mode_sense_mode_sub_page_pkt->page_len, (uint16_t) (len - sizeof(struct iscsi_scsi_mode_sense_mode_sub_page_data_packet)) );
+
+ memset( mode_sense_mode_sub_page_pkt->params, 0, (len - offsetof(struct iscsi_scsi_mode_sense_mode_sub_page_data_packet, params)) );
+ }
+}
+
+/**
+ * @brief Handles a specific mode sense page or sub page.
+ *
+ * This function also sets the SCSI
+ * status result code accordingly.
+ *
+ * @param[in] image Pointer to DNBD3 image to get
+ * the mode sense data from. May NOT be
+ * NULL, so be careful.
+ * @param[in] scsi_task Pointer to iSCSI SCSI task
+ * responsible for this mode sense
+ * task. NULL is NOT allowed here,
+ * take caution.
+ * @param[in] mode_sense_mode_page_pkt Pointer to mode sense parameter
+ * mode page or sub page data packet
+ * to process. If this is NULL, only
+ * the length of page is calculated.
+ * @param[in] pc Page control (PC).
+ * @param[in] page Page code.
+ * @param[in] sub_page Sub page code.
+ * @return Number of bytes occupied or a
+ * negative error code otherwise.
+ */
+static int iscsi_scsi_emu_primary_mode_sense_page(dnbd3_image_t *image, iscsi_scsi_task *scsi_task, iscsi_scsi_mode_sense_mode_page_data_packet *mode_sense_mode_page_pkt, const uint pc, const uint page, const uint sub_page)
+{
+ uint page_len;
+ int len = 0;
+
+ switch ( pc ) {
+ case ISCSI_SCSI_CDB_MODE_SENSE_6_PAGE_CONTROL_CURRENT_VALUES :
+ case ISCSI_SCSI_CDB_MODE_SENSE_6_PAGE_CONTROL_CHG_VALUES :
+ case ISCSI_SCSI_CDB_MODE_SENSE_6_PAGE_CONTROL_DEFAULT_VALUES : {
+ break;
+ }
+ default : {
+ iscsi_scsi_task_status_set( scsi_task, ISCSI_SCSI_STATUS_CHECK_COND, ISCSI_SCSI_SENSE_KEY_ILLEGAL_REQ, ISCSI_SCSI_ASC_SAVING_PARAMETERS_NOT_SUPPORTED, ISCSI_SCSI_ASCQ_CAUSE_NOT_REPORTABLE );
+
+ return -1;
+
+ break;
+ }
+ }
+
+ switch ( page ) {
+ case ISCSI_SCSI_MODE_SENSE_MODE_PAGE_CODE_VENDOR_SPEC :
+ case ISCSI_SCSI_MODE_SENSE_MODE_PAGE_CODE_FORMAT_DEVICE :
+ case ISCSI_SCSI_MODE_SENSE_MODE_PAGE_CODE_RIGID_DISK_GEOMETRY :
+ case ISCSI_SCSI_MODE_SENSE_MODE_PAGE_CODE_RIGID_DISK_GEOMETRY_2 :
+ case ISCSI_SCSI_MODE_SENSE_MODE_PAGE_CODE_RESERVED :
+ case ISCSI_SCSI_MODE_SENSE_MODE_PAGE_CODE_OBSELETE :
+ case ISCSI_SCSI_MODE_SENSE_MODE_PAGE_CODE_MEDIUM_TYPES_SUPPORTED :
+ case ISCSI_SCSI_MODE_SENSE_MODE_PAGE_CODE_NOTCH_AND_PARTITION :
+ case ISCSI_SCSI_MODE_SENSE_MODE_PAGE_CODE_OBSELETE_2 :
+ case ISCSI_SCSI_MODE_SENSE_MODE_PAGE_CODE_RESERVED_2 :
+ case ISCSI_SCSI_MODE_SENSE_MODE_PAGE_CODE_RESERVED_3 :
+ case ISCSI_SCSI_MODE_SENSE_MODE_PAGE_CODE_RESERVED_4 :
+ case ISCSI_SCSI_MODE_SENSE_MODE_PAGE_CODE_RESERVED_5 :
+ case ISCSI_SCSI_MODE_SENSE_MODE_PAGE_CODE_RESERVED_6 :
+ case ISCSI_SCSI_MODE_SENSE_MODE_PAGE_CODE_ENCLOSURE_SERVICES_MGMT :
+ case ISCSI_SCSI_MODE_SENSE_MODE_PAGE_CODE_RESERVED_7 :
+ case ISCSI_SCSI_MODE_SENSE_MODE_PAGE_CODE_RESERVED_8 :
+ case ISCSI_SCSI_MODE_SENSE_MODE_PAGE_CODE_RESERVED_9 :
+ case ISCSI_SCSI_MODE_SENSE_MODE_PAGE_CODE_PROTOCOL_SPEC_LUN :
+ case ISCSI_SCSI_MODE_SENSE_MODE_PAGE_CODE_PROTOCOL_SPEC_PORT :
+ case ISCSI_SCSI_MODE_SENSE_MODE_PAGE_CODE_RESERVED_10 :
+ case ISCSI_SCSI_MODE_SENSE_MODE_PAGE_CODE_RESERVED_11 :
+ case ISCSI_SCSI_MODE_SENSE_MODE_PAGE_CODE_RESERVED_12 :
+ case ISCSI_SCSI_MODE_SENSE_MODE_PAGE_CODE_RESERVED_13 :
+ case ISCSI_SCSI_MODE_SENSE_MODE_PAGE_CODE_VENDOR_SPEC_2 :
+ case ISCSI_SCSI_MODE_SENSE_MODE_PAGE_CODE_VENDOR_SPEC_3 :
+ case ISCSI_SCSI_MODE_SENSE_MODE_PAGE_CODE_VENDOR_SPEC_4 :
+ case ISCSI_SCSI_MODE_SENSE_MODE_PAGE_CODE_VENDOR_SPEC_5 :
+ case ISCSI_SCSI_MODE_SENSE_MODE_PAGE_CODE_VENDOR_SPEC_6 :
+ case ISCSI_SCSI_MODE_SENSE_MODE_PAGE_CODE_VENDOR_SPEC_7 :
+ case ISCSI_SCSI_MODE_SENSE_MODE_PAGE_CODE_VENDOR_SPEC_8 :
+ case ISCSI_SCSI_MODE_SENSE_MODE_PAGE_CODE_VENDOR_SPEC_9 :
+ case ISCSI_SCSI_MODE_SENSE_MODE_PAGE_CODE_VENDOR_SPEC_10 :
+ case ISCSI_SCSI_MODE_SENSE_MODE_PAGE_CODE_VENDOR_SPEC_11 :
+ case ISCSI_SCSI_MODE_SENSE_MODE_PAGE_CODE_VENDOR_SPEC_12 :
+ case ISCSI_SCSI_MODE_SENSE_MODE_PAGE_CODE_VENDOR_SPEC_13 :
+ case ISCSI_SCSI_MODE_SENSE_MODE_PAGE_CODE_VENDOR_SPEC_14 :
+ case ISCSI_SCSI_MODE_SENSE_MODE_PAGE_CODE_VENDOR_SPEC_15 :
+ case ISCSI_SCSI_MODE_SENSE_MODE_PAGE_CODE_VENDOR_SPEC_16 :
+ case ISCSI_SCSI_MODE_SENSE_MODE_PAGE_CODE_VENDOR_SPEC_17 :
+ case ISCSI_SCSI_MODE_SENSE_MODE_PAGE_CODE_VENDOR_SPEC_18 :
+ case ISCSI_SCSI_MODE_SENSE_MODE_PAGE_CODE_VENDOR_SPEC_19 :
+ case ISCSI_SCSI_MODE_SENSE_MODE_PAGE_CODE_VENDOR_SPEC_20 :
+ case ISCSI_SCSI_MODE_SENSE_MODE_PAGE_CODE_VENDOR_SPEC_21 :
+ case ISCSI_SCSI_MODE_SENSE_MODE_PAGE_CODE_VENDOR_SPEC_22 :
+ case ISCSI_SCSI_MODE_SENSE_MODE_PAGE_CODE_VENDOR_SPEC_23 :
+ case ISCSI_SCSI_MODE_SENSE_MODE_PAGE_CODE_VENDOR_SPEC_24 :
+ case ISCSI_SCSI_MODE_SENSE_MODE_PAGE_CODE_VENDOR_SPEC_25 :
+ case ISCSI_SCSI_MODE_SENSE_MODE_PAGE_CODE_VENDOR_SPEC_26 :
+ case ISCSI_SCSI_MODE_SENSE_MODE_PAGE_CODE_VENDOR_SPEC_27 :
+ case ISCSI_SCSI_MODE_SENSE_MODE_PAGE_CODE_VENDOR_SPEC_28 :
+ case ISCSI_SCSI_MODE_SENSE_MODE_PAGE_CODE_VENDOR_SPEC_29 :
+ case ISCSI_SCSI_MODE_SENSE_MODE_PAGE_CODE_VENDOR_SPEC_30 :
+ case ISCSI_SCSI_MODE_SENSE_MODE_PAGE_CODE_VENDOR_SPEC_31 :
+ case ISCSI_SCSI_MODE_SENSE_MODE_PAGE_CODE_VENDOR_SPEC_32 : {
+ break;
+ }
+ case ISCSI_SCSI_MODE_SENSE_MODE_PAGE_CODE_READ_WRITE_ERR_RECOVERY : {
+ if ( sub_page != 0U )
+ break;
+
+ page_len = sizeof(struct iscsi_scsi_mode_sense_read_write_err_recovery_mode_page_data_packet);
+
+ iscsi_scsi_emu_primary_mode_sense_page_init( mode_sense_mode_page_pkt, page_len, page, sub_page );
+
+ len += page_len;
+
+ break;
+ }
+ case ISCSI_SCSI_MODE_SENSE_MODE_PAGE_CODE_DISCONNECT_RECONNECT : {
+ if ( sub_page != 0U )
+ break;
+
+ page_len = sizeof(struct iscsi_scsi_mode_sense_disconnect_reconnect_mode_page_data_packet);
+
+ iscsi_scsi_emu_primary_mode_sense_page_init( mode_sense_mode_page_pkt, page_len, page, sub_page );
+
+ len += page_len;
+
+ break;
+ }
+ case ISCSI_SCSI_MODE_SENSE_MODE_PAGE_CODE_VERIFY_ERR_RECOVERY : {
+ if ( sub_page != 0U )
+ break;
+
+ page_len = sizeof(struct iscsi_scsi_mode_sense_verify_err_recovery_mode_page_data_packet);
+
+ iscsi_scsi_emu_primary_mode_sense_page_init( mode_sense_mode_page_pkt, page_len, page, sub_page );
+
+ len += page_len;
+
+ break;
+ }
+ case ISCSI_SCSI_MODE_SENSE_MODE_PAGE_CODE_CACHING : {
+ if ( sub_page != 0U )
+ break;
+
+ iscsi_scsi_mode_sense_caching_mode_page_data_packet *mode_sense_caching_mode_page_pkt = (iscsi_scsi_mode_sense_caching_mode_page_data_packet *) mode_sense_mode_page_pkt;
+
+ page_len = sizeof(struct iscsi_scsi_mode_sense_caching_mode_page_data_packet);
+
+ iscsi_scsi_emu_primary_mode_sense_page_init( mode_sense_mode_page_pkt, page_len, page, sub_page );
+
+ if ( (mode_sense_mode_page_pkt != NULL) && iscsi_scsi_emu_io_type_is_supported( image, ISCSI_SCSI_EMU_IO_TYPE_WRITE_CACHE ) && (pc != ISCSI_SCSI_CDB_MODE_SENSE_6_PAGE_CONTROL_CHG_VALUES) )
+ mode_sense_caching_mode_page_pkt->flags |= ISCSI_SCSI_MODE_SENSE_CACHING_MODE_PAGE_FLAGS_WCE;
+
+ if ( (mode_sense_mode_page_pkt != NULL) && (pc != ISCSI_SCSI_CDB_MODE_SENSE_6_PAGE_CONTROL_CHG_VALUES) )
+ mode_sense_caching_mode_page_pkt->flags |= ISCSI_SCSI_MODE_SENSE_CACHING_MODE_PAGE_FLAGS_RCD;
+
+ len += page_len;
+
+ break;
+ }
+ case ISCSI_SCSI_MODE_SENSE_MODE_PAGE_CODE_CONTROL : {
+ switch ( sub_page ) {
+ case ISCSI_SCSI_MODE_SENSE_MODE_SUB_PAGE_CODE_CONTROL : {
+ page_len = sizeof(struct iscsi_scsi_mode_sense_control_mode_page_data_packet);
+
+ iscsi_scsi_emu_primary_mode_sense_page_init( mode_sense_mode_page_pkt, page_len, page, sub_page );
+
+ len += page_len;
+
+ break;
+ }
+ case ISCSI_SCSI_MODE_SENSE_MODE_SUB_PAGE_CODE_CONTROL_EXT : {
+ /* Control Extension */
+
+ page_len = sizeof(struct iscsi_scsi_mode_sense_control_ext_mode_page_data_packet);
+
+ iscsi_scsi_emu_primary_mode_sense_page_init( mode_sense_mode_page_pkt, page_len, page, sub_page );
+
+ len += page_len;
+
+ break;
+ }
+ case ISCSI_SCSI_MODE_SENSE_MODE_SUB_PAGE_CODE_CONTROL_ALL : {
+ len += iscsi_scsi_emu_primary_mode_sense_page( image, scsi_task, ((mode_sense_mode_page_pkt != NULL) ? (iscsi_scsi_mode_sense_mode_page_data_packet *) (((uint8_t *) mode_sense_mode_page_pkt) + len) : NULL), pc, page, ISCSI_SCSI_MODE_SENSE_MODE_SUB_PAGE_CODE_CONTROL );
+ len += iscsi_scsi_emu_primary_mode_sense_page( image, scsi_task, ((mode_sense_mode_page_pkt != NULL) ? (iscsi_scsi_mode_sense_mode_page_data_packet *) (((uint8_t *) mode_sense_mode_page_pkt) + len) : NULL), pc, page, ISCSI_SCSI_MODE_SENSE_MODE_SUB_PAGE_CODE_CONTROL_EXT );
+
+ break;
+ }
+ default : {
+ break;
+ }
+ }
+
+ break;
+ }
+ case ISCSI_SCSI_MODE_SENSE_MODE_PAGE_CODE_XOR_CONTROL : {
+ if ( sub_page != 0U )
+ break;
+
+ page_len = sizeof(struct iscsi_scsi_mode_sense_xor_ext_mode_page_data_packet);
+
+ iscsi_scsi_emu_primary_mode_sense_page_init( mode_sense_mode_page_pkt, page_len, page, sub_page );
+
+ len += page_len;
+
+ break;
+ }
+ case ISCSI_SCSI_MODE_SENSE_MODE_PAGE_CODE_POWER_COND : {
+ if ( sub_page != 0U )
+ break;
+
+ page_len = sizeof(struct iscsi_scsi_mode_sense_power_cond_mode_page_data_packet);
+
+ iscsi_scsi_emu_primary_mode_sense_page_init( mode_sense_mode_page_pkt, page_len, page, sub_page );
+
+ len += page_len;
+
+ break;
+ }
+ case ISCSI_SCSI_MODE_SENSE_MODE_PAGE_CODE_INFO_EXCEPTIOS_CONTROL : {
+ if ( sub_page != 0U )
+ break;
+
+ page_len = sizeof(struct iscsi_scsi_mode_sense_info_exceptions_control_mode_page_data_packet);
+
+ iscsi_scsi_emu_primary_mode_sense_page_init( mode_sense_mode_page_pkt, page_len, page, sub_page );
+
+ len += page_len;
+
+ break;
+ }
+ case ISCSI_SCSI_MODE_SENSE_MODE_PAGE_CODE_REPORT_ALL_MODE_PAGES : {
+ uint i;
+
+ switch ( sub_page ) {
+ case ISCSI_SCSI_MODE_SENSE_MODE_SUB_PAGE_CODE_REPORT_ALL_MODE_PAGES : {
+ for ( i = ISCSI_SCSI_MODE_SENSE_MODE_PAGE_CODE_VENDOR_SPEC; i < ISCSI_SCSI_MODE_SENSE_MODE_PAGE_CODE_REPORT_ALL_MODE_PAGES; i++ ) {
+ len += iscsi_scsi_emu_primary_mode_sense_page( image, scsi_task, ((mode_sense_mode_page_pkt != NULL) ? (iscsi_scsi_mode_sense_mode_page_data_packet *) (((uint8_t *) mode_sense_mode_page_pkt) + len) : NULL), pc, i, ISCSI_SCSI_MODE_SENSE_MODE_SUB_PAGE_CODE_REPORT_ALL_MODE_PAGES );
+ }
+
+ break;
+ }
+ case ISCSI_SCSI_MODE_SENSE_MODE_SUB_PAGE_CODE_REPORT_ALL_MODE_SUB_PAGES : {
+ for ( i = ISCSI_SCSI_MODE_SENSE_MODE_PAGE_CODE_VENDOR_SPEC; i < ISCSI_SCSI_MODE_SENSE_MODE_PAGE_CODE_REPORT_ALL_MODE_PAGES; i++ ) {
+ len += iscsi_scsi_emu_primary_mode_sense_page( image, scsi_task, ((mode_sense_mode_page_pkt != NULL) ? (iscsi_scsi_mode_sense_mode_page_data_packet *) (((uint8_t *) mode_sense_mode_page_pkt) + len) : NULL), pc, i, ISCSI_SCSI_MODE_SENSE_MODE_SUB_PAGE_CODE_REPORT_ALL_MODE_PAGES );
+ }
+
+ for ( i = ISCSI_SCSI_MODE_SENSE_MODE_PAGE_CODE_VENDOR_SPEC; i < ISCSI_SCSI_MODE_SENSE_MODE_PAGE_CODE_REPORT_ALL_MODE_PAGES; i++ ) {
+ len += iscsi_scsi_emu_primary_mode_sense_page( image, scsi_task, ((mode_sense_mode_page_pkt != NULL) ? (iscsi_scsi_mode_sense_mode_page_data_packet *) (((uint8_t *) mode_sense_mode_page_pkt) + len) : NULL), pc, i, ISCSI_SCSI_MODE_SENSE_MODE_SUB_PAGE_CODE_REPORT_ALL_MODE_SUB_PAGES );
+ }
+
+ break;
+ }
+ default : {
+ break;
+ }
+ }
+
+ break;
+ }
+ default : {
+ break;
+ }
+ }
+
+ return len;
+}
+
+/**
+ * @brief Executes a mode sense operation on a DNBD3 image.
+ *
+ * This function also sets the SCSI
+ * status result code accordingly.
+ *
+ * @param[in] image Pointer to DNBD3 image to get
+ * the mode sense data from. May
+ * NOT be NULL, so be careful.
+ * @param[in] scsi_task Pointer to iSCSI SCSI task
+ * responsible for this mode sense
+ * task. NULL is NOT allowed here,
+ * take caution.
+ * @param[in] mode_sense_6_parameter_hdr_data_pkt Pointer to mode sense parameter
+ * header data packet to fill the
+ * mode sense data with. If this is
+ * NULL, only the length of sense
+ * data is calculated.
+ * @param[in] hdr_len Length of parameter header in bytes.
+ * @param[in] block_desc_len Length of LBA parameter block
+ * descriptor in bytes.
+ * @param[in] long_lba Long Logical Block Address (LONG_LBA) bit.
+ * @param[in] pc Page control (PC).
+ * @param[in] page_code Page code.
+ * @param[in] sub_page_code Sub page code.
+ * @return Total length of sense data on successful
+ * operation, a negative error code
+ * otherwise.
+ */
+static int iscsi_scsi_emu_primary_mode_sense(dnbd3_image_t *image, iscsi_scsi_task *scsi_task, iscsi_scsi_mode_sense_6_parameter_header_data_packet *mode_sense_6_parameter_hdr_data_pkt, const uint hdr_len, const uint block_desc_len, const uint long_lba, const uint pc, const uint page_code, const uint sub_page_code)
+{
+ iscsi_scsi_mode_sense_mode_page_data_packet *mode_sense_mode_page_pkt = (iscsi_scsi_mode_sense_mode_page_data_packet *) ((mode_sense_6_parameter_hdr_data_pkt != NULL) ? (((uint8_t *) mode_sense_6_parameter_hdr_data_pkt) + hdr_len + block_desc_len) : NULL);
+ const int page_len = iscsi_scsi_emu_primary_mode_sense_page( image, scsi_task, mode_sense_mode_page_pkt, pc, page_code, sub_page_code );
+
+ if ( page_len < 0 )
+ return -1;
+
+ const uint alloc_len = (hdr_len + block_desc_len + page_len);
+
+ if ( mode_sense_6_parameter_hdr_data_pkt == NULL )
+ return alloc_len;
+
+ if ( hdr_len == sizeof(struct iscsi_scsi_mode_sense_6_parameter_header_data_packet) ) {
+ mode_sense_6_parameter_hdr_data_pkt->mode_data_len = (uint8_t) (alloc_len - sizeof(uint8_t));
+ mode_sense_6_parameter_hdr_data_pkt->medium_type = 0U;
+ mode_sense_6_parameter_hdr_data_pkt->flags = (int8_t) ((iscsi_scsi_emu_io_type_is_supported( image, ISCSI_SCSI_EMU_IO_TYPE_PHYSICAL_READ_ONLY ) || iscsi_scsi_emu_io_type_is_supported( image, ISCSI_SCSI_EMU_IO_TYPE_WRITE_PROTECT )) ? ISCSI_SCSI_MODE_SENSE_6_PARAM_HDR_DATA_FLAGS_WP : 0);
+ mode_sense_6_parameter_hdr_data_pkt->block_desc_len = (uint8_t) block_desc_len;
+ } else {
+ iscsi_scsi_mode_sense_10_parameter_header_data_packet *mode_sense_10_parameter_hdr_data_pkt = (iscsi_scsi_mode_sense_10_parameter_header_data_packet *) mode_sense_6_parameter_hdr_data_pkt;
+
+ iscsi_put_be16( (uint8_t *) &mode_sense_10_parameter_hdr_data_pkt->mode_data_len, (uint16_t) (alloc_len - sizeof(uint16_t)) );
+ mode_sense_10_parameter_hdr_data_pkt->medium_type = 0U;
+ mode_sense_10_parameter_hdr_data_pkt->flags = (int8_t) ((iscsi_scsi_emu_io_type_is_supported( image, ISCSI_SCSI_EMU_IO_TYPE_PHYSICAL_READ_ONLY ) || iscsi_scsi_emu_io_type_is_supported( image, ISCSI_SCSI_EMU_IO_TYPE_WRITE_PROTECT )) ? ISCSI_SCSI_MODE_SENSE_10_PARAM_HDR_DATA_FLAGS_WP : 0);
+ mode_sense_10_parameter_hdr_data_pkt->long_lba = (uint8_t) long_lba;
+ mode_sense_10_parameter_hdr_data_pkt->reserved = 0U;
+ iscsi_put_be16( (uint8_t *) &mode_sense_10_parameter_hdr_data_pkt->block_desc_len, (uint16_t) block_desc_len );
+ }
+
+ const uint64_t num_blocks = iscsi_scsi_emu_block_get_count( image );
+ const uint32_t block_size = iscsi_scsi_emu_block_get_size( image );
+
+ if ( block_desc_len == sizeof(struct iscsi_scsi_mode_sense_lba_parameter_block_desc_data_packet) ) {
+ iscsi_scsi_mode_sense_lba_parameter_block_desc_data_packet *lba_parameter_block_desc = (iscsi_scsi_mode_sense_lba_parameter_block_desc_data_packet *) (((uint8_t *) mode_sense_6_parameter_hdr_data_pkt) + hdr_len);
+
+ if ( num_blocks > 0xFFFFFFFFULL )
+ lba_parameter_block_desc->num_blocks = 0xFFFFFFFFUL; // Minus one does not require endianess conversion
+ else
+ iscsi_put_be32( (uint8_t *) &lba_parameter_block_desc->num_blocks, (uint32_t) num_blocks );
+
+ lba_parameter_block_desc->reserved = 0U;
+ iscsi_put_be24( (uint8_t *) &lba_parameter_block_desc->block_len, block_size );
+ } else if ( block_desc_len == sizeof(struct iscsi_scsi_mode_sense_long_lba_parameter_block_desc_data_packet) ) {
+ iscsi_scsi_mode_sense_long_lba_parameter_block_desc_data_packet *long_lba_parameter_block_desc = (iscsi_scsi_mode_sense_long_lba_parameter_block_desc_data_packet *) (((uint8_t *) mode_sense_6_parameter_hdr_data_pkt) + hdr_len);
+
+ iscsi_put_be64( (uint8_t *) &long_lba_parameter_block_desc->num_blocks, num_blocks );
+ long_lba_parameter_block_desc->reserved = 0UL;
+ iscsi_put_be32( (uint8_t *) &long_lba_parameter_block_desc->block_len, block_size );
+ }
+
+ return alloc_len;
+}
+
+/**
+ * @brief Executes SCSI non-block emulation on a DNBD3 image.
+ *
+ * This function determines the
+ * non-block based SCSI opcode and
+ * executes it.
+ *
+ * @param[in] scsi_task Pointer to iSCSI SCSI task
+ * to process the SCSI non-block
+ * operation for and may NOT be NULL,
+ * be careful.
+ * @return 0 on successful operation, a negative
+ * error code otherwise.
+ */
+static int iscsi_scsi_emu_primary_process(iscsi_scsi_task *scsi_task)
+{
+ iscsi_scsi_lun *lun = scsi_task->lun;
+ uint alloc_len;
+ uint len;
+ int rc;
+
+ switch ( scsi_task->cdb->opcode ) {
+ case ISCSI_SCSI_OPCODE_INQUIRY : {
+ const iscsi_scsi_cdb_inquiry *cdb_inquiry = (iscsi_scsi_cdb_inquiry *) scsi_task->cdb;
+
+ if ( !iscsi_scsi_emu_image_init( scsi_task, true ) )
+ break;
+
+ alloc_len = iscsi_get_be16(cdb_inquiry->alloc_len);
+ len = alloc_len;
+
+ if ( len < ISCSI_DEFAULT_RECV_DS_LEN )
+ len = ISCSI_DEFAULT_RECV_DS_LEN;
+
+ iscsi_scsi_std_inquiry_data_packet *std_inquiry_data_pkt = NULL;
+
+ if ( len > 0U ) {
+ std_inquiry_data_pkt = (iscsi_scsi_std_inquiry_data_packet *) malloc( len );
+
+ if ( std_inquiry_data_pkt == NULL ) {
+ iscsi_scsi_task_status_set( scsi_task, ISCSI_SCSI_STATUS_CHECK_COND, ISCSI_SCSI_SENSE_KEY_NOT_READY, ISCSI_SCSI_ASC_LOGICAL_UNIT_NOT_READY, ISCSI_SCSI_ASCQ_BECOMING_READY );
+
+ break;
+ }
+ }
+
+ rc = iscsi_scsi_emu_primary_inquiry( lun->image, scsi_task, cdb_inquiry, std_inquiry_data_pkt, len );
+
+ if ( (rc >= 0) && (len > 0U) ) {
+ if ( len > alloc_len )
+ len = alloc_len;
+
+ scsi_task->buf = (uint8_t *) std_inquiry_data_pkt;
+
+ if ( rc < (int) len )
+ memset( (((uint8_t *) std_inquiry_data_pkt) + rc), 0, (len - rc) );
+
+ rc = len;
+ }
+
+ if ( rc >= 0 ) {
+ scsi_task->xfer_pos = rc;
+ scsi_task->status = ISCSI_SCSI_STATUS_GOOD;
+ }
+
+ break;
+ }
+ case ISCSI_SCSI_OPCODE_REPORTLUNS : {
+ const iscsi_scsi_cdb_report_luns *cdb_report_luns = (iscsi_scsi_cdb_report_luns *) scsi_task->cdb;
+
+ alloc_len = iscsi_get_be32(cdb_report_luns->alloc_len);
+ rc = iscsi_scsi_emu_check_len( scsi_task, alloc_len, (sizeof(struct iscsi_scsi_report_luns_parameter_data_lun_list_packet) + sizeof(struct iscsi_scsi_report_luns_parameter_data_lun_entry_packet)) );
+
+ if ( rc < 0 )
+ break;
+
+ len = alloc_len;
+
+ if ( len < ISCSI_DEFAULT_RECV_DS_LEN )
+ len = ISCSI_DEFAULT_RECV_DS_LEN;
+
+ iscsi_scsi_report_luns_parameter_data_lun_list_packet *report_luns_parameter_data_pkt = NULL;
+
+ if ( len > 0U ) {
+ report_luns_parameter_data_pkt = (iscsi_scsi_report_luns_parameter_data_lun_list_packet *) malloc( len );
+
+ if ( report_luns_parameter_data_pkt == NULL ) {
+ iscsi_scsi_task_status_set( scsi_task, ISCSI_SCSI_STATUS_CHECK_COND, ISCSI_SCSI_SENSE_KEY_NOT_READY, ISCSI_SCSI_ASC_LOGICAL_UNIT_NOT_READY, ISCSI_SCSI_ASCQ_BECOMING_READY );
+
+ break;
+ }
+ }
+
+ rc = iscsi_scsi_emu_primary_report_luns( lun, report_luns_parameter_data_pkt, len, cdb_report_luns->select_report );
+
+ if ( rc < 0 ) {
+ free( report_luns_parameter_data_pkt );
+ iscsi_scsi_task_status_set( scsi_task, ISCSI_SCSI_STATUS_CHECK_COND, ISCSI_SCSI_SENSE_KEY_NO_SENSE, ISCSI_SCSI_ASC_NO_ADDITIONAL_SENSE, ISCSI_SCSI_ASCQ_CAUSE_NOT_REPORTABLE );
+
+ break;
+ }
+
+ len = rc;
+
+ if ( len > 0U ) {
+ if ( len > alloc_len )
+ len = alloc_len;
+
+ scsi_task->buf = (uint8_t *) report_luns_parameter_data_pkt;
+ }
+
+ scsi_task->xfer_pos = len;
+ scsi_task->status = ISCSI_SCSI_STATUS_GOOD;
+
+ break;
+ }
+ case ISCSI_SCSI_OPCODE_MODESELECT6 : {
+ const iscsi_scsi_cdb_mode_select_6 *cdb_mode_select_6 = (iscsi_scsi_cdb_mode_select_6 *) scsi_task->cdb;
+
+ alloc_len = cdb_mode_select_6->param_list_len;
+
+ if ( alloc_len == 0U )
+ break;
+
+ rc = iscsi_scsi_emu_check_len( scsi_task, alloc_len, sizeof(struct iscsi_scsi_mode_select_6_parameter_list_packet) );
+
+ if ( rc < 0 )
+ break;
+
+ len = scsi_task->len;
+
+ if ( alloc_len < sizeof(struct iscsi_scsi_mode_select_6_parameter_list_packet) )
+ alloc_len = sizeof(struct iscsi_scsi_mode_select_6_parameter_list_packet);
+
+ rc = iscsi_scsi_emu_check_len( scsi_task, len, alloc_len );
+
+ if ( rc < 0 )
+ break;
+
+ scsi_task->xfer_pos = alloc_len;
+ scsi_task->status = ISCSI_SCSI_STATUS_GOOD;
+
+ break;
+ }
+ case ISCSI_SCSI_OPCODE_MODESELECT10 : {
+ const iscsi_scsi_cdb_mode_select_10 *cdb_mode_select_10 = (iscsi_scsi_cdb_mode_select_10 *) scsi_task->cdb;
+
+ alloc_len = iscsi_get_be16(cdb_mode_select_10->param_list_len);
+
+ if ( alloc_len == 0U )
+ break;
+
+ rc = iscsi_scsi_emu_check_len( scsi_task, alloc_len, sizeof(struct iscsi_scsi_mode_select_10_parameter_list_packet) );
+
+ if ( rc < 0 )
+ break;
+
+ len = scsi_task->len;
+
+ if ( alloc_len < sizeof(struct iscsi_scsi_mode_select_10_parameter_list_packet) )
+ alloc_len = sizeof(struct iscsi_scsi_mode_select_10_parameter_list_packet);
+
+ rc = iscsi_scsi_emu_check_len( scsi_task, len, alloc_len );
+
+ if ( rc < 0 )
+ break;
+
+ scsi_task->xfer_pos = alloc_len;
+ scsi_task->status = ISCSI_SCSI_STATUS_GOOD;
+
+ break;
+ }
+ case ISCSI_SCSI_OPCODE_MODESENSE6 : {
+ const iscsi_scsi_cdb_mode_sense_6 *cdb_mode_sense_6 = (iscsi_scsi_cdb_mode_sense_6 *) scsi_task->cdb;
+
+ if ( !iscsi_scsi_emu_image_init( scsi_task, true ) )
+ break;
+
+ alloc_len = cdb_mode_sense_6->alloc_len;
+
+ const uint block_desc_len = (((cdb_mode_sense_6->flags & ISCSI_SCSI_CDB_MODE_SENSE_6_FLAGS_DBD) == 0) ? sizeof(struct iscsi_scsi_mode_sense_lba_parameter_block_desc_data_packet) : 0U);
+ const uint pc = ISCSI_SCSI_CDB_MODE_SENSE_6_GET_PAGE_CONTROL(cdb_mode_sense_6->page_code_control);
+ const uint page = ISCSI_SCSI_CDB_MODE_SENSE_6_GET_PAGE_CODE(cdb_mode_sense_6->page_code_control);
+ const uint sub_page = cdb_mode_sense_6->sub_page_code;
+
+ rc = iscsi_scsi_emu_primary_mode_sense( lun->image, scsi_task, NULL, sizeof(struct iscsi_scsi_mode_sense_6_parameter_header_data_packet), block_desc_len, 0U, pc, page, sub_page );
+
+ if ( rc < 0 )
+ break;
+
+ len = rc;
+
+ iscsi_scsi_mode_sense_6_parameter_header_data_packet *mode_sense_6_parameter_hdr_data_pkt = (iscsi_scsi_mode_sense_6_parameter_header_data_packet *) malloc( len );
+
+ if ( mode_sense_6_parameter_hdr_data_pkt == NULL ) {
+ iscsi_scsi_task_status_set( scsi_task, ISCSI_SCSI_STATUS_CHECK_COND, ISCSI_SCSI_SENSE_KEY_NOT_READY, ISCSI_SCSI_ASC_LOGICAL_UNIT_NOT_READY, ISCSI_SCSI_ASCQ_BECOMING_READY );
+
+ break;
+ }
+
+ rc = iscsi_scsi_emu_primary_mode_sense( lun->image, scsi_task, mode_sense_6_parameter_hdr_data_pkt, sizeof(struct iscsi_scsi_mode_sense_6_parameter_header_data_packet), block_desc_len, 0U, pc, page, sub_page );
+
+ if ( rc < 0 ) {
+ free( mode_sense_6_parameter_hdr_data_pkt );
+ iscsi_scsi_task_status_set( scsi_task, ISCSI_SCSI_STATUS_CHECK_COND, ISCSI_SCSI_SENSE_KEY_ILLEGAL_REQ, ISCSI_SCSI_ASC_INVALID_FIELD_IN_CDB, ISCSI_SCSI_ASCQ_CAUSE_NOT_REPORTABLE );
+
+ break;
+ }
+
+ if ( (rc >= 0) && (len > 0U) ) {
+ if ( len > alloc_len )
+ len = alloc_len;
+
+ scsi_task->buf = (uint8_t *) mode_sense_6_parameter_hdr_data_pkt;
+ rc = len;
+ }
+
+ if ( rc >= 0 ) {
+ scsi_task->xfer_pos = rc;
+ scsi_task->status = ISCSI_SCSI_STATUS_GOOD;
+ }
+
+ break;
+ }
+ case ISCSI_SCSI_OPCODE_MODESENSE10 : {
+ const iscsi_scsi_cdb_mode_sense_10 *cdb_mode_sense_10 = (iscsi_scsi_cdb_mode_sense_10 *) scsi_task->cdb;
+
+ if ( !iscsi_scsi_emu_image_init( scsi_task, true ) )
+ break;
+
+ alloc_len = iscsi_get_be16(cdb_mode_sense_10->alloc_len);
+
+ const uint long_lba = (((cdb_mode_sense_10->flags & ISCSI_SCSI_CDB_MODE_SENSE_10_FLAGS_LLBAA) != 0) ? ISCSI_SCSI_MODE_SENSE_10_PARAM_HDR_DATA_LONGLBA : 0U);
+ const uint block_desc_len = (((cdb_mode_sense_10->flags & ISCSI_SCSI_CDB_MODE_SENSE_10_FLAGS_DBD) == 0) ? ((long_lba != 0) ? sizeof(struct iscsi_scsi_mode_sense_long_lba_parameter_block_desc_data_packet) : sizeof(struct iscsi_scsi_mode_sense_lba_parameter_block_desc_data_packet)) : 0U);
+ const uint pc10 = ISCSI_SCSI_CDB_MODE_SENSE_10_GET_PAGE_CONTROL(cdb_mode_sense_10->page_code_control);
+ const uint page10 = ISCSI_SCSI_CDB_MODE_SENSE_10_GET_PAGE_CODE(cdb_mode_sense_10->page_code_control);
+ const uint sub_page10 = cdb_mode_sense_10->sub_page_code;
+
+ rc = iscsi_scsi_emu_primary_mode_sense( lun->image, scsi_task, NULL, sizeof(struct iscsi_scsi_mode_sense_10_parameter_header_data_packet), block_desc_len, long_lba, pc10, page10, sub_page10 );
+
+ if ( rc < 0 )
+ break;
+
+ len = rc;
+
+ iscsi_scsi_mode_sense_10_parameter_header_data_packet *mode_sense_10_parameter_hdr_data_pkt = (iscsi_scsi_mode_sense_10_parameter_header_data_packet *) malloc( len );
+
+ if ( mode_sense_10_parameter_hdr_data_pkt == NULL ) {
+ iscsi_scsi_task_status_set( scsi_task, ISCSI_SCSI_STATUS_CHECK_COND, ISCSI_SCSI_SENSE_KEY_NOT_READY, ISCSI_SCSI_ASC_LOGICAL_UNIT_NOT_READY, ISCSI_SCSI_ASCQ_BECOMING_READY );
+
+ break;
+ }
+
+ rc = iscsi_scsi_emu_primary_mode_sense( lun->image, scsi_task, (iscsi_scsi_mode_sense_6_parameter_header_data_packet *) mode_sense_10_parameter_hdr_data_pkt, sizeof(struct iscsi_scsi_mode_sense_10_parameter_header_data_packet), block_desc_len, long_lba, pc10, page10, sub_page10 );
+
+ if ( rc < 0 ) {
+ free( mode_sense_10_parameter_hdr_data_pkt );
+ iscsi_scsi_task_status_set( scsi_task, ISCSI_SCSI_STATUS_CHECK_COND, ISCSI_SCSI_SENSE_KEY_ILLEGAL_REQ, ISCSI_SCSI_ASC_INVALID_FIELD_IN_CDB, ISCSI_SCSI_ASCQ_CAUSE_NOT_REPORTABLE );
+
+ break;
+ }
+
+ if ( (rc >= 0) && (len > 0U) ) {
+ if ( len > alloc_len )
+ len = alloc_len;
+
+ scsi_task->buf = (uint8_t *) mode_sense_10_parameter_hdr_data_pkt;
+ rc = len;
+ }
+
+ if ( rc >= 0 ) {
+ scsi_task->xfer_pos = rc;
+ scsi_task->status = ISCSI_SCSI_STATUS_GOOD;
+ }
+
+ break;
+ }
+ case ISCSI_SCSI_OPCODE_REQUESTSENSE : {
+ const iscsi_scsi_cdb_req_sense *cdb_req_sense = (iscsi_scsi_cdb_req_sense *) scsi_task->cdb;
+
+ if ( (cdb_req_sense->flags & ISCSI_SCSI_CDB_REQ_SENSE_FLAGS_DESC) != 0 ) {
+ iscsi_scsi_task_status_set( scsi_task, ISCSI_SCSI_STATUS_CHECK_COND, ISCSI_SCSI_SENSE_KEY_ILLEGAL_REQ, ISCSI_SCSI_ASC_INVALID_FIELD_IN_CDB, ISCSI_SCSI_ASCQ_CAUSE_NOT_REPORTABLE );
+
+ break;
+ }
+
+ alloc_len = cdb_req_sense->alloc_len;
+
+ iscsi_scsi_task_sense_data_build( scsi_task, ISCSI_SCSI_SENSE_KEY_NO_SENSE, ISCSI_SCSI_ASC_NO_ADDITIONAL_SENSE, ISCSI_SCSI_ASCQ_CAUSE_NOT_REPORTABLE );
+
+ len = scsi_task->sense_data_len;
+
+ if ( len > 0U ) {
+ iscsi_scsi_sense_data_check_cond_packet *sense_data = (iscsi_scsi_sense_data_check_cond_packet *) malloc( len );
+
+ if ( sense_data == NULL ) {
+ iscsi_scsi_task_status_set( scsi_task, ISCSI_SCSI_STATUS_CHECK_COND, ISCSI_SCSI_SENSE_KEY_NOT_READY, ISCSI_SCSI_ASC_LOGICAL_UNIT_NOT_READY, ISCSI_SCSI_ASCQ_BECOMING_READY );
+
+ break;
+ }
+
+ memcpy( sense_data, scsi_task->sense_data, len );
+
+ if ( len > alloc_len )
+ len = alloc_len;
+
+ scsi_task->buf = (uint8_t *) sense_data;
+ }
+
+ scsi_task->xfer_pos = len;
+ scsi_task->status = ISCSI_SCSI_STATUS_GOOD;
+
+ break;
+ }
+ case ISCSI_SCSI_OPCODE_LOGSELECT :
+ case ISCSI_SCSI_OPCODE_LOGSENSE : {
+ iscsi_scsi_task_status_set( scsi_task, ISCSI_SCSI_STATUS_CHECK_COND, ISCSI_SCSI_SENSE_KEY_ILLEGAL_REQ, ISCSI_SCSI_ASC_INVALID_COMMAND_OPERATION_CODE, ISCSI_SCSI_ASCQ_CAUSE_NOT_REPORTABLE );
+
+ break;
+ }
+ case ISCSI_SCSI_OPCODE_TESTUNITREADY : {
+ if ( !iscsi_scsi_emu_image_init( scsi_task, false ) )
+ break;
+
+ scsi_task->xfer_pos = 0UL;
+ scsi_task->status = ISCSI_SCSI_STATUS_GOOD;
+
+ break;
+ }
+ case ISCSI_SCSI_OPCODE_STARTSTOPUNIT : {
+ // TODO: Handle eject image and power saving (suspend and standby) modes.
+
+ if ( !iscsi_scsi_emu_image_init( scsi_task, true ) )
+ break;
+
+ scsi_task->xfer_pos = 0UL;
+ scsi_task->status = ISCSI_SCSI_STATUS_GOOD;
+
+ break;
+ }
+ case ISCSI_SCSI_OPCODE_PERSISTENT_RESERVE_OUT : {
+ const iscsi_scsi_cdb_pr_reserve_out *cdb_pr_reserve_out = (iscsi_scsi_cdb_pr_reserve_out *) scsi_task->cdb;
+
+ alloc_len = iscsi_get_be32(cdb_pr_reserve_out->param_list_len);
+ rc = iscsi_scsi_emu_check_len( scsi_task, alloc_len, sizeof(struct iscsi_scsi_pr_reserve_out_parameter_list_packet) );
+
+ if ( rc < 0 )
+ break;
+
+ len = scsi_task->len;
+
+ iscsi_scsi_pr_reserve_out_parameter_list_packet *pr_reserve_out_parameter_list = (iscsi_scsi_pr_reserve_out_parameter_list_packet *) malloc( len );
+
+ if ( pr_reserve_out_parameter_list == NULL ) {
+ iscsi_scsi_task_status_set( scsi_task, ISCSI_SCSI_STATUS_CHECK_COND, ISCSI_SCSI_SENSE_KEY_NOT_READY, ISCSI_SCSI_ASC_LOGICAL_UNIT_NOT_READY, ISCSI_SCSI_ASCQ_BECOMING_READY );
+
+ break;
+ }
+
+ if ( len < sizeof(struct iscsi_scsi_pr_reserve_out_parameter_list_packet) ) {
+ free( pr_reserve_out_parameter_list );
+
+ break;
+ }
+
+ rc = iscsi_scsi_pr_out( scsi_task, pr_reserve_out_parameter_list, cdb_pr_reserve_out, len );
+
+ if ( rc < 0 ) {
+ free( pr_reserve_out_parameter_list );
+
+ break;
+ }
+
+ scsi_task->xfer_pos = alloc_len;
+ scsi_task->status = ISCSI_SCSI_STATUS_GOOD;
+
+ free( pr_reserve_out_parameter_list );
+
+ break;
+ }
+ case ISCSI_SCSI_OPCODE_PERSISTENT_RESERVE_IN : {
+ const iscsi_scsi_cdb_pr_reserve_in *cdb_pr_reserve_in = (iscsi_scsi_cdb_pr_reserve_in *) scsi_task->cdb;
+
+ alloc_len = iscsi_get_be16(cdb_pr_reserve_in->param_list_len);
+ len = alloc_len;
+
+ iscsi_scsi_pr_reserve_in_parameter_data_packet *pr_reserve_in_parameter_data = (iscsi_scsi_pr_reserve_in_parameter_data_packet *) malloc( len );
+
+ if ( pr_reserve_in_parameter_data == NULL ) {
+ iscsi_scsi_task_status_set( scsi_task, ISCSI_SCSI_STATUS_CHECK_COND, ISCSI_SCSI_SENSE_KEY_NOT_READY, ISCSI_SCSI_ASC_LOGICAL_UNIT_NOT_READY, ISCSI_SCSI_ASCQ_BECOMING_READY );
+
+ break;
+ }
+
+ rc = iscsi_scsi_pr_in( scsi_task, pr_reserve_in_parameter_data, cdb_pr_reserve_in, len );
+
+ if ( (rc >= 0) && (len > 0U) ) {
+ if ( len > alloc_len )
+ len = alloc_len;
+
+ scsi_task->buf = (uint8_t *) pr_reserve_in_parameter_data;
+ rc = len;
+ }
+
+ if ( rc >= 0 ) {
+ scsi_task->xfer_pos = rc;
+ scsi_task->status = ISCSI_SCSI_STATUS_GOOD;
+ }
+
+ break;
+ }
+ case ISCSI_SCSI_OPCODE_RESERVE6 : {
+ const iscsi_scsi_cdb_pr_reserve_6 *cdb_pr_reserve_6 = (iscsi_scsi_cdb_pr_reserve_6 *) scsi_task->cdb;
+
+ rc = iscsi_scsi_pr_reserve_scsi2( scsi_task, cdb_pr_reserve_6 );
+
+ if ( rc >= 0 ) {
+ scsi_task->xfer_pos = rc;
+ scsi_task->status = ISCSI_SCSI_STATUS_GOOD;
+ }
+
+ break;
+ }
+ case ISCSI_SCSI_OPCODE_RESERVE10 : {
+ const iscsi_scsi_cdb_pr_reserve_10 *cdb_pr_reserve_10 = (iscsi_scsi_cdb_pr_reserve_10 *) scsi_task->cdb;
+
+ rc = iscsi_scsi_pr_reserve_scsi2( scsi_task, (iscsi_scsi_cdb_pr_reserve_6 *) cdb_pr_reserve_10 );
+ rc = iscsi_get_be16(cdb_pr_reserve_10->param_list_len);
+
+ if ( rc >= 0 ) {
+ scsi_task->xfer_pos = rc;
+ scsi_task->status = ISCSI_SCSI_STATUS_GOOD;
+ }
+
+ break;
+ }
+ case ISCSI_SCSI_OPCODE_RELEASE6 :
+ case ISCSI_SCSI_OPCODE_RELEASE10 : {
+ rc = iscsi_scsi_pr_release_scsi2( scsi_task );
+
+ if ( rc >= 0 ) {
+ scsi_task->xfer_pos = rc;
+ scsi_task->status = ISCSI_SCSI_STATUS_GOOD;
+ }
+
+ break;
+ }
+ default : {
+ return ISCSI_SCSI_TASK_RUN_UNKNOWN;
+
+ break;
+ }
+ }
+
+ return ISCSI_SCSI_TASK_RUN_COMPLETE;
+}
+
+/**
+ * @brief Executes the iSCSI SCSI emulation for an iSCSI SCSI task.
+ *
+ * This function also handles all SCSI emulation
+ * tasks for DNBD3 image mapping.
+ *
+ * @param[in] scsi_task Pointer to iSCSI SCSI task for which
+ * SCSI should be emulated and may NOT be NULL,
+ * so be careful.
+ * @return 0 on successful SCSI emulation or a
+ * negative error code otherwise.
+ */
+int iscsi_scsi_emu_exec(iscsi_scsi_task *scsi_task)
+{
+ int rc = iscsi_scsi_emu_block_process( scsi_task );
+
+ if ( rc == ISCSI_SCSI_TASK_RUN_UNKNOWN ) {
+ rc = iscsi_scsi_emu_primary_process( scsi_task );
+
+ if ( rc == ISCSI_SCSI_TASK_RUN_UNKNOWN ) {
+ iscsi_scsi_task_status_set( scsi_task, ISCSI_SCSI_STATUS_CHECK_COND, ISCSI_SCSI_SENSE_KEY_ILLEGAL_REQ, ISCSI_SCSI_ASC_INVALID_COMMAND_OPERATION_CODE, ISCSI_SCSI_ASCQ_CAUSE_NOT_REPORTABLE );
+
+ return ISCSI_SCSI_TASK_RUN_COMPLETE;
+ }
+ }
+
+ return rc;
+}
+
+/**
* @brief Allocates and initializes an iSCSI port.
*
* THis function marks the port in use, but does
@@ -2558,7 +7704,7 @@ iscsi_port *iscsi_port_create(const uint8_t *name, const uint64_t id, const uint
return NULL;
}
- const uint name_len = (uint) strlen( (char *) name ) + 1;
+ const uint name_len = (uint) (strlen( (char *) name ) + 1UL);
port->name = (uint8_t *) malloc( name_len );
@@ -2582,6 +7728,28 @@ iscsi_port *iscsi_port_create(const uint8_t *name, const uint64_t id, const uint
}
/**
+ * @brief iSCSI port destructor callback for hash map.
+ *
+ * Callback function for deallocation of an iSCSI
+ * port stored in the iSCSI device hash map.
+ *
+ * @param[in] key Pointer to zero padded key. NULL is
+ * an invalid pointer here, so be careful.
+ * @param[in] key_size Number of bytes for the key.
+ * @param[in] value Value of the key, NULL is allowed.
+ * @param[in,out] user_data This argument is not used by
+ * this function and should be always NULL for now, as
+ * there is a possibility for future usage.
+ * @return Always returns 0 as this function cannot fail.
+ */
+int iscsi_port_destroy_callback(uint8_t *key, const size_t key_size, uint8_t *value, uint8_t *user_data)
+{
+ iscsi_port_destroy( (iscsi_port *) value );
+
+ return 0;
+}
+
+/**
* @brief Deallocates all resources acquired iscsi_port_create.
*
* This function also frees the port name and transport ID,
@@ -2622,7 +7790,7 @@ void iscsi_port_destroy(iscsi_port *port)
*/
uint8_t *iscsi_port_get_name(const iscsi_port *port)
{
- return port->name;
+ return port->name;
}
/**
@@ -2654,10 +7822,10 @@ int iscsi_port_transport_id_set(iscsi_port *port, const uint8_t *name, const uin
return ISCSI_CONNECT_PDU_READ_ERR_FATAL;
}
- const uint name_len = (uint) strlen( (char *) tmp_buf ) + 1;
- const uint len = iscsi_align(name_len, ISCSI_ALIGN_SIZE);
+ const uint name_len = (uint) (strlen( (char *) tmp_buf ) + 1U);
+ const uint len = ISCSI_ALIGN(name_len, ISCSI_ALIGN_SIZE);
- if ( (len < 20UL) || ((len + offsetof(struct iscsi_transport_id, name)) >= 65536UL) ) {
+ if ( (len < 20U) || ((len + offsetof(struct iscsi_transport_id, name)) >= 65536U) ) {
logadd( LOG_ERROR, "iscsi_port_transport_id_set: Out of memory allocating SCSI transport ID for iSCSI port" );
free( tmp_buf );
@@ -2675,19 +7843,206 @@ int iscsi_port_transport_id_set(iscsi_port *port, const uint8_t *name, const uin
return ISCSI_CONNECT_PDU_READ_ERR_FATAL;
}
- port->transport_id->id = (ISCSI_TRANSPORT_ID_FORMAT << 6U) | ISCSI_TRANSPORT_ID_PROTOCOL_ID_ISCSI;
+ port->transport_id->id = (ISCSI_TRANSPORT_ID_PUT_PROTOCOL_ID(ISCSI_TRANSPORT_ID_PROTOCOL_ID_ISCSI) | ISCSI_TRANSPORT_ID_PUT_FORMAT(ISCSI_TRANSPORT_ID_PROTOCOL_ID_ISCSI));
port->transport_id->reserved = 0U;
iscsi_put_be16( (uint8_t *) &port->transport_id->add_len, (uint16_t) len );
memcpy( ((uint8_t *) port->transport_id) + offsetof(struct iscsi_transport_id, name), tmp_buf, name_len );
- memset( ((uint8_t *) port->transport_id) + offsetof(struct iscsi_transport_id, name) + name_len, 0, (name_len & (ISCSI_ALIGN_SIZE - 1)) );
+ memset( ((uint8_t *) port->transport_id) + offsetof(struct iscsi_transport_id, name) + name_len, 0, (len - name_len) );
port->transport_id_len = (uint16_t) (offsetof(struct iscsi_transport_id, name) + len);
+ free( tmp_buf );
+
return ISCSI_CONNECT_PDU_READ_OK;
}
/**
+ * @brief Creates and initializes an iSCSI device with a maximum number of LUNs.
+ *
+ * This function creates a virtual SCSI device
+ * which links the DNBD3 images to their LUNs.
+ *
+ * @param[in] name Pointer to name of iSCSI device,
+ * may NOT be NULL, so be careful.
+ * @param[in] lun_id Initial LUN identifier to create.
+ * @param[in] protocol_id Protocol identifier.
+ * @return Pointer to iSCSI device or NULL in
+ * case of an error.
+ */
+iscsi_device *iscsi_device_create(const uint8_t *name, const int lun_id, const uint8_t protocol_id)
+{
+ iscsi_device *device = (iscsi_device *) malloc( sizeof(struct iscsi_device) );
+
+ if ( device == NULL ) {
+ logadd( LOG_ERROR, "iscsi_device_create: Out of memory allocating iSCSI device" );
+
+ return NULL;
+ }
+
+ const uint len = (uint) (strlen( (char *) name ) + 1U);
+
+ device->name = malloc( len );
+
+ if ( device->name == NULL ) {
+ logadd( LOG_ERROR, "iscsi_device_create: Out of memory allocating iSCSI device name" );
+
+ free( device );
+
+ return NULL;
+ }
+
+ memcpy( device->name, name, len );
+
+ device->luns = iscsi_hashmap_create( 8U );
+
+ if ( device->luns == NULL ) {
+ logadd( LOG_ERROR, "iscsi_device_create: Out of memory allocating iSCSI device LUN hash map" );
+
+ free( device->name );
+ free( device );
+
+ return NULL;
+ }
+
+ iscsi_scsi_lun *lun = iscsi_scsi_lun_create( lun_id );
+
+ if ( lun == NULL ) {
+ logadd( LOG_ERROR, "iscsi_device_create: Out of memory allocating iSCSI device LUN hash map" );
+
+ iscsi_hashmap_destroy( device->luns );
+ free( device->name );
+ free( device );
+
+ return NULL;
+ }
+
+ if ( pthread_rwlock_init( &device->luns_rwlock, NULL ) != 0 ) {
+ iscsi_scsi_lun_destroy( lun );
+ iscsi_hashmap_destroy( device->luns );
+ free( device->name );
+ free( device );
+
+ return NULL;
+ }
+
+ const uint64_t lun_hash = lun_id;
+ uint8_t *hash_key = iscsi_hashmap_key_create( (uint8_t *) &lun_hash, sizeof(lun_hash) );
+
+ if ( hash_key == NULL ) {
+ logadd( LOG_ERROR, "iscsi_device_create: Out of memory allocating iSCSI device LUN hash map" );
+
+ pthread_rwlock_destroy( &device->luns_rwlock );
+ iscsi_scsi_lun_destroy( lun );
+ iscsi_hashmap_destroy( device->luns );
+ free( device->name );
+ free( device );
+
+ return NULL;
+ }
+
+ const int rc = iscsi_hashmap_put( device->luns, hash_key, sizeof(lun_hash), (uint8_t *) lun );
+
+ if ( rc < 0 ) {
+ iscsi_hashmap_key_destroy( hash_key );
+ pthread_rwlock_destroy( &device->luns_rwlock );
+ iscsi_scsi_lun_destroy( lun );
+ iscsi_hashmap_destroy( device->luns );
+ free( device->name );
+ free( device );
+
+ return NULL;
+ }
+
+ lun->device = device;
+
+ device->ports = iscsi_hashmap_create( 0U );
+
+ if ( device->ports == NULL ) {
+ logadd( LOG_ERROR, "iscsi_device_create: Out of memory allocating iSCSI device ports hash map" );
+
+ iscsi_hashmap_key_destroy( hash_key );
+ pthread_rwlock_destroy( &device->luns_rwlock );
+ iscsi_scsi_lun_destroy( lun );
+ iscsi_hashmap_destroy( device->luns );
+ free( device->name );
+ free( device );
+
+ return NULL;
+ }
+
+ device->id = 0;
+ device->flags = 0;
+ device->active_conns = 0UL;
+ device->protocol_id = protocol_id;
+
+ return device;
+}
+
+/**
+ * @brief iSCSI device destructor callback for hash map.
+ *
+ * Callback function for deallocation of an iSCSI
+ * device stored in the hash map managing all iSCSI
+ * devices.
+ *
+ * @param[in] key Pointer to zero padded key. NULL is
+ * an invalid pointer here, so be careful.
+ * @param[in] key_size Number of bytes for the key.
+ * @param[in] value Value of the key, NULL is allowed.
+ * @param[in,out] user_data This argument is not used by
+ * this function and should be always NULL for now, as
+ * there is a possibility for future usage.
+ * @return Always returns 0 as this function cannot fail.
+ */
+int iscsi_device_destroy_callback(uint8_t *key, const size_t key_size, uint8_t *value, uint8_t *user_data)
+{
+ iscsi_device_destroy( (iscsi_device *) value );
+ iscsi_hashmap_key_destroy( key );
+
+ return 0;
+}
+
+/**
+ * @brief Deallocates all resources acquired by iscsi_device_create.
+ *
+ * This function also frees the associated
+ * iSCSI ports, LUNs and the device name.
+ *
+ * @param[in] device Pointer to iSCSI device to be freed. May
+ * be NULL in which case this function does
+ * nothing at all.
+ */
+void iscsi_device_destroy(iscsi_device *device)
+{
+ if ( device != NULL ) {
+ if ( device->ports != NULL ) {
+ iscsi_hashmap_iterate( device->ports, iscsi_port_destroy_callback, NULL );
+ iscsi_hashmap_destroy( device->ports );
+
+ device->ports = NULL;
+ }
+
+ pthread_rwlock_destroy( &device->luns_rwlock );
+
+ if ( device->luns != NULL ) {
+ iscsi_hashmap_iterate( device->luns, iscsi_scsi_lun_destroy_callback, NULL );
+ iscsi_hashmap_destroy( device->luns );
+
+ device->luns = NULL;
+ }
+
+ if ( device->name != NULL ) {
+ free( device->name );
+
+ device->name = NULL;
+ }
+
+ free( device );
+ }
+}
+
+/**
* @brief Gets an iSCSI device being in use by portal group identifier.
*
* This function uses the unique portal group
@@ -2710,7 +8065,666 @@ iscsi_port *iscsi_device_find_port_by_portal_group_tag(const iscsi_device *devic
if ( (port == NULL) || ((port->flags & ISCSI_PORT_FLAGS_IN_USE) == 0) )
return NULL;
- return port;
+ return port;
+}
+
+/**
+ * @brief Searches an iSCSI LUN by LUN identifier.
+ *
+ * This function searches for an iSCSI LUN by
+ * iterating through the iSCSI device LUN
+ * hash map.
+ *
+ * @param[in] device Pointer to iSCSI device to
+ * search in the LUN hash map. May NOT be
+ * NULL, so be careful.
+ * @param[in] lun_id LUN identifier to be searched
+ * for.
+ * @return Pointer to found iSCSI LUN or NULL in
+ * case no iSCSI LUN has a matching LUN
+ * identifier.
+ */
+iscsi_scsi_lun *iscsi_device_find_lun(iscsi_device *device, const int lun_id)
+{
+ const uint64_t hash_key = (uint64_t) lun_id;
+ iscsi_scsi_lun *lun;
+
+ const int rc = iscsi_hashmap_get( device->luns, (uint8_t *) &hash_key, sizeof(hash_key), (uint8_t **) &lun );
+
+ if ( (rc < 0) || ((lun->flags & ISCSI_SCSI_LUN_FLAGS_REMOVED) != 0) )
+ return NULL;
+
+ return lun;
+}
+
+/**
+ * @brief Creates, initializes and adds an iSCSI target port to an iSCSI device.
+ *
+ * This function checks whether the iSCSI
+ * target port already exists for the
+ * device.
+ *
+ * @param[in] device Pointer to iSCSI device to
+ * add the port for. May NOT be NULL, so
+ * be careful.
+ * @param[in] name Pointer to string containing
+ * the name for the iSCSI target port.
+ * NULL is NOT allowed here, take caution.
+ * @param[in] id Unique iSCSI target port
+ * identifier to be used.
+ * @return 0 on successful operation, 1 if
+ * the port already exists or a
+ * negative error code otherwise.
+ */
+int iscsi_device_port_add(iscsi_device *device, const uint8_t *name, const uint64_t id)
+{
+ if ( iscsi_hashmap_contains( device->ports, (uint8_t *) &id, sizeof(id) ) )
+ return 1;
+
+ iscsi_port *port = iscsi_port_create( name, id, (uint16_t) iscsi_hashmap_size( device->ports ) );
+
+ if ( port == NULL )
+ return -1;
+
+ const int rc = iscsi_hashmap_put( device->ports, (uint8_t *) &port->id, sizeof(port->id), (uint8_t *) port );
+
+ if ( rc < 0 ) {
+ iscsi_port_destroy( port );
+
+ return -1;
+ }
+
+ return 0;
+}
+
+/**
+ * @brief Enqueues an iSCSI SCSI task to the first LUN of an iSCSI device.
+ *
+ * This function adds an iSCSI SCSI task
+ * with an unique task identifier to the
+ * first LUN of an iSCSI device.
+ *
+ * @param[in] device Pointer to iSCSI device to enqueue
+ * the task to and may NOT be NULL, so be
+ * careful.
+ * @param[in] scsi_task Pointer to iSCSI SCSI task to enqueue
+ * to the associated device. NULL is not
+ * allowed here, take caution.
+ */
+void iscsi_device_scsi_task_queue(iscsi_device *device, iscsi_scsi_task *scsi_task)
+{
+ iscsi_scsi_lun_task_exec( scsi_task->lun, scsi_task );
+}
+
+/**
+ * @brief Checks if an iSCSI target node NAA or EUI hex identifier is valid.
+ *
+ * This function checks if the NAA or
+ * EUI onlycontains only valid
+ * hexadecimal characters.
+ *
+ * @param[in] name Pointer to NAA or EUI name string
+ * to be validated, may NOT be NULL, so
+ * be careful.
+ * @param[in] pos Position of the hexadecimal string
+ * to validate.
+ * @param[in] len Length of the hexadecimal string
+ * to validate.
+ * @retval true The NAA or EUI format is valid.
+ * @retval false The NAA or EUI format is invalid.
+ */
+static bool iscsi_target_node_check_hex(const uint8_t *name, const size_t pos, const size_t len)
+{
+ for ( size_t i = pos; i < len; i++ ) {
+ const uint8_t c = name[i];
+
+ if ( (c < '0') || ((c > '9') && (c < 'A')) || ((c > 'F') && (c < 'a')) || (c > 'f') )
+ return false;
+ }
+
+ return true;
+}
+
+/**
+ * @brief Checks if an iSCSI target node name is valid.
+ *
+ * This function checks the maximum allowed
+ * length of the target name and also if it
+ * contains only valid characters.\n
+ * If the target name starts with 'iqn.' it
+ * checks for valid 'iqn.YYYY-MM.' pattern.\n
+ * If target name starts with 'naa.' or
+ * 'eui.' instead, it will check if the
+ * 16 follow up characters are a valid
+ * hexadecimal string.
+ *
+ * @param[in] name Pointer to target name string to be
+ * validated, may NOT be NULL, so be
+ * careful.
+ * @return 0 if all checks passed successfully,
+ * a negative error code otherwise.
+ */
+static int iscsi_target_node_check_name(const uint8_t *name)
+{
+ if ( iscsi_globvec->target_name_check == ISCSI_GLOBALS_TARGET_NAME_CHECK_NONE )
+ return 0;
+
+ const size_t len = strlen( (char *) name );
+
+ if ( len > ISCSI_TARGET_NODE_MAX_NAME_LEN )
+ return -1;
+
+ if ( (iscsi_globvec->target_name_check == ISCSI_GLOBALS_TARGET_NAME_CHECK_FULL) || (strncasecmp( (char *) name, ISCSI_TARGET_NODE_NAME_IQN_PREFIX, ISCSI_STRLEN(ISCSI_TARGET_NODE_NAME_IQN_PREFIX) ) == 0) || (strncasecmp( (char *) name, ISCSI_TARGET_NODE_NAME_NAA_PREFIX, ISCSI_STRLEN(ISCSI_TARGET_NODE_NAME_NAA_PREFIX) ) == 0) || (strncasecmp( (char *) name, ISCSI_TARGET_NODE_NAME_EUI_PREFIX, ISCSI_STRLEN(ISCSI_TARGET_NODE_NAME_EUI_PREFIX) ) == 0) ) {
+ for ( size_t i = 0; i < len; i++ ) {
+ const uint8_t c = name[i];
+
+ if ( (c <= 0x2CU) || (c == 0x2FU) || ((c >= 0x3BU && c <= 0x40U)) || ((c >= 0x5BU) && (c <= 0x60U)) || ((c >= 0x7BU) && (c <= 0x7FU)) )
+ return -1;
+ }
+ }
+
+ if ( ((strncasecmp( (char *) name, ISCSI_TARGET_NODE_NAME_IQN_PREFIX, ISCSI_STRLEN(ISCSI_TARGET_NODE_NAME_IQN_PREFIX) ) == 0) && (!isdigit(name[4]) || !isdigit(name[5]) || !isdigit(name[6]) || !isdigit(name[7]) || (name[8] != '-') || (name[9] < '0') || (name[9] > '1') || ((name[9] == '0') && ((name[10] < '1') && (name[10] > '9'))) || ((name[9] == '1') && ((name[10] < '0') || (name[10] > '2'))) || (name[11] != '.'))) || (((strncasecmp( (char *) name, ISCSI_TARGET_NODE_NAME_NAA_PREFIX, ISCSI_STRLEN(ISCSI_TARGET_NODE_NAME_NAA_PREFIX) ) == 0) && ((len == (ISCSI_STRLEN(ISCSI_TARGET_NODE_NAME_NAA_PREFIX) + 16)) || (len == (ISCSI_STRLEN(ISCSI_TARGET_NODE_NAME_NAA_PREFIX) + 32))) && !iscsi_target_node_check_hex( name, ISCSI_STRLEN(ISCSI_TARGET_NODE_NAME_NAA_PREFIX), len )) || (((strncasecmp( (char *) name, ISCSI_TARGET_NODE_NAME_EUI_PREFIX, ISCSI_STRLEN(ISCSI_TARGET_NODE_NAME_EUI_PREFIX) ) == 0) && (len == (ISCSI_STRLEN(ISCSI_TARGET_NODE_NAME_EUI_PREFIX) + 16))) && !iscsi_target_node_check_hex( name, ISCSI_STRLEN(ISCSI_TARGET_NODE_NAME_EUI_PREFIX), len ))) )
+ return -1;
+
+ return 0;
+}
+
+/**
+ * @brief Checks if the iSCSI target node flags are valid.
+ *
+ * This function checks if the set flags
+ * are contradicting themselves or are
+ * okay.
+ *
+ * @param[in] flags Target node flags to check.
+ * @param[in] chap_group CHAP group to check.
+ * @return 0 if flags are valid, a negative
+ * error code otherwise.
+ */
+static int iscsi_target_node_check_flags(const int flags, const int32_t chap_group)
+{
+ if ( chap_group < 0L )
+ return -1;
+
+ if ( (((flags & ISCSI_TARGET_NODE_FLAGS_CHAP_DISABLE) == 0) && ((flags & ISCSI_TARGET_NODE_FLAGS_CHAP_REQUIRE) == 0) && ((flags & ISCSI_TARGET_NODE_FLAGS_CHAP_MUTUAL) == 0)) || // Auto
+ (((flags & ISCSI_TARGET_NODE_FLAGS_CHAP_DISABLE) != 0) && ((flags & ISCSI_TARGET_NODE_FLAGS_CHAP_REQUIRE) == 0) && ((flags & ISCSI_TARGET_NODE_FLAGS_CHAP_MUTUAL) == 0)) || // None
+ (((flags & ISCSI_TARGET_NODE_FLAGS_CHAP_DISABLE) == 0) && ((flags & ISCSI_TARGET_NODE_FLAGS_CHAP_REQUIRE) != 0) && ((flags & ISCSI_TARGET_NODE_FLAGS_CHAP_MUTUAL) == 0)) || // CHAP
+ (((flags & ISCSI_TARGET_NODE_FLAGS_CHAP_DISABLE) == 0) && ((flags & ISCSI_TARGET_NODE_FLAGS_CHAP_REQUIRE) != 0) && ((flags & ISCSI_TARGET_NODE_FLAGS_CHAP_MUTUAL) != 0)) ) // CHAP Mutual
+ return 0;
+
+ return -1;
+}
+
+/**
+ * @brief Creates, initializes and adds a portal group to an iSCSI target node.
+ *
+ * Callback function for each element while iterating
+ * through the iSCSI global vector portal group
+ * hash map.
+ *
+ * @param[in] key Pointer to zero padded key. NULL is
+ * an invalid pointer here, so be careful.
+ * @param[in] key_size Number of bytes for the key.
+ * @param[in] value Value of the key, NULL creates an
+ * empty key assignment.
+ * @param[in,out] user_data Pointer to the iSCSI target
+ * node to be added and may NOT be NULL, so be
+ * careful.
+ * @retval -1 An error occured during adding the
+ * iSCSI portal group to the iSCSI target node.
+ * @retval 0 The iSCSI portal group has been
+ * added successfully.
+ */
+int iscsi_target_node_create_callback(uint8_t *key, const size_t key_size, uint8_t *value, uint8_t *user_data)
+{
+ iscsi_target_node *target = (iscsi_target_node *) user_data;
+ iscsi_portal_group *portal_group = (iscsi_portal_group *) value;
+ uint8_t *port_name = iscsi_sprintf_alloc( "%s,t,0x%4.4" PRIx64, target->device->name, portal_group->tag );
+
+ if ( port_name == NULL )
+ return -1;
+
+ const int rc = iscsi_device_port_add( target->device, port_name, (uint64_t) portal_group->tag );
+
+ free( port_name );
+
+ return rc;
+}
+
+/**
+ * @brief Creates and initializes an iSCSI target node.
+ *
+ * This function also allocates the underlying SCSI
+ * device and always initializes the first LUN.
+ *
+ * @param[in] name Pointer to IQN name of target node,
+ * may NOT be NULL, so be careful.
+ * @param[in] alias Pointer to alias of IQN name.
+ * @param[in] index Target node index number.
+ * @param[in] lun_id LUN identifier to associate with underlying SCSI device.
+ * @param[in] queue_depth Maximum queue depth.
+ * @param[in] flags Flags for this target node.
+ * @param[in] chap_group CHAP group to associate this node with.
+ * @param[in] header_digest Header digest size (always MUST be 0 or 4 for now).
+ * @param[in] data_digest Data digest size (always MUST be 0 or 4 for now).
+ * @return Pointer to iSCSI target node on successful
+ * operation or NULL in case of an error.
+ */
+iscsi_target_node *iscsi_target_node_create(uint8_t *name, const uint8_t *alias, const int index, const int lun_id, const uint queue_depth, const int flags, const int32_t chap_group, const int header_digest, const int data_digest)
+{
+ if ( (name == NULL) || (iscsi_target_node_check_name( name ) < 0) || (iscsi_target_node_check_flags( flags, chap_group ) < 0) )
+ return NULL;
+
+ iscsi_target_node *target = (iscsi_target_node *) malloc( sizeof(struct iscsi_target_node) );
+
+ if ( target == NULL ) {
+ logadd( LOG_ERROR, "iscsi_target_node_create: Out of memory allocating iSCSI target node" );
+
+ return NULL;
+ }
+
+ const uint name_len = (uint) (strlen( (char *) name ) + 1U);
+
+ target->name = malloc( name_len );
+
+ if ( target->name == NULL ) {
+ logadd( LOG_ERROR, "iscsi_target_node_create: Out of memory allocating iSCSI target node name" );
+
+ free( target );
+
+ return NULL;
+ }
+
+ memcpy( target->name, name, name_len );
+
+ if ( alias != NULL ) {
+ const uint alias_len = (uint) (strlen( (char *) alias ) + 1U);
+
+ target->alias = malloc( alias_len );
+
+ if ( target->alias == NULL ) {
+ logadd( LOG_ERROR, "iscsi_target_node_create: Out of memory allocating iSCSI target node alias" );
+
+ free( target->name );
+ free( target );
+
+ return NULL;
+ }
+
+ memcpy( target->alias, alias, alias_len );
+ } else {
+ target->alias = NULL;
+ }
+
+ dnbd3_image_t *image = iscsi_target_node_image_get( name );
+
+ if ( image == NULL ) {
+ if ( target->alias != NULL )
+ free( target->alias );
+
+ free( target->name );
+ free( target );
+
+ return NULL;
+ }
+
+ const uint key_len = (uint) (strlen( (char *) image->name ) + 1U);
+ uint8_t *hash_key = iscsi_hashmap_key_create( (uint8_t *) image->name, key_len );
+ iscsi_device *device = NULL;
+
+ pthread_rwlock_wrlock( &iscsi_globvec->devices_rwlock );
+ int rc = iscsi_hashmap_get( iscsi_globvec->devices, hash_key, key_len, (uint8_t **) &device );
+
+ if ( device != NULL ) {
+ pthread_rwlock_wrlock( &device->luns_rwlock );
+
+ iscsi_scsi_lun *lun = iscsi_device_find_lun( device, lun_id );
+
+ if ( lun == NULL ) {
+ lun = iscsi_scsi_lun_create( lun_id );
+
+ if ( lun == NULL ) {
+ logadd( LOG_ERROR, "iscsi_target_node_create: Out of memory allocating iSCSI device LUN hash map" );
+
+ pthread_rwlock_unlock( &device->luns_rwlock );
+ pthread_rwlock_unlock( &iscsi_globvec->devices_rwlock );
+ iscsi_hashmap_key_destroy( hash_key );
+
+ if ( target->alias != NULL )
+ free( target->alias );
+
+ free( target->name );
+ free( target );
+
+ return NULL;
+ }
+
+ const uint64_t lun_hash = lun_id;
+ uint8_t *lun_hash_key = iscsi_hashmap_key_create( (uint8_t *) &lun_hash, sizeof(lun_hash) );
+
+ if ( lun_hash_key == NULL ) {
+ logadd( LOG_ERROR, "iscsi_target_node_create: Out of memory allocating iSCSI device LUN hash map" );
+
+ pthread_rwlock_unlock( &device->luns_rwlock );
+ pthread_rwlock_unlock( &iscsi_globvec->devices_rwlock );
+ iscsi_scsi_lun_destroy( lun );
+ iscsi_hashmap_key_destroy( hash_key );
+
+ if ( target->alias != NULL )
+ free( target->alias );
+
+ free( target->name );
+ free( target );
+
+ return NULL;
+ }
+
+ const int rc = iscsi_hashmap_put( device->luns, lun_hash_key, sizeof(lun_hash), (uint8_t *) lun );
+
+ if ( rc < 0 ) {
+ pthread_rwlock_unlock( &device->luns_rwlock );
+ pthread_rwlock_unlock( &iscsi_globvec->devices_rwlock );
+ iscsi_hashmap_key_destroy( lun_hash_key );
+ iscsi_scsi_lun_destroy( lun );
+ iscsi_hashmap_key_destroy( hash_key );
+
+ if ( target->alias != NULL )
+ free( target->alias );
+
+ free( target->name );
+ free( target );
+
+ return NULL;
+ }
+ }
+
+ pthread_rwlock_unlock( &device->luns_rwlock );
+ iscsi_hashmap_key_destroy( hash_key );
+
+ hash_key = NULL;
+ } else {
+ device = iscsi_device_create( (uint8_t *) image->name, lun_id, ISCSI_TRANSPORT_ID_PROTOCOL_ID_ISCSI );
+
+ if ( device == NULL ) {
+ logadd( LOG_ERROR, "iscsi_target_node_create: Out of memory allocating iSCSI target device" );
+
+ pthread_rwlock_unlock( &iscsi_globvec->devices_rwlock );
+ iscsi_hashmap_key_destroy( hash_key );
+
+ if ( target->alias != NULL )
+ free( target->alias );
+
+ free( target->name );
+ free( target );
+
+ return NULL;
+ }
+
+ rc = iscsi_hashmap_put( iscsi_globvec->devices, hash_key, key_len, (uint8_t *) device );
+
+ if ( rc < 0 ) {
+ pthread_rwlock_unlock( &iscsi_globvec->devices_rwlock );
+ iscsi_device_destroy( device );
+ iscsi_hashmap_key_destroy( hash_key );
+
+ if ( target->alias != NULL )
+ free( target->alias );
+
+ free( target->name );
+ free( target );
+
+ return NULL;
+ }
+ }
+
+ device->active_conns++;
+
+ pthread_rwlock_unlock( &iscsi_globvec->devices_rwlock );
+
+ target->device = device;
+
+ pthread_rwlock_rdlock( &iscsi_globvec->portal_groups_rwlock );
+ rc = iscsi_hashmap_iterate( iscsi_globvec->portal_groups, iscsi_target_node_create_callback, (uint8_t *) target );
+ pthread_rwlock_unlock( &iscsi_globvec->portal_groups_rwlock );
+
+ if ( rc < 0 ) {
+ if ( hash_key != NULL ) {
+ pthread_rwlock_wrlock( &iscsi_globvec->devices_rwlock );
+ iscsi_hashmap_remove( iscsi_globvec->devices, hash_key, key_len );
+ pthread_rwlock_unlock( &iscsi_globvec->devices_rwlock );
+ iscsi_device_destroy( device );
+ iscsi_hashmap_key_destroy( hash_key );
+ } else {
+ pthread_rwlock_wrlock( &iscsi_globvec->devices_rwlock );
+
+ device->active_conns--;
+
+ pthread_rwlock_unlock( &iscsi_globvec->devices_rwlock );
+ }
+
+ if ( target->alias != NULL )
+ free( target->alias );
+
+ free( target->name );
+ free( target );
+
+ return NULL;
+ }
+
+ target->num = index;
+ target->queue_depth = queue_depth;
+ target->flags = flags;
+ target->header_digest = header_digest;
+ target->data_digest = data_digest;
+ target->chap_group = chap_group;
+ target->active_conns = 0UL;
+
+ return target;
+}
+
+/**
+ * @brief iSCSI target node destructor callback for hash map.
+ *
+ * Callback function for deallocation of an iSCSI
+ * target node stored in the hash map managing all
+ * iSCSI target nodes.
+ *
+ * @param[in] key Pointer to zero padded key. NULL is
+ * an invalid pointer here, so be careful.
+ * @param[in] key_size Number of bytes for the key.
+ * @param[in] value Value of the key, NULL is allowed.
+ * @param[in,out] user_data This argument is not used by
+ * this function and should be always NULL for now, as
+ * there is a possibility for future usage.
+ * @return Always returns 0 as this function cannot fail.
+ */
+int iscsi_target_node_destroy_callback(uint8_t *key, const size_t key_size, uint8_t *value, uint8_t *user_data)
+{
+ iscsi_target_node_destroy( (iscsi_target_node *) value );
+ iscsi_hashmap_key_destroy( key );
+
+ return 0;
+}
+
+/**
+ * @brief Deallocates all resources acquired by iscsi_target_node_create.
+ *
+ * This function also frees the IQN name,
+ * IQN alias and the associated SCSI device.
+ *
+ * @param[in] target Pointer to iSCSI target node to be freed.
+ * May be NULL in which case this function
+ * does nothing at all.
+ */
+void iscsi_target_node_destroy(iscsi_target_node *target)
+{
+ if ( target != NULL ) {
+ if ( target->alias != NULL ) {
+ free( target->alias );
+
+ target->alias = NULL;
+ }
+
+ if ( target->name != NULL ) {
+ free( target->name );
+
+ target->name = NULL;
+ }
+
+ free( target );
+ }
+}
+
+/**
+ * @brief Sends a buffer from a source iSCSI IQN to target iSCSI IQNs.
+ *
+ * This function sends a buffer starting from a
+ * specified position to one, multiple or all
+ * target IQNs.\n
+ * This function also checks the input and output
+ * IQN for conforming to iSCSI specifications.
+ *
+ * @param[in] conn Pointer to iSCSI connection to write the buffer.
+ * @param[in] dst_iqn Pointer to string containing the target IQNs,
+ * NULL is not allowed here, take caution.
+ * @param[in] src_iqn Pointer to string containing the source IQN.
+ * May NOT be NULL, so be careful.
+ * @param[in] buf Pointer to output buffer.
+ * @param[in] pos Position in buffer in bytes to start sending.
+ * @param[in] len Length of buffer in bytes.
+ * @return The new position of the written data or a
+ * negative error code otherwise.
+ */
+int32_t iscsi_target_node_send(iscsi_connection *conn, const uint8_t *dst_iqn, const uint8_t *src_iqn, uint8_t *buf, const uint32_t pos, const uint32_t len)
+{
+ // TODO: Implement function.
+
+ return -1;
+}
+
+/**
+ * @brief Calculates the WWN using 64-bit IEEE Extended NAA for a name.
+ *
+ * @param[in] name Pointer to string containing the
+ * name to calculate the IEEE Extended
+ * NAA for. NULL is NOT allowed here, so
+ * take caution.
+ * @return A 64-bit unsigned integer for
+ * storing the IEEE Extended NAA.
+ */
+uint64_t iscsi_target_node_wwn_get(const uint8_t *name)
+{
+ uint64_t value = 0ULL;
+ int i = 0;
+
+ while ( name[i] != '\0' ) {
+ value = (value * 131ULL) + name[i++];
+ }
+
+ const uint64_t id_a = ((value & 0xFFF000000ULL) << 24ULL);
+
+ return ((value & 0xFFFFFFULL) | 0x2000000347000000ULL | id_a);
+}
+
+/**
+ * @brief Extracts the DNBD3 image out of an iSCSI IQN string and opens the DNBD3 image.
+ *
+ * This function uses the : separator as
+ * specified by the IQN standard.\n
+ * If no colons are in the IQN string,
+ * the complete string will be
+ * considered the image file name.\n
+ * The image file name is assumed
+ * before the last colon and is
+ * either directly opened or if
+ * that fails, a WWN name by
+ * IEEE Extended NAA is tried as
+ * well.\n
+ * The image revision is assumed
+ * after the last colon.
+ * @param[in] iqn Pointer to iSCSI IQN string. This
+ * is not allowed to be NULL, so be careful.
+ * @return Pointer to DNBD3 image if successful
+ * operation or NULL if failed.
+ */
+dnbd3_image_t *iscsi_target_node_image_get(uint8_t *iqn)
+{
+ uint8_t *image_name = iqn;
+ uint8_t *image_rev = NULL;
+ uint8_t *tmp = (uint8_t *) strchr( (char *) iqn, ':' );
+
+ while ( tmp != NULL ) {
+ tmp++;
+
+ if ( image_rev != NULL )
+ image_name = image_rev;
+
+ image_rev = tmp;
+ tmp = (uint8_t *) strchr( (char *) tmp, ':' );
+ }
+
+ if ( image_rev == NULL )
+ image_rev = image_name;
+
+ const uint len = (uint) (image_rev - image_name);
+
+ if ( len > 0U ) {
+ tmp = (uint8_t *) malloc( len );
+
+ if ( tmp == NULL ) {
+ logadd( LOG_ERROR, "iscsi_target_node_image_get: Out of memory while allocating DNBD3 image name for iSCSI target node" );
+
+ return NULL;
+ }
+
+ memcpy( tmp, image_name, (len - 1) );
+ tmp[len - 1] = '\0';
+ } else {
+ tmp = image_name;
+ }
+
+ const uint16_t rev = (uint16_t) ((len > 0U) ? atoi( (char *) image_rev ) : 0);
+ dnbd3_image_t *image = image_getOrLoad( (char *) image_name, rev );
+
+ if ( image == NULL ) {
+ image = image_getOrLoad( (char *) tmp, rev );
+
+ if ( image == NULL ) {
+ if ( strncasecmp( (char *) image_name, ISCSI_TARGET_NODE_NAME_WWN_PREFIX, ISCSI_STRLEN(ISCSI_TARGET_NODE_NAME_WWN_PREFIX) ) == 0 ) {
+ uint64_t wwn = strtoull( (char *) (image_name + ISCSI_STRLEN(ISCSI_TARGET_NODE_NAME_WWN_PREFIX)), NULL, 16 );
+
+ image = image_getByWwn( wwn, rev, true );
+
+ if ( image == NULL ) {
+ wwn = strtoull( (char *) (tmp + ISCSI_STRLEN(ISCSI_TARGET_NODE_NAME_WWN_PREFIX)), NULL, 16 );
+ image = image_getByWwn( wwn, rev, true );
+ }
+ } else if ( strncasecmp( (char *) image_name, ISCSI_TARGET_NODE_NAME_NAA_PREFIX, ISCSI_STRLEN(ISCSI_TARGET_NODE_NAME_NAA_PREFIX) ) == 0 ) {
+ uint64_t wwn = strtoull( (char *) (image_name + ISCSI_STRLEN(ISCSI_TARGET_NODE_NAME_NAA_PREFIX)), NULL, 16 );
+
+ image = image_getByWwn( wwn, rev, true );
+
+ if ( image == NULL ) {
+ wwn = strtoull( (char *) (tmp + ISCSI_STRLEN(ISCSI_TARGET_NODE_NAME_NAA_PREFIX)), NULL, 16 );
+ image = image_getByWwn( wwn, rev, true );
+ }
+ } else if ( strncasecmp( (char *) image_name, ISCSI_TARGET_NODE_NAME_EUI_PREFIX, ISCSI_STRLEN(ISCSI_TARGET_NODE_NAME_EUI_PREFIX) ) == 0 ) {
+ uint64_t wwn = (0x2ULL << 60ULL) | (strtoull( (char *) (image_name + ISCSI_STRLEN(ISCSI_TARGET_NODE_NAME_EUI_PREFIX)), NULL, 16 ) & 0x0FFFFFFFFFFFFFFFULL);
+
+ image = image_getByWwn( wwn, rev, true );
+
+ if ( image == NULL ) {
+ wwn = (0x2ULL << 60ULL) | (strtoull( (char *) (tmp + ISCSI_STRLEN(ISCSI_TARGET_NODE_NAME_EUI_PREFIX)), NULL, 16 ) & 0x0FFFFFFFFFFFFFFFULL);
+ image = image_getByWwn( wwn, rev, true );
+ }
+ }
+ }
+ }
+
+ if ( len > 0U )
+ free( tmp );
+
+ return image;
}
/**
@@ -2721,30 +8735,28 @@ iscsi_port *iscsi_device_find_port_by_portal_group_tag(const iscsi_device *devic
*
* @param[in] key Pointer to zero padded key. NULL is
* an invalid pointer here, so be careful.
- * @param[in] key_size Number of bytes for the key, MUST
- * be a multiple of 8 bytes which is NOT checked, so
- * be careful.
+ * @param[in] key_size Number of bytes for the key.
* @param[in] value Value of the key, NULL creates an
* empty key assignment.
* @param[in,out] user_data Pointer to a data structure
* containing the iSCSI target node and the name to be
* searched for and may NOT be NULL, so be careful.
* @retval -1 The target node has been found and stored
- * in the result strcuture. Therefore, no further
+ * in the result structure. Therefore, no further
* searching is needed.
* @retval 0 The target node has not been found yet.
*/
int iscsi_target_node_find_callback(uint8_t *key, const size_t key_size, uint8_t *value, uint8_t *user_data)
{
iscsi_target_node_find_name *target_find = (iscsi_target_node_find_name *) user_data;
- iscsi_target_node *target = (iscsi_target_node *) value;
+ iscsi_target_node *target = (iscsi_target_node *) value;
if ( strcasecmp( (char *) target->name, (char *) target_find->name ) != 0 )
- return 0L;
+ return 0;
target_find->target = target;
- return -1L;
+ return -1;
}
/**
@@ -2766,9 +8778,58 @@ iscsi_target_node *iscsi_target_node_find(uint8_t *target_name)
iscsi_target_node_find_name target_find = {NULL, target_name};
+ pthread_rwlock_wrlock( &iscsi_globvec->target_nodes_rwlock );
iscsi_hashmap_iterate( iscsi_globvec->target_nodes, iscsi_target_node_find_callback, (uint8_t *) &target_find );
- return target_find.target;
+ iscsi_target_node *target = target_find.target;
+
+ if ( target == NULL ) {
+ dnbd3_image_t *image = iscsi_target_node_image_get( target_name );
+
+ if ( image == NULL ) {
+ pthread_rwlock_unlock( &iscsi_globvec->target_nodes_rwlock );
+
+ return NULL;
+ }
+
+ target = iscsi_target_node_create( target_name, NULL, 0, image->rid, 16U, 0, 0L, 0, 0 );
+
+ if ( target == NULL ) {
+ logadd( LOG_ERROR, "iscsi_target_node_find: Out of memory while allocating iSCSI target node" );
+
+ pthread_rwlock_unlock( &iscsi_globvec->target_nodes_rwlock );
+
+ return NULL;
+ }
+
+ const uint key_len = (uint) (strlen( (char *) target_name ) + 1U);
+ uint8_t *hash_key = iscsi_hashmap_key_create( target_name, key_len );
+
+ if ( hash_key == NULL ) {
+ logadd( LOG_ERROR, "iscsi_target_node_find: Out of memory while allocating iSCSI target node" );
+
+ pthread_rwlock_unlock( &iscsi_globvec->target_nodes_rwlock );
+ iscsi_target_node_destroy( target );
+
+ return NULL;
+ }
+
+ int rc = iscsi_hashmap_put( iscsi_globvec->target_nodes, (uint8_t *) hash_key, key_len, (uint8_t *) target );
+
+ if ( rc < 0 ) {
+ pthread_rwlock_unlock( &iscsi_globvec->target_nodes_rwlock );
+ iscsi_hashmap_key_destroy( (uint8_t *) hash_key );
+ iscsi_target_node_destroy( target );
+
+ return NULL;
+ }
+ }
+
+ target->active_conns++;
+
+ pthread_rwlock_unlock( &iscsi_globvec->target_nodes_rwlock );
+
+ return target;
}
/**
@@ -2813,7 +8874,7 @@ int iscsi_target_node_access(iscsi_connection *conn, iscsi_target_node *target,
{
// TODO: Implement access check function
- return 0L;
+ return 0;
}
/**
@@ -2841,79 +8902,71 @@ iscsi_session *iscsi_session_create(iscsi_connection *conn, iscsi_target_node *t
return NULL;
}
- session->max_conns = ISCSI_SESSION_DEFAULT_MAX_CONNECTIONS;
- session->max_outstanding_r2t = ISCSI_SESSION_DEFAULT_MAX_OUTSTANDING_R2T;
- session->default_time_to_wait = ISCSI_SESSION_DEFAULT_TIME_TO_WAIT;
- session->default_time_to_retain = ISCSI_SESSION_DEFAULT_TIME_TO_RETAIN;
- session->first_burst_len = ISCSI_SESSION_DEFAULT_FIRST_BURST_LEN;
- session->max_burst_len = ISCSI_SESSION_DEFAULT_MAX_BURST_LEN;
- session->init_r2t = ISCSI_SESSION_DEFAULT_INIT_R2T;
- session->immediate_data = ISCSI_SESSION_DEFAULT_IMMEDIATE_DATA;
- session->data_pdu_in_order = ISCSI_SESSION_DEFAULT_DATA_PDU_IN_ORDER;
- session->data_seq_in_order = ISCSI_SESSION_DEFAULT_DATA_SEQ_IN_ORDER;
- session->err_recovery_level = ISCSI_SESSION_DEFAULT_ERR_RECOVERY_LEVEL;
- session->tag = conn->pg_tag;
-
- session->connections = iscsi_hashmap_create( session->max_conns );
+ session->tag = conn->pg_tag;
+ session->flags = 0;
- if ( session->connections == NULL ) {
- logadd( LOG_ERROR, "iscsi_session_create: Out of memory allocating iSCSI session connection hash map" );
+ if ( (iscsi_globvec->flags & ISCSI_GLOBALS_FLAGS_INIT_R2T) != 0 )
+ session->flags |= ISCSI_SESSION_FLAGS_INIT_R2T;
- free( session );
+ if ( (iscsi_globvec->flags & ISCSI_GLOBALS_FLAGS_IMMEDIATE_DATA) != 0 )
+ session->flags |= ISCSI_SESSION_FLAGS_IMMEDIATE_DATA;
- return NULL;
- }
+ if ( (iscsi_globvec->flags & ISCSI_GLOBALS_FLAGS_DATA_PDU_IN_ORDER) != 0 )
+ session->flags |= ISCSI_SESSION_FLAGS_DATA_PDU_IN_ORDER;
- uint8_t *conn_key = iscsi_hashmap_key_create( (uint8_t *) &conn->cid, sizeof(conn->cid) );
+ if ( (iscsi_globvec->flags & ISCSI_GLOBALS_FLAGS_DATA_SEQ_IN_ORDER) != 0 )
+ session->flags |= ISCSI_SESSION_FLAGS_DATA_SEQ_IN_ORDER;
- if ( conn_key == NULL ) {
- logadd( LOG_ERROR, "iscsi_session_create: Out of memory allocating iSCSI session connection hash map" );
+ session->conns = 1UL;
+ session->max_conns = iscsi_globvec->max_session_conns;
+ session->max_outstanding_r2t = iscsi_globvec->max_outstanding_r2t;
+ session->default_time_to_wait = iscsi_globvec->default_time_to_wait;
+ session->default_time_to_retain = iscsi_globvec->default_time_to_retain;
+ session->first_burst_len = iscsi_globvec->first_burst_len;
+ session->max_burst_len = iscsi_globvec->max_burst_len;
+ session->err_recovery_level = iscsi_globvec->err_recovery_level;
- iscsi_hashmap_destroy( session->connections );
- free( session );
-
- return NULL;
- }
-
- iscsi_hashmap_put( session->connections, conn_key, sizeof(conn->cid), (uint8_t *) conn );
-
- session->target = target;
- session->isid = 0LL;
- session->type = type;
- session->current_text_init_task_tag = 0xFFFFFFFFUL;
+ iscsi_list_create( &session->conn_list );
+ iscsi_list_enqueue( &session->conn_list, &conn->node );
- session->key_value_pairs = iscsi_hashmap_create( 32UL );
+ session->key_value_pairs = iscsi_hashmap_create( ((sizeof(iscsi_session_key_value_pair_lut) / sizeof(struct iscsi_key_value_pair_lut_entry)) - 1) );
if ( session->key_value_pairs == NULL ) {
logadd( LOG_ERROR, "iscsi_session_create: Out of memory allocating iSCSI session key and value pairs hash map" );
- iscsi_hashmap_key_destroy( conn_key );
- iscsi_hashmap_destroy( session->connections );
free( session );
return NULL;
}
+ session->target = target;
+ session->isid = 0ULL;
+ session->tsih = 0ULL;
+ session->queue_depth = 0U;
+ session->type = type;
+ session->exp_cmd_sn = 0UL;
+ session->max_cmd_sn = 0UL;
+ session->current_text_init_task_tag = 0xFFFFFFFFUL;
+
int rc = iscsi_session_init_key_value_pairs( session->key_value_pairs );
- rc |= iscsi_update_int_key_value_pair( session->key_value_pairs, (uint8_t *) ISCSI_LOGIN_AUTH_SESSION_TEXT_KEY_MAX_CONNECTIONS, session->max_conns );
- rc |= iscsi_update_int_key_value_pair( session->key_value_pairs, (uint8_t *) ISCSI_LOGIN_AUTH_SESSION_TEXT_KEY_MAX_OUTSTANDING_R2T, session->max_outstanding_r2t );
- rc |= iscsi_update_int_key_value_pair( session->key_value_pairs, (uint8_t *) ISCSI_LOGIN_AUTH_SESSION_TEXT_KEY_DEFAULT_TIME_WAIT, session->default_time_to_wait );
- rc |= iscsi_update_int_key_value_pair( session->key_value_pairs, (uint8_t *) ISCSI_LOGIN_AUTH_SESSION_TEXT_KEY_DEFAULT_TIME_RETAIN, session->default_time_to_retain );
- rc |= iscsi_update_int_key_value_pair( session->key_value_pairs, (uint8_t *) ISCSI_LOGIN_AUTH_SESSION_TEXT_KEY_FIRST_BURST_LEN, session->first_burst_len );
- rc |= iscsi_update_int_key_value_pair( session->key_value_pairs, (uint8_t *) ISCSI_LOGIN_AUTH_SESSION_TEXT_KEY_MAX_BURST_LEN, session->max_burst_len );
- rc |= iscsi_update_bool_key_value_pair( session->key_value_pairs, (uint8_t *) ISCSI_LOGIN_AUTH_SESSION_TEXT_KEY_IMMEDIATE_DATA, session->immediate_data );
- rc |= iscsi_update_bool_key_value_pair( session->key_value_pairs, (uint8_t *) ISCSI_LOGIN_AUTH_SESSION_TEXT_KEY_DATA_PDU_IN_ORDER, session->data_pdu_in_order );
- rc |= iscsi_update_bool_key_value_pair( session->key_value_pairs, (uint8_t *) ISCSI_LOGIN_AUTH_SESSION_TEXT_KEY_DATA_SEQ_IN_ORDER, session->data_seq_in_order );
- rc |= iscsi_update_int_key_value_pair( session->key_value_pairs, (uint8_t *) ISCSI_LOGIN_AUTH_SESSION_TEXT_KEY_ERR_RECOVERY_LEVEL, session->err_recovery_level );
- rc |= iscsi_update_int_key_value_pair( conn->key_value_pairs, (uint8_t *) ISCSI_LOGIN_AUTH_SESSION_TEXT_KEY_MAX_RECV_DS_LEN, conn->max_recv_ds_len );
+ rc |= iscsi_update_int_key_value_pair( session->key_value_pairs, ISCSI_LOGIN_AUTH_SESSION_TEXT_KEY_MAX_CONNECTIONS, session->max_conns );
+ rc |= iscsi_update_int_key_value_pair( session->key_value_pairs, ISCSI_LOGIN_AUTH_SESSION_TEXT_KEY_MAX_OUTSTANDING_R2T, session->max_outstanding_r2t );
+ rc |= iscsi_update_int_key_value_pair( session->key_value_pairs, ISCSI_LOGIN_AUTH_SESSION_TEXT_KEY_DEFAULT_TIME_WAIT, session->default_time_to_wait );
+ rc |= iscsi_update_int_key_value_pair( session->key_value_pairs, ISCSI_LOGIN_AUTH_SESSION_TEXT_KEY_DEFAULT_TIME_RETAIN, session->default_time_to_retain );
+ rc |= iscsi_update_int_key_value_pair( session->key_value_pairs, ISCSI_LOGIN_AUTH_SESSION_TEXT_KEY_FIRST_BURST_LEN, session->first_burst_len );
+ rc |= iscsi_update_int_key_value_pair( session->key_value_pairs, ISCSI_LOGIN_AUTH_SESSION_TEXT_KEY_MAX_BURST_LEN, session->max_burst_len );
+ rc |= iscsi_update_bool_key_value_pair( session->key_value_pairs, ISCSI_LOGIN_AUTH_SESSION_TEXT_KEY_INITIAL_R2T, (session->flags & ISCSI_SESSION_FLAGS_INIT_R2T) );
+ rc |= iscsi_update_bool_key_value_pair( session->key_value_pairs, ISCSI_LOGIN_AUTH_SESSION_TEXT_KEY_IMMEDIATE_DATA, (session->flags & ISCSI_SESSION_FLAGS_IMMEDIATE_DATA) );
+ rc |= iscsi_update_bool_key_value_pair( session->key_value_pairs, ISCSI_LOGIN_AUTH_SESSION_TEXT_KEY_DATA_PDU_IN_ORDER, (session->flags & ISCSI_SESSION_FLAGS_DATA_PDU_IN_ORDER) );
+ rc |= iscsi_update_bool_key_value_pair( session->key_value_pairs, ISCSI_LOGIN_AUTH_SESSION_TEXT_KEY_DATA_SEQ_IN_ORDER, (session->flags & ISCSI_SESSION_FLAGS_DATA_SEQ_IN_ORDER) );
+ rc |= iscsi_update_int_key_value_pair( session->key_value_pairs, ISCSI_LOGIN_AUTH_SESSION_TEXT_KEY_ERR_RECOVERY_LEVEL, session->err_recovery_level );
+ rc |= iscsi_update_int_key_value_pair( conn->key_value_pairs, ISCSI_LOGIN_AUTH_SESSION_TEXT_KEY_MAX_RECV_DS_LEN, conn->max_recv_ds_len );
if ( rc != 0 ) {
logadd( LOG_ERROR, "iscsi_session_create: Out of memory adding iSCSI session key and integer value pair" );
iscsi_hashmap_iterate( session->key_value_pairs, iscsi_hashmap_key_destroy_value_callback, NULL );
iscsi_hashmap_destroy( session->key_value_pairs );
- iscsi_hashmap_key_destroy( conn_key );
- iscsi_hashmap_destroy( session->connections );
free( session );
return NULL;
@@ -2923,18 +8976,43 @@ iscsi_session *iscsi_session_create(iscsi_connection *conn, iscsi_target_node *t
}
/**
+ * @brief iSCSI session destructor callback for hash map.
+ *
+ * Callback function for deallocation of an iSCSI
+ * session stored in the hash map managing all iSCSI
+ * sessions.
+ *
+ * @param[in] key Pointer to zero padded key. NULL is
+ * an invalid pointer here, so be careful.
+ * @param[in] key_size Number of bytes for the key.
+ * @param[in] value Value of the key, NULL is allowed.
+ * @param[in,out] user_data This argument is not used by
+ * this function and should be always NULL for now, as
+ * there is a possibility for future usage.
+ * @return Always returns 0 as this function cannot fail.
+ */
+int iscsi_session_destroy_callback(uint8_t *key, const size_t key_size, uint8_t *value, uint8_t *user_data)
+{
+ iscsi_session_destroy( (iscsi_session *) value );
+ iscsi_hashmap_key_destroy( key );
+
+ return 0;
+}
+
+/**
* @brief Deallocates all resources acquired by iscsi_session_create.
*
* This function also frees the associated key and value pairs,
- * the attached connections as well as frees the initator port.
+ * the attached connections as well as frees the initiator
+ * port.
*
- * @param[in] session iSCSI session to be freed. May be NULL
- * in which case this function does nothing at all.
+ * @param[in] session Pointer to iSCSI session to be freed.
+ * May be NULL in which case this function does nothing at all.
*/
void iscsi_session_destroy(iscsi_session *session)
{
if ( session != NULL ) {
- session->tag = 0L;
+ session->tag = 0ULL;
session->target = NULL;
session->type = ISCSI_SESSION_TYPE_INVALID;
@@ -2945,17 +9023,18 @@ void iscsi_session_destroy(iscsi_session *session)
session->key_value_pairs = NULL;
}
- if ( session->connections != NULL ) {
- iscsi_hashmap_iterate( session->connections, iscsi_connection_destroy_callback, NULL );
- iscsi_hashmap_destroy( session->connections );
+ iscsi_connection *conn;
+ iscsi_connection *tmp;
- session->connections = NULL;
+ iscsi_list_foreach_safe_node ( &session->conn_list, conn, tmp ) {
+ iscsi_list_remove( &conn->node );
+ iscsi_connection_destroy( conn );
}
- if ( session->initiator_port != NULL ) {
- iscsi_port_destroy( session->initiator_port );
+ if ( session->init_port != NULL ) {
+ iscsi_port_destroy( session->init_port );
- session->initiator_port = NULL;
+ session->init_port = NULL;
}
free( session ); // TODO: Check if potential reusage of session makes sense.
@@ -2980,14 +9059,14 @@ void iscsi_session_destroy(iscsi_session *session)
*/
static int iscsi_init_key_value_pairs(iscsi_hashmap *key_value_pairs, const iscsi_key_value_pair_lut_entry *lut)
{
- for ( uint i = 0; lut[i].key != NULL; i++ ) {
+ for ( uint i = 0U; lut[i].key != NULL; i++ ) {
const int rc = iscsi_add_key_value_pair( key_value_pairs, lut[i].key, lut[i].value );
if ( rc < 0 )
return rc;
}
- return 0L;
+ return 0;
}
/**
@@ -3029,10 +9108,10 @@ iscsi_connection *iscsi_connection_create(iscsi_portal *portal, const int sock)
}
conn->session = NULL;
- conn->key_value_pairs = iscsi_hashmap_create( 32UL );
+ conn->key_value_pairs = iscsi_hashmap_create( ((sizeof(iscsi_connection_key_value_pair_lut) / sizeof(struct iscsi_key_value_pair_lut_entry)) - 1) );
if ( conn->key_value_pairs == NULL ) {
- logadd( LOG_ERROR, "iscsi_create_connection: Out of memory while allocating iSCSI text key / value pair hash map" );
+ logadd( LOG_ERROR, "iscsi_create_connection: Out of memory while allocating iSCSI login text key / value pair hash map" );
free( conn );
@@ -3049,7 +9128,20 @@ iscsi_connection *iscsi_connection_create(iscsi_portal *portal, const int sock)
return NULL;
}
- conn->partial_pairs = NULL;
+ conn->partial_pairs = NULL;
+ conn->text_key_value_pairs = iscsi_hashmap_create( ((sizeof(iscsi_connection_key_value_pair_lut) / sizeof(struct iscsi_key_value_pair_lut_entry)) - 1) );
+
+ if ( conn->text_key_value_pairs == NULL ) {
+ logadd( LOG_ERROR, "iscsi_create_connection: Out of memory while allocating iSCSI text key / value pair hash map" );
+
+ iscsi_hashmap_iterate( conn->key_value_pairs, iscsi_hashmap_key_destroy_value_callback, NULL );
+ iscsi_hashmap_destroy( conn->key_value_pairs );
+ free( conn );
+
+ return NULL;
+ }
+
+ conn->text_partial_pairs = NULL;
conn->device = NULL;
conn->init_port = NULL;
conn->init_name = NULL;
@@ -3059,34 +9151,119 @@ iscsi_connection *iscsi_connection_create(iscsi_portal *portal, const int sock)
conn->target_name_short = NULL;
conn->portal_host = NULL;
conn->portal_port = NULL;
- conn->header_digest = 0L;
- conn->data_digest = 0L;
- conn->pdu_processing = NULL;
+ conn->pdu_processing = NULL;
+
+ iscsi_list_create( &conn->scsi_data_in_queued_tasks );
+
conn->login_response_pdu = NULL;
- conn->id = 0L;
- conn->sock = sock;
- conn->pdu_recv_state = ISCSI_CONNECT_PDU_RECV_STATE_WAIT_PDU_READY;
- conn->flags = 0L;
- conn->state = ISCSI_CONNECT_STATE_INVALID;
- conn->login_phase = ISCSI_LOGIN_RESPONSE_FLAGS_NEXT_STAGE_SECURITY_NEGOTIATION;
- conn->max_recv_ds_len = ISCSI_DEFAULT_RECV_DS_LEN;
- conn->pg_tag = portal->group->tag;
- conn->isid.a = 0;
- conn->isid.b = 0;
- conn->isid.c = 0;
- conn->isid.d = 0;
- conn->tsih = 0U;
- conn->cid = 0U;
- conn->init_task_tag = 0UL;
- conn->auth_chap.phase = ISCSI_AUTH_CHAP_PHASE_NONE;
- conn->chap_group = 0L;
- conn->stat_sn = 0UL;
- conn->exp_stat_sn = 0UL;
+
+ iscsi_list_create( &conn->pdus_write );
+ iscsi_list_create( &conn->pdus_snack );
+ iscsi_list_create( &conn->r2t_tasks_active );
+ iscsi_list_create( &conn->r2t_tasks_queue );
+
+ conn->target_send_total_size = 0U;
+ conn->scsi_data_in_cnt = 0U;
+ conn->scsi_data_out_cnt = 0U;
+ conn->task_cnt = 0U;
+ conn->r2t_pending = 0U;
+ conn->header_digest = 0;
+ conn->data_digest = 0;
+ conn->id = 0;
+ conn->sock = sock;
+ conn->pdu_recv_state = ISCSI_CONNECT_PDU_RECV_STATE_WAIT_PDU_READY;
+ conn->flags = 0;
+ conn->state = ISCSI_CONNECT_STATE_INVALID;
+ conn->login_phase = ISCSI_LOGIN_RESPONSE_FLAGS_NEXT_STAGE_SECURITY_NEGOTIATION;
+ conn->max_recv_ds_len = ISCSI_DEFAULT_RECV_DS_LEN;
+ conn->pg_tag = portal->group->tag;
+ conn->isid.a = 0;
+ conn->isid.b = 0;
+ conn->isid.c = 0;
+ conn->isid.d = 0;
+ conn->tsih = 0U;
+ conn->cid = 0U;
+ conn->state_negotiated = 0U;
+ conn->session_state_negotiated = 0UL;
+ conn->init_task_tag = 0UL;
+ conn->target_xfer_tag = 0UL;
+ conn->auth_chap.phase = ISCSI_AUTH_CHAP_PHASE_NONE;
+ conn->chap_group = 0L;
+ conn->stat_sn = 0UL;
+ conn->exp_stat_sn = 0UL;
+
+ iscsi_list_create( &conn->exec_queue );
+
+ conn->stat_iscsi_opcodes = iscsi_hashmap_create( 256U );
+
+ if ( conn->stat_iscsi_opcodes == NULL ) {
+ logadd( LOG_ERROR, "iscsi_create: Out of memory while initializing iSCSI global vector iSCSI opcode statistics" );
+
+ iscsi_hashmap_destroy( conn->text_key_value_pairs );
+ iscsi_hashmap_iterate( conn->key_value_pairs, iscsi_hashmap_key_destroy_value_callback, NULL );
+ iscsi_hashmap_destroy( conn->key_value_pairs );
+ free( conn );
+
+ return NULL;
+ }
+
+ conn->stat_scsi_opcodes = iscsi_hashmap_create( 256U );
+
+ if ( conn->stat_scsi_opcodes == NULL ) {
+ logadd( LOG_ERROR, "iscsi_create: Out of memory while initializing iSCSI global vector iSCSI SCSI opcode statistics" );
+
+ iscsi_hashmap_destroy( conn->stat_iscsi_opcodes );
+ iscsi_hashmap_destroy( conn->text_key_value_pairs );
+ iscsi_hashmap_iterate( conn->key_value_pairs, iscsi_hashmap_key_destroy_value_callback, NULL );
+ iscsi_hashmap_destroy( conn->key_value_pairs );
+ free( conn );
+
+ return NULL;
+ }
return conn;
}
/**
+ * @brief Deallocates all pending iSCSI tasks and PDUs associated with an iSCSI connection.
+ *
+ * This function only removes tasks which are
+ * not enqueued.
+ *
+ * @param[in] conn Pointer to iSCSI connection of which to
+ * deallocate all the tasks and PDUs. May NOT
+ * be NULL, so be careful.
+ */
+static int iscsi_connection_tasks_destroy(iscsi_connection *conn)
+{
+ iscsi_pdu *pdu;
+ iscsi_pdu *tmp_pdu;
+
+ iscsi_list_foreach_safe_node ( &conn->pdus_snack, pdu, tmp_pdu ) {
+ iscsi_list_remove( &pdu->node );
+ iscsi_connection_pdu_destroy( pdu );
+ }
+
+ iscsi_task *task;
+ iscsi_task *tmp_task;
+
+ iscsi_list_foreach_safe_node ( &conn->scsi_data_in_queued_tasks, task, tmp_task ) {
+ if ( (task->flags & ISCSI_TASK_FLAGS_QUEUED) != 0 )
+ continue;
+
+ iscsi_list_remove( &task->node );
+ iscsi_task_destroy( task );
+ }
+
+ iscsi_list_foreach_safe_node ( &conn->pdus_write, pdu, tmp_pdu ) {
+ iscsi_list_remove( &pdu->node );
+ iscsi_connection_pdu_destroy( pdu );
+ }
+
+ return ((conn->task_cnt != 0) ? -1 : 0);
+}
+
+/**
* @brief iSCSI connection destructor callback for hash map.
*
* Callback function for deallocation of an iSCSI
@@ -3095,9 +9272,7 @@ iscsi_connection *iscsi_connection_create(iscsi_portal *portal, const int sock)
*
* @param[in] key Pointer to zero padded key. NULL is
* an invalid pointer here, so be careful.
- * @param[in] key_size Number of bytes for the key, MUST
- * be a multiple of 8 bytes which is NOT checked, so
- * be careful.
+ * @param[in] key_size Number of bytes for the key.
* @param[in] value Value of the key, NULL is allowed.
* @param[in,out] user_data This argument is not used by
* this function and should be always NULL for now, as
@@ -3109,7 +9284,7 @@ int iscsi_connection_destroy_callback(uint8_t *key, const size_t key_size, uint8
iscsi_connection_destroy( (iscsi_connection *) value );
iscsi_hashmap_key_destroy( key );
- return 0L;
+ return 0;
}
/**
@@ -3128,43 +9303,100 @@ int iscsi_connection_destroy_callback(uint8_t *key, const size_t key_size, uint8
void iscsi_connection_destroy(iscsi_connection *conn)
{
if ( conn != NULL ) {
+ iscsi_hashmap_iterate( conn->stat_scsi_opcodes, iscsi_hashmap_key_destroy_value_callback, NULL );
+ iscsi_hashmap_destroy( conn->stat_scsi_opcodes );
+ conn->stat_scsi_opcodes = NULL;
+
+ iscsi_hashmap_iterate( conn->stat_iscsi_opcodes, iscsi_hashmap_key_destroy_value_callback, NULL );
+ iscsi_hashmap_destroy( conn->stat_iscsi_opcodes );
+ conn->stat_iscsi_opcodes = NULL;
+
+ iscsi_task *task;
+ iscsi_task *tmp;
+
+ iscsi_list_foreach_safe_node ( &conn->r2t_tasks_queue, task, tmp ) {
+ iscsi_list_remove( &task->node );
+ iscsi_task_destroy( task );
+ }
+
+ iscsi_list_foreach_safe_node ( &conn->r2t_tasks_active, task, tmp ) {
+ iscsi_list_remove( &task->node );
+ iscsi_task_destroy( task );
+ }
+
+ iscsi_pdu *pdu;
+ iscsi_pdu *tmp_pdu;
+
+ iscsi_list_foreach_safe_node ( &conn->pdus_snack, pdu, tmp_pdu ) {
+ iscsi_list_remove( &pdu->node );
+ iscsi_connection_pdu_destroy( pdu );
+ }
+
+ iscsi_list_foreach_safe_node ( &conn->pdus_write, pdu, tmp_pdu ) {
+ iscsi_list_remove( &pdu->node );
+ iscsi_connection_pdu_destroy( pdu );
+ }
+
+ iscsi_list_foreach_safe_node ( &conn->scsi_data_in_queued_tasks, task, tmp ) {
+ iscsi_list_remove( &task->node );
+ iscsi_task_destroy( task );
+ }
+
+ if ( conn->pdu_processing != NULL ) {
+ iscsi_connection_pdu_destroy( conn->pdu_processing );
+
+ conn->pdu_processing = NULL;
+ }
+
if ( conn->portal_port != NULL ) {
- free ( conn->portal_port );
+ free( conn->portal_port );
conn->portal_port = NULL;
}
if ( conn->portal_host != NULL ) {
- free ( conn->portal_host );
+ free( conn->portal_host );
conn->portal_host = NULL;
}
if ( conn->target_name_short != NULL ) {
- free ( conn->target_name_short );
+ free( conn->target_name_short );
conn->target_name_short = NULL;
}
if ( conn->init_adr != NULL ) {
- free ( conn->init_adr );
+ free( conn->init_adr );
conn->init_adr = NULL;
}
if ( conn->init_name != NULL ) {
- free ( conn->init_name );
+ free( conn->init_name );
conn->init_name = NULL;
}
+ if ( conn->text_partial_pairs != NULL ) {
+ free( conn->text_partial_pairs );
+
+ conn->text_partial_pairs = NULL;
+ }
+
+ if ( conn->text_key_value_pairs != NULL ) {
+ iscsi_hashmap_iterate( conn->text_key_value_pairs, iscsi_hashmap_key_destroy_value_callback, NULL );
+ iscsi_hashmap_destroy( conn->text_key_value_pairs );
+
+ conn->text_key_value_pairs = NULL;
+ }
+
if ( conn->partial_pairs != NULL ) {
- free ( conn->partial_pairs );
+ free( conn->partial_pairs );
conn->partial_pairs = NULL;
}
-
if ( conn->key_value_pairs != NULL ) {
iscsi_hashmap_iterate( conn->key_value_pairs, iscsi_hashmap_key_destroy_value_callback, NULL );
iscsi_hashmap_destroy( conn->key_value_pairs );
@@ -3208,8 +9440,8 @@ void iscsi_connection_schedule(iscsi_connection *conn)
/**
* @brief Reads data for the specified iSCSI connection from its TCP socket.
*
- * The TCP socket is marked as non-blocking, so this function may not read
- * all data requested.
+ * The TCP socket is marked as non-blocking, so this function
+ * may not read all data requested.
*
* Returns ISCSI_CONNECT_PDU_READ_ERR_FATAL if the operation
* indicates a fatal error with the TCP connection (including
@@ -3217,14 +9449,14 @@ void iscsi_connection_schedule(iscsi_connection *conn)
*
* Otherwise returns the number of bytes successfully read.
*/
-int iscsi_connection_read(const iscsi_connection *conn, uint8_t *buf, const uint len)
+int32_t iscsi_connection_read(const iscsi_connection *conn, uint8_t *buf, const uint32_t len)
{
- if ( len == 0 )
- return 0;
+ if ( len == 0UL )
+ return 0L;
- const int rc = (int) recv( conn->sock, buf, len, MSG_WAITALL );
+ const int32_t rc = (int32_t) recv( conn->sock, buf, (size_t) len, MSG_WAITALL );
- return (rc > 0) ? rc : ISCSI_CONNECT_PDU_READ_ERR_FATAL;
+ return ((rc > 0L) ? rc : (int32_t) ISCSI_CONNECT_PDU_READ_ERR_FATAL);
}
/**
@@ -3239,14 +9471,74 @@ int iscsi_connection_read(const iscsi_connection *conn, uint8_t *buf, const uint
*
* Otherwise returns the number of bytes successfully written.
*/
-int iscsi_connection_write(const iscsi_connection *conn, uint8_t *buf, const uint len)
+int32_t iscsi_connection_write(const iscsi_connection *conn, uint8_t *buf, const uint32_t len)
{
- if ( len == 0 )
- return 0;
+ if ( len == 0UL )
+ return 0L;
+
+ const int32_t rc = (int32_t) sock_sendAll( conn->sock, buf, (size_t) len, ISCSI_CONNECT_SOCKET_WRITE_RETRIES );
+
+ return ((rc > 0L) ? rc : (int32_t) ISCSI_CONNECT_PDU_READ_ERR_FATAL);
+}
+
+/**
+ * @brief This function handles all queued iSCSI SCSI Data In tasks.
+ *
+ * This function also creates a sub task
+ * if the data transfer length exceeds
+ * the maximum allowed chunk size.
+ *
+ * @param[in] conn Pointer to iSCSI connection of which the
+ * queued SCSI Data In tasks should be
+ * handled. May NOT be NULL, so be careful.
+ * @return 0 on successful task handling, a
+ * negative error code otherwise.
+ */
+int iscsi_connection_handle_scsi_data_in_queued_tasks(iscsi_connection *conn)
+{
+ while ( !iscsi_list_empty( &conn->scsi_data_in_queued_tasks ) && (conn->scsi_data_in_cnt < ISCSI_DEFAULT_MAX_DATA_IN_PER_CONNECTION) ) {
+ iscsi_task *task = (iscsi_task *) iscsi_list_peek( &conn->scsi_data_in_queued_tasks );
+
+ if ( task->pos < task->scsi_task.xfer_len ) {
+ const uint32_t len = (task->scsi_task.xfer_len - task->pos);
+ iscsi_task *sub_task = iscsi_task_create( conn, task, iscsi_scsi_task_xfer_complete );
+
+ if ( sub_task == NULL )
+ return ISCSI_CONNECT_PDU_READ_ERR_FATAL;
+
+ sub_task->scsi_task.buf = NULL;
+ sub_task->scsi_task.pos = task->pos;
+
+ pthread_rwlock_rdlock( &conn->device->luns_rwlock );
+
+ if ( iscsi_device_find_lun( conn->device, task->lun_id ) == NULL ) {
+ pthread_rwlock_unlock( &conn->device->luns_rwlock );
+
+ iscsi_list_remove( &task->node );
+
+ task->pos += len;
+ sub_task->scsi_task.len = 0UL;
+ sub_task->scsi_task.xfer_len = len;
- const int rc = (int) send( conn->sock, buf, len, 0L );
+ iscsi_scsi_task_lun_process_none( &sub_task->scsi_task );
+ iscsi_scsi_task_xfer_complete( &sub_task->scsi_task );
- return (rc > 0) ? rc : ISCSI_CONNECT_PDU_READ_ERR_FATAL;
+ return ISCSI_CONNECT_PDU_READ_OK;
+ }
+
+ pthread_rwlock_unlock( &conn->device->luns_rwlock );
+
+ sub_task->scsi_task.len = ((len < ISCSI_DEFAULT_MAX_RECV_DS_LEN) ? len : ISCSI_DEFAULT_MAX_RECV_DS_LEN);
+ task->pos += sub_task->scsi_task.len;
+
+ iscsi_task_queue( conn, sub_task );
+ }
+
+ if ( task->pos == task->scsi_task.xfer_len )
+ iscsi_list_remove( &task->node );
+ }
+
+ return ISCSI_CONNECT_PDU_READ_OK;
}
/**
@@ -3278,8 +9570,7 @@ int iscsi_connection_init_key_value_pairs(iscsi_hashmap *key_value_pairs)
* special key and value pair for. NULL is
* a forbidden value here, so take caution.
* @param[in] key_value_pair Pointer to special key and value pair
- * containing its attributes and may NOT
- * be NULL, so be careful.
+ * containing its attributes.
* @param[in] key Pointer to special key to be written to
* output buffer. NULL is NOT allowed,
* take caution.
@@ -3292,40 +9583,42 @@ int iscsi_connection_init_key_value_pairs(iscsi_hashmap *key_value_pairs)
* @return New buffer position in bytes or a negative
* error code.
*/
-static int iscsi_append_special_key_value_pair_packet(iscsi_connection *conn, iscsi_key_value_pair *key_value_pair, const uint8_t *key, uint8_t *buf, uint pos, const uint len)
+static int32_t iscsi_append_special_key_value_pair_packet(iscsi_connection *conn, iscsi_key_value_pair *key_value_pair, const uint8_t *key, uint8_t *buf, uint32_t pos, const uint32_t len)
{
+ if ( key_value_pair == NULL )
+ return pos;
+
if ( (key_value_pair->flags & ISCSI_TEXT_KEY_VALUE_PAIR_FLAGS_OVERRIDE_DEFAULT) != 0 ) {
- if ( (int) (len - pos) < 1L )
+ if ( pos >= len )
return -1L;
- pos += snprintf( (char *) (buf + pos), (len - pos), "%s=%ld", key, ISCSI_DEFAULT_MAX_RECV_DS_LEN ) + 1;
+ pos += (uint32_t) (snprintf( (char *) (buf + pos), (len - pos), "%s=%" PRId32, key, (uint32_t) ISCSI_DEFAULT_MAX_RECV_DS_LEN ) + 1);
}
if ( (key_value_pair->flags & ISCSI_TEXT_KEY_VALUE_PAIR_FLAGS_USE_OTHER_MAX_VALUE) != 0 ) {
- if ( (int) (len - pos) < 1L )
+ if ( pos >= len )
return -1L;
uint8_t *first_burst_len_val = NULL;
- int rc = iscsi_get_key_value_pair( conn->session->key_value_pairs, (uint8_t *) ISCSI_LOGIN_AUTH_SESSION_TEXT_KEY_FIRST_BURST_LEN, &first_burst_len_val );
- uint first_burst_len = (rc < 0) ? ISCSI_SESSION_DEFAULT_FIRST_BURST_LEN : (uint) atol( (char *) first_burst_len_val );
+ int rc = iscsi_get_key_value_pair( conn->session->key_value_pairs, ISCSI_LOGIN_AUTH_SESSION_TEXT_KEY_FIRST_BURST_LEN, &first_burst_len_val );
+ uint32_t first_burst_len = ((rc < 0) ? iscsi_globvec->first_burst_len : (uint32_t) atol( (char *) first_burst_len_val ));
uint8_t *max_burst_len_val;
- rc = iscsi_get_key_value_pair( conn->session->key_value_pairs, (uint8_t *) ISCSI_LOGIN_AUTH_SESSION_TEXT_KEY_MAX_BURST_LEN, &max_burst_len_val );
- uint max_burst_len = (rc < 0) ? ISCSI_SESSION_DEFAULT_MAX_BURST_LEN : (uint) atol( (char *) max_burst_len_val );
+ rc = iscsi_get_key_value_pair( conn->session->key_value_pairs, ISCSI_LOGIN_AUTH_SESSION_TEXT_KEY_MAX_BURST_LEN, &max_burst_len_val );
+ uint32_t max_burst_len = ((rc < 0) ? iscsi_globvec->max_burst_len : (uint32_t) atol( (char *) max_burst_len_val ));
if ( first_burst_len > max_burst_len ) {
first_burst_len = max_burst_len;
if ( first_burst_len_val != NULL ) {
- sprintf( (char *) first_burst_len_val, "%d", first_burst_len );
+ sprintf( (char *) first_burst_len_val, "%" PRId32, first_burst_len );
}
}
- pos += snprintf( (char *) (buf + pos), (len - pos), "%s=%d", key, first_burst_len ) + 1;
+ pos += (uint32_t) (snprintf( (char *) (buf + pos), (len - pos), "%s=%" PRId32, key, first_burst_len ) + 1);
}
return pos;
-
}
/**
@@ -3336,8 +9629,7 @@ static int iscsi_append_special_key_value_pair_packet(iscsi_connection *conn, is
* buffer and truncates if necessary.
*
* @param[in] key_value_pair Pointer to key and value pair containing
- * its attributes and may NOT be NULL, so be
- * careful.
+ * its attributes.
* @param[in] key Pointer to key to be written to output
* buffer. NULL is NOT allowed, take caution.
* @param[in] value Pointer to value of the key that should
@@ -3352,13 +9644,13 @@ static int iscsi_append_special_key_value_pair_packet(iscsi_connection *conn, is
* @return New buffer position in bytes or a negative
* error code.
*/
-static int iscsi_append_key_value_pair_packet(const iscsi_key_value_pair *key_value_pair, const uint8_t *key, const uint8_t *value, uint8_t *buf, uint pos, const uint len)
+static int32_t iscsi_append_key_value_pair_packet(const iscsi_key_value_pair *key_value_pair, const uint8_t *key, const uint8_t *value, uint8_t *buf, uint32_t pos, const uint32_t len)
{
- if ( (key_value_pair->type != ISCSI_TEXT_KEY_VALUE_PAIR_TYPE_DECLARATIVE) && (key_value_pair->type != ISCSI_TEXT_KEY_VALUE_PAIR_TYPE_NUM_DECLARATIVE) ) {
- if ( (int) (len - pos) < 1L )
+ if ( (key_value_pair == NULL) || ((key_value_pair->type != ISCSI_TEXT_KEY_VALUE_PAIR_TYPE_DECLARATIVE) && (key_value_pair->type != ISCSI_TEXT_KEY_VALUE_PAIR_TYPE_NUM_DECLARATIVE)) ) {
+ if ( pos >= len )
return -1L;
- pos += snprintf( (char *) (buf + pos), (len - pos), "%s=%s", key, value ) + 1;
+ pos += (uint32_t) (snprintf( (char *) (buf + pos), (len - pos), "%s=%s", key, value ) + 1);
}
return pos;
@@ -3377,16 +9669,30 @@ static int iscsi_append_key_value_pair_packet(const iscsi_key_value_pair *key_va
* @return Pointer to original value, if the value is
* allowed or NULL otherwise.
*/
-static uint8_t *iscsi_negotiate_key_value_pair_list(const iscsi_key_value_pair *key_value_pair, uint8_t *old_value)
+static uint8_t *iscsi_negotiate_key_value_pair_list(const iscsi_key_value_pair *key_value_pair, const uint8_t *old_value)
{
- const uint8_t *list = key_value_pair->list_range;
+ uint8_t *list = key_value_pair->list_range;
+ const uint8_t *value = (uint8_t *) strchr( (char *) old_value, ',' );
+ size_t val_len = ((value != NULL) ? (size_t) (value - old_value) : strlen( (char *) old_value ));
- do {
- if ( strcasecmp( (char *) list, (char *) old_value ) == 0 )
- return old_value;
+ for ( ;; ) {
+ const size_t len = strlen( (char *) list );
+
+ if ( (val_len == len) && (strncasecmp( (char *) list, (char *) old_value, len ) == 0) )
+ return list;
- list += (strlen( (char *) list ) + 1);
- } while ( list[0] != '\0' );
+ list += (len + 1);
+
+ if ( list[0] == '\0' ) {
+ if ( value == NULL )
+ break;
+
+ old_value = value;
+ list = key_value_pair->list_range;
+ value = (uint8_t *) strchr( (char *) ++old_value, ',' );
+ val_len = ((value != NULL) ? (size_t) (value - old_value) : strlen( (char *) old_value ));
+ }
+ }
return NULL;
}
@@ -3410,16 +9716,16 @@ static uint8_t *iscsi_negotiate_key_value_pair_list(const iscsi_key_value_pair *
*/
static uint8_t *iscsi_negotiate_key_value_pair_num(const iscsi_key_value_pair *key_value_pair, uint8_t *old_value, uint8_t *value)
{
- int old_int_val = (int) atol( (char *) key_value_pair->value );
+ int32_t old_int_val = (int32_t) atol( (char *) key_value_pair->value );
if ( (key_value_pair->flags & ISCSI_TEXT_KEY_VALUE_PAIR_FLAGS_USE_PREVIOUS_VALUE) != 0 )
- old_int_val = (int) atol( (char *) old_value );
+ old_int_val = (int32_t) atol( (char *) old_value );
- int int_val = (int) atol( (char *) value );
+ int32_t int_val = (int32_t) atol( (char *) value );
- const uint8_t *range = key_value_pair->list_range;
- const int range_min = (int) atol( (char *) range );
- const int range_max = (int) atol( (char *) (range + strlen( (char *) range ) + 1) );
+ const uint8_t *range = key_value_pair->list_range;
+ const int32_t range_min = (int32_t) atol( (char *) range );
+ const int32_t range_max = (int32_t) atol( (char *) (range + strlen( (char *) range ) + 1) );
if ( (old_int_val < range_min) || (old_int_val > range_max) )
return NULL;
@@ -3444,7 +9750,7 @@ static uint8_t *iscsi_negotiate_key_value_pair_num(const iscsi_key_value_pair *k
}
}
- sprintf( (char *) old_value, "%d", old_int_val );
+ sprintf( (char *) old_value, "%" PRId32, old_int_val );
return old_value;
}
@@ -3477,18 +9783,15 @@ static uint8_t *iscsi_negotiate_key_value_pair_num(const iscsi_key_value_pair *k
static uint8_t *iscsi_negotiate_key_value_pair_bool(const iscsi_key_value_pair *key_value_pair, uint8_t *old_value, uint8_t *value, uint8_t *bool_value, int *update_key_value_pair)
{
const uint8_t *list_bool_true = key_value_pair->list_range;
- const uint8_t *list_bool_false = list_bool_true + strlen( (char *) list_bool_true ) + 1;
+ const uint8_t *list_bool_false = list_bool_true + strlen( (char *) list_bool_true ) + 1UL;
- if ( (strcasecmp( (char *) old_value, (char *) list_bool_true ) == 0) || (strcasecmp( (char *) old_value, (char *) list_bool_false ) == 0) ) {
- *update_key_value_pair = 0L;
+ if ( (strcasecmp( (char *) old_value, (char *) list_bool_true ) != 0) && (strcasecmp( (char *) old_value, (char *) list_bool_false ) != 0) ) {
+ *update_key_value_pair = 0;
return (uint8_t *) "Reject";
}
- if ( strcasecmp( (char *) value, (char *) bool_value ) == 0 )
- return bool_value;
-
- return key_value_pair->value;
+ return ((strcasecmp( (char *) value, (char *) bool_value ) == 0) ? bool_value : old_value);
}
/**
@@ -3533,7 +9836,7 @@ static uint8_t *iscsi_negotiate_key_value_pair_all(const iscsi_key_value_pair *k
}
case ISCSI_TEXT_KEY_VALUE_PAIR_TYPE_BOOL_OR : {
uint8_t *list_bool_true = key_value_pair->list_range;
- uint8_t *list_bool_false = list_bool_true + strlen( (char *) list_bool_true ) + 1;
+ uint8_t *list_bool_false = list_bool_true + strlen( (char *) list_bool_true ) + 1UL;
return iscsi_negotiate_key_value_pair_bool( key_value_pair, old_value, value, list_bool_false, update_key_value_pair );
@@ -3597,9 +9900,7 @@ static int iscsi_negotiate_key_value_pairs_state(iscsi_connection *conn, const i
*
* @param[in] key Pointer to zero padded key. NULL is
* an invalid pointer here, so be careful.
- * @param[in] key_size Number of bytes for the key, MUST
- * be a multiple of 8 bytes which is NOT checked, so
- * be careful.
+ * @param[in] key_size Number of bytes for the key.
* @param[in] value Value of the key, NULL is allowed.
* @param[in,out] user_data Pointer to integer value which is
* 1 is this is discovery, or 0 if not.
@@ -3611,30 +9912,30 @@ int iscsi_negotiate_key_value_pair_callback(uint8_t *key, const size_t key_size,
iscsi_connection *conn = key_value_pair_packet->conn;
iscsi_hashmap *key_value_pairs = conn->key_value_pairs;
iscsi_key_value_pair *key_value_pair = NULL;
- int type = 0L;
+ int type = 0;
int rc = iscsi_hashmap_get( iscsi_globvec->connection_key_value_pairs, key, key_size, (uint8_t **) &key_value_pair);
if ( rc < 0 ) {
key_value_pairs = conn->session->key_value_pairs;
- type = 1L;
+ type = 1;
rc = iscsi_hashmap_get( iscsi_globvec->session_key_value_pairs, key, key_size, (uint8_t **) &key_value_pair);
}
if ( (rc == 0) && (key_value_pair->flags & (ISCSI_TEXT_KEY_VALUE_PAIR_FLAGS_CHAP_TYPE | ISCSI_TEXT_KEY_VALUE_PAIR_FLAGS_SPECIAL_HANDLING)) != 0 )
- return 0L;
+ return 0;
- int update_key_value_pair = 1L;
+ int update_key_value_pair = 1;
uint8_t *conn_sess_val;
if ( rc < 0 ) {
conn_sess_val = (uint8_t *) "NotUnderstood";
- update_key_value_pair = 0L;
+ update_key_value_pair = 0;
} else if ( (key_value_pair_packet->discovery != 0) && ((key_value_pair->flags & ISCSI_TEXT_KEY_VALUE_PAIR_FLAGS_DISCOVERY_IGNORE) != 0) ) {
conn_sess_val = (uint8_t *) "Irrelevant";
- update_key_value_pair = 0L;
+ update_key_value_pair = 0;
} else {
rc = iscsi_negotiate_key_value_pairs_state( conn, key_value_pair, type );
@@ -3647,19 +9948,19 @@ int iscsi_negotiate_key_value_pair_callback(uint8_t *key, const size_t key_size,
if ( (key_value_pair != NULL) && (key_value_pair->type > ISCSI_TEXT_KEY_VALUE_PAIR_TYPE_UNSPECIFIED) ) {
if ( (key_value_pair->flags & ISCSI_TEXT_KEY_VALUE_PAIR_FLAGS_USE_PREVIOUS_VALUE) != 0 ) {
uint8_t *max_burst_len_val;
- uint first_burst_len = (uint) atol( (char *) value );
- uint max_burst_len;
+ uint32_t first_burst_len = (uint32_t) atol( (char *) value );
+ uint32_t max_burst_len;
- rc = iscsi_get_key_value_pair( key_value_pairs, (uint8_t *) ISCSI_LOGIN_AUTH_SESSION_TEXT_KEY_MAX_BURST_LEN, &max_burst_len_val );
+ rc = iscsi_get_key_value_pair( key_value_pairs, ISCSI_LOGIN_AUTH_SESSION_TEXT_KEY_MAX_BURST_LEN, &max_burst_len_val );
- max_burst_len = (rc < 0) ? ISCSI_SESSION_DEFAULT_MAX_BURST_LEN : (uint) atol( (char *) max_burst_len_val );
+ max_burst_len = ((rc < 0) ? iscsi_globvec->max_burst_len : (uint32_t) atol( (char *) max_burst_len_val ));
if ( (first_burst_len < ISCSI_MAX_DS_SIZE) && (first_burst_len > max_burst_len) )
- sprintf( (char *) value, "%d", first_burst_len );
+ sprintf( (char *) value, "%" PRId32, first_burst_len );
}
if ( (key_value_pair->flags & ISCSI_TEXT_KEY_VALUE_PAIR_FLAGS_TARGET_DECLARATIVE) != 0 )
- update_key_value_pair = 0L;
+ update_key_value_pair = 0;
conn_sess_val = iscsi_negotiate_key_value_pair_all( key_value_pair, value, conn_sess_val, &update_key_value_pair );
}
@@ -3670,16 +9971,16 @@ int iscsi_negotiate_key_value_pair_callback(uint8_t *key, const size_t key_size,
key_value_pair_packet->pos = iscsi_append_key_value_pair_packet( key_value_pair, key, conn_sess_val, key_value_pair_packet->buf, key_value_pair_packet->pos, key_value_pair_packet->len );
- if ( (int) key_value_pair_packet->pos < 0 )
+ if ( (int32_t) key_value_pair_packet->pos < 0L )
return key_value_pair_packet->pos;
key_value_pair_packet->pos = iscsi_append_special_key_value_pair_packet( conn, key_value_pair, key, key_value_pair_packet->buf, key_value_pair_packet->pos, key_value_pair_packet->len );
- if ( (int) key_value_pair_packet->pos < 0 )
+ if ( (int32_t) key_value_pair_packet->pos < 0L )
return key_value_pair_packet->pos;
}
- return 0L;
+ return 0;
}
/**
@@ -3698,21 +9999,21 @@ int iscsi_negotiate_key_value_pair_callback(uint8_t *key, const size_t key_size,
* could be negotiated, a negative error
* code otherwise.
*/
-int iscsi_negotiate_key_value_pairs(iscsi_connection *conn, iscsi_hashmap *key_value_pairs, uint8_t *buf, const uint pos, const uint len)
+int32_t iscsi_negotiate_key_value_pairs(iscsi_connection *conn, iscsi_hashmap *key_value_pairs, uint8_t *buf, const uint32_t pos, const uint32_t len)
{
if ( pos > len ) {
- buf[len - 1] = '\0';
+ buf[len - 1UL] = '\0';
return len;
}
uint8_t *type;
- int rc = iscsi_get_key_value_pair( key_value_pairs, (uint8_t *) ISCSI_LOGIN_AUTH_SECURITY_TEXT_KEY_SESSION_TYPE, &type );
+ int rc = iscsi_get_key_value_pair( key_value_pairs, ISCSI_LOGIN_AUTH_SECURITY_TEXT_KEY_SESSION_TYPE, &type );
if ( rc < 0 )
- rc = iscsi_get_key_value_pair( conn->session->key_value_pairs, (uint8_t *) ISCSI_LOGIN_AUTH_SECURITY_TEXT_KEY_SESSION_TYPE, &type );
+ rc = iscsi_get_key_value_pair( conn->session->key_value_pairs, ISCSI_LOGIN_AUTH_SECURITY_TEXT_KEY_SESSION_TYPE, &type );
- const int discovery = ((rc == 0) && (strcasecmp( (char *) type, "Discovery" ) == 0)) ? 1L : 0L;
+ const int discovery = (((rc == 0) && (strcasecmp( (char *) type, "Discovery" ) == 0)) ? 1 : 0);
iscsi_key_value_pair_packet key_value_pair_packet = {conn, buf, pos, len, discovery};
iscsi_hashmap_iterate( key_value_pairs, iscsi_negotiate_key_value_pair_callback, (uint8_t *) &key_value_pair_packet );
@@ -3726,7 +10027,7 @@ int iscsi_negotiate_key_value_pairs(iscsi_connection *conn, iscsi_hashmap *key_v
* This function converts string representations of
* integer and boolean key and value pairs.
*
- * @param[in] conn iSCSI connection which holds the
+ * @param[in] conn Pointer to iSCSI connection which holds the
* copies of the key and value pairs.
* @retval -1 An error occured during the copy process,
* e.g. memory is exhausted.
@@ -3736,7 +10037,7 @@ int iscsi_connection_copy_key_value_pairs(iscsi_connection *conn)
{
int32_t int_val;
- int rc = iscsi_get_int_key_value_pair( conn->key_value_pairs, (uint8_t *) ISCSI_LOGIN_AUTH_SESSION_TEXT_KEY_MAX_RECV_DS_LEN, &int_val);
+ int rc = iscsi_get_int_key_value_pair( conn->key_value_pairs, ISCSI_LOGIN_AUTH_SESSION_TEXT_KEY_MAX_RECV_DS_LEN, &int_val);
if ( rc != 0 )
return rc;
@@ -3748,63 +10049,67 @@ int iscsi_connection_copy_key_value_pairs(iscsi_connection *conn)
uint8_t *value;
- rc = iscsi_get_key_value_pair( conn->key_value_pairs, (uint8_t *) ISCSI_LOGIN_AUTH_SESSION_TEXT_KEY_HEADER_DIGEST, &value);
+ rc = iscsi_get_key_value_pair( conn->key_value_pairs, ISCSI_LOGIN_AUTH_SESSION_TEXT_KEY_HEADER_DIGEST, &value);
if ( rc != 0 )
return rc;
- conn->header_digest = (strcasecmp( (char *) value, "CRC32C" ) == 0) ? ISCSI_DIGEST_SIZE : 0L;
+ conn->header_digest = ((strcasecmp( (char *) value, "CRC32C" ) == 0) ? ISCSI_DIGEST_SIZE : 0);
- rc = iscsi_get_key_value_pair( conn->key_value_pairs, (uint8_t *) ISCSI_LOGIN_AUTH_SESSION_TEXT_KEY_DATA_DIGEST, &value);
+ rc = iscsi_get_key_value_pair( conn->key_value_pairs, ISCSI_LOGIN_AUTH_SESSION_TEXT_KEY_DATA_DIGEST, &value);
if ( rc != 0 )
return rc;
- conn->data_digest = (strcasecmp( (char *) value, "CRC32C" ) == 0) ? ISCSI_DIGEST_SIZE : 0L;
+ conn->data_digest = ((strcasecmp( (char *) value, "CRC32C" ) == 0) ? ISCSI_DIGEST_SIZE : 0);
- rc = iscsi_get_int_key_value_pair( conn->key_value_pairs, (uint8_t *) ISCSI_LOGIN_AUTH_SESSION_TEXT_KEY_MAX_CONNECTIONS, &int_val);
+ rc = iscsi_get_int_key_value_pair( conn->session->key_value_pairs, ISCSI_LOGIN_AUTH_SESSION_TEXT_KEY_MAX_CONNECTIONS, &int_val);
if ( rc != 0 )
return rc;
conn->session->max_conns = int_val;
- rc = iscsi_get_int_key_value_pair( conn->key_value_pairs, (uint8_t *) ISCSI_LOGIN_AUTH_SESSION_TEXT_KEY_MAX_OUTSTANDING_R2T, &int_val);
+ rc = iscsi_get_int_key_value_pair( conn->session->key_value_pairs, ISCSI_LOGIN_AUTH_SESSION_TEXT_KEY_MAX_OUTSTANDING_R2T, &int_val);
if ( rc != 0 )
return rc;
conn->session->max_outstanding_r2t = int_val;
- rc = iscsi_get_int_key_value_pair( conn->key_value_pairs, (uint8_t *) ISCSI_LOGIN_AUTH_SESSION_TEXT_KEY_FIRST_BURST_LEN, &int_val);
+ rc = iscsi_get_int_key_value_pair( conn->session->key_value_pairs, ISCSI_LOGIN_AUTH_SESSION_TEXT_KEY_FIRST_BURST_LEN, &int_val);
if ( rc != 0 )
return rc;
conn->session->first_burst_len = int_val;
- rc = iscsi_get_int_key_value_pair( conn->key_value_pairs, (uint8_t *) ISCSI_LOGIN_AUTH_SESSION_TEXT_KEY_MAX_BURST_LEN, &int_val);
+ rc = iscsi_get_int_key_value_pair( conn->session->key_value_pairs, ISCSI_LOGIN_AUTH_SESSION_TEXT_KEY_MAX_BURST_LEN, &int_val);
if ( rc != 0 )
return rc;
conn->session->max_burst_len = int_val;
- rc = iscsi_get_bool_key_value_pair( conn->key_value_pairs, (uint8_t *) ISCSI_LOGIN_AUTH_SESSION_TEXT_KEY_INITIAL_R2T, &int_val);
+ rc = iscsi_get_bool_key_value_pair( conn->session->key_value_pairs, ISCSI_LOGIN_AUTH_SESSION_TEXT_KEY_INITIAL_R2T, &int_val);
if ( rc != 0 )
return rc;
- conn->session->init_r2t = int_val;
+ conn->session->flags &= ~(ISCSI_SESSION_FLAGS_INIT_R2T | ISCSI_SESSION_FLAGS_IMMEDIATE_DATA);
+
+ if ( int_val != 0L )
+ conn->session->flags |= ISCSI_SESSION_FLAGS_INIT_R2T;
- rc = iscsi_get_bool_key_value_pair( conn->key_value_pairs, (uint8_t *) ISCSI_LOGIN_AUTH_SESSION_TEXT_KEY_IMMEDIATE_DATA, &int_val);
+ rc = iscsi_get_bool_key_value_pair( conn->session->key_value_pairs, ISCSI_LOGIN_AUTH_SESSION_TEXT_KEY_IMMEDIATE_DATA, &int_val);
if ( rc != 0 )
return rc;
- conn->session->immediate_data = int_val;
+ if ( int_val != 0L )
+ conn->session->flags |= ISCSI_SESSION_FLAGS_IMMEDIATE_DATA;
- return 0L;
+ return 0;
}
/**
@@ -3830,11 +10135,11 @@ int iscsi_connection_copy_key_value_pairs(iscsi_connection *conn)
* @return 0 if authentication methods were handled successfully,
* a negative error code otherwise.
*/
-static int iscsi_connection_auth_key_value_pairs(iscsi_connection *conn, iscsi_hashmap *key_value_pairs, const uint8_t *auth_method, uint8_t *buf, const uint pos, const uint len)
+static int32_t iscsi_connection_auth_key_value_pairs(iscsi_connection *conn, iscsi_hashmap *key_value_pairs, const uint8_t *auth_method, uint8_t *buf, const uint pos, const uint len)
{
// TODO: Implement CHAP and other authentication methods.
- return 0L;
+ return 0;
}
/**
@@ -3853,10 +10158,10 @@ static int iscsi_connection_auth_key_value_pairs(iscsi_connection *conn, iscsi_h
*/
static int iscsi_connection_check_key_value_pairs(iscsi_connection *conn)
{
- if ( (conn->session->first_burst_len > conn->session->max_burst_len) || (conn->session->first_burst_len < 512) || (conn->session->max_burst_len < 512) || (conn->session->max_burst_len > ISCSI_SESSION_DEFAULT_MAX_BURST_LEN) || (conn->max_recv_ds_len < 512) || (conn->max_recv_ds_len > ISCSI_SESSION_DEFAULT_MAX_BURST_LEN) )
- return -1L;
+ if ( (conn->session->first_burst_len > conn->session->max_burst_len) || (conn->session->first_burst_len < 512UL) || (conn->session->max_burst_len < 512UL) || (conn->session->max_burst_len > iscsi_globvec->max_burst_len) || (conn->max_recv_ds_len < 512UL) || (conn->max_recv_ds_len > iscsi_globvec->max_burst_len) )
+ return -1;
- return 0L;
+ return 0;
}
/**
@@ -3893,17 +10198,17 @@ static int iscsi_connection_update_key_value_pairs(iscsi_connection *conn)
conn->state = ISCSI_CONNECT_STATE_EXITING;
if ( conn->sock < 0 )
- return -1L;
+ return -1;
uint recv_buf_len = conn->session->first_burst_len;
- if ( recv_buf_len < 4096 )
- recv_buf_len = 4096UL;
- else if ( recv_buf_len > 8192 )
- recv_buf_len = 8192UL;
+ if ( recv_buf_len < 4096U )
+ recv_buf_len = 4096U;
+ else if ( recv_buf_len > 8192U )
+ recv_buf_len = 8192U;
recv_buf_len += (uint) (sizeof(struct iscsi_bhs_packet) + ISCSI_MAX_AHS_SIZE + conn->header_digest + conn->data_digest); // BHS + maximum AHS size + header and data digest overhead
- recv_buf_len <<= 2UL; // Receive up to four streams at once.
+ recv_buf_len <<= 2U; // Receive up to four streams at once.
setsockopt( conn->sock, SOL_SOCKET, SO_RCVBUF, &recv_buf_len, sizeof(recv_buf_len)); // Not being able to set the buffer is NOT fatal, so ignore error.
@@ -3917,22 +10222,30 @@ static int iscsi_connection_update_key_value_pairs(iscsi_connection *conn)
* to be sent via TCP/IP.
*
* @param[in] conn Pointer to ISCSI connection to send the TCP/IP
- * packet with.
+ * packet with. May NOT be NULL, so be
+ * careful.
* @param[in] login_response_pdu Pointer to login response PDU to
- * be sent via TCP/IP.
+ * be sent via TCP/IP. NULL is NOT
+ * allowed here, take caution.
* @param[in] key_value_pairs Pointer to hash map of key and value pairs
* to be used for login response storage.
- * @paran[in] callback Pointer to post processing callback function
+ * @param[in] callback Pointer to post processing callback function
* after sending the TCP/IP packet.
+ * @return 0 if the login response has been sent
+ * successfully, a negative error code otherwise.
*/
-static void iscsi_connection_pdu_login_response(iscsi_connection *conn, iscsi_pdu *login_response_pdu, iscsi_hashmap *key_value_pairs, iscsi_connection_xfer_complete_callback callback)
+static int iscsi_connection_pdu_login_response(iscsi_connection *conn, iscsi_pdu *login_response_pdu, iscsi_hashmap *key_value_pairs, iscsi_connection_xfer_complete_callback callback)
{
- iscsi_login_response_packet *login_response_pkt = (iscsi_login_response_packet *) login_response_pdu->bhs_pkt;
+ const uint32_t ds_len = login_response_pdu->ds_len;
+
+ login_response_pdu->ds_len = login_response_pdu->len;
+
+ iscsi_login_response_packet *login_response_pkt = (iscsi_login_response_packet *) iscsi_connection_pdu_append( login_response_pdu, login_response_pdu->ahs_len, 0, ds_len, 0 );
login_response_pkt->version_max = ISCSI_VERSION_MAX;
login_response_pkt->version_active = ISCSI_VERSION_MAX;
- iscsi_put_be24( (uint8_t *) &login_response_pkt->ds_len, login_response_pdu->ds_len );
+ iscsi_put_be32( (uint8_t *) &login_response_pkt->total_ahs_len, ds_len ); // TotalAHSLength is always 0 and DataSegmentLength is 24-bit, so write in one step.
iscsi_put_be32( (uint8_t *) &login_response_pkt->stat_sn, conn->stat_sn++ );
if ( conn->session != NULL ) {
@@ -3946,10 +10259,14 @@ static void iscsi_connection_pdu_login_response(iscsi_connection *conn, iscsi_pd
if ( login_response_pkt->status_class != ISCSI_LOGIN_RESPONSE_STATUS_CLASS_SUCCESS )
login_response_pkt->flags &= (int8_t) ~(ISCSI_LOGIN_RESPONSE_FLAGS_TRANSIT | ISCSI_LOGIN_RESPONSE_FLAGS_CURRENT_STAGE_MASK | ISCSI_LOGIN_RESPONSE_FLAGS_NEXT_STAGE_MASK );
- iscsi_hashmap_iterate( key_value_pairs, iscsi_hashmap_key_destroy_value_callback, NULL );
- iscsi_hashmap_destroy( key_value_pairs );
-
iscsi_connection_pdu_write( conn, login_response_pdu, callback, (uint8_t *) conn );
+
+ if ( key_value_pairs != NULL ) {
+ iscsi_hashmap_iterate( key_value_pairs, iscsi_hashmap_key_destroy_value_callback, NULL );
+ iscsi_hashmap_destroy( key_value_pairs );
+ }
+
+ return ISCSI_CONNECT_PDU_READ_OK;
}
/**
@@ -4008,27 +10325,15 @@ static void iscsi_connection_pdu_login_ok_complete(uint8_t *user_data)
* @return 0 if initialization was successful, a negative error
* code otherwise.
*/
-static int iscsi_login_response_init(iscsi_pdu *login_response_pdu, const iscsi_pdu *pdu)
+static int iscsi_connection_pdu_login_response_init(iscsi_pdu *login_response_pdu, const iscsi_pdu *pdu)
{
iscsi_login_req_packet *login_req_pkt = (iscsi_login_req_packet *) pdu->bhs_pkt;
- iscsi_login_response_packet *bhs_pkt = (iscsi_login_response_packet *) login_response_pdu->bhs_pkt;
-
- bhs_pkt->opcode = ISCSI_SERVER_LOGIN_RES;
-
- iscsi_login_response_packet *login_response_pkt = (iscsi_login_response_packet *) iscsi_append_ds_packet( (iscsi_bhs_packet *) bhs_pkt, pdu->header_digest_size, ISCSI_DEFAULT_RECV_DS_LEN, pdu->data_digest_size );
-
- if ( login_response_pkt == NULL ) {
- bhs_pkt->status_class = ISCSI_LOGIN_RESPONSE_STATUS_CLASS_SERVER_ERR;
- bhs_pkt->status_detail = ISCSI_LOGIN_RESPONSE_STATUS_DETAILS_SERVER_ERR_OUT_OF_RESOURCES;
-
- return ISCSI_CONNECT_PDU_READ_ERR_FATAL;
- }
+ iscsi_login_response_packet *login_response_pkt = (iscsi_login_response_packet *) login_response_pdu->bhs_pkt;
- login_response_pdu->bhs_pkt = (iscsi_bhs_packet *) login_response_pkt;
- login_response_pdu->ds_cmd_data = (iscsi_ds_cmd_data *) (((uint8_t *) login_response_pkt) + sizeof(struct iscsi_bhs_packet) + pdu->header_digest_size);
- login_response_pdu->ds_len = ISCSI_DEFAULT_RECV_DS_LEN;
+ login_response_pdu->ds_len = 0UL;
- login_response_pkt->flags |= (int8_t) (login_req_pkt->flags & (ISCSI_LOGIN_REQ_FLAGS_TRANSIT | ISCSI_LOGIN_REQ_FLAGS_CONTINUE | ISCSI_LOGIN_REQ_FLAGS_CURRENT_STAGE_MASK));
+ login_response_pkt->opcode = ISCSI_OPCODE_SERVER_LOGIN_RES;
+ login_response_pkt->flags = (int8_t) (login_req_pkt->flags & (ISCSI_LOGIN_REQ_FLAGS_TRANSIT | ISCSI_LOGIN_REQ_FLAGS_CONTINUE | ISCSI_LOGIN_REQ_FLAGS_CURRENT_STAGE_MASK));
if ( (login_response_pkt->flags & ISCSI_LOGIN_RESPONSE_FLAGS_TRANSIT) != 0 )
login_response_pkt->flags |= (login_req_pkt->flags & ISCSI_LOGIN_REQ_FLAGS_NEXT_STAGE_MASK);
@@ -4039,10 +10344,16 @@ static int iscsi_login_response_init(iscsi_pdu *login_response_pdu, const iscsi_
login_response_pkt->isid.d = login_req_pkt->isid.d; // Copying over doesn't change endianess.
login_response_pkt->tsih = login_req_pkt->tsih; // Copying over doesn't change endianess.'
login_response_pkt->init_task_tag = login_req_pkt->init_task_tag; // Copying over doesn't change endianess.
+ login_response_pkt->reserved = 0UL;
login_response_pdu->cmd_sn = iscsi_get_be32(login_req_pkt->cmd_sn);
- if ( login_response_pkt->tsih != 0 )
+ if ( login_response_pkt->tsih != 0U )
login_response_pkt->stat_sn = login_req_pkt->exp_stat_sn; // Copying over doesn't change endianess.'
+ else
+ login_response_pkt->stat_sn = 0UL;
+
+ login_response_pkt->reserved2 = 0U;
+ login_response_pkt->reserved3 = 0ULL;
if ( ((login_response_pkt->flags & ISCSI_LOGIN_RESPONSE_FLAGS_TRANSIT) != 0) && ((login_response_pkt->flags & ISCSI_LOGIN_RESPONSE_FLAGS_CONTINUE) != 0) ) {
login_response_pkt->status_class = ISCSI_LOGIN_RESPONSE_STATUS_CLASS_CLIENT_ERR;
@@ -4060,11 +10371,11 @@ static int iscsi_login_response_init(iscsi_pdu *login_response_pdu, const iscsi_
login_response_pkt->status_detail = ISCSI_LOGIN_RESPONSE_STATUS_DETAILS_CLIENT_ERR_MISC;
return ISCSI_CONNECT_PDU_READ_ERR_LOGIN_RESPONSE;
- } else {
- login_response_pkt->status_class = ISCSI_LOGIN_RESPONSE_STATUS_CLASS_SUCCESS;
- login_response_pkt->status_detail = ISCSI_LOGIN_RESPONSE_STATUS_DETAILS_SUCCESS;
}
+ login_response_pkt->status_class = ISCSI_LOGIN_RESPONSE_STATUS_CLASS_SUCCESS;
+ login_response_pkt->status_detail = ISCSI_LOGIN_RESPONSE_STATUS_DETAILS_SUCCESS;
+
return ISCSI_CONNECT_PDU_READ_OK;
}
@@ -4134,8 +10445,8 @@ static inline uint64_t iscsi_connection_get_isid(const iscsi_isid *isid)
* @param[in] response_pdu Pointer to response PDU to initialize the
* port from, NULL is NOT allowed here, so be careful.
* @param[in] key_value_pairs Pointer to the hash map containing the key
- * and value pair for the initator name. May NOT be NULL,
- * so take caution.
+ * and value pair for the initiator name. May NOT be
+ * NULL, so take caution.
* @param[out] init_port_name Pointer to store the full qualified name
* of the initiator port and may NOT be NULL, so be careful.
* @return 0 in case the port could be initialized
@@ -4147,7 +10458,7 @@ static int iscsi_connection_login_init_port(iscsi_connection *conn, iscsi_pdu *r
{
iscsi_login_response_packet *login_response_pkt = (iscsi_login_response_packet *) response_pdu->bhs_pkt;
uint8_t *init_name;
- int rc = iscsi_get_key_value_pair( key_value_pairs, (uint8_t *) ISCSI_LOGIN_AUTH_SECURITY_TEXT_KEY_INITIATOR_NAME, &init_name );
+ int rc = iscsi_get_key_value_pair( key_value_pairs, ISCSI_LOGIN_AUTH_SECURITY_TEXT_KEY_INITIATOR_NAME, &init_name );
if ( rc != 0 ) {
login_response_pkt->status_class = ISCSI_LOGIN_RESPONSE_STATUS_CLASS_CLIENT_ERR;
@@ -4204,7 +10515,7 @@ static int iscsi_connection_login_session_type(iscsi_pdu *login_response_pdu, is
{
iscsi_login_response_packet *login_response_pkt = (iscsi_login_response_packet *) login_response_pdu->bhs_pkt;
uint8_t *type_str;
- int rc = iscsi_get_key_value_pair( key_value_pairs, (uint8_t *) ISCSI_LOGIN_AUTH_SECURITY_TEXT_KEY_SESSION_TYPE, &type_str );
+ int rc = iscsi_get_key_value_pair( key_value_pairs, ISCSI_LOGIN_AUTH_SECURITY_TEXT_KEY_SESSION_TYPE, &type_str );
if ( (rc == 0) && (type_str != NULL) ) {
if ( strcasecmp( (char *) type_str, "Discovery" ) == 0 ) {
@@ -4220,7 +10531,7 @@ static int iscsi_connection_login_session_type(iscsi_pdu *login_response_pdu, is
return ISCSI_CONNECT_PDU_READ_ERR_LOGIN_RESPONSE;
}
} else {
- if ( login_response_pkt->tsih != 0 ) {
+ if ( login_response_pkt->tsih != 0U ) {
*type = ISCSI_SESSION_TYPE_NORMAL;
} else {
login_response_pkt->status_class = ISCSI_LOGIN_RESPONSE_STATUS_CLASS_CLIENT_ERR;
@@ -4278,17 +10589,30 @@ static int iscsi_connection_login_check_target(iscsi_connection *conn, iscsi_pdu
uint8_t *redirect_adr = iscsi_target_node_get_redirect( conn, *target );
if ( redirect_adr != NULL ) {
- if ( iscsi_update_key_value_pair( login_response_pdu->key_value_pairs, (uint8_t *) ISCSI_LOGIN_AUTH_SECURITY_TEXT_KEY_TARGET_ADDRESS, redirect_adr ) == 0 ) {
- login_response_pkt->status_class = ISCSI_LOGIN_RESPONSE_STATUS_CLASS_REDIRECT;
- login_response_pkt->status_detail = ISCSI_LOGIN_RESPONSE_STATUS_DETAILS_REDIRECT_TEMP;
+ iscsi_key_value_pair *key_value_pair;
+ const int rc = iscsi_hashmap_get( iscsi_globvec->session_key_value_pairs, ISCSI_LOGIN_AUTH_SECURITY_TEXT_KEY_TARGET_ADDRESS, strlen( (char *) ISCSI_LOGIN_AUTH_SECURITY_TEXT_KEY_TARGET_ADDRESS ) + 1, (uint8_t **) &key_value_pair);
- return ISCSI_CONNECT_PDU_READ_ERR_LOGIN_RESPONSE;
+ if ( rc < 0 ) {
+ login_response_pkt->status_class = ISCSI_LOGIN_RESPONSE_STATUS_CLASS_SERVER_ERR;
+ login_response_pkt->status_detail = ISCSI_LOGIN_RESPONSE_STATUS_DETAILS_SERVER_ERR_OUT_OF_RESOURCES;
+
+ return ISCSI_CONNECT_PDU_READ_ERR_FATAL;
}
- login_response_pkt->status_class = ISCSI_LOGIN_RESPONSE_STATUS_CLASS_SERVER_ERR;
- login_response_pkt->status_detail = ISCSI_LOGIN_RESPONSE_STATUS_DETAILS_SERVER_ERR_OUT_OF_RESOURCES;
+ const int32_t ds_len = iscsi_append_key_value_pair_packet( key_value_pair, ISCSI_LOGIN_AUTH_SECURITY_TEXT_KEY_TARGET_ADDRESS, redirect_adr, (uint8_t *) login_response_pdu->ds_cmd_data, login_response_pdu->ds_len, login_response_pdu->len );
- return ISCSI_CONNECT_PDU_READ_ERR_FATAL;
+ if ( ds_len < 0L ) {
+ login_response_pkt->status_class = ISCSI_LOGIN_RESPONSE_STATUS_CLASS_SERVER_ERR;
+ login_response_pkt->status_detail = ISCSI_LOGIN_RESPONSE_STATUS_DETAILS_SERVER_ERR_OUT_OF_RESOURCES;
+
+ return ISCSI_CONNECT_PDU_READ_ERR_FATAL;
+ }
+
+ login_response_pdu->ds_len = ds_len;
+ login_response_pkt->status_class = ISCSI_LOGIN_RESPONSE_STATUS_CLASS_REDIRECT;
+ login_response_pkt->status_detail = ISCSI_LOGIN_RESPONSE_STATUS_DETAILS_REDIRECT_TEMP;
+
+ return ISCSI_CONNECT_PDU_READ_ERR_LOGIN_RESPONSE;
}
if ( iscsi_target_node_access( conn, *target, conn->init_name, conn->init_adr ) < 0 ) {
@@ -4313,14 +10637,19 @@ static int iscsi_connection_login_check_target(iscsi_connection *conn, iscsi_pdu
*/
static iscsi_session *iscsi_session_get_by_tsih(const uint16_t tsih)
{
- if ( tsih == 0 )
+ if ( tsih == 0U )
return NULL;
const uint64_t hash_key = tsih;
iscsi_session *session;
+
+ pthread_rwlock_rdlock( &iscsi_globvec->sessions_rwlock );
+
int rc = iscsi_hashmap_get( iscsi_globvec->sessions, (uint8_t *) &hash_key, sizeof(hash_key), (uint8_t **) &session );
- return (rc == 0) ? session : NULL;
+ pthread_rwlock_unlock( &iscsi_globvec->sessions_rwlock );
+
+ return ((rc == 0) ? session : NULL);
}
/**
@@ -4334,29 +10663,25 @@ static iscsi_session *iscsi_session_get_by_tsih(const uint16_t tsih)
* @param[in] init_port_name Pointer to initiator port name,
* may NOT be NULL, so take caution.
* @param[in] tsih Target Session Identifying Handle (TSIH).
- * @param[in] cid Connection ID (CID).
* @return Upper 8 bits of contain status class, lower 8
* bits status detail. All 16 bits set to zero
* indicate success.
*/
-static uint16_t iscsi_session_append(iscsi_connection *conn, const uint8_t *init_port_name, const uint16_t tsih, const uint16_t cid)
+static uint16_t iscsi_session_append(iscsi_connection *conn, const uint8_t *init_port_name, const uint16_t tsih)
{
iscsi_session *session = iscsi_session_get_by_tsih( tsih );
- if ( (session == NULL) || (conn->pg_tag != session->tag) || (strcasecmp( (char *) init_port_name, (char *) iscsi_port_get_name( session->initiator_port ) ) != 0) || (conn->target != session->target) )
+ if ( (session == NULL) || (conn->pg_tag != session->tag) || (strcasecmp( (char *) init_port_name, (char *) iscsi_port_get_name( session->init_port ) ) != 0) || (conn->target != session->target) )
return (ISCSI_LOGIN_RESPONSE_STATUS_CLASS_CLIENT_ERR << 8U) | ISCSI_LOGIN_RESPONSE_STATUS_DETAILS_CLIENT_ERR_NO_SESSION_SPANNING;
- if ( iscsi_hashmap_size( session->connections ) >= session->max_conns )
+ if ( session->conns >= session->max_conns )
return (ISCSI_LOGIN_RESPONSE_STATUS_CLASS_CLIENT_ERR << 8U) | ISCSI_LOGIN_RESPONSE_STATUS_DETAILS_CLIENT_ERR_TOO_MANY_CONNECTIONS;
conn->session = session;
- uint8_t *conn_key = iscsi_hashmap_key_create( (uint8_t *) &cid, sizeof(cid) );
+ iscsi_list_enqueue( &session->conn_list, &conn->node );
- if ( conn_key == NULL )
- return (ISCSI_LOGIN_RESPONSE_STATUS_CLASS_SERVER_ERR << 8U) | ISCSI_LOGIN_RESPONSE_STATUS_DETAILS_SERVER_ERR_OUT_OF_RESOURCES;
-
- iscsi_hashmap_put( session->connections, conn_key, sizeof(cid), (uint8_t *) conn );
+ session->conns++;
return (ISCSI_LOGIN_RESPONSE_STATUS_CLASS_SUCCESS << 8U) | ISCSI_LOGIN_RESPONSE_STATUS_DETAILS_SUCCESS;
}
@@ -4380,10 +10705,10 @@ static uint16_t iscsi_session_append(iscsi_connection *conn, const uint8_t *init
static int iscsi_connection_login_check_session(iscsi_connection *conn, iscsi_pdu *login_response_pdu, uint8_t *init_port_name, uint cid)
{
iscsi_login_response_packet *login_response_pkt = (iscsi_login_response_packet *) login_response_pdu->bhs_pkt;
- int rc = 0L;
+ int rc = 0;
- if ( login_response_pkt->tsih != 0 ) {
- rc = iscsi_session_append( conn, init_port_name, iscsi_get_be16(login_response_pkt->tsih), (uint16_t) cid );
+ if ( login_response_pkt->tsih != 0U ) {
+ rc = iscsi_session_append( conn, init_port_name, iscsi_get_be16(login_response_pkt->tsih) );
if ( rc != 0 ) {
login_response_pkt->status_class = (uint8_t) (rc >> 8U);
@@ -4409,16 +10734,29 @@ static int iscsi_connection_login_check_session(iscsi_connection *conn, iscsi_pd
* @param[in] pdu Pointer to iSCSI login request PDU, may NOT
* be NULL, so be careful.
*/
-void iscsi_login_response_reject_init(iscsi_pdu *login_response_pdu, const iscsi_pdu *pdu)
+void iscsi_connection_login_response_reject(iscsi_pdu *login_response_pdu, const iscsi_pdu *pdu)
{
iscsi_login_response_packet *login_response_pkt = (iscsi_login_response_packet *) login_response_pdu->bhs_pkt;
- login_response_pkt->opcode = ISCSI_SERVER_LOGIN_RES;
- login_response_pkt->version_max = ISCSI_VERSION_MAX;
- login_response_pkt->version_active = ISCSI_VERSION_MAX;
- login_response_pkt->init_task_tag = ((iscsi_login_req_packet *) pdu->bhs_pkt)->init_task_tag;
- login_response_pkt->status_class = ISCSI_LOGIN_RESPONSE_STATUS_CLASS_CLIENT_ERR;
- login_response_pkt->status_detail = ISCSI_LOGIN_RESPONSE_STATUS_DETAILS_CLIENT_ERR_INVALID_LOGIN_REQ_TYPE;
+ login_response_pkt->opcode = ISCSI_OPCODE_SERVER_LOGIN_RES;
+ login_response_pkt->flags = 0;
+ login_response_pkt->version_max = ISCSI_VERSION_MAX;
+ login_response_pkt->version_active = ISCSI_VERSION_MAX;
+ *(uint32_t *) &login_response_pkt->total_ahs_len = 0UL; // TotalAHSLength and DataSegmentLength are always 0, so write in one step.
+ login_response_pkt->isid.a = 0U;
+ login_response_pkt->isid.b = 0U;
+ login_response_pkt->isid.c = 0U;
+ login_response_pkt->isid.d = 0U;
+ login_response_pkt->tsih = 0U;
+ login_response_pkt->init_task_tag = ((iscsi_login_req_packet *) pdu->bhs_pkt)->init_task_tag;
+ login_response_pkt->reserved = 0UL;
+ login_response_pkt->stat_sn = 0UL;
+ login_response_pkt->exp_cmd_sn = 0UL;
+ login_response_pkt->max_cmd_sn = 0UL;
+ login_response_pkt->status_class = ISCSI_LOGIN_RESPONSE_STATUS_CLASS_CLIENT_ERR;
+ login_response_pkt->status_detail = ISCSI_LOGIN_RESPONSE_STATUS_DETAILS_CLIENT_ERR_INVALID_LOGIN_REQ_TYPE;
+ login_response_pkt->reserved2 = 0U;
+ login_response_pkt->reserved3 = 0ULL;
}
/**
@@ -4429,12 +10767,24 @@ void iscsi_login_response_reject_init(iscsi_pdu *login_response_pdu, const iscsi
* filling the data until everything has been read.
*
* @param[in] conn Pointer to connection to link the PDU with.
- * If this is NULL the connection has to be linked later.
+ * If this is NULL the connection has to be
+ * linked later.
+ * @param[in] ahs_len Length of AHS packet data to be appended.
+ * @param[in] header_digest_size Length of header digest. Currently,
+ * only 0, in which case the header digest will
+ * be removed, or 4 for CRC32C are allowed.
+ * @param[in] ds_len Length of DataSegment packet data to be appended.
+ * May not exceed 16MiB - 1 (16777215 bytes).
+ * @param[in] data_digest_size Length of optional data digest (0 or
+ * 4 for now) to add.
* @return Pointer to allocated and zero filled PDU or NULL
* in case of an error (usually memory exhaustion).
*/
-iscsi_pdu *iscsi_connection_pdu_create(iscsi_connection *conn)
+iscsi_pdu *iscsi_connection_pdu_create(iscsi_connection *conn, const uint ahs_len, const int header_digest_size, const uint32_t ds_len, const int data_digest_size)
{
+ if ( (ahs_len > ISCSI_MAX_AHS_SIZE) || ((header_digest_size != 0) && (header_digest_size != ISCSI_DIGEST_SIZE)) || ((data_digest_size != 0) && data_digest_size != ISCSI_DIGEST_SIZE) || (ds_len > ISCSI_MAX_DS_SIZE) )
+ return NULL;
+
iscsi_pdu *pdu = (iscsi_pdu *) malloc( sizeof(struct iscsi_pdu) );
if ( pdu == NULL ) {
@@ -4443,38 +10793,46 @@ iscsi_pdu *iscsi_connection_pdu_create(iscsi_connection *conn)
return NULL;
}
- pdu->bhs_pkt = iscsi_create_packet();
-
- if ( pdu->bhs_pkt == NULL ) {
- free( pdu );
-
- return NULL;
- }
+ const uint32_t pkt_ds_len = ISCSI_ALIGN(ds_len, ISCSI_ALIGN_SIZE);
+ const uint32_t len = (uint32_t) (sizeof(struct iscsi_bhs_packet) + (uint32_t) ahs_len + header_digest_size + pkt_ds_len + ((pkt_ds_len != 0UL) ? (uint32_t) data_digest_size : 0UL));
+ iscsi_bhs_packet *bhs_pkt = malloc( len );
- pdu->ahs_pkt = NULL;
- pdu->header_digest = NULL;
- pdu->ds_cmd_data = NULL;
- pdu->data_digest = NULL;
- pdu->key_value_pairs = iscsi_hashmap_create( 32UL );
+ if ( bhs_pkt == NULL ) {
+ logadd( LOG_ERROR, "iscsi_pdu_create: Out of memory while allocating iSCSI PDU packet data" );
- if ( pdu->key_value_pairs == NULL ) {
free( pdu );
return NULL;
}
- pdu->flags = 0L;
- pdu->header_digest_size = 0L;
- pdu->header_digest_read_len = 0UL;
- pdu->data_digest_size = 0L;
- pdu->data_digest_read_len = 0UL;
- pdu->bhs_read_len = 0UL;
- pdu->ahs_len = 0UL;
- pdu->ahs_read_len = 0UL;
- pdu->ds_len = 0UL;
- pdu->pos = 0UL;
- pdu->conn = conn;
- pdu->cmd_sn = 0UL;
+ pdu->node.succ = NULL;
+ pdu->node.pred = NULL;
+ pdu->bhs_pkt = bhs_pkt;
+ pdu->ahs_pkt = ((ahs_len != 0U) ? (iscsi_ahs_packet *) (((uint8_t *) bhs_pkt) + sizeof(struct iscsi_bhs_packet) ) : NULL);
+ pdu->header_digest = ((header_digest_size != 0) ? (iscsi_header_digest *) (((uint8_t *) bhs_pkt) + sizeof(struct iscsi_bhs_packet) + ahs_len) : NULL);
+ pdu->ds_cmd_data = ((pkt_ds_len != 0UL) ? (iscsi_scsi_ds_cmd_data *) (((uint8_t *) bhs_pkt) + sizeof(struct iscsi_bhs_packet) + ahs_len + header_digest_size) : NULL);
+ pdu->data_digest = (((pkt_ds_len != 0uL) && (data_digest_size != 0)) ? (iscsi_data_digest *) (((uint8_t *) bhs_pkt) + sizeof(struct iscsi_bhs_packet) + ahs_len + header_digest_size + ISCSI_ALIGN(pkt_ds_len, ISCSI_ALIGN_SIZE)) : NULL);
+ pdu->task = NULL;
+ pdu->conn = conn;
+ pdu->xfer_complete_callback = NULL;
+ pdu->xfer_complete_user_data = NULL;
+ pdu->flags = 0;
+ pdu->ref = 1UL;
+ pdu->bhs_pos = 0U;
+ pdu->ahs_pos = 0U;
+ pdu->ahs_len = ahs_len;
+ pdu->header_digest_pos = 0U;
+ pdu->header_digest_size = header_digest_size;
+ pdu->ds_len = pkt_ds_len;
+ pdu->pos = 0UL;
+ pdu->len = pkt_ds_len;
+ pdu->data_digest_pos = 0U;
+ pdu->data_digest_size = data_digest_size;
+ pdu->task_ref_cnt = 0U;
+ pdu->cmd_sn = 0UL;
+
+ if ( pkt_ds_len != 0UL )
+ memset( (((uint8_t *) pdu->ds_cmd_data) + ds_len), 0, (pkt_ds_len - ds_len) );
return pdu;
}
@@ -4485,23 +10843,21 @@ iscsi_pdu *iscsi_connection_pdu_create(iscsi_connection *conn)
* All associated data which has been read so
* far will be freed as well.
*
- * @param[in] pdu PDU structure to be deallocated, may be NULL
- * in which case this function does nothing.
+ * @param[in] pdu Pointer to PDU structure to be deallocated,
+ * may be NULL in which case this function
+ * does nothing.
*/
void iscsi_connection_pdu_destroy(iscsi_pdu *pdu)
{
- if ( pdu != NULL ) {
- if ( pdu->key_value_pairs != NULL ) {
- iscsi_hashmap_iterate( pdu->key_value_pairs, iscsi_hashmap_key_destroy_value_callback, NULL );
- iscsi_hashmap_destroy( pdu->key_value_pairs );
-
- pdu->key_value_pairs = NULL;
- }
-
+ if ( (pdu != NULL) && (--pdu->ref == 0UL) ) {
if ( pdu->bhs_pkt != NULL ) {
free( pdu->bhs_pkt );
- pdu->bhs_pkt = NULL;
+ pdu->bhs_pkt = NULL;
+ pdu->ahs_pkt = NULL;
+ pdu->header_digest = NULL;
+ pdu->ds_cmd_data = NULL;
+ pdu->data_digest = NULL;
}
free( pdu );
@@ -4509,6 +10865,377 @@ void iscsi_connection_pdu_destroy(iscsi_pdu *pdu)
}
/**
+ * @brief Appends packet data to an iSCSI PDU structure used by connections.
+ *
+ * This function adjusts the pointers if
+ * the packet data size needs to be
+ * extended.
+ *
+ * @param[in] pdu Pointer to iSCSI PDU where to append
+ * the packet data to. May NOT be NULL, so
+ * be careful.
+ * @param[in] ahs_len Length of AHS packet data to be appended.
+ * @param[in] header_digest_size Length of header digest. Currently,
+ * only 0, in which case the header digest will
+ * be removed, or 4 for CRC32C are allowed.
+ * @param[in] ds_len Length of DataSegment packet data to be appended.
+ * May not exceed 16MiB - 1 (16777215 bytes).
+ * @param[in] data_digest_size Length of optional data digest (0 or
+ * 4 for now) to add.
+ * @return Pointer to allocated and zero filled PDU or NULL
+ * in case of an error (usually memory exhaustion).
+ */
+iscsi_bhs_packet *iscsi_connection_pdu_append(iscsi_pdu *pdu, const uint ahs_len, const int header_digest_size, const uint32_t ds_len, const int data_digest_size)
+{
+ if ( (ahs_len > ISCSI_MAX_AHS_SIZE) || ((header_digest_size != 0) && (header_digest_size != ISCSI_DIGEST_SIZE)) || ((data_digest_size != 0) && data_digest_size != ISCSI_DIGEST_SIZE) || (ds_len > ISCSI_MAX_DS_SIZE) )
+ return NULL;
+
+ if ( (ahs_len != pdu->ahs_len) || (header_digest_size != pdu->header_digest_size) || (ds_len != pdu->ds_len) || (data_digest_size != pdu->data_digest_size) ) {
+ iscsi_bhs_packet *bhs_pkt;
+ const uint32_t pkt_ds_len = ISCSI_ALIGN(ds_len, ISCSI_ALIGN_SIZE);
+ const uint32_t old_len = (uint32_t) (sizeof(struct iscsi_bhs_packet) + (uint32_t) pdu->ahs_len + pdu->header_digest_size + pdu->ds_len + ((pdu->ds_len != 0UL) ? (uint32_t) pdu->data_digest_size : 0UL));
+ const uint32_t new_len = (uint32_t) (sizeof(struct iscsi_bhs_packet) + (uint32_t) ahs_len + header_digest_size + pkt_ds_len + ((pkt_ds_len != 0UL) ? (uint32_t) data_digest_size : 0UL));
+
+ if ( new_len > old_len ) {
+ bhs_pkt = realloc( pdu->bhs_pkt, new_len );
+
+ if ( bhs_pkt == NULL ) {
+ logadd( LOG_ERROR, "iscsi_connection_pdu_append: Out of memory while reallocating iSCSI PDU packet data" );
+
+ return NULL;
+ }
+
+ pdu->bhs_pkt = bhs_pkt;
+ } else {
+ bhs_pkt = pdu->bhs_pkt;
+ }
+
+ pdu->ahs_pkt = ((ahs_len != 0U) ? (iscsi_ahs_packet *) (((uint8_t *) bhs_pkt) + sizeof(struct iscsi_bhs_packet) ) : NULL);
+ pdu->header_digest = ((header_digest_size != 0) ? (iscsi_header_digest *) (((uint8_t *) bhs_pkt) + sizeof(struct iscsi_bhs_packet) + ahs_len) : NULL);
+ pdu->ds_cmd_data = ((pkt_ds_len != 0UL) ? (iscsi_scsi_ds_cmd_data *) (((uint8_t *) bhs_pkt) + sizeof(struct iscsi_bhs_packet) + ahs_len + header_digest_size) : NULL);
+ pdu->data_digest = (((pkt_ds_len != 0UL) && (data_digest_size != 0)) ? (iscsi_data_digest *) (((uint8_t *) bhs_pkt) + sizeof(struct iscsi_bhs_packet) + ahs_len + header_digest_size + pkt_ds_len) : NULL);
+ pdu->ahs_len = ahs_len;
+ pdu->header_digest_size = header_digest_size;
+ pdu->ds_len = pkt_ds_len;
+ pdu->len = pkt_ds_len;
+ pdu->data_digest_size = data_digest_size;
+
+ if ( pkt_ds_len != 0UL )
+ memset( (((uint8_t *) pdu->ds_cmd_data) + ds_len), 0, (pkt_ds_len - ds_len) );
+ }
+
+ return pdu->bhs_pkt;
+}
+
+/**
+ * @brief Frees an iSCSI PDU structure used by using connection callback function.
+ *
+ * This function frees an iSCSI PDU structure.
+ *
+ * @param[in] conn Pointer to iSCSI connection to free
+ * the PDU from. May NOT be NULL, so take caution.
+ * @param[in] pdu Pointer to iSCSI PDU structure to be
+ * freed. NULL is NOT allowed here, so take
+ * caution.
+ */
+void iscsi_connection_pdu_free(iscsi_connection *conn, iscsi_pdu *pdu)
+{
+ iscsi_connection_xfer_complete_callback callback = pdu->xfer_complete_callback;
+ uint8_t *user_data = pdu->xfer_complete_user_data;
+
+ pdu->xfer_complete_callback = NULL;
+
+ if ( pdu->task != NULL )
+ iscsi_task_destroy( pdu->task );
+
+ iscsi_connection_pdu_destroy( pdu );
+
+ if ( callback != NULL )
+ callback( user_data );
+}
+
+/**
+ * @brief Retrieves the pointer to an specific AHS packet from an iSCSI PDU by index.
+ *
+ * Gets the pointer of an AHS packet by specified index.
+ *
+ * @param[in] pdu Pointer to iSCSI PDU of which the
+ * AHS packet should be retrieved. May
+ * NOT be NULL, so be careful.
+ * @param[in] index Zero-based index number of AHS packet to
+ * be received.
+ * @return The pointer to the AHS packet at specified index on
+ * success or NULL in case of an error or if the specific index
+ * is out of range.
+ */
+iscsi_ahs_packet *iscsi_connection_pdu_ahs_packet_get(const iscsi_pdu *pdu, const int index)
+{
+ iscsi_ahs_packet *ahs_pkt = pdu->ahs_pkt; // First AHS packet
+
+ if ( ahs_pkt == NULL )
+ return NULL;
+
+ int count = index;
+ uint ahs_len = pdu->ahs_len;
+
+ while ( (int) ahs_len > 0 ) {
+ if ( count-- < 0 )
+ return ahs_pkt;
+
+ uint len = iscsi_get_be16(ahs_pkt->len) + offsetof(struct iscsi_ahs_packet, data); // Total length of current AHS packet
+
+ len = ISCSI_ALIGN(len, ISCSI_ALIGN_SIZE);
+ ahs_len -= len;
+ ahs_pkt = (iscsi_ahs_packet *) (((uint8_t *) ahs_pkt) + (len - offsetof(struct iscsi_ahs_packet, data))); // Advance pointer to next AHS packet
+ }
+
+ logadd( LOG_ERROR, "iscsi_connection_pdu_ahs_packet_get: Specified index for AHS packet does not exist" );
+
+ return NULL;
+}
+
+/**
+ * @brief Counts number of AHS packets of an iSCSI PDU.
+ *
+ * Gets the total number of AHS packets.
+ *
+ * @param[in] pdu Pointer to iscsi PDU of which the
+ * number of AHS packets should be counted.
+ * May NOT be NULL, so be careful.
+ * @return The number of AHS packets or 0 if no AHS
+ * packet data is available.
+ */
+int iscsi_connection_pdu_ahs_packet_count(const iscsi_pdu *pdu)
+{
+ const iscsi_ahs_packet *ahs_pkt = pdu->ahs_pkt; // First AHS packet
+
+ if ( ahs_pkt == NULL )
+ return 0;
+
+ int count = 0;
+ uint ahs_len = pdu->ahs_len;
+
+ while ( (int) ahs_len > 0 ) {
+ uint len = iscsi_get_be16(ahs_pkt->len) + offsetof(struct iscsi_ahs_packet, data); // Total length of current AHS packet
+
+ len = ISCSI_ALIGN(len, ISCSI_ALIGN_SIZE);
+ ahs_len -= len;
+ ahs_pkt = (iscsi_ahs_packet *) (((uint8_t *) ahs_pkt) + (len - offsetof(struct iscsi_ahs_packet, data))); // Advance pointer to next AHS packet
+ count++;
+ }
+
+ return count;
+}
+
+/// CRC32C lookup table. Created with a polynomial reflect value of 0x82F63B78.
+static const uint32_t crc32c_lut[] = {
+ 0x00000000, 0xF26B8303, 0xE13B70F7, 0x1350F3F4, 0xC79A971F, 0x35F1141C, 0x26A1E7E8, 0xD4CA64EB,
+ 0x8AD958CF, 0x78B2DBCC, 0x6BE22838, 0x9989AB3B, 0x4D43CFD0, 0xBF284CD3, 0xAC78BF27, 0x5E133C24,
+ 0x105EC76F, 0xE235446C, 0xF165B798, 0x030E349B, 0xD7C45070, 0x25AFD373, 0x36FF2087, 0xC494A384,
+ 0x9A879FA0, 0x68EC1CA3, 0x7BBCEF57, 0x89D76C54, 0x5D1D08BF, 0xAF768BBC, 0xBC267848, 0x4E4DFB4B,
+ 0x20BD8EDE, 0xD2D60DDD, 0xC186FE29, 0x33ED7D2A, 0xE72719C1, 0x154C9AC2, 0x061C6936, 0xF477EA35,
+ 0xAA64D611, 0x580F5512, 0x4B5FA6E6, 0xB93425E5, 0x6DFE410E, 0x9F95C20D, 0x8CC531F9, 0x7EAEB2FA,
+ 0x30E349B1, 0xC288CAB2, 0xD1D83946, 0x23B3BA45, 0xF779DEAE, 0x05125DAD, 0x1642AE59, 0xE4292D5A,
+ 0xBA3A117E, 0x4851927D, 0x5B016189, 0xA96AE28A, 0x7DA08661, 0x8FCB0562, 0x9C9BF696, 0x6EF07595,
+ 0x417B1DBC, 0xB3109EBF, 0xA0406D4B, 0x522BEE48, 0x86E18AA3, 0x748A09A0, 0x67DAFA54, 0x95B17957,
+ 0xCBA24573, 0x39C9C670, 0x2A993584, 0xD8F2B687, 0x0C38D26C, 0xFE53516F, 0xED03A29B, 0x1F682198,
+ 0x5125DAD3, 0xA34E59D0, 0xB01EAA24, 0x42752927, 0x96BF4DCC, 0x64D4CECF, 0x77843D3B, 0x85EFBE38,
+ 0xDBFC821C, 0x2997011F, 0x3AC7F2EB, 0xC8AC71E8, 0x1C661503, 0xEE0D9600, 0xFD5D65F4, 0x0F36E6F7,
+ 0x61C69362, 0x93AD1061, 0x80FDE395, 0x72966096, 0xA65C047D, 0x5437877E, 0x4767748A, 0xB50CF789,
+ 0xEB1FCBAD, 0x197448AE, 0x0A24BB5A, 0xF84F3859, 0x2C855CB2, 0xDEEEDFB1, 0xCDBE2C45, 0x3FD5AF46,
+ 0x7198540D, 0x83F3D70E, 0x90A324FA, 0x62C8A7F9, 0xB602C312, 0x44694011, 0x5739B3E5, 0xA55230E6,
+ 0xFB410CC2, 0x092A8FC1, 0x1A7A7C35, 0xE811FF36, 0x3CDB9BDD, 0xCEB018DE, 0xDDE0EB2A, 0x2F8B6829,
+ 0x82F63B78, 0x709DB87B, 0x63CD4B8F, 0x91A6C88C, 0x456CAC67, 0xB7072F64, 0xA457DC90, 0x563C5F93,
+ 0x082F63B7, 0xFA44E0B4, 0xE9141340, 0x1B7F9043, 0xCFB5F4A8, 0x3DDE77AB, 0x2E8E845F, 0xDCE5075C,
+ 0x92A8FC17, 0x60C37F14, 0x73938CE0, 0x81F80FE3, 0x55326B08, 0xA759E80B, 0xB4091BFF, 0x466298FC,
+ 0x1871A4D8, 0xEA1A27DB, 0xF94AD42F, 0x0B21572C, 0xDFEB33C7, 0x2D80B0C4, 0x3ED04330, 0xCCBBC033,
+ 0xA24BB5A6, 0x502036A5, 0x4370C551, 0xB11B4652, 0x65D122B9, 0x97BAA1BA, 0x84EA524E, 0x7681D14D,
+ 0x2892ED69, 0xDAF96E6A, 0xC9A99D9E, 0x3BC21E9D, 0xEF087A76, 0x1D63F975, 0x0E330A81, 0xFC588982,
+ 0xB21572C9, 0x407EF1CA, 0x532E023E, 0xA145813D, 0x758FE5D6, 0x87E466D5, 0x94B49521, 0x66DF1622,
+ 0x38CC2A06, 0xCAA7A905, 0xD9F75AF1, 0x2B9CD9F2, 0xFF56BD19, 0x0D3D3E1A, 0x1E6DCDEE, 0xEC064EED,
+ 0xC38D26C4, 0x31E6A5C7, 0x22B65633, 0xD0DDD530, 0x0417B1DB, 0xF67C32D8, 0xE52CC12C, 0x1747422F,
+ 0x49547E0B, 0xBB3FFD08, 0xA86F0EFC, 0x5A048DFF, 0x8ECEE914, 0x7CA56A17, 0x6FF599E3, 0x9D9E1AE0,
+ 0xD3D3E1AB, 0x21B862A8, 0x32E8915C, 0xC083125F, 0x144976B4, 0xE622F5B7, 0xF5720643, 0x07198540,
+ 0x590AB964, 0xAB613A67, 0xB831C993, 0x4A5A4A90, 0x9E902E7B, 0x6CFBAD78, 0x7FAB5E8C, 0x8DC0DD8F,
+ 0xE330A81A, 0x115B2B19, 0x020BD8ED, 0xF0605BEE, 0x24AA3F05, 0xD6C1BC06, 0xC5914FF2, 0x37FACCF1,
+ 0x69E9F0D5, 0x9B8273D6, 0x88D28022, 0x7AB90321, 0xAE7367CA, 0x5C18E4C9, 0x4F48173D, 0xBD23943E,
+ 0xF36E6F75, 0x0105EC76, 0x12551F82, 0xE03E9C81, 0x34F4F86A, 0xC69F7B69, 0xD5CF889D, 0x27A40B9E,
+ 0x79B737BA, 0x8BDCB4B9, 0x988C474D, 0x6AE7C44E, 0xBE2DA0A5, 0x4C4623A6, 0x5F16D052, 0xAD7D5351};
+
+/**
+ * @brief Calculates digest (CRC32C).
+ *
+ * Calculates CRC32C with 0x82F63B78 polynomial
+ * reflect according to iSCSI specs.\n
+ * TODO: Implement optimized SSE4.2 and ARM versions
+ *
+ * @param[in] data Pointer to data to calculate CRC32C for.
+ * @param[in] len Length of data to be calculated. Must be
+ * divisable by 4 which is guaranteed by iSCSI standard.
+ * @param[in] crc32c Previous CRC32C in case of multiple passes.
+ * @return CRC32C value. THis function cannot fail.
+ */
+static inline uint32_t iscsi_crc32c_update(const uint8_t *data, const uint len, uint32_t crc32c)
+{
+ for ( uint i = 0; i < len; i += 4 ) {
+ crc32c = (crc32c >> 8UL) ^ crc32c_lut[(crc32c ^ data[i]) & 0xFF];
+ crc32c = (crc32c >> 8UL) ^ crc32c_lut[(crc32c ^ data[i + 1]) & 0xFF];
+ crc32c = (crc32c >> 8UL) ^ crc32c_lut[(crc32c ^ data[i + 2]) & 0xFF];
+ crc32c = (crc32c >> 8UL) ^ crc32c_lut[(crc32c ^ data[i + 3]) & 0xFF];
+ }
+
+ return crc32c;
+}
+
+/**
+ * @brief Calculate and store iSCSI header digest (CRC32C).
+ *
+ * Calculates header digest (CRC32C) with
+ * 0x82F63B78 polynomial reflect according
+ * to iSCSI specs and stores the result in
+ * the iSCSI packet data. This function
+ * cannot fail.
+ *
+ * @param[out] header_digest Pointer to iSCSI header digest
+ * packet data to put CRC32C into.
+ * May NOT be NULL, so be careful.
+ * @param[in] packet_data Pointer to ISCSI BHS packet to
+ * calculate CRC32C for. NULL is NOT
+ * allowed here, take caution.
+ * @param[in] ahs_len AHS segment length in bytes.
+ */
+void iscsi_connection_pdu_digest_header_update(iscsi_header_digest *header_digest, const iscsi_bhs_packet *packet_data, const uint ahs_len)
+{
+ const uint32_t crc32c = iscsi_crc32c_update( (uint8_t *) packet_data, (sizeof(struct iscsi_bhs_packet) + ahs_len), ISCSI_CRC32C_INITIAL ) ^ ISCSI_CRC32C_XOR;
+
+ iscsi_put_le32( (uint8_t *) &header_digest->crc32c, crc32c );
+}
+
+/**
+ * @brief Validates a stored iSCSI header digest (CRC32C) with actual header data.
+ *
+ * Verifies header digest (CRC32C) with
+ * 0x82F63B78 polynomial reflect according
+ * to iSCSI specs. This function cannot
+ * fail.
+ *
+ * @param[in] header_digest Pointer to iSCSI header digest
+ * packet data to compare CRC32C with.
+ * May NOT be NULL, so be careful.
+ * @param[in] packet_data Pointer to ISCSI BHS packet to
+ * validate CRC32C for. May NOT be NULL,
+ * so be careful.
+ * @param[in] ahs_len AHS segment length in bytes.
+ * @retval true CRC32C matches the stored value.
+ * @retval false CRC32C does NOT match the stored value.
+ */
+bool iscsi_connection_pdu_digest_header_verify(const iscsi_header_digest *header_digest, const iscsi_bhs_packet *packet_data, const uint ahs_len)
+{
+ const uint32_t crc32c = iscsi_crc32c_update( (uint8_t *) packet_data, (sizeof(struct iscsi_bhs_packet) + ahs_len), ISCSI_CRC32C_INITIAL ) ^ ISCSI_CRC32C_XOR;
+
+ return (iscsi_get_le32(crc32c) == header_digest->crc32c);
+}
+
+/**
+ * @brief Calculate iSCSI data digest (CRC32C).
+ *
+ * Calculates data digest (CRC32) with
+ * 0x82F63B78 polynomial reflect of a
+ * whole DataSegment (CRC32C) according
+ * to the iSCSI specs.\n
+ * The resulting CRC32C will be stored
+ * in the iSCSI packet.
+ *
+ * @param[out] data_digest Pointer to iSCSI data digest
+ * packet data to put CRC32C into.
+ * May NOT be NULL, so be careful.
+ * @param[in] ds_cmd_data Pointer to iSCSI DataSegment packet to
+ * calculate CRC32C for. NULL is NOT
+ * allowed here, take caution.
+ * @param[in] ds_len Data segment length in bytes.
+ */
+void iscsi_connection_pdu_digest_data_update(iscsi_data_digest *data_digest, const iscsi_scsi_ds_cmd_data *ds_cmd_data, const uint32_t ds_len)
+{
+ const uint32_t crc32c = iscsi_crc32c_update( (uint8_t *) ds_cmd_data, ISCSI_ALIGN(ds_len, ISCSI_DIGEST_SIZE), ISCSI_CRC32C_INITIAL ) ^ ISCSI_CRC32C_XOR;
+
+ iscsi_put_le32( (uint8_t *) &data_digest->crc32c, crc32c );
+}
+
+/**
+ * @brief Validates a stored iSCSI data digest (CRC32C) with actual DataSegment.
+ *
+ * Verifies data digest (CRC32C) with
+ * 0x82F63B78 polynomial reflect according
+ * to iSCSI specs. This function cannot
+ * fail.
+ *
+ * @param[out] data_digest Pointer to iSCSI data digest
+ * packet data to compare CRC32C with.
+ * May NOT be NULL, so be careful.
+ * @param[in] ds_cmd_data Pointer to iSCSI DataSegment
+ * packet to calculate CRC32C for. May NOT
+ * be NULL, so be careful.
+ * @param[in] ds_len Data segment length in bytes.
+ * @retval true CRC32C matches the stored value.
+ * @retval false CRC32C does NOT match the stored value.
+ */
+bool iscsi_connection_pdu_digest_data_verify(const iscsi_data_digest *data_digest, const iscsi_scsi_ds_cmd_data *ds_cmd_data, const uint32_t ds_len)
+{
+ const uint32_t crc32c = iscsi_crc32c_update( (uint8_t *) ds_cmd_data, ISCSI_ALIGN(ds_len, ISCSI_DIGEST_SIZE), ISCSI_CRC32C_INITIAL ) ^ ISCSI_CRC32C_XOR;
+
+ return (iscsi_get_le32(crc32c) == data_digest->crc32c);
+}
+
+/**
+ * @brief Checks whether iSCSI PDU cleanup procedure has to be deferred.
+ *
+ * This function checks whether the cleanup
+ * process of a written PDU has to be
+ * deferred to a later stage.
+ *
+ * @param[in] pdu Pointer to iSCSI PDU to be checked for
+ * deferrred cleanup processs.
+ * @retval true The PDUs cleanup stage has to be
+ * deferred to a later stage.
+ * @retval false The PDU can be cleaned up immediately.
+ */
+static inline bool iscsi_connection_pdu_free_is_deferred(const iscsi_pdu *pdu)
+{
+ return ((pdu != NULL) && ((pdu->bhs_pkt->opcode == ISCSI_OPCODE_SERVER_READY_XFER) || (pdu->bhs_pkt->opcode == ISCSI_OPCODE_SERVER_SCSI_DATA_IN)));
+}
+
+/**
+ * @brief Handles iSCSI PDU cleanup after the PDU has been sent via TCP/IP to the client.
+ *
+ * This function checks whether there are PDU
+ * cleanup actions required and either frees
+ * the PDU or adds it to the PDU Sequence
+ * Number Acknowledgement (SNACK) list.
+ *
+ * @param[in] user_data Pointer to iSCSI PDU which completed
+ * the TCP/IP write. May NOT be NULL, so be
+ * careful.
+ */
+static void iscsi_connection_pdu_write_complete(uint8_t *user_data, int err)
+{
+ iscsi_pdu *pdu = (iscsi_pdu *) user_data;
+ iscsi_connection *conn = pdu->conn;
+
+ if ( conn->state >= ISCSI_CONNECT_STATE_EXITING )
+ return;
+
+ iscsi_list_remove( &pdu->node );
+
+ if ( err != 0 )
+ conn->state = ISCSI_CONNECT_STATE_EXITING;
+
+ if ( ((conn->flags & ISCSI_CONNECT_FLAGS_FULL_FEATURE) != 0) && (conn->session->err_recovery_level > 0UL) && iscsi_connection_pdu_free_is_deferred( pdu ) )
+ iscsi_list_enqueue( &conn->pdus_snack, &pdu->node );
+ else
+ iscsi_connection_pdu_free( conn, pdu );
+}
+
+/**
* @brief Writes and sends a response PDU to the client.
*
* This function sends a response PDU to the
@@ -4516,9 +11243,9 @@ void iscsi_connection_pdu_destroy(iscsi_pdu *pdu)
* If a header or data digest (CRC32C) needs to
* be calculated, this is done as well.
*
- * @param[in] conn iSCSI connection to handle. May
+ * @param[in] conn Pointer to iSCSI connection to handle. May
* NOT be NULL, so take caution.
- * @param[in] pdu iSCSI server response PDU to send.
+ * @param[in] pdu Pointer to iSCSI server response PDU to send.
* May NOT be NULL, so be careful.
* @param[in] callback Callback function to be invoked
* after TCP/IP packet has been sent successfully.
@@ -4529,24 +11256,104 @@ void iscsi_connection_pdu_destroy(iscsi_pdu *pdu)
*/
void iscsi_connection_pdu_write(iscsi_connection *conn, iscsi_pdu *pdu, iscsi_connection_xfer_complete_callback callback, uint8_t *user_data)
{
+ if ( ISCSI_GET_OPCODE(pdu->bhs_pkt->opcode) != ISCSI_OPCODE_CLIENT_LOGIN_REQ ) {
+ if ( pdu->header_digest != NULL )
+ iscsi_connection_pdu_digest_header_update( pdu->header_digest, pdu->bhs_pkt, pdu->ahs_len );
+
+ if ( pdu->data_digest != NULL )
+ iscsi_connection_pdu_digest_data_update( pdu->data_digest, pdu->ds_cmd_data, pdu->ds_len );
+ }
+
+ pdu->xfer_complete_callback = callback;
+ pdu->xfer_complete_user_data = user_data;
+
+ iscsi_list_enqueue( &conn->pdus_write, &pdu->node );
+
if ( conn->state >= ISCSI_CONNECT_STATE_EXITING )
return;
- if ( ISCSI_GET_OPCODE(pdu->bhs_pkt->opcode) != ISCSI_CLIENT_LOGIN_REQ ) {
- if ( conn->header_digest != 0 )
- iscsi_calc_header_digest( pdu->bhs_pkt );
+ const uint32_t len = (uint) (sizeof(struct iscsi_bhs_packet) + pdu->ahs_len + conn->header_digest + ISCSI_ALIGN(pdu->ds_len, ISCSI_ALIGN_SIZE) + conn->data_digest);
+ const int32_t rc = iscsi_connection_write( conn, (uint8_t *) pdu->bhs_pkt, len );
+ iscsi_connection_exec_queue *exec_queue = (iscsi_connection_exec_queue *) malloc( sizeof(struct iscsi_connection_exec_queue) );
+
+ if ( exec_queue == NULL ) {
+ logadd( LOG_ERROR, "iscsi_connection_pdu_write: Out of memory while allocating execution queue for PDU write" );
- if ( (conn->data_digest != 0) && (pdu->ds_len != 0) )
- iscsi_calc_data_digest( pdu->bhs_pkt, conn->header_digest );
+ return;
}
- const uint len = (uint) (sizeof(struct iscsi_bhs_packet) + pdu->ahs_len + conn->header_digest + iscsi_align(pdu->ds_len, ISCSI_ALIGN_SIZE) + conn->data_digest);
+ exec_queue->data.pdu_write.callback = iscsi_connection_pdu_write_complete;
+ exec_queue->data.pdu_write.user_data = (uint8_t *) pdu;
+ exec_queue->data.pdu_write.err = ((rc == (int32_t) len) ? 0 : -1);
+ exec_queue->type = ISCSI_CONNECT_EXEC_QUEUE_TYPE_PDU_WRITE;
- // TODO: Do the writing in a queue.
- iscsi_connection_write( conn, (uint8_t *) pdu->bhs_pkt, len );
+ iscsi_list_enqueue( &conn->exec_queue, &exec_queue->node );
+}
- if ( callback != NULL )
- callback( user_data );
+/**
+ * @brief Compares if the first iSCSI 32-bit sequence numbers is smaller than the second one.
+ *
+ * This function almost does the same as an
+ * unsigned compare but with special
+ * handling for "negative" numbers.
+ *
+ * @param[in] seq_num First iSCSI sequence number to be compared.
+ * @param[in] seq_num_2 Second iSCSI sequence number to be compared.
+ * @retval true if first sequence number is smaller than
+ * the second one.
+ * @retval false if first sequence number is equal or
+ * larger than the second one.
+ */
+static inline int iscsi_seq_num_cmp_lt(const uint32_t seq_num, const uint32_t seq_num_2)
+{
+ return (seq_num != seq_num_2) && (((seq_num < seq_num_2) && ((seq_num_2 - seq_num) < 2147483648UL)) || ((seq_num > seq_num_2) && ((seq_num - seq_num_2)) > 2147483648UL));
+}
+
+/**
+ * @brief Compares if the first iSCSI 32-bit sequence numbers is larger than the second one.
+ *
+ * This function almost does the same as an
+ * unsigned compare but with special
+ * handling for "negative" numbers.
+ *
+ * @param[in] seq_num First iSCSI sequence number to be compared.
+ * @param[in] seq_num_2 Second iSCSI sequence number to be compared.
+ * @retval true if first sequence number is larger than
+ * the second one.
+ * @retval false if first sequence number is equal or
+ * smaller than the second one.
+ */
+static inline int iscsi_seq_num_cmp_gt(const uint32_t seq_num, const uint32_t seq_num_2)
+{
+ return (seq_num != seq_num_2) && (((seq_num < seq_num_2) && ((seq_num_2 - seq_num) > 2147483648UL)) || ((seq_num > seq_num_2) && ((seq_num - seq_num_2)) < 2147483648UL));
+}
+
+/**
+ * @brief Removes an acknowledged PDU from SNACK PDU doubly linked list by ExpStatSN.
+ *
+ * This function is invoked when ExpStatSN becomes
+ * invalid.
+ *
+ * @param[in] conn Pointer to iSCSI connection to be removed,
+ * may NOT be NULL, so be careful.
+ * @param[in] exp_stat_sn First ExpStatSN to not to be removed.
+ */
+void iscsi_connection_pdu_ack_remove(iscsi_connection *conn, const uint32_t exp_stat_sn)
+{
+ conn->exp_stat_sn = ((exp_stat_sn < conn->stat_sn) ? exp_stat_sn : conn->stat_sn);
+
+ iscsi_pdu *pdu;
+ iscsi_pdu *tmp;
+
+ iscsi_list_foreach_safe_node ( &conn->pdus_snack, pdu, tmp ) {
+ iscsi_scsi_response_packet *scsi_response_pkt = (iscsi_scsi_response_packet *) pdu->bhs_pkt;
+ const uint32_t stat_sn = iscsi_get_be32(scsi_response_pkt->stat_sn);
+
+ if ( iscsi_seq_num_cmp_lt( stat_sn, conn->exp_stat_sn ) ) {
+ iscsi_list_remove( &pdu->node );
+ iscsi_connection_pdu_free( conn, pdu );
+ }
+ }
}
/**
@@ -4568,7 +11375,8 @@ static int iscsi_connection_handle_reject(iscsi_connection *conn, iscsi_pdu *pdu
{
pdu->flags |= ISCSI_PDU_FLAGS_REJECTED;
- iscsi_pdu *response_pdu = iscsi_connection_pdu_create( conn );
+ const uint32_t ds_len = (uint32_t) sizeof(struct iscsi_bhs_packet) + ((uint32_t) pdu->bhs_pkt->total_ahs_len << 2UL);
+ iscsi_pdu *response_pdu = iscsi_connection_pdu_create( conn, 0U, conn->header_digest, ds_len, conn->data_digest );
if ( response_pdu == NULL ) {
logadd( LOG_ERROR, "iscsi_connection_handle_reject: Out of memory while allocating iSCSI reject response PDU" );
@@ -4576,48 +11384,29 @@ static int iscsi_connection_handle_reject(iscsi_connection *conn, iscsi_pdu *pdu
return ISCSI_CONNECT_PDU_READ_ERR_FATAL;
}
- const uint32_t ds_len = (uint32_t) sizeof(struct iscsi_bhs_packet) + ((uint32_t) pdu->bhs_pkt->total_ahs_len << 2UL) + conn->header_digest;
- iscsi_reject_packet *reject_pkt = (iscsi_reject_packet *) iscsi_append_ds_packet( response_pdu->bhs_pkt, conn->header_digest, ds_len, conn->data_digest );
-
- if ( reject_pkt == NULL ) {
- logadd( LOG_ERROR, "iscsi_connection_handle_reject: Out of memory while allocating iSCSI reject packet data" );
+ iscsi_reject_packet *reject_pkt = (iscsi_reject_packet *) response_pdu->bhs_pkt;
- iscsi_connection_pdu_destroy( response_pdu );
-
- return ISCSI_CONNECT_PDU_READ_ERR_FATAL;
- }
-
- response_pdu->bhs_pkt = (iscsi_bhs_packet *) reject_pkt;
-
- if ( conn->header_digest != 0 ) {
- response_pdu->header_digest = (iscsi_header_digest *) (((iscsi_bhs_packet *) reject_pkt) + 1);
- response_pdu->header_digest_size = conn->header_digest;
- }
-
- response_pdu->ds_cmd_data = (iscsi_ds_cmd_data *) (((uint8_t *) reject_pkt) + sizeof(struct iscsi_bhs_packet) + conn->header_digest);
- response_pdu->ds_len = ds_len;
-
- if ( conn->data_digest != 0 ) {
- response_pdu->data_digest = (iscsi_data_digest *) (((uint8_t *) response_pdu->ds_cmd_data) + iscsi_align(ds_len, ISCSI_ALIGN_SIZE));
- response_pdu->data_digest_size = conn->data_digest;
- }
-
- reject_pkt->opcode = ISCSI_SERVER_REJECT;
- reject_pkt->flags |= -0x80;
- reject_pkt->reason = (uint8_t) reason_code;
- iscsi_put_be24( (uint8_t *) &reject_pkt->ds_len, ds_len );
- reject_pkt->tag = 0xFFFFFFFFUL;
+ reject_pkt->opcode = ISCSI_OPCODE_SERVER_REJECT;
+ reject_pkt->flags = -0x80;
+ reject_pkt->reason = (uint8_t) reason_code;
+ reject_pkt->reserved = 0U;
+ iscsi_put_be32( (uint8_t *) &reject_pkt->total_ahs_len, ds_len ); // TotalAHSLength is always 0 and DataSegmentLength is 24-bit, so write in one step.
+ reject_pkt->reserved2 = 0ULL;
+ reject_pkt->tag = 0xFFFFFFFFUL; // Minus one does not require endianess conversion
+ reject_pkt->reserved3 = 0UL;
iscsi_put_be32( (uint8_t *) &reject_pkt->stat_sn, conn->stat_sn++ );
if ( conn->session != NULL ) {
iscsi_put_be32( (uint8_t *) &reject_pkt->exp_cmd_sn, conn->session->exp_cmd_sn );
iscsi_put_be32( (uint8_t *) &reject_pkt->max_cmd_sn, conn->session->max_cmd_sn );
} else {
- iscsi_put_be32( (uint8_t *) &reject_pkt->exp_cmd_sn, 1 );
- iscsi_put_be32( (uint8_t *) &reject_pkt->max_cmd_sn, 1 );
+ iscsi_put_be32( (uint8_t *) &reject_pkt->exp_cmd_sn, 1UL );
+ iscsi_put_be32( (uint8_t *) &reject_pkt->max_cmd_sn, 1UL );
}
- memcpy( ((uint8_t *) reject_pkt) + sizeof(struct iscsi_bhs_packet), pdu->bhs_pkt, ds_len );
+ reject_pkt->reserved4 = 0ULL;
+
+ memcpy( response_pdu->ds_cmd_data, pdu->bhs_pkt, ds_len );
iscsi_connection_pdu_write( conn, response_pdu, NULL, NULL );
@@ -4631,18 +11420,45 @@ static int iscsi_connection_handle_reject(iscsi_connection *conn, iscsi_pdu *pdu
* Number (CmdSN) for incoming data sent by
* the client.
*
- * @param[in] conn iSCSI connection to handle. May
+ * @param[in] conn Pointer to iSCSI connection to handle. May
* NOT be NULL, so take caution.
- * @param[in] pdu iSCSI client request PDU to handle.
+ * @param[in] pdu Pointer to iSCSI client request PDU to handle.
* May be NULL in which case an error is returned.
* @return 0 on success. A negative value indicates
* an error. A positive value a warning.
*/
static int iscsi_connection_update_cmd_sn(iscsi_connection *conn, iscsi_pdu *pdu)
{
- // TODO: Implement update CmdSN.
+ iscsi_session *session = conn->session;
- return 0L;
+ if ( session == NULL )
+ return ISCSI_CONNECT_PDU_READ_ERR_FATAL;
+
+ iscsi_scsi_cmd_packet *scsi_cmd_pkt = (iscsi_scsi_cmd_packet *) pdu->bhs_pkt;
+ const int opcode = ISCSI_GET_OPCODE(scsi_cmd_pkt->opcode);
+
+ pdu->cmd_sn = iscsi_get_be32(scsi_cmd_pkt->cmd_sn);
+
+ if ( session->err_recovery_level == 0UL ) {
+ if ( (scsi_cmd_pkt->opcode & ISCSI_OPCODE_FLAGS_IMMEDIATE) == 0 ) {
+ if ( (iscsi_seq_num_cmp_lt( pdu->cmd_sn, session->exp_cmd_sn ) || iscsi_seq_num_cmp_gt( pdu->cmd_sn, session->max_cmd_sn )) && ((session->type == ISCSI_SESSION_TYPE_NORMAL) && (opcode != ISCSI_OPCODE_CLIENT_SCSI_DATA_OUT)) )
+ return ISCSI_CONNECT_PDU_READ_ERR_FATAL;
+ } else if ( (pdu->cmd_sn != session->exp_cmd_sn) && (opcode != ISCSI_OPCODE_CLIENT_NOP_OUT) )
+ return ISCSI_CONNECT_PDU_READ_ERR_FATAL;
+ }
+
+ uint32_t exp_stat_sn = iscsi_get_be32(scsi_cmd_pkt->exp_stat_sn);
+
+ if ( iscsi_seq_num_cmp_gt( exp_stat_sn, conn->stat_sn ) )
+ exp_stat_sn = conn->stat_sn;
+
+ if ( session->err_recovery_level > 0UL )
+ iscsi_connection_pdu_ack_remove( conn, exp_stat_sn );
+
+ if ( ((scsi_cmd_pkt->opcode & ISCSI_OPCODE_FLAGS_IMMEDIATE) == 0) && (opcode != ISCSI_OPCODE_CLIENT_SCSI_DATA_OUT) )
+ session->exp_cmd_sn++;
+
+ return ISCSI_CONNECT_PDU_READ_OK;
}
/**
@@ -4653,9 +11469,9 @@ static int iscsi_connection_update_cmd_sn(iscsi_connection *conn, iscsi_pdu *pdu
* If a response needs to be sent, this will
* be done as well.
*
- * @param[in] conn iSCSI connection to handle. May
+ * @param[in] conn Pointer to iSCSI connection to handle. May
* NOT be NULL, so take caution.
- * @param[in] pdu iSCSI client request PDU to handle.
+ * @param[in] pdu Pointer to iSCSI client request PDU to handle.
* May be NULL in which case an error is returned.
* @return 0 on success. A negative value indicates
* an error. A positive value a warning.
@@ -4672,12 +11488,12 @@ static int iscsi_connection_pdu_header_handle_login_req(iscsi_connection *conn,
if ( pdu->ds_len > ISCSI_DEFAULT_RECV_DS_LEN )
return iscsi_connection_handle_reject( conn, pdu, ISCSI_REJECT_REASON_PROTOCOL_ERR );
- iscsi_pdu *login_response_pdu = iscsi_connection_pdu_create( conn );
+ iscsi_pdu *login_response_pdu = iscsi_connection_pdu_create( conn, 0U, 0, ISCSI_DEFAULT_RECV_DS_LEN, 0 );
if ( login_response_pdu == NULL )
return ISCSI_CONNECT_PDU_READ_ERR_FATAL;
- const int rc = iscsi_login_response_init( login_response_pdu, pdu );
+ const int rc = iscsi_connection_pdu_login_response_init( login_response_pdu, pdu );
if ( rc < 0 ) {
iscsi_connection_pdu_login_response( conn, login_response_pdu, NULL, iscsi_connection_pdu_login_err_complete );
@@ -4698,9 +11514,9 @@ static int iscsi_connection_pdu_header_handle_login_req(iscsi_connection *conn,
* If a response needs to be sent, this will
* be done as well.
*
- * @param[in] conn iSCSI connection to handle. May
+ * @param[in] conn Pointer to iSCSI connection to handle. May
* NOT be NULL, so take caution.
- * @param[in] pdu iSCSI client request PDU to handle.
+ * @param[in] pdu Pointer to iSCSI client request PDU to handle.
* May be NULL in which case an error is returned.
* @return 0 on success. A negative value indicates
* an error. A positive value a warning.
@@ -4717,13 +11533,13 @@ static int iscsi_connection_pdu_header_handle_nop_out(iscsi_connection *conn, is
const uint32_t init_task_tag = iscsi_get_be32(nop_out_pkt->init_task_tag);
const uint32_t target_xfer_tag = iscsi_get_be32(nop_out_pkt->target_xfer_tag);
- if ( (target_xfer_tag != 0xFFFFFFFFUL) && (target_xfer_tag != conn->id) )
+ if ( (target_xfer_tag != 0xFFFFFFFFUL) && (target_xfer_tag != (uint32_t) conn->id) )
return iscsi_connection_handle_reject( conn, pdu, ISCSI_REJECT_REASON_INVALID_PDU_FIELD ); // TODO: Check if this is the correct error code.
- if ( (init_task_tag == 0xFFFFFFFFUL) && (nop_out_pkt->opcode & 0x40) == 0 )
+ if ( (init_task_tag == 0xFFFFFFFFUL) && (nop_out_pkt->opcode & ISCSI_OPCODE_FLAGS_IMMEDIATE) == 0 )
return ISCSI_CONNECT_PDU_READ_ERR_FATAL;
- return 0L;
+ return ISCSI_CONNECT_PDU_READ_OK;
}
/**
@@ -4734,18 +11550,136 @@ static int iscsi_connection_pdu_header_handle_nop_out(iscsi_connection *conn, is
* If a response needs to be sent, this will
* be done as well.
*
- * @param[in] conn iSCSI connection to handle. May
+ * @param[in] conn Pointer to iSCSI connection to handle. May
* NOT be NULL, so take caution.
- * @param[in] pdu iSCSI client request PDU to handle.
+ * @param[in] pdu Pointer to iSCSI client request PDU to handle.
* May be NULL in which case an error is returned.
* @return 0 on success. A negative value indicates
* an error. A positive value a warning.
*/
static int iscsi_connection_pdu_header_handle_scsi_cmd(iscsi_connection *conn, iscsi_pdu *pdu)
{
- // TODO: Implement opcode.
+ iscsi_scsi_cmd_packet *stat_scsi_cmd_pkt = (iscsi_scsi_cmd_packet *) pdu->bhs_pkt;
+ uint64_t stat_opcode = (uint64_t) stat_scsi_cmd_pkt->scsi_cdb.opcode;
+ uint64_t *stat_value = NULL;
+ int stat_rc = iscsi_hashmap_get( conn->stat_scsi_opcodes, (uint8_t *) &stat_opcode, sizeof(stat_opcode), (uint8_t **) &stat_value );
- return 0;
+ if ( stat_value == NULL ) {
+ stat_value = malloc( sizeof(uint64_t) );
+
+ if ( stat_value != NULL ) {
+ uint8_t *stat_key = iscsi_hashmap_key_create( (uint8_t *) &stat_opcode, sizeof(stat_opcode) );
+
+ if ( stat_key != NULL ) {
+ *stat_value = 0ULL;
+
+ stat_rc = iscsi_hashmap_put( conn->stat_scsi_opcodes, stat_key, sizeof(stat_opcode), (uint8_t *) stat_value );
+
+ if ( stat_rc < 0 ) {
+ iscsi_hashmap_key_destroy( stat_key );
+ free( stat_value );
+ stat_value = NULL;
+ }
+ } else {
+ free( stat_value );
+ stat_value = NULL;
+ }
+ }
+ }
+
+ if ( stat_value != NULL )
+ (*stat_value)++;
+
+ if ( conn->session->type != ISCSI_SESSION_TYPE_NORMAL )
+ return ISCSI_CONNECT_PDU_READ_ERR_FATAL;
+
+ iscsi_scsi_cmd_packet *scsi_cmd_pkt = (iscsi_scsi_cmd_packet *) pdu->bhs_pkt;
+
+ if ( (scsi_cmd_pkt->flags_task & (ISCSI_SCSI_CMD_FLAGS_TASK_READ | ISCSI_SCSI_CMD_FLAGS_TASK_WRITE)) == (ISCSI_SCSI_CMD_FLAGS_TASK_READ | ISCSI_SCSI_CMD_FLAGS_TASK_WRITE) ) // Bidirectional transfer is not supported
+ return ISCSI_CONNECT_PDU_READ_ERR_FATAL;
+
+ iscsi_task *task = iscsi_task_create( conn, NULL, iscsi_scsi_task_xfer_complete );
+
+ if ( task == NULL )
+ return ISCSI_CONNECT_PDU_READ_ERR_FATAL;
+
+ uint32_t exp_xfer_len = iscsi_get_be32(scsi_cmd_pkt->exp_xfer_len);
+
+ task->scsi_task.buf = (uint8_t *) pdu->ds_cmd_data;
+ task->scsi_task.len = (uint) (((uint8_t *) pdu->ds_cmd_data) - ((uint8_t *) pdu->bhs_pkt));
+ task->scsi_task.cdb = &scsi_cmd_pkt->scsi_cdb;
+ task->scsi_task.xfer_len = exp_xfer_len;
+ task->scsi_task.target_port = conn->target_port;
+ task->scsi_task.init_port = conn->init_port;
+ task->init_task_tag = iscsi_get_be32(scsi_cmd_pkt->init_task_tag);
+ task->pdu = pdu;
+ pdu->ref++;
+
+ const uint64_t lun = iscsi_get_be64(scsi_cmd_pkt->lun);
+ const int lun_id = iscsi_scsi_lun_get_from_iscsi( lun );
+
+ task->lun_id = lun_id;
+
+ pthread_rwlock_rdlock( &conn->device->luns_rwlock );
+
+ task->scsi_task.lun = iscsi_device_find_lun( conn->device, lun_id );
+
+ pthread_rwlock_unlock( &conn->device->luns_rwlock );
+
+ if ( task->scsi_task.lun == NULL ) {
+ iscsi_scsi_task_lun_process_none( &task->scsi_task );
+ iscsi_scsi_task_xfer_complete( &task->scsi_task );
+
+ return ISCSI_CONNECT_PDU_READ_OK;
+ }
+
+ if ( ((scsi_cmd_pkt->flags_task & (ISCSI_SCSI_CMD_FLAGS_TASK_READ | ISCSI_SCSI_CMD_FLAGS_TASK_WRITE)) == 0) && (exp_xfer_len > 0UL) ) {
+ iscsi_task_destroy( task );
+
+ return iscsi_connection_handle_reject( conn, pdu, ISCSI_REJECT_REASON_INVALID_PDU_FIELD );
+ }
+
+ if ( (scsi_cmd_pkt->flags_task & ISCSI_SCSI_CMD_FLAGS_TASK_READ) != 0 ) {
+ task->scsi_task.flags |= ISCSI_SCSI_TASK_FLAGS_XFER_READ;
+ } else if ( (scsi_cmd_pkt->flags_task & ISCSI_SCSI_CMD_FLAGS_TASK_WRITE) != 0 ) {
+ task->scsi_task.flags |= ISCSI_SCSI_TASK_FLAGS_XFER_WRITE;
+
+ if ( (conn->session->err_recovery_level > 0UL) && (iscsi_r2t_find_pdu_bhs( conn, pdu ) != NULL) ) {
+ iscsi_task_response( conn, task );
+ iscsi_task_destroy( task );
+
+ return ISCSI_CONNECT_PDU_READ_OK;
+ }
+
+ if ( pdu->ds_len > (uint) (sizeof(struct iscsi_bhs_packet) + ISCSI_MAX_AHS_SIZE + conn->header_digest + iscsi_globvec->first_burst_len + conn->data_digest) ) {
+ iscsi_task_destroy( task );
+
+ return iscsi_connection_handle_reject( conn, pdu, ISCSI_REJECT_REASON_PROTOCOL_ERR );
+ }
+
+ if ( pdu->ds_len > exp_xfer_len ) {
+ iscsi_task_destroy( task );
+
+ return iscsi_connection_handle_reject( conn, pdu, ISCSI_REJECT_REASON_PROTOCOL_ERR );
+ }
+
+ if ( (((conn->session->flags & ISCSI_SESSION_FLAGS_IMMEDIATE_DATA) == 0) && (pdu->ds_len > 0UL)) || (pdu->ds_len > conn->session->first_burst_len) ) {
+ iscsi_task_destroy( task );
+
+ return iscsi_connection_handle_reject( conn, pdu, ISCSI_REJECT_REASON_PROTOCOL_ERR );
+ }
+
+ if ( ((scsi_cmd_pkt->flags_task & ISCSI_SCSI_CMD_FLAGS_FINAL) != 0) && (pdu->ds_len < exp_xfer_len) ) {
+ if ( exp_xfer_len > ISCSI_DEFAULT_MAX_RECV_DS_LEN )
+ exp_xfer_len = ISCSI_DEFAULT_MAX_RECV_DS_LEN;
+
+ pdu->len = exp_xfer_len;
+ }
+ }
+
+ pdu->task = task;
+
+ return ISCSI_CONNECT_PDU_READ_OK;
}
/**
@@ -4756,9 +11690,9 @@ static int iscsi_connection_pdu_header_handle_scsi_cmd(iscsi_connection *conn, i
* If a response needs to be sent, this will
* be done as well.
*
- * @param[in] conn iSCSI connection to handle. May
+ * @param[in] conn Pointer to iSCSI connection to handle. May
* NOT be NULL, so take caution.
- * @param[in] pdu iSCSI client request PDU to handle.
+ * @param[in] pdu Pointer to iSCSI client request PDU to handle.
* May be NULL in which case an error is returned.
* @return 0 on success. A negative value indicates
* an error. A positive value a warning.
@@ -4778,18 +11712,231 @@ static int iscsi_connection_pdu_header_handle_task_func_req(iscsi_connection *co
* If a response needs to be sent, this will
* be done as well.
*
- * @param[in] conn iSCSI connection to handle. May
+ * @param[in] conn Pointer to iSCSI connection to handle. May
* NOT be NULL, so take caution.
- * @param[in] pdu iSCSI client request PDU to handle.
+ * @param[in] pdu Pointer to iSCSI client request PDU to handle.
* May be NULL in which case an error is returned.
* @return 0 on success. A negative value indicates
* an error. A positive value a warning.
*/
static int iscsi_connection_pdu_header_handle_text_req(iscsi_connection *conn, iscsi_pdu *pdu)
{
- // TODO: Implement opcode.
+ if ( pdu->ds_len > (uint) (sizeof(struct iscsi_bhs_packet) + ISCSI_MAX_AHS_SIZE + conn->header_digest + iscsi_globvec->first_burst_len + conn->data_digest) )
+ return iscsi_connection_handle_reject( conn, pdu, ISCSI_REJECT_REASON_PROTOCOL_ERR );
- return 0;
+ iscsi_text_req_packet *text_req_pkt = (iscsi_text_req_packet *) pdu->bhs_pkt;
+
+ const uint32_t init_task_tag = iscsi_get_be32(text_req_pkt->init_task_tag);
+ const uint32_t exp_stat_sn = iscsi_get_be32(text_req_pkt->exp_stat_sn);
+
+ if ( exp_stat_sn != conn->stat_sn )
+ conn->stat_sn = exp_stat_sn;
+
+ if ( (text_req_pkt->flags & (ISCSI_TEXT_REQ_FLAGS_CONTINUE | ISCSI_TEXT_REQ_FLAGS_FINAL)) == (ISCSI_TEXT_REQ_FLAGS_CONTINUE | ISCSI_TEXT_REQ_FLAGS_FINAL) )
+ return ISCSI_CONNECT_PDU_READ_ERR_FATAL;
+
+ if ( conn->session->current_text_init_task_tag == 0xFFFFFFFFUL )
+ conn->session->current_text_init_task_tag = init_task_tag;
+ else
+ return iscsi_connection_handle_reject( conn, pdu, ISCSI_REJECT_REASON_PROTOCOL_ERR );
+
+ return ISCSI_CONNECT_PDU_READ_OK;
+}
+
+/**
+ * @brief Searches an iSCSI PDU by Basic Header Segment (BHS) in the Ready To Transfer (R2T) active and queued task hash map.
+ *
+ * This function searches for an iSCSI PDU by
+ * iterating through the iSCSI connection active
+ * and queued Ready To Transfer tasks hash map.
+ *
+ * @param[in] conn Pointer to iSCSI connection to
+ * search in the active and queued Ready To
+ * Transfer tasks hash map. May NOT be NULL, so
+ * be careful.
+ * @param[in] pdu Pointer to iSCSI PDU of which
+ * the Basic Header Segment (BHS) should be
+ * searched for. NULL is NOT allowed here, so
+ * take caution.
+ * @return Pointer to found iSCSI PDU or NULL in
+ * case neither an iSCSI active nor enqueued
+ * Ready To Transfer (R2T) task has a matching
+ * Basic Header Segment (BHS).
+ */
+iscsi_pdu *iscsi_r2t_find_pdu_bhs(iscsi_connection *conn, iscsi_pdu *pdu)
+{
+ iscsi_task *task;
+
+ iscsi_list_foreach_node ( &conn->r2t_tasks_active, task ) {
+ if ( memcmp( task->pdu->bhs_pkt, pdu->bhs_pkt, sizeof(struct iscsi_bhs_packet) ) == 0 )
+ return task->pdu;
+ }
+
+ iscsi_list_foreach_node ( &conn->r2t_tasks_queue, task ) {
+ if ( memcmp( task->pdu->bhs_pkt, pdu->bhs_pkt, sizeof(struct iscsi_bhs_packet) ) == 0 )
+ return task->pdu;
+ }
+
+ return NULL;
+}
+
+/**
+ * @brief Sends an iSCSI Ready To Transfer Sequence Number (R2TSN) packet to the initiator.
+ *
+ * This function allocates and initializes a
+ * Ready To Transfer Sequence Number (R2TSN)
+ * packet to be sent to the client.
+ *
+ * @param[in] conn Pointer to iSCSI connection which
+ * maintains the R2TSN, may NOT be NULL,
+ * so be careful.
+ * @param[in] task Pointer to iSCSI task handling
+ * the R2TSN. NULL is NOT allowed here,
+ * take caution.
+ * @param[in,out] r2t_sn Pointer to 32-bit integer containing
+ * the R2TSN which is incremented after
+ * storing it in the response packet data.
+ * NULL is prohibited, so take caution.
+ * @param[in] pos Offset in bytes of transfer data.
+ * @param[in] len Length in bytes of transfer data.
+ * @param[in] target_xfer_tag Target Transfer Tag (TTT) for data.
+ * @return 0 on successful packet sending, a negative
+ * error code otherwise.
+ */
+int iscsi_r2t_send(iscsi_connection *conn, iscsi_task *task, uint32_t *r2t_sn, const uint32_t pos, const uint32_t len, const uint32_t target_xfer_tag)
+{
+ iscsi_pdu *response_pdu = iscsi_connection_pdu_create( conn, 0U, conn->header_digest, 0UL, conn->data_digest );
+
+ if ( response_pdu == NULL ) {
+ logadd( LOG_ERROR, "iscsi_r2t_send: Out of memory while allocating iSCSI Ready To Transfer response PDU" );
+
+ return ISCSI_CONNECT_PDU_READ_ERR_FATAL;
+ }
+
+ iscsi_r2t_packet *r2t_pkt = (iscsi_r2t_packet *) response_pdu->bhs_pkt;
+
+ r2t_pkt->opcode = ISCSI_OPCODE_SERVER_READY_XFER;
+ r2t_pkt->flags = -0x80;
+ r2t_pkt->reserved = 0U;
+ *(uint32_t *) &r2t_pkt->total_ahs_len = 0UL; // TotalAHSLength and DataSegmentLength are always 0, so write in one step.
+
+ const uint64_t lun = iscsi_scsi_lun_get_from_scsi( task->lun_id );
+
+ iscsi_put_be64( (uint8_t *) &r2t_pkt->lun, lun );
+ iscsi_put_be32( (uint8_t *) &r2t_pkt->init_task_tag, task->init_task_tag );
+ iscsi_put_be32( (uint8_t *) &r2t_pkt->target_xfer_tag, target_xfer_tag );
+ iscsi_put_be32( (uint8_t *) &r2t_pkt->stat_sn, conn->stat_sn );
+ iscsi_put_be32( (uint8_t *) &r2t_pkt->exp_cmd_sn, conn->session->exp_cmd_sn );
+ iscsi_put_be32( (uint8_t *) &r2t_pkt->max_cmd_sn, conn->session->max_cmd_sn );
+ r2t_pkt->data_sn = 0UL;
+ iscsi_put_be32( (uint8_t *) &r2t_pkt->r2t_sn, (*r2t_sn)++ );
+
+ task->r2t_data_sn = 0UL;
+
+ iscsi_put_be32( (uint8_t *) &r2t_pkt->buf_offset, (uint32_t) pos );
+ iscsi_put_be32( (uint8_t *) &r2t_pkt->des_data_xfer_len, (uint32_t) len );
+
+ response_pdu->task = task;
+ task->scsi_task.ref++;
+
+ iscsi_connection_pdu_write( conn, response_pdu, NULL, NULL );
+
+ return ISCSI_CONNECT_PDU_READ_OK;
+}
+
+/**
+ * @brief Searches an iSCSI PDU task by Ready To Transfer Sequence Number (R2TSN) and removes it from PDU SNACK doubly linked list.
+ *
+ * This function searches for an iSCSI PDU task
+ * by iterating through the iSCSI connection
+ * Sequence Number Acknowledgement (SNACK)
+ * and matches the Ready To Transfer Sequence
+ * Number (R2TSN).\n
+ * If found, the PDU will be removed from the
+ * PDU SNACK doubly linked list.
+ *
+ * @param[in] conn Pointer to iSCSI connection to
+ * search in the Sequence Number
+ * Acknowledgement (SNACK) hash map. May NOT be
+ * NULL, so be careful.
+ * @param[in] task Pointer to iSCSI task to search
+ * for in the Sequence Number Acknowledgement
+ * (SNACK) hash map. NULL is not allowed here,
+ * take caution.
+ * @param[in] r2t_sn Ready To Transfer Sequence Number
+ * (R2TSN) to be searched for.
+ * @return Pointer to found iSCSI PDU or NULL in
+ * case no iSCSI PDU has a matching Ready To Transfer
+ * Sequence Number (R2TSN).
+ */
+static iscsi_pdu *iscsi_r2t_remove_pdu_from_snack_list(iscsi_connection *conn, iscsi_task *task, const uint32_t r2t_sn)
+{
+ iscsi_pdu *pdu;
+
+ iscsi_list_foreach_node ( &conn->pdus_snack, pdu ) {
+ if ( pdu->bhs_pkt->opcode == ISCSI_OPCODE_SERVER_READY_XFER ) {
+ iscsi_r2t_packet *r2t_pkt = (iscsi_r2t_packet *) pdu->bhs_pkt;
+
+ if ( (pdu->task == task) && (iscsi_get_be32(r2t_pkt->r2t_sn) == r2t_sn) ) {
+ iscsi_list_remove( &pdu->node );
+
+ return pdu;
+ }
+ }
+ }
+
+ return NULL;
+}
+
+/**
+ * @brief Resends the Ready To Transfer (R2T) packet data.
+ *
+ * This function either sends a new R2T packet or
+ * resends an already sent one.
+ *
+ * @param[in] conn Pointer to iSCSI connection to send the
+ * R2T packet for, may NOT be NULL, so be careful.
+ * @param[in] task Pointer to iSCSI task responsible for
+ * sending the R2T packet. NULL is NOT allowed
+ * here, take caution.
+ * @param[in] r2t_sn_ack R2TSN acknowledged number.
+ * @param[in] r2t_sn_send_new 0 resends an already sent
+ * R2T packet, any other value will send a new
+ * packet.
+ * @return 0 if packet was sent successfully, a negative
+ * error code otherwise.
+ */
+static int iscsi_r2t_recovery_send(iscsi_connection *conn, iscsi_task *task, const uint32_t r2t_sn_ack, const int r2t_sn_send_new)
+{
+ iscsi_pdu *pdu = iscsi_r2t_remove_pdu_from_snack_list( conn, task, r2t_sn_ack );
+
+ if ( pdu == NULL )
+ return ISCSI_CONNECT_PDU_READ_ERR_FATAL;
+
+ iscsi_r2t_packet *r2t_pkt = (iscsi_r2t_packet *) pdu->bhs_pkt;
+
+ if ( r2t_sn_send_new != 0 ) {
+ const uint32_t des_data_xfer_len = r2t_pkt->des_data_xfer_len;
+
+ task->r2t_sn_ack++;
+
+ uint32_t len = (des_data_xfer_len - task->r2t_next_exp_pos);
+
+ if ( len > conn->session->max_burst_len )
+ len = conn->session->max_burst_len;
+
+ iscsi_connection_pdu_free( conn, pdu );
+
+ const int rc = iscsi_r2t_send( conn, task, &task->r2t_sn, task->r2t_next_exp_pos, len, task->target_xfer_tag );
+
+ if ( rc < 0 )
+ return ISCSI_CONNECT_PDU_READ_ERR_FATAL;
+ } else {
+ iscsi_put_be32( (uint8_t *) &r2t_pkt->stat_sn, conn->stat_sn );
+ iscsi_connection_pdu_write( conn, pdu, NULL, NULL );
+ }
+
+ return ISCSI_CONNECT_PDU_READ_OK;
}
/**
@@ -4800,18 +11947,100 @@ static int iscsi_connection_pdu_header_handle_text_req(iscsi_connection *conn, i
* If a response needs to be sent, this will
* be done as well.
*
- * @param[in] conn iSCSI connection to handle. May
+ * @param[in] conn Pointer to iSCSI connection to handle. May
* NOT be NULL, so take caution.
- * @param[in] pdu iSCSI client request PDU to handle.
+ * @param[in] pdu Pointer to iSCSI client request PDU to handle.
* May be NULL in which case an error is returned.
* @return 0 on success. A negative value indicates
* an error. A positive value a warning.
*/
static int iscsi_connection_pdu_header_handle_scsi_data_out(iscsi_connection *conn, iscsi_pdu *pdu)
{
- // TODO: Implement opcode.
+ if ( conn->session->type == ISCSI_SESSION_TYPE_DISCOVERY )
+ return ISCSI_CONNECT_PDU_READ_ERR_FATAL;
- return 0;
+ if ( pdu->ds_len > ISCSI_DEFAULT_MAX_RECV_DS_LEN )
+ return iscsi_connection_handle_reject( conn, pdu, ISCSI_REJECT_REASON_PROTOCOL_ERR );
+
+ iscsi_scsi_data_out_req_packet *scsi_data_out_req_pkt = (iscsi_scsi_data_out_req_packet *) pdu->bhs_pkt;
+ const uint32_t target_xfer_tag = iscsi_get_be32(scsi_data_out_req_pkt->target_xfer_tag);
+
+ iscsi_task *task = iscsi_task_find( conn, target_xfer_tag );
+
+ if ( task == NULL )
+ return iscsi_connection_handle_reject( conn, pdu, ISCSI_REJECT_REASON_INVALID_PDU_FIELD );
+
+ pthread_rwlock_rdlock( &conn->device->luns_rwlock );
+
+ iscsi_scsi_lun *lun = iscsi_device_find_lun( conn->device, task->lun_id );
+
+ pthread_rwlock_unlock( &conn->device->luns_rwlock );
+
+ if ( pdu->ds_len > ISCSI_DEFAULT_MAX_RECV_DS_LEN )
+ return iscsi_connection_handle_reject( conn, pdu, ISCSI_REJECT_REASON_PROTOCOL_ERR );
+
+ const uint32_t init_task_tag = iscsi_get_be32(scsi_data_out_req_pkt->init_task_tag);
+
+ if ( task->init_task_tag != init_task_tag )
+ return iscsi_connection_handle_reject( conn, pdu, ISCSI_REJECT_REASON_INVALID_PDU_FIELD );
+
+ const uint32_t data_sn = iscsi_get_be32(scsi_data_out_req_pkt->data_sn);
+
+ if ( data_sn != task->r2t_data_sn ) {
+ if ( conn->session->err_recovery_level > 0UL ) {
+ const int rc = iscsi_r2t_recovery_send( conn, task, task->r2t_sn_ack, 1 );
+
+ if ( rc == 0 )
+ return ISCSI_CONNECT_PDU_READ_OK;
+ }
+
+ return iscsi_connection_handle_reject( conn, pdu, ISCSI_REJECT_REASON_PROTOCOL_ERR );
+ }
+
+ const uint32_t buf_offset = iscsi_get_be32(scsi_data_out_req_pkt->buf_offset);
+
+ if ( buf_offset != task->r2t_next_exp_pos )
+ return ISCSI_CONNECT_PDU_READ_ERR_FATAL;
+
+ const uint32_t xfer_len = task->scsi_task.xfer_len;
+
+ task->r2t_len = pdu->ds_len;
+ task->r2t_next_exp_pos += pdu->ds_len;
+ task->r2t_data_sn++;
+
+ if ( task->r2t_len > conn->session->max_burst_len )
+ return ISCSI_CONNECT_PDU_READ_ERR_FATAL;
+
+ if ( (int8_t) scsi_data_out_req_pkt->opcode < 0 )
+ task->r2t_len = 0UL;
+
+ if ( xfer_len == task->r2t_next_exp_pos ) {
+ task->r2t_sn_ack++;
+ } else if ( ((int8_t) scsi_data_out_req_pkt->opcode < 0) && (xfer_len > task->r2t_next_exp_pos) ) {
+ task->r2t_sn_ack++;
+
+ uint32_t len = (xfer_len - task->r2t_next_exp_pos);
+
+ if ( len > conn->session->max_burst_len )
+ len = conn->session->max_burst_len;
+
+ const int rc = iscsi_r2t_send( conn, task, &task->r2t_sn, task->r2t_next_exp_pos, len, task->target_xfer_tag );
+
+ if ( rc < 0 )
+ return ISCSI_CONNECT_PDU_READ_ERR_FATAL;
+
+ task->r2t_next_exp_pos += len;
+ }
+
+ if ( lun == NULL )
+ return iscsi_connection_handle_reject( conn, pdu, ISCSI_REJECT_REASON_PROTOCOL_ERR );
+
+ if ( task->scsi_task.buf != NULL ) {
+ pdu->ds_cmd_data = (iscsi_scsi_ds_cmd_data *) (task->scsi_task.buf + task->len);
+ pdu->ds_len = ISCSI_DEFAULT_MAX_RECV_DS_LEN;
+ }
+
+ return ISCSI_CONNECT_PDU_READ_OK;
}
/**
@@ -4822,18 +12051,69 @@ static int iscsi_connection_pdu_header_handle_scsi_data_out(iscsi_connection *co
* If a response needs to be sent, this will
* be done as well.
*
- * @param[in] conn iSCSI connection to handle. May
+ * @param[in] conn Pointer to iSCSI connection to handle. May
* NOT be NULL, so take caution.
- * @param[in] pdu iSCSI client request PDU to handle.
+ * @param[in] pdu Pointer to iSCSI client request PDU to handle.
* May be NULL in which case an error is returned.
* @return 0 on success. A negative value indicates
* an error. A positive value a warning.
*/
static int iscsi_connection_pdu_header_handle_logout_req(iscsi_connection *conn, iscsi_pdu *pdu)
{
- // TODO: Implement opcode.
+ iscsi_logout_req_packet *logout_req_pkt = (iscsi_logout_req_packet *) pdu->bhs_pkt;
- return 0;
+ if ( (conn->session != NULL) && (conn->session->type == ISCSI_SESSION_TYPE_DISCOVERY) && (logout_req_pkt->reason_code != ISCSI_LOGOUT_REQ_REASON_CODE_CLOSE_SESSION) )
+ return ISCSI_CONNECT_PDU_READ_ERR_FATAL;
+
+ iscsi_pdu *response_pdu = iscsi_connection_pdu_create( conn, 0U, conn->header_digest, 0UL, conn->data_digest );
+
+ if ( response_pdu == NULL ) {
+ logadd( LOG_ERROR, "iscsi_connection_pdu_header_handle_logout_req: Out of memory while allocating iSCSI logout response PDU" );
+
+ return ISCSI_CONNECT_PDU_READ_ERR_FATAL;
+ }
+
+ iscsi_logout_response_packet *logout_response_pkt = (iscsi_logout_response_packet *) response_pdu->bhs_pkt;
+
+ logout_response_pkt->opcode = ISCSI_OPCODE_SERVER_LOGOUT_RES;
+ logout_response_pkt->flags = -0x80;
+
+ const uint16_t cid = iscsi_get_be16(logout_req_pkt->cid);
+
+ if ( cid == conn->cid ) {
+ conn->flags |= ISCSI_CONNECT_FLAGS_LOGGED_OUT;
+
+ logout_response_pkt->response = ISCSI_LOGOUT_RESPONSE_CLOSED_SUCCESSFULLY;
+ } else {
+ logout_response_pkt->response = ISCSI_LOGOUT_RESPONSE_CID_NOT_FOUND;
+ }
+
+ logout_response_pkt->reserved = 0U;
+ *(uint32_t *) &logout_response_pkt->total_ahs_len = 0UL; // TotalAHSLength and DataSegmentLength are always 0, so write in one step.
+ logout_response_pkt->reserved2 = 0ULL;
+ logout_response_pkt->init_task_tag = logout_req_pkt->init_task_tag; // Copying over doesn't change endianess.
+ logout_response_pkt->reserved3 = 0UL;
+ iscsi_put_be32( (uint8_t *) &logout_response_pkt->stat_sn, conn->stat_sn++ );
+
+ if ( conn->session != NULL ) {
+ if ( conn->session->conns == 1UL )
+ conn->session->max_cmd_sn++;
+
+ iscsi_put_be32( (uint8_t *) &logout_response_pkt->exp_cmd_sn, conn->session->exp_cmd_sn );
+ iscsi_put_be32( (uint8_t *) &logout_response_pkt->max_cmd_sn, conn->session->max_cmd_sn );
+ } else {
+ iscsi_put_be32( (uint8_t *) &logout_response_pkt->exp_cmd_sn, pdu->cmd_sn );
+ iscsi_put_be32( (uint8_t *) &logout_response_pkt->max_cmd_sn, pdu->cmd_sn );
+ }
+
+ logout_response_pkt->reserved4 = 0UL;
+ logout_response_pkt->time_wait = 0U;
+ logout_response_pkt->time_retain = 0U;
+ logout_response_pkt->reserved5 = 0UL;
+
+ iscsi_connection_pdu_write( conn, response_pdu, NULL, NULL );
+
+ return ISCSI_CONNECT_PDU_READ_OK;
}
/**
@@ -4844,9 +12124,9 @@ static int iscsi_connection_pdu_header_handle_logout_req(iscsi_connection *conn,
* If a response needs to be sent, this will
* be done as well.
*
- * @param[in] conn iSCSI connection to handle. May
+ * @param[in] conn Pointer to iSCSI connection to handle. May
* NOT be NULL, so take caution.
- * @param[in] pdu iSCSI client request PDU to handle.
+ * @param[in] pdu Pointer to iSCSI client request PDU to handle.
* May be NULL in which case an error is returned.
* @return 0 on success. A negative value indicates
* an error. A positive value a warning.
@@ -4866,9 +12146,9 @@ static int iscsi_connection_pdu_header_handle_snack_req(iscsi_connection *conn,
* If a response needs to be sent, this will
* be done as well.
*
- * @param[in] conn iSCSI connection to handle. May
+ * @param[in] conn Pointer to iSCSI connection to handle. May
* NOT be NULL, so take caution.
- * @param[in] pdu iSCSI client request PDU to handle.
+ * @param[in] pdu Pointer to iSCSI client request PDU to handle.
* May be NULL in which case an error is returned.
* @return 0 on success. A negative value indicates
* an error. A positive value a warning.
@@ -4876,20 +12156,20 @@ static int iscsi_connection_pdu_header_handle_snack_req(iscsi_connection *conn,
static int iscsi_connection_pdu_header_handle(iscsi_connection *conn, iscsi_pdu *pdu)
{
if ( pdu == NULL )
- return -1L;
+ return ISCSI_CONNECT_PDU_READ_ERR_FATAL;
const int opcode = ISCSI_GET_OPCODE(pdu->bhs_pkt->opcode);
- if ( opcode == ISCSI_CLIENT_LOGIN_REQ )
+ if ( opcode == ISCSI_OPCODE_CLIENT_LOGIN_REQ )
return iscsi_connection_pdu_header_handle_login_req( conn, pdu );
if ( ((conn->flags & ISCSI_CONNECT_FLAGS_FULL_FEATURE) == 0) && (conn->state == ISCSI_CONNECT_STATE_RUNNING) ) {
- iscsi_pdu *login_response_pdu = iscsi_connection_pdu_create( conn );
+ iscsi_pdu *login_response_pdu = iscsi_connection_pdu_create( conn, 0U, 0, 0UL, 0 );
if ( login_response_pdu == NULL )
return ISCSI_CONNECT_PDU_READ_ERR_FATAL;
- iscsi_login_response_reject_init( login_response_pdu, pdu );
+ iscsi_connection_login_response_reject( login_response_pdu, pdu );
iscsi_connection_pdu_write( conn, login_response_pdu, NULL, NULL );
return ISCSI_CONNECT_PDU_READ_ERR_LOGIN_RESPONSE;
@@ -4903,37 +12183,37 @@ static int iscsi_connection_pdu_header_handle(iscsi_connection *conn, iscsi_pdu
return rc;
switch ( opcode ) {
- case ISCSI_CLIENT_NOP_OUT : {
+ case ISCSI_OPCODE_CLIENT_NOP_OUT : {
rc = iscsi_connection_pdu_header_handle_nop_out( conn, pdu );
break;
}
- case ISCSI_CLIENT_SCSI_CMD : {
+ case ISCSI_OPCODE_CLIENT_SCSI_CMD : {
rc = iscsi_connection_pdu_header_handle_scsi_cmd( conn, pdu );
break;
}
- case ISCSI_CLIENT_TASK_FUNC_REQ : {
+ case ISCSI_OPCODE_CLIENT_TASK_FUNC_REQ : {
rc = iscsi_connection_pdu_header_handle_task_func_req( conn, pdu );
break;
}
- case ISCSI_CLIENT_TEXT_REQ : {
+ case ISCSI_OPCODE_CLIENT_TEXT_REQ : {
rc = iscsi_connection_pdu_header_handle_text_req( conn, pdu );
break;
}
- case ISCSI_CLIENT_SCSI_DATA_OUT : {
+ case ISCSI_OPCODE_CLIENT_SCSI_DATA_OUT : {
rc = iscsi_connection_pdu_header_handle_scsi_data_out( conn, pdu );
break;
}
- case ISCSI_CLIENT_LOGOUT_REQ : {
+ case ISCSI_OPCODE_CLIENT_LOGOUT_REQ : {
rc = iscsi_connection_pdu_header_handle_logout_req( conn, pdu );
break;
}
- case ISCSI_CLIENT_SNACK_REQ : {
+ case ISCSI_OPCODE_CLIENT_SNACK_REQ : {
rc = iscsi_connection_pdu_header_handle_snack_req( conn, pdu );
break;
@@ -4945,6 +12225,9 @@ static int iscsi_connection_pdu_header_handle(iscsi_connection *conn, iscsi_pdu
}
}
+ if ( rc < 0 )
+ logadd( LOG_ERROR, "Fatal error during header handler (opcode 0x%02x) detected for device %s", (int) opcode, (conn->device != NULL ? (char *) conn->device->name : "(null)") );
+
return rc;
}
@@ -4956,9 +12239,9 @@ static int iscsi_connection_pdu_header_handle(iscsi_connection *conn, iscsi_pdu
* If a response needs to be sent, this will
* be done as well.
*
- * @param[in] conn iSCSI connection to handle. May
+ * @param[in] conn Pointer to iSCSI connection to handle. May
* NOT be NULL, so take caution.
- * @param[in] pdu iSCSI client request PDU to handle.
+ * @param[in] pdu Pointer to iSCSI client request PDU to handle.
* May be NULL in which case an error is returned.
* @return 0 on success. A negative value indicates
* an error. A positive value a warning.
@@ -4966,7 +12249,7 @@ static int iscsi_connection_pdu_header_handle(iscsi_connection *conn, iscsi_pdu
static int iscsi_connection_pdu_data_handle_nop_out(iscsi_connection *conn, iscsi_pdu *pdu)
{
iscsi_nop_out_packet *nop_out_pkt = (iscsi_nop_out_packet *) pdu->bhs_pkt;
- uint32_t ds_len = pdu->ds_len;
+ uint32_t ds_len = pdu->ds_len;
if ( ds_len > conn->max_recv_ds_len )
ds_len = conn->max_recv_ds_len;
@@ -4979,7 +12262,7 @@ static int iscsi_connection_pdu_data_handle_nop_out(iscsi_connection *conn, iscs
if ( init_task_tag == 0xFFFFFFFFUL )
return ISCSI_CONNECT_PDU_READ_OK;
- iscsi_pdu *response_pdu = iscsi_connection_pdu_create( conn );
+ iscsi_pdu *response_pdu = iscsi_connection_pdu_create( conn, 0U, conn->header_digest, ds_len, conn->data_digest );
if ( response_pdu == NULL ) {
logadd( LOG_ERROR, "iscsi_connection_pdu_data_handle_nop_out: Out of memory while allocating iSCSI NOP-In response PDU" );
@@ -4987,44 +12270,27 @@ static int iscsi_connection_pdu_data_handle_nop_out(iscsi_connection *conn, iscs
return ISCSI_CONNECT_PDU_READ_ERR_FATAL;
}
- iscsi_nop_in_packet *nop_in_pkt = (iscsi_nop_in_packet *) iscsi_append_ds_packet( response_pdu->bhs_pkt, conn->header_digest, ds_len, conn->data_digest );
-
- if ( nop_in_pkt == NULL ) {
- logadd( LOG_ERROR, "iscsi_connection_pdu_data_handle_nop_out: Out of memory while allocating iSCSI NOP-In packet data" );
-
- iscsi_connection_pdu_destroy( response_pdu );
-
- return ISCSI_CONNECT_PDU_READ_ERR_FATAL;
- }
-
- response_pdu->bhs_pkt = (iscsi_bhs_packet *) nop_in_pkt;
-
- if ( conn->header_digest != 0 ) {
- response_pdu->header_digest = (iscsi_header_digest *) (((iscsi_bhs_packet *) nop_in_pkt) + 1);
- response_pdu->header_digest_size = conn->header_digest;
- }
-
- response_pdu->ds_cmd_data = (iscsi_ds_cmd_data *) (((uint8_t *) nop_in_pkt) + sizeof(struct iscsi_bhs_packet) + conn->header_digest);
- response_pdu->ds_len = ds_len;
-
- if ( conn->data_digest != 0 ) {
- response_pdu->data_digest = (iscsi_data_digest *) (((uint8_t *) response_pdu->ds_cmd_data) + iscsi_align(ds_len, ISCSI_ALIGN_SIZE));
- response_pdu->data_digest_size = conn->data_digest;
- }
+ iscsi_nop_in_packet *nop_in_pkt = (iscsi_nop_in_packet *) response_pdu->bhs_pkt;
- nop_in_pkt->opcode = ISCSI_SERVER_NOP_IN;
+ nop_in_pkt->opcode = ISCSI_OPCODE_SERVER_NOP_IN;
nop_in_pkt->flags = -0x80;
- iscsi_put_be24( (uint8_t *) &nop_in_pkt->ds_len, ds_len );
+ nop_in_pkt->reserved = 0U;
+ iscsi_put_be32( (uint8_t *) &nop_in_pkt->total_ahs_len, ds_len ); // TotalAHSLength is always 0 and DataSegmentLength is 24-bit, so write in one step.
iscsi_put_be64( (uint8_t *) &nop_in_pkt->lun, lun );
- nop_in_pkt->target_xfer_tag = 0xFFFFFFFFUL;
iscsi_put_be32( (uint8_t *) &nop_in_pkt->init_task_tag, init_task_tag );
+ nop_in_pkt->target_xfer_tag = 0xFFFFFFFFUL; // Minus one does not require endianess conversion
iscsi_put_be32( (uint8_t *) &nop_in_pkt->stat_sn, conn->stat_sn++ );
- if ( (nop_out_pkt->opcode & 0x40) == 0 )
+ if ( (nop_out_pkt->opcode & ISCSI_OPCODE_FLAGS_IMMEDIATE) == 0 )
conn->session->max_cmd_sn++;
iscsi_put_be32( (uint8_t *) &nop_in_pkt->exp_cmd_sn, conn->session->exp_cmd_sn );
iscsi_put_be32( (uint8_t *) &nop_in_pkt->max_cmd_sn, conn->session->max_cmd_sn );
+ nop_in_pkt->reserved2 = 0UL;
+ nop_in_pkt->reserved3 = 0ULL;
+
+ if ( ds_len != 0UL )
+ memcpy( response_pdu->ds_cmd_data, pdu->ds_cmd_data, ds_len );
iscsi_connection_pdu_write( conn, response_pdu, NULL, NULL );
@@ -5034,6 +12300,139 @@ static int iscsi_connection_pdu_data_handle_nop_out(iscsi_connection *conn, iscs
}
/**
+ * @brief Handles an incoming iSCSI payload data SCSI read command request PDU.
+ *
+ * This function handles SCSI read command request
+ * payload data sent by the client.\n
+ * If a response needs to be sent, this will
+ * be done as well.
+ *
+ * @param[in] conn Pointer to iSCSI connection to handle. May
+ * NOT be NULL, so take caution.
+ * @param[in] task Pointer to iSCSI task associated for reading.
+ * May be NULL in which case an error is returned.
+ * @return 0 on success. A negative value indicates
+ * an error. A positive value a warning.
+ */
+static int iscsi_connection_pdu_data_handle_scsi_cmd_read(iscsi_connection *conn, iscsi_task *task)
+{
+ if ( task->scsi_task.xfer_len <= ISCSI_DEFAULT_MAX_RECV_DS_LEN ) {
+ task->parent = NULL;
+ task->scsi_task.buf = NULL;
+ task->scsi_task.pos = 0UL;
+ task->scsi_task.len = task->scsi_task.xfer_len;
+
+ iscsi_task_queue( conn, task );
+
+ return ISCSI_CONNECT_PDU_READ_OK;
+ }
+
+ iscsi_list_create( &task->sub_tasks );
+
+ task->pos = 0UL;
+
+ iscsi_list_enqueue( &conn->scsi_data_in_queued_tasks, &task->node );
+
+ return iscsi_connection_handle_scsi_data_in_queued_tasks( conn );
+}
+
+/**
+ * @brief Creates and submits a sub task for writing.
+ *
+ * This function is also assigns the task with
+ * an iSCSI PDU.
+ *
+ * @param[in] conn Pointer to iSCSI connection which handles
+ * this task. May NOT be NULL, so be caureful.
+ * @param[in] task Pointer to iSCSI task which should be the
+ * parent of the new sub task. NULL if NOT
+ * allowed here, so take caution.
+ * @param[in] pdu Pointer to iSCSI PDU which contains
+ * the desired buffer length to write. NULL
+ * is a prohibited value here, take caution.
+ * @param[in] buf Pointer to buffer containing
+ * the write data and may NOT be NULL, so
+ * be careful.
+ * @return 0 on successful sub task submit or a
+ * negative error code otherwise.
+ */
+static int iscsi_task_sub_task_submit_write(iscsi_connection *conn, iscsi_task *task, iscsi_pdu *pdu, uint8_t *buf)
+{
+ iscsi_task *sub_task = iscsi_task_create( conn, task, iscsi_scsi_task_xfer_complete );
+
+ if ( sub_task == NULL )
+ return ISCSI_CONNECT_PDU_READ_ERR_FATAL;
+
+ sub_task->scsi_task.buf = buf;
+ sub_task->scsi_task.pos = task->pos;
+ sub_task->scsi_task.len = pdu->ds_len;
+
+ pdu->task = sub_task;
+ pdu->ref++;
+
+ task->pos += pdu->ds_len;
+
+ iscsi_task_queue( conn, sub_task );
+
+ return ISCSI_CONNECT_PDU_READ_OK;
+}
+
+/**
+ * @brief Handles an incoming iSCSI payload data SCSI write command request PDU.
+ *
+ * This function handles SCSI write command
+ * request payload data sent by the client.\n
+ * If a response needs to be sent, this will
+ * be done as well.
+ *
+ * @param[in] conn Pointer to iSCSI connection to handle. May
+ * NOT be NULL, so take caution.
+ * @param[in] task Pointer to iSCSI task associated for reading.
+ * May be NULL in which case an error is returned.
+ * @return 0 on success. A negative value indicates
+ * an error. A positive value a warning.
+ */
+static int iscsi_connection_pdu_data_handle_scsi_cmd_write(iscsi_connection *conn, iscsi_task *task)
+{
+ iscsi_pdu *pdu = task->pdu;
+ iscsi_scsi_cmd_packet *scsi_cmd_pkt = (iscsi_scsi_cmd_packet *) pdu->bhs_pkt;
+ const uint32_t xfer_len = task->scsi_task.xfer_len;
+
+ if ( ((scsi_cmd_pkt->flags_task & ISCSI_SCSI_CMD_FLAGS_FINAL) != 0) && (pdu->ds_len < xfer_len) ) {
+ int rc = iscsi_task_xfer_add( conn, task );
+
+ if ( rc < 0 ) {
+ iscsi_task_destroy( task );
+
+ return ISCSI_CONNECT_PDU_READ_ERR_FATAL;
+ }
+
+ if ( pdu->ds_len != 0UL ) {
+ rc = iscsi_task_sub_task_submit_write( conn, task, pdu, (uint8_t *) pdu->ds_cmd_data );
+
+ if ( rc < 0 ) {
+ iscsi_task_destroy( task );
+
+ return ISCSI_CONNECT_PDU_READ_ERR_FATAL;
+ }
+ }
+
+ return ISCSI_CONNECT_PDU_READ_OK;
+ }
+
+ if ( pdu->ds_len == xfer_len ) {
+ iscsi_scsi_task *scsi_task = &task->scsi_task;
+
+ scsi_task->buf = (uint8_t *) pdu->ds_cmd_data;
+ scsi_task->len = xfer_len;
+ }
+
+ iscsi_task_queue( conn, task );
+
+ return ISCSI_CONNECT_PDU_READ_OK;
+}
+
+/**
* @brief Handles an incoming iSCSI payload data SCSI command request PDU.
*
* This function handles SCSI command request payload
@@ -5041,18 +12440,46 @@ static int iscsi_connection_pdu_data_handle_nop_out(iscsi_connection *conn, iscs
* If a response needs to be sent, this will
* be done as well.
*
- * @param[in] conn iSCSI connection to handle. May
+ * @param[in] conn Pointer to iSCSI connection to handle. May
* NOT be NULL, so take caution.
- * @param[in] pdu iSCSI client request PDU to handle.
+ * @param[in] pdu Pointer to iSCSI client request PDU to handle.
* May be NULL in which case an error is returned.
* @return 0 on success. A negative value indicates
* an error. A positive value a warning.
*/
static int iscsi_connection_pdu_data_handle_scsi_cmd(iscsi_connection *conn, iscsi_pdu *pdu)
{
- // TODO: Implement opcode.
+ iscsi_task *task = pdu->task;
+
+ if ( task == NULL )
+ return ISCSI_CONNECT_PDU_READ_OK;
+
+ pthread_rwlock_rdlock( &conn->device->luns_rwlock );
+
+ if ( iscsi_device_find_lun( conn->device, task->lun_id ) == NULL ) {
+ pthread_rwlock_unlock( &conn->device->luns_rwlock );
+ iscsi_scsi_task_lun_process_none( &task->scsi_task );
+ iscsi_scsi_task_xfer_complete( &task->scsi_task );
+
+ return ISCSI_CONNECT_PDU_READ_OK;
+ }
+
+ pthread_rwlock_unlock( &conn->device->luns_rwlock );
- return 0L;
+ if ( (task->scsi_task.flags & ISCSI_SCSI_TASK_FLAGS_XFER_READ) != 0 ) {
+ return iscsi_connection_pdu_data_handle_scsi_cmd_read( conn, task );
+ } else if ( (task->scsi_task.flags & ISCSI_SCSI_TASK_FLAGS_XFER_WRITE) != 0 ) {
+ return iscsi_connection_pdu_data_handle_scsi_cmd_write( conn, task );
+ } else if ( (task->scsi_task.flags & (ISCSI_SCSI_TASK_FLAGS_XFER_READ | ISCSI_SCSI_TASK_FLAGS_XFER_WRITE)) == 0 ) {
+ iscsi_task_queue( conn, task );
+
+ return ISCSI_CONNECT_PDU_READ_OK;
+ }
+
+ pdu->task = NULL;
+ iscsi_task_destroy( task );
+
+ return ISCSI_CONNECT_PDU_READ_ERR_FATAL;
}
/**
@@ -5067,12 +12494,12 @@ static int iscsi_connection_pdu_data_handle_scsi_cmd(iscsi_connection *conn, isc
*/
static int iscsi_connection_chap_negotiate(iscsi_connection *conn)
{
- int rc = 0L;
+ int rc = 0;
if ( (conn->flags & ISCSI_CONNECT_FLAGS_CHAP_DISABLE) != 0 )
- rc = iscsi_update_key_value_pair( conn->key_value_pairs, (uint8_t *) ISCSI_LOGIN_AUTH_SECURITY_TEXT_KEY_AUTH_METHOD, (uint8_t *) "None" );
+ rc = iscsi_update_key_value_pair( conn->key_value_pairs, ISCSI_LOGIN_AUTH_SECURITY_TEXT_KEY_AUTH_METHOD, (uint8_t *) "None" );
else if ( (conn->flags & ISCSI_CONNECT_FLAGS_CHAP_REQUIRE) != 0 )
- rc = iscsi_update_key_value_pair( conn->key_value_pairs, (uint8_t *) ISCSI_LOGIN_AUTH_SECURITY_TEXT_KEY_AUTH_METHOD, (uint8_t *) "CHAP" );
+ rc = iscsi_update_key_value_pair( conn->key_value_pairs, ISCSI_LOGIN_AUTH_SECURITY_TEXT_KEY_AUTH_METHOD, (uint8_t *) "CHAP" );
return rc;
}
@@ -5154,13 +12581,13 @@ static int iscsi_connection_login_chap_negotiate(iscsi_connection *conn, const i
*/
static int iscsi_connection_login_digest_negotiate(iscsi_connection *conn, const iscsi_target_node *target)
{
- int rc = 0L;
+ int rc = 0;
if ( target->header_digest != 0 )
- rc = iscsi_update_key_value_pair( conn->key_value_pairs, (uint8_t *) ISCSI_LOGIN_AUTH_SESSION_TEXT_KEY_HEADER_DIGEST, (uint8_t *) "CRC32C" );
+ rc = iscsi_update_key_value_pair( conn->key_value_pairs, ISCSI_LOGIN_AUTH_SESSION_TEXT_KEY_HEADER_DIGEST, (uint8_t *) "CRC32C" );
if ( target->data_digest != 0 )
- rc = iscsi_update_key_value_pair( conn->key_value_pairs, (uint8_t *) ISCSI_LOGIN_AUTH_SESSION_TEXT_KEY_DATA_DIGEST, (uint8_t *) "CRC32C" );
+ rc = iscsi_update_key_value_pair( conn->key_value_pairs, ISCSI_LOGIN_AUTH_SESSION_TEXT_KEY_DATA_DIGEST, (uint8_t *) "CRC32C" );
return rc;
}
@@ -5188,7 +12615,7 @@ static int iscsi_connection_login_session_normal(iscsi_connection *conn, iscsi_p
iscsi_target_node *target = NULL;
uint8_t *target_name;
iscsi_login_response_packet *login_response_pkt = (iscsi_login_response_packet *) login_response_pdu->bhs_pkt;
- int rc = iscsi_get_key_value_pair( key_value_pairs, (uint8_t *) ISCSI_LOGIN_AUTH_SECURITY_TEXT_KEY_TARGET_NAME, &target_name );
+ int rc = iscsi_get_key_value_pair( key_value_pairs, ISCSI_LOGIN_AUTH_SECURITY_TEXT_KEY_TARGET_NAME, &target_name );
if ( (rc < 0) || (target_name == NULL) ) {
login_response_pkt->status_class = ISCSI_LOGIN_RESPONSE_STATUS_CLASS_CLIENT_ERR;
@@ -5271,11 +12698,27 @@ static int iscsi_connection_login_set_info(iscsi_connection *conn, iscsi_pdu *lo
return ISCSI_CONNECT_PDU_READ_ERR_LOGIN_RESPONSE;
}
- conn->session->initiator_port = init_port;
- conn->stat_sn = iscsi_get_be32(login_response_pkt->stat_sn);
- conn->session->isid = isid;
+ conn->session->init_port = init_port;
+ conn->stat_sn = iscsi_get_be32(login_response_pkt->stat_sn);
+ conn->session->isid = isid;
+
+ pthread_rwlock_wrlock( &iscsi_globvec->sessions_rwlock );
+ iscsi_hashmap_key_create_id( iscsi_globvec->sessions, &conn->session->tsih );
+
+ int rc = iscsi_hashmap_put( iscsi_globvec->sessions, (uint8_t *) &conn->session->tsih, sizeof(conn->session->tsih), (uint8_t *) conn->session );
+
+ pthread_rwlock_unlock( &iscsi_globvec->sessions_rwlock );
+
+ if ( rc < 0 ) {
+ iscsi_session_destroy( conn->session );
+ conn->session = NULL;
+
+ iscsi_port_destroy( init_port );
+
+ return rc;
+ }
- const int rc = iscsi_port_transport_id_set( conn->session->initiator_port, conn->init_name, isid );
+ rc = iscsi_port_transport_id_set( conn->session->init_port, conn->init_name, isid );
if ( rc < 0 ) {
iscsi_session_destroy( conn->session );
@@ -5286,12 +12729,12 @@ static int iscsi_connection_login_set_info(iscsi_connection *conn, iscsi_pdu *lo
return rc;
}
- conn->session->queue_depth = (target != NULL) ? target->queue_depth : 1UL;
+ conn->session->queue_depth = ((target != NULL) ? target->queue_depth : 1U);
conn->session->exp_cmd_sn = login_response_pdu->cmd_sn;
- conn->session->max_cmd_sn = login_response_pdu->cmd_sn + conn->session->queue_depth - 1;
+ conn->session->max_cmd_sn = (uint32_t) (login_response_pdu->cmd_sn + (uint32_t) conn->session->queue_depth - 1UL);
}
- conn->init_port = conn->session->initiator_port;
+ conn->init_port = conn->session->init_port;
return ISCSI_CONNECT_PDU_READ_OK;
}
@@ -5317,57 +12760,81 @@ static int iscsi_connection_login_set_target_info(iscsi_connection *conn, iscsi_
int rc;
if ( target != NULL ) {
- rc = iscsi_update_key_value_pair( conn->session->key_value_pairs, (uint8_t *) ISCSI_LOGIN_AUTH_SECURITY_TEXT_KEY_TARGET_ALIAS, ((target->alias != NULL) ? target->alias : (uint8_t *) "") );
+ rc = iscsi_update_key_value_pair( conn->session->key_value_pairs, ISCSI_LOGIN_AUTH_SECURITY_TEXT_KEY_TARGET_ALIAS, ((target->alias != NULL) ? target->alias : (uint8_t *) "") );
if ( rc < 0 )
return ISCSI_CONNECT_PDU_READ_ERR_LOGIN_PARAMETER;
}
- uint8_t *tmp_buf = iscsi_sprintf_alloc( "%s:%s,%d", conn->portal_host, conn->portal_port, conn->pg_tag );
+ uint8_t *tmp_buf = iscsi_sprintf_alloc( "%s:%s,%" PRIu64, conn->portal_host, conn->portal_port, conn->pg_tag );
if ( tmp_buf == NULL )
return ISCSI_CONNECT_PDU_READ_ERR_FATAL;
- rc = iscsi_update_key_value_pair( conn->session->key_value_pairs, (uint8_t *) ISCSI_LOGIN_AUTH_SECURITY_TEXT_KEY_TARGET_ADDRESS, tmp_buf );
+ rc = iscsi_update_key_value_pair( conn->session->key_value_pairs, ISCSI_LOGIN_AUTH_SECURITY_TEXT_KEY_TARGET_ADDRESS, tmp_buf );
free( tmp_buf );
if ( rc < 0 )
return ISCSI_CONNECT_PDU_READ_ERR_LOGIN_PARAMETER;
- rc = iscsi_update_int_key_value_pair( conn->session->key_value_pairs, (uint8_t *) ISCSI_LOGIN_AUTH_SECURITY_TEXT_KEY_TARGET_PORTAL_GROUP_TAG, conn->pg_tag );
+ rc = iscsi_update_int_key_value_pair( conn->session->key_value_pairs, ISCSI_LOGIN_AUTH_SECURITY_TEXT_KEY_TARGET_PORTAL_GROUP_TAG, (int32_t) conn->pg_tag );
if ( rc < 0 )
return ISCSI_CONNECT_PDU_READ_ERR_LOGIN_PARAMETER;
if ( target != NULL ) {
- rc = iscsi_get_key_value_pair( conn->session->key_value_pairs, (uint8_t *) ISCSI_LOGIN_AUTH_SECURITY_TEXT_KEY_TARGET_ALIAS, &tmp_buf );
+ rc = iscsi_get_key_value_pair( conn->session->key_value_pairs, ISCSI_LOGIN_AUTH_SECURITY_TEXT_KEY_TARGET_ALIAS, &tmp_buf );
if ( (rc == 0) && (strlen( (char *) tmp_buf ) != 0) ) {
- rc = iscsi_update_key_value_pair( login_response_pdu->key_value_pairs, (uint8_t *) ISCSI_LOGIN_AUTH_SECURITY_TEXT_KEY_TARGET_ALIAS, tmp_buf );
+ iscsi_key_value_pair *key_value_pair;
+ rc = iscsi_hashmap_get( iscsi_globvec->session_key_value_pairs, ISCSI_LOGIN_AUTH_SECURITY_TEXT_KEY_TARGET_ALIAS, strlen( (char *) ISCSI_LOGIN_AUTH_SECURITY_TEXT_KEY_TARGET_ALIAS ) + 1, (uint8_t **) &key_value_pair);
if ( rc < 0 )
return ISCSI_CONNECT_PDU_READ_ERR_FATAL;
+
+ const int32_t ds_len = iscsi_append_key_value_pair_packet( key_value_pair, ISCSI_LOGIN_AUTH_SECURITY_TEXT_KEY_TARGET_ALIAS, tmp_buf, (uint8_t *) login_response_pdu->ds_cmd_data, login_response_pdu->ds_len, login_response_pdu->len );
+
+ if ( ds_len < 0L )
+ return ISCSI_CONNECT_PDU_READ_ERR_FATAL;
+
+ login_response_pdu->len = ds_len;
}
if ( type == ISCSI_SESSION_TYPE_DISCOVERY ) {
- rc = iscsi_get_key_value_pair( conn->session->key_value_pairs, (uint8_t *) ISCSI_LOGIN_AUTH_SECURITY_TEXT_KEY_TARGET_ADDRESS, &tmp_buf );
+ rc = iscsi_get_key_value_pair( conn->session->key_value_pairs, ISCSI_LOGIN_AUTH_SECURITY_TEXT_KEY_TARGET_ADDRESS, &tmp_buf );
if ( (rc == 0) && (strlen( (char *) tmp_buf ) != 0) ) {
- rc = iscsi_update_key_value_pair( login_response_pdu->key_value_pairs, (uint8_t *) ISCSI_LOGIN_AUTH_SECURITY_TEXT_KEY_TARGET_ADDRESS, tmp_buf );
+ iscsi_key_value_pair *key_value_pair;
+ rc = iscsi_hashmap_get( iscsi_globvec->session_key_value_pairs, ISCSI_LOGIN_AUTH_SECURITY_TEXT_KEY_TARGET_ADDRESS, strlen( (char *) ISCSI_LOGIN_AUTH_SECURITY_TEXT_KEY_TARGET_ADDRESS ) + 1, (uint8_t **) &key_value_pair);
if ( rc < 0 )
return ISCSI_CONNECT_PDU_READ_ERR_FATAL;
+
+ const int32_t ds_len = iscsi_append_key_value_pair_packet( key_value_pair, ISCSI_LOGIN_AUTH_SECURITY_TEXT_KEY_TARGET_ADDRESS, tmp_buf, (uint8_t *) login_response_pdu->ds_cmd_data, login_response_pdu->ds_len, login_response_pdu->len );
+
+ if ( ds_len < 0L )
+ return ISCSI_CONNECT_PDU_READ_ERR_FATAL;
+
+ login_response_pdu->ds_len = ds_len;
}
}
- rc = iscsi_get_key_value_pair( conn->session->key_value_pairs, (uint8_t *) ISCSI_LOGIN_AUTH_SECURITY_TEXT_KEY_TARGET_PORTAL_GROUP_TAG, &tmp_buf );
+ rc = iscsi_get_key_value_pair( conn->session->key_value_pairs, ISCSI_LOGIN_AUTH_SECURITY_TEXT_KEY_TARGET_PORTAL_GROUP_TAG, &tmp_buf );
if ( rc == 0 ) {
- rc = iscsi_update_key_value_pair( login_response_pdu->key_value_pairs, (uint8_t *) ISCSI_LOGIN_AUTH_SECURITY_TEXT_KEY_TARGET_PORTAL_GROUP_TAG, tmp_buf );
+ iscsi_key_value_pair *key_value_pair;
+ rc = iscsi_hashmap_get( iscsi_globvec->session_key_value_pairs, ISCSI_LOGIN_AUTH_SECURITY_TEXT_KEY_TARGET_PORTAL_GROUP_TAG, strlen( (char *) ISCSI_LOGIN_AUTH_SECURITY_TEXT_KEY_TARGET_PORTAL_GROUP_TAG ) + 1, (uint8_t **) &key_value_pair);
if ( rc < 0 )
return ISCSI_CONNECT_PDU_READ_ERR_FATAL;
+
+ const int32_t ds_len = iscsi_append_key_value_pair_packet( key_value_pair, ISCSI_LOGIN_AUTH_SECURITY_TEXT_KEY_TARGET_PORTAL_GROUP_TAG, tmp_buf, (uint8_t *) login_response_pdu->ds_cmd_data, login_response_pdu->ds_len, login_response_pdu->len );
+
+ if ( ds_len < 0L )
+ return ISCSI_CONNECT_PDU_READ_ERR_FATAL;
+
+ login_response_pdu->ds_len = ds_len;
}
}
@@ -5391,7 +12858,7 @@ static int iscsi_connection_login_set_target_info(iscsi_connection *conn, iscsi_
*/
static int iscsi_connection_handle_login_phase_none(iscsi_connection *conn, iscsi_pdu *login_response_pdu, iscsi_hashmap *key_value_pairs, uint cid)
{
- uint8_t *init_port_name;
+ uint8_t *init_port_name = NULL;
iscsi_login_response_packet *login_response_pkt = (iscsi_login_response_packet *) login_response_pdu->bhs_pkt;
conn->device = NULL;
@@ -5399,15 +12866,22 @@ static int iscsi_connection_handle_login_phase_none(iscsi_connection *conn, iscs
int rc = iscsi_connection_login_init_port( conn, login_response_pdu, key_value_pairs, &init_port_name );
- if ( rc < 0 )
+ if ( rc < 0 ) {
+ if ( init_port_name != NULL )
+ free( init_port_name );
+
return rc;
+ }
int type;
rc = iscsi_connection_login_session_type( login_response_pdu, key_value_pairs, &type );
- if ( rc < 0 )
+ if ( rc < 0 ) {
+ free( init_port_name );
+
return rc;
+ }
if ( type == ISCSI_SESSION_TYPE_NORMAL ) {
rc = iscsi_connection_login_session_normal( conn, login_response_pdu, key_value_pairs, init_port_name, cid );
@@ -5419,21 +12893,26 @@ static int iscsi_connection_handle_login_phase_none(iscsi_connection *conn, iscs
login_response_pkt->status_class = ISCSI_LOGIN_RESPONSE_STATUS_CLASS_CLIENT_ERR;
login_response_pkt->status_detail = ISCSI_LOGIN_RESPONSE_STATUS_DETAILS_CLIENT_ERR_MISSING_PARAMETER;
- return ISCSI_CONNECT_PDU_READ_ERR_LOGIN_RESPONSE;
+ rc = ISCSI_CONNECT_PDU_READ_ERR_LOGIN_RESPONSE;
}
- if ( rc < 0 )
+ if ( rc < 0 ) {
+ free( init_port_name );
+
return rc;
+ }
rc = iscsi_connection_login_set_info( conn, login_response_pdu, init_port_name, type, cid );
+ free( init_port_name );
+
if ( rc < 0 )
return rc;
if ( type == ISCSI_SESSION_TYPE_DISCOVERY ) {
- conn->session->max_conns = 1;
+ conn->session->max_conns = 1UL;
- rc = iscsi_add_int_key_value_pair( conn->session->key_value_pairs, (uint8_t *) ISCSI_LOGIN_AUTH_SESSION_TEXT_KEY_MAX_CONNECTIONS, conn->session->max_conns );
+ rc = iscsi_add_int_key_value_pair( conn->session->key_value_pairs, ISCSI_LOGIN_AUTH_SESSION_TEXT_KEY_MAX_CONNECTIONS, conn->session->max_conns );
if ( rc < 0 )
return ISCSI_CONNECT_PDU_READ_ERR_LOGIN_PARAMETER;
@@ -5462,10 +12941,10 @@ static int iscsi_connecction_handle_login_response_csg_bit(iscsi_connection *con
{
iscsi_login_response_packet *login_response_pkt = (iscsi_login_response_packet *) login_response_pdu->bhs_pkt;
- switch ( ISCSI_LOGIN_RESPONSE_FLAGS_GET_NEXT_STAGE(login_response_pkt->flags) ) {
+ switch ( ISCSI_LOGIN_RESPONSE_FLAGS_GET_CURRENT_STAGE(login_response_pkt->flags) ) {
case ISCSI_LOGIN_RESPONSE_FLAGS_CURRENT_STAGE_SECURITY_NEGOTIATION : {
uint8_t *auth_method;
- const int rc = iscsi_get_key_value_pair( key_value_pairs, (uint8_t *) ISCSI_LOGIN_AUTH_SECURITY_TEXT_KEY_AUTH_METHOD, (uint8_t **) &auth_method );
+ const int rc = iscsi_get_key_value_pair( key_value_pairs, ISCSI_LOGIN_AUTH_SECURITY_TEXT_KEY_AUTH_METHOD, (uint8_t **) &auth_method );
if ( rc < 0 ) {
login_response_pkt->status_class = ISCSI_LOGIN_RESPONSE_STATUS_CLASS_CLIENT_ERR;
@@ -5477,9 +12956,9 @@ static int iscsi_connecction_handle_login_response_csg_bit(iscsi_connection *con
if ( strcasecmp( (char *) auth_method, "None" ) == 0 ) {
conn->flags |= ISCSI_CONNECT_FLAGS_AUTH;
} else {
- const int ds_len = iscsi_connection_auth_key_value_pairs( conn, key_value_pairs, auth_method, (uint8_t *) login_response_pdu->ds_cmd_data, login_response_pdu->pos, login_response_pdu->ds_len );
+ const int32_t ds_len = iscsi_connection_auth_key_value_pairs( conn, key_value_pairs, auth_method, (uint8_t *) login_response_pdu->ds_cmd_data, login_response_pdu->ds_len, login_response_pdu->len );
- if ( ds_len < 0 ) {
+ if ( ds_len < 0L ) {
login_response_pkt->status_class = ISCSI_LOGIN_RESPONSE_STATUS_CLASS_CLIENT_ERR;
login_response_pkt->status_detail = ISCSI_LOGIN_RESPONSE_STATUS_DETAILS_CLIENT_ERR_AUTH_ERR;
@@ -5597,7 +13076,7 @@ static int iscsi_connecction_handle_login_response_t_bit(iscsi_connection *conn,
case ISCSI_LOGIN_RESPONSE_FLAGS_NEXT_STAGE_FULL_FEATURE_PHASE : {
conn->login_phase = ISCSI_LOGIN_RESPONSE_FLAGS_NEXT_STAGE_FULL_FEATURE_PHASE;
- iscsi_put_be16( (uint8_t *) &login_response_pkt->tsih, conn->session->tsih );
+ iscsi_put_be16( (uint8_t *) &login_response_pkt->tsih, (uint16_t) conn->session->tsih );
const int rc = iscsi_connection_login_session_info_notify( conn, login_response_pdu );
@@ -5638,23 +13117,23 @@ static int iscsi_connecction_handle_login_response_t_bit(iscsi_connection *conn,
static int iscsi_connecction_handle_login_response(iscsi_connection *conn, iscsi_pdu *login_response_pdu, iscsi_hashmap *key_value_pairs)
{
iscsi_login_response_packet *login_response_pkt = (iscsi_login_response_packet *) login_response_pdu->bhs_pkt;
- const int ds_len = iscsi_negotiate_key_value_pairs( conn, key_value_pairs, (uint8_t *) login_response_pdu->ds_cmd_data, login_response_pdu->pos, login_response_pdu->ds_len );
+ const int32_t ds_len = iscsi_negotiate_key_value_pairs( conn, key_value_pairs, (uint8_t *) login_response_pdu->ds_cmd_data, login_response_pdu->ds_len, login_response_pdu->len );
- if ( ds_len < 0 ) {
+ if ( ds_len < 0L ) {
login_response_pkt->status_class = ISCSI_LOGIN_RESPONSE_STATUS_CLASS_CLIENT_ERR;
login_response_pkt->status_detail = ISCSI_LOGIN_RESPONSE_STATUS_DETAILS_CLIENT_ERR_AUTH_ERR;
return ISCSI_CONNECT_PDU_READ_ERR_LOGIN_RESPONSE;
}
- login_response_pdu->ds_len = (uint) ds_len;
+ login_response_pdu->ds_len = (uint32_t) ds_len;
int rc = iscsi_connecction_handle_login_response_csg_bit( conn, login_response_pdu, key_value_pairs );
if ( rc < 0 )
return rc;
- if ( (login_response_pkt->flags & ISCSI_LOGIN_RESPONSE_FLAGS_TRANSIT) != 0)
+ if ( (login_response_pkt->flags & ISCSI_LOGIN_RESPONSE_FLAGS_TRANSIT) != 0 )
rc = iscsi_connecction_handle_login_response_t_bit( conn, login_response_pdu );
return rc;
@@ -5668,9 +13147,9 @@ static int iscsi_connecction_handle_login_response(iscsi_connection *conn, iscsi
* If a response needs to be sent, this will
* be done as well.
*
- * @param[in] conn iSCSI connection to handle. May
+ * @param[in] conn Pointer to iSCSI connection to handle. May
* NOT be NULL, so take caution.
- * @param[in] pdu iSCSI client request PDU to handle.
+ * @param[in] pdu Pointer to iSCSI client request PDU to handle.
* May be NULL in which case an error is returned.
* @return 0 on success. A negative value indicates
* an error. A positive value a warning.
@@ -5680,9 +13159,9 @@ static int iscsi_connection_pdu_data_handle_login_req(iscsi_connection *conn, is
iscsi_pdu *login_response_pdu = (iscsi_pdu *) conn->login_response_pdu;
if ( login_response_pdu == NULL )
- return 0L;
+ return ISCSI_CONNECT_PDU_READ_OK;
- iscsi_hashmap *key_value_pairs = iscsi_hashmap_create( 32UL );
+ iscsi_hashmap *key_value_pairs = iscsi_hashmap_create( (((sizeof(iscsi_session_key_value_pair_lut) / sizeof(struct iscsi_key_value_pair_lut_entry)) - 1) + ((sizeof(iscsi_connection_key_value_pair_lut) / sizeof(struct iscsi_key_value_pair_lut_entry)) - 1)) );
if ( key_value_pairs == NULL )
return ISCSI_CONNECT_PDU_READ_ERR_FATAL;
@@ -5694,7 +13173,7 @@ static int iscsi_connection_pdu_data_handle_login_req(iscsi_connection *conn, is
if ( rc < 0 ) {
iscsi_connection_pdu_login_response( conn, login_response_pdu, NULL, iscsi_connection_pdu_login_err_complete );
- return 0L;
+ return ISCSI_CONNECT_PDU_READ_OK;
}
if ( conn->state == ISCSI_CONNECT_STATE_INVALID ) {
@@ -5703,7 +13182,7 @@ static int iscsi_connection_pdu_data_handle_login_req(iscsi_connection *conn, is
if ( (rc == ISCSI_CONNECT_PDU_READ_ERR_LOGIN_RESPONSE) || (rc == ISCSI_CONNECT_PDU_READ_ERR_LOGIN_PARAMETER) ) {
iscsi_connection_pdu_login_response( conn, login_response_pdu, key_value_pairs, iscsi_connection_pdu_login_err_complete );
- return 0L;
+ return ISCSI_CONNECT_PDU_READ_OK;
}
}
@@ -5712,14 +13191,31 @@ static int iscsi_connection_pdu_data_handle_login_req(iscsi_connection *conn, is
if ( rc == ISCSI_CONNECT_PDU_READ_ERR_LOGIN_RESPONSE ) {
iscsi_connection_pdu_login_response( conn, login_response_pdu, key_value_pairs, iscsi_connection_pdu_login_err_complete );
- return 0L;
+ return ISCSI_CONNECT_PDU_READ_OK;
}
conn->state = ISCSI_CONNECT_STATE_RUNNING;
iscsi_connection_pdu_login_response( conn, login_response_pdu, key_value_pairs, iscsi_connection_pdu_login_ok_complete );
- return 0L;
+ return ISCSI_CONNECT_PDU_READ_OK;
+}
+
+/**
+ * @brief Callback function after text response has been sent.
+ *
+ * This function is invoked after the text
+ * response has been sent to the client via
+ * TCP/IP.
+ *
+ * @param[in] user_data Pointer to iSCSI connection which
+ * was used for sending the response.
+ */
+static void iscsi_connection_pdu_text_complete(uint8_t *user_data)
+{
+ iscsi_connection *conn = (iscsi_connection *) user_data;
+
+ iscsi_connection_update_key_value_pairs( conn );
}
/**
@@ -5730,18 +13226,165 @@ static int iscsi_connection_pdu_data_handle_login_req(iscsi_connection *conn, is
* If a response needs to be sent, this will
* be done as well.
*
- * @param[in] conn iSCSI connection to handle. May
+ * @param[in] conn Pointer to iSCSI connection to handle. May
* NOT be NULL, so take caution.
- * @param[in] pdu iSCSI client request PDU to handle.
+ * @param[in] pdu Pointer to iSCSI client request PDU to handle.
* May be NULL in which case an error is returned.
* @return 0 on success. A negative value indicates
* an error. A positive value a warning.
*/
static int iscsi_connection_pdu_data_handle_text_req(iscsi_connection *conn, iscsi_pdu *pdu)
{
- // TODO: Implement opcode.
+ iscsi_hashmap *key_value_pairs = iscsi_hashmap_create( (((sizeof(iscsi_session_key_value_pair_lut) / sizeof(struct iscsi_key_value_pair_lut_entry)) - 1) + ((sizeof(iscsi_connection_key_value_pair_lut) / sizeof(struct iscsi_key_value_pair_lut_entry)) - 1)) );
+
+ if ( key_value_pairs == NULL )
+ return ISCSI_CONNECT_PDU_READ_ERR_FATAL;
- return 0L;
+ iscsi_text_req_packet *text_req_pkt = (iscsi_text_req_packet *) pdu->bhs_pkt;
+ int rc = iscsi_parse_key_value_pairs( key_value_pairs, (uint8_t *) pdu->ds_cmd_data, pdu->ds_len, ((text_req_pkt->flags & ISCSI_TEXT_REQ_FLAGS_CONTINUE) != 0), &conn->text_partial_pairs );
+
+ if ( rc < 0 ) {
+ iscsi_hashmap_iterate( key_value_pairs, iscsi_hashmap_key_destroy_value_callback, NULL );
+ iscsi_hashmap_destroy( key_value_pairs );
+
+ return ISCSI_CONNECT_PDU_READ_ERR_FATAL;
+ }
+
+ if ( (pdu->ds_len == 0UL) && (iscsi_hashmap_size( key_value_pairs ) == 0U) ) {
+ iscsi_hashmap *tmp_hashmap = key_value_pairs;
+ key_value_pairs = conn->text_key_value_pairs;
+ conn->text_key_value_pairs = tmp_hashmap;
+ }
+
+ iscsi_pdu *response_pdu = iscsi_connection_pdu_create( conn, 0U, conn->header_digest, conn->max_recv_ds_len, conn->data_digest );
+
+ if ( response_pdu == NULL ) {
+ logadd( LOG_ERROR, "iscsi_connection_pdu_data_handle_text_req: Out of memory while allocating iSCSI text response PDU" );
+
+ iscsi_hashmap_iterate( key_value_pairs, iscsi_hashmap_key_destroy_value_callback, NULL );
+ iscsi_hashmap_destroy( key_value_pairs );
+
+ return ISCSI_CONNECT_PDU_READ_ERR_FATAL;
+ }
+
+ response_pdu->ds_len = 0UL;
+
+ int32_t ds_len = iscsi_negotiate_key_value_pairs( conn, key_value_pairs, (uint8_t *) response_pdu->ds_cmd_data, response_pdu->ds_len, response_pdu->len );
+
+ if ( ds_len < 0L ) {
+ iscsi_hashmap_iterate( key_value_pairs, iscsi_hashmap_key_destroy_value_callback, NULL );
+ iscsi_hashmap_destroy( key_value_pairs );
+ iscsi_connection_pdu_destroy( response_pdu );
+
+ return ISCSI_CONNECT_PDU_READ_ERR_FATAL;
+ }
+
+ iscsi_text_response_packet *text_response_pkt = (iscsi_text_response_packet *) response_pdu->bhs_pkt;
+
+ text_response_pkt->opcode = ISCSI_OPCODE_SERVER_TEXT_RES;
+ text_response_pkt->flags = 0;
+
+ if ( (text_req_pkt->flags & ISCSI_TEXT_REQ_FLAGS_CONTINUE) != 0 )
+ text_response_pkt->flags |= (int8_t) ISCSI_TEXT_RESPONSE_FLAGS_CONTINUE;
+
+ if ( (text_req_pkt->flags & ISCSI_TEXT_REQ_FLAGS_FINAL) != 0 )
+ text_response_pkt->flags |= (int8_t) ISCSI_TEXT_RESPONSE_FLAGS_FINAL;
+
+ text_req_pkt->reserved = 0U;
+
+ uint8_t *send_targets_val;
+ rc = iscsi_get_key_value_pair( key_value_pairs, ISCSI_LOGIN_AUTH_SESSION_TEXT_KEY_SEND_TARGETS, &send_targets_val );
+
+ if ( rc < 0 ) {
+ uint8_t *type_val;
+ rc = iscsi_get_key_value_pair( conn->session->key_value_pairs, ISCSI_LOGIN_AUTH_SECURITY_TEXT_KEY_SESSION_TYPE, &type_val );
+
+ if ( (rc >= 0) && (type_val != NULL) && (strcasecmp( (char *) type_val, "Discovery" ) == 0) ) {
+ iscsi_hashmap_iterate( key_value_pairs, iscsi_hashmap_key_destroy_value_callback, NULL );
+ iscsi_hashmap_destroy( key_value_pairs );
+ iscsi_connection_pdu_destroy( response_pdu );
+
+ return ISCSI_CONNECT_PDU_READ_ERR_FATAL;
+ }
+ } else {
+ uint8_t *type_val;
+ rc = iscsi_get_key_value_pair( conn->session->key_value_pairs, ISCSI_LOGIN_AUTH_SECURITY_TEXT_KEY_SESSION_TYPE, &type_val );
+
+ if ( (rc >= 0) && (type_val != NULL) && (strcasecmp( (char *) type_val, "Discovery" ) == 0) ) {
+ if ( send_targets_val[0] == '\0' )
+ send_targets_val = (uint8_t *) "ALL";
+
+ ds_len = iscsi_target_node_send( conn, send_targets_val, conn->init_name, (uint8_t *) response_pdu->ds_cmd_data, ds_len, response_pdu->len );
+ } else {
+ if ( send_targets_val[0] == '\0' )
+ send_targets_val = conn->target_port->name;
+
+ if ( strcasecmp( (char *) send_targets_val, "ALL" ) == 0 ) {
+ iscsi_key_value_pair *key_value_pair;
+ rc = iscsi_hashmap_get( iscsi_globvec->session_key_value_pairs, ISCSI_LOGIN_AUTH_SESSION_TEXT_KEY_SEND_TARGETS, strlen( (char *) ISCSI_LOGIN_AUTH_SESSION_TEXT_KEY_SEND_TARGETS ) + 1, (uint8_t **) &key_value_pair);
+
+ if ( rc < 0 ) {
+ iscsi_hashmap_iterate( key_value_pairs, iscsi_hashmap_key_destroy_value_callback, NULL );
+ iscsi_hashmap_destroy( key_value_pairs );
+ iscsi_connection_pdu_destroy( response_pdu );
+
+ return ISCSI_CONNECT_PDU_READ_ERR_FATAL;
+ }
+
+ ds_len = iscsi_append_key_value_pair_packet( key_value_pair, ISCSI_LOGIN_AUTH_SESSION_TEXT_KEY_SEND_TARGETS, (uint8_t *) "Reject", (uint8_t *) response_pdu->ds_cmd_data, ds_len, response_pdu->len );
+ } else {
+ ds_len = iscsi_target_node_send( conn, send_targets_val, conn->init_name, (uint8_t *) response_pdu->ds_cmd_data, ds_len, response_pdu->len );
+ }
+ }
+
+ if ( conn->target_send_total_size == 0U ) {
+ text_response_pkt->flags |= (int8_t) ISCSI_TEXT_RESPONSE_FLAGS_CONTINUE;
+ text_response_pkt->flags &= (int8_t) ~ISCSI_TEXT_RESPONSE_FLAGS_FINAL;
+ }
+ }
+
+ if ( ds_len < 0L ) {
+ iscsi_hashmap_iterate( key_value_pairs, iscsi_hashmap_key_destroy_value_callback, NULL );
+ iscsi_hashmap_destroy( key_value_pairs );
+ iscsi_connection_pdu_destroy( response_pdu );
+
+ return ISCSI_CONNECT_PDU_READ_ERR_FATAL;
+ }
+
+ if ( conn->target_send_total_size == 0U ) {
+ iscsi_hashmap_iterate( key_value_pairs, iscsi_hashmap_key_destroy_value_callback, NULL );
+ iscsi_hashmap_destroy( key_value_pairs );
+ } else {
+ conn->text_key_value_pairs = key_value_pairs;
+ }
+
+ text_response_pkt = (iscsi_text_response_packet *) iscsi_connection_pdu_append( response_pdu, response_pdu->ahs_len, conn->header_digest, ds_len, conn->data_digest );
+
+ iscsi_put_be32( (uint8_t *) &text_response_pkt->total_ahs_len, ds_len ); // TotalAHSLength is always 0 and DataSegmentLength is 24-bit, so write in one step.
+ text_response_pkt->lun = text_req_pkt->lun; // Copying over doesn't change endianess.
+ text_response_pkt->init_task_tag = text_req_pkt->init_task_tag; // Copying over doesn't change endianess.
+
+ if ( (text_req_pkt->flags & ISCSI_TEXT_REQ_FLAGS_FINAL) != 0 ) {
+ text_response_pkt->target_xfer_tag = 0xFFFFFFFFUL; // Minus one does not require endianess conversion
+
+ conn->session->current_text_init_task_tag = 0xFFFFFFFFUL;
+ } else {
+ iscsi_put_be32( (uint8_t *) &text_response_pkt->target_xfer_tag, ((uint32_t) conn->id + 1UL) );
+ }
+
+ iscsi_put_be32( (uint8_t *) &text_response_pkt->stat_sn, conn->stat_sn++ );
+
+ if ( (text_response_pkt->opcode & ISCSI_OPCODE_FLAGS_IMMEDIATE) == 0 )
+ conn->session->max_cmd_sn++;
+
+ iscsi_put_be32( (uint8_t *) &text_response_pkt->exp_cmd_sn, conn->session->exp_cmd_sn );
+ iscsi_put_be32( (uint8_t *) &text_response_pkt->max_cmd_sn, conn->session->max_cmd_sn );
+ text_response_pkt->reserved2[0] = 0ULL;
+ text_response_pkt->reserved2[1] = 0ULL;
+
+ iscsi_connection_pdu_write( conn, response_pdu, iscsi_connection_pdu_text_complete, (uint8_t *) conn );
+
+ return ISCSI_CONNECT_PDU_READ_OK;
}
/**
@@ -5752,9 +13395,9 @@ static int iscsi_connection_pdu_data_handle_text_req(iscsi_connection *conn, isc
* If a response needs to be sent, this will
* be done as well.
*
- * @param[in] conn iSCSI connection to handle. May
+ * @param[in] conn Pointer to iSCSI connection to handle. May
* NOT be NULL, so take caution.
- * @param[in] pdu iSCSI client request PDU to handle.
+ * @param[in] pdu Pointer to iSCSI client request PDU to handle.
* May be NULL in which case an error is returned.
* @return 0 on success. A negative value indicates
* an error. A positive value a warning.
@@ -5763,7 +13406,7 @@ static int iscsi_connection_pdu_data_handle_scsi_data_out(iscsi_connection *conn
{
// TODO: Implement opcode.
- return 0L;
+ return 0;
}
/**
@@ -5774,9 +13417,9 @@ static int iscsi_connection_pdu_data_handle_scsi_data_out(iscsi_connection *conn
* If a response needs to be sent, this will
* be done as well.
*
- * @param[in] conn iSCSI connection to handle. May
+ * @param[in] conn Pointer to iSCSI connection to handle. May
* NOT be NULL, so take caution.
- * @param[in] pdu iSCSI client request PDU to handle.
+ * @param[in] pdu Pointer to iSCSI client request PDU to handle.
* May be NULL in which case an error is returned.
* @return 0 on success. A negative value indicates
* an error. A positive value a warning.
@@ -5788,34 +13431,34 @@ static int iscsi_connection_pdu_data_handle(iscsi_connection *conn, iscsi_pdu *p
const uint8_t opcode = ISCSI_GET_OPCODE(pdu->bhs_pkt->opcode);
switch ( opcode ) {
- case ISCSI_CLIENT_NOP_OUT : {
+ case ISCSI_OPCODE_CLIENT_NOP_OUT : {
rc = iscsi_connection_pdu_data_handle_nop_out( conn, pdu );
break;
}
- case ISCSI_CLIENT_SCSI_CMD : {
+ case ISCSI_OPCODE_CLIENT_SCSI_CMD : {
rc = iscsi_connection_pdu_data_handle_scsi_cmd( conn, pdu );
break;
}
- case ISCSI_CLIENT_LOGIN_REQ : {
+ case ISCSI_OPCODE_CLIENT_LOGIN_REQ : {
rc = iscsi_connection_pdu_data_handle_login_req( conn, pdu );
break;
}
- case ISCSI_CLIENT_TEXT_REQ : {
+ case ISCSI_OPCODE_CLIENT_TEXT_REQ : {
rc = iscsi_connection_pdu_data_handle_text_req( conn, pdu );
break;
}
- case ISCSI_CLIENT_SCSI_DATA_OUT : {
+ case ISCSI_OPCODE_CLIENT_SCSI_DATA_OUT : {
rc = iscsi_connection_pdu_data_handle_scsi_data_out( conn, pdu );
break;
}
- case ISCSI_CLIENT_TASK_FUNC_REQ :
- case ISCSI_CLIENT_LOGOUT_REQ :
- case ISCSI_CLIENT_SNACK_REQ : {
+ case ISCSI_OPCODE_CLIENT_TASK_FUNC_REQ :
+ case ISCSI_OPCODE_CLIENT_LOGOUT_REQ :
+ case ISCSI_OPCODE_CLIENT_SNACK_REQ : {
break;
}
default : {
@@ -5825,6 +13468,9 @@ static int iscsi_connection_pdu_data_handle(iscsi_connection *conn, iscsi_pdu *p
}
}
+ if ( rc < 0 )
+ logadd( LOG_ERROR, "Fatal error during payload handler (opcode 0x%02x) detected for device %s", (int) opcode, (conn->device != NULL ? (char *) conn->device->name : "(null)") );
+
return rc;
}
@@ -5839,17 +13485,46 @@ static int iscsi_connection_pdu_data_handle(iscsi_connection *conn, iscsi_pdu *p
* to be sure that all data packets have been
* received.
*
- * @param[in] conn iSCSI connection to read TCP/IP data from.
- * @param[in] pdu iSCSI PDU to read TCP/IP data into.
+ * @param[in] conn Pointer to iSCSI connection to read TCP/IP data from.
+ * @param[in] pdu Pointer to iSCSI PDU to read TCP/IP data into.
* @retval -1 Fatal error occured during processing the PDU.
* @retval 0 Read operation was successful and next read is ready.
* @retval 1 Read operation was successful and PDU was fully processed.
*/
int iscsi_connection_pdu_data_read(iscsi_connection *conn, iscsi_pdu *pdu)
{
- // TODO: Implement DS read.
+ const uint32_t ds_len = pdu->ds_len;
- return 0;
+ if ( pdu->pos < ds_len ) {
+ const int32_t len = iscsi_connection_read( conn, (((uint8_t *) pdu->ds_cmd_data) + pdu->pos), (ds_len - pdu->pos) );
+
+ if ( len < 0L )
+ return len;
+
+ pdu->pos += len;
+ }
+
+ if ( pdu->pos < ds_len )
+ return ISCSI_CONNECT_PDU_READ_PROCESSED;
+
+ if ( pdu->data_digest != NULL ) {
+ if ( (int) pdu->data_digest_pos < pdu->data_digest_size ) {
+ const int32_t len = iscsi_connection_read( conn, (((uint8_t *) pdu->data_digest) + pdu->data_digest_pos), (pdu->data_digest_size - pdu->data_digest_pos) );
+
+ if ( len < 0L )
+ return len;
+
+ pdu->data_digest_pos += len;
+
+ if ( (int) pdu->data_digest_pos < pdu->data_digest_size )
+ return ISCSI_CONNECT_PDU_READ_OK;
+ }
+
+ if ( !iscsi_connection_pdu_digest_data_verify( pdu->data_digest, pdu->ds_cmd_data, ds_len ) )
+ return ISCSI_CONNECT_PDU_READ_ERR_FATAL;
+ }
+
+ return ISCSI_CONNECT_PDU_READ_OK;
}
/**
@@ -5863,7 +13538,7 @@ int iscsi_connection_pdu_data_read(iscsi_connection *conn, iscsi_pdu *pdu)
* to be sure that all data packets have been
* received.
*
- * @param[in] conn iSCSI connection to read TCP/IP data from.
+ * @param[in] conn Pointer to iSCSI connection to read TCP/IP data from.
* @retval -1 Fatal error occured during processing the PDU.
* @retval 0 Read operation was successful and next read is ready.
* @retval 1 Read operation was successful and PDU was fully processed.
@@ -5879,7 +13554,7 @@ static int iscsi_connection_pdu_read(iscsi_connection *conn)
switch ( conn->pdu_recv_state ) {
case ISCSI_CONNECT_PDU_RECV_STATE_WAIT_PDU_READY : {
- conn->pdu_processing = iscsi_connection_pdu_create( conn );
+ conn->pdu_processing = iscsi_connection_pdu_create( conn, 0U, conn->header_digest, 0UL, conn->data_digest );
if ( conn->pdu_processing == NULL )
return ISCSI_CONNECT_PDU_READ_ERR_FATAL;
@@ -5889,18 +13564,18 @@ static int iscsi_connection_pdu_read(iscsi_connection *conn)
break;
}
case ISCSI_CONNECT_PDU_RECV_STATE_WAIT_PDU_HDR : {
- if ( pdu->bhs_read_len < sizeof(struct iscsi_bhs_packet) ) {
- const int len = iscsi_connection_read( conn, (((uint8_t *) pdu->bhs_pkt) + pdu->bhs_read_len), (sizeof(struct iscsi_bhs_packet) - pdu->bhs_read_len) );
+ if ( pdu->bhs_pos < sizeof(struct iscsi_bhs_packet) ) {
+ const int32_t len = iscsi_connection_read( conn, (((uint8_t *) pdu->bhs_pkt) + pdu->bhs_pos), (sizeof(struct iscsi_bhs_packet) - pdu->bhs_pos) );
- if ( len < 0 ) {
+ if ( len < 0L ) {
conn->pdu_recv_state = ISCSI_CONNECT_PDU_RECV_STATE_ERR;
break;
}
- pdu->bhs_read_len += len;
+ pdu->bhs_pos += len;
- if ( pdu->bhs_read_len < sizeof(struct iscsi_bhs_packet) )
+ if ( pdu->bhs_pos < sizeof(struct iscsi_bhs_packet) )
return ISCSI_CONNECT_PDU_READ_OK;
}
@@ -5910,73 +13585,89 @@ static int iscsi_connection_pdu_read(iscsi_connection *conn)
break;
}
- pdu->ds_len = iscsi_align(pdu->ds_len, ISCSI_ALIGN_SIZE);
- pdu->pos = pdu->ds_len;
+ iscsi_bhs_packet *bhs_pkt = pdu->bhs_pkt;
+ const uint ahs_len = ((uint) bhs_pkt->total_ahs_len << 2U);
+ const uint32_t ds_len = iscsi_get_be24(bhs_pkt->ds_len);
- const uint ahs_len = (uint) pdu->bhs_pkt->total_ahs_len << 2UL;
+ bhs_pkt = iscsi_connection_pdu_append( pdu, ahs_len, conn->header_digest, ds_len, conn->data_digest );
- if ( pdu->ahs_read_len < ahs_len ) {
- if ( pdu->ahs_pkt == NULL ) {
- pdu->ahs_pkt = (iscsi_ahs_packet *) iscsi_append_ahs_packet( pdu->bhs_pkt, (uint32_t) ahs_len );
+ if ( bhs_pkt == NULL )
+ return ISCSI_CONNECT_PDU_READ_ERR_FATAL;
+
+ uint64_t stat_opcode = (uint64_t) ISCSI_GET_OPCODE(bhs_pkt->opcode);
+ uint64_t *stat_value = NULL;
+ int stat_rc = iscsi_hashmap_get( conn->stat_iscsi_opcodes, (uint8_t *) &stat_opcode, sizeof(stat_opcode), (uint8_t **) &stat_value );
+
+ if ( stat_value == NULL ) {
+ stat_value = malloc( sizeof(uint64_t) );
+
+ if ( stat_value != NULL ) {
+ uint8_t *stat_key = iscsi_hashmap_key_create( (uint8_t *) &stat_opcode, sizeof(stat_opcode) );
+
+ if ( stat_key != NULL ) {
+ *stat_value = 0ULL;
- if ( pdu->ahs_pkt == NULL )
- return ISCSI_CONNECT_PDU_READ_ERR_FATAL;
+ stat_rc = iscsi_hashmap_put( conn->stat_iscsi_opcodes, stat_key, sizeof(stat_opcode), (uint8_t *) stat_value );
- pdu->ahs_pkt = (iscsi_ahs_packet *) (((iscsi_bhs_packet *) pdu->bhs_pkt) + 1);
+ if ( stat_rc < 0 ) {
+ iscsi_hashmap_key_destroy( stat_key );
+ free( stat_value );
+ stat_value = NULL;
+ }
+ } else {
+ free( stat_value );
+ stat_value = NULL;
+ }
}
+ }
- const int len = iscsi_connection_read( conn, (((uint8_t *) pdu->ahs_pkt) + pdu->ahs_read_len), (ahs_len - pdu->ahs_read_len) );
+ if ( stat_value != NULL )
+ (*stat_value)++;
- if ( len < 0 ) {
+ if ( pdu->ahs_pos < ahs_len ) {
+ const int32_t len = iscsi_connection_read( conn, (((uint8_t *) pdu->ahs_pkt) + pdu->ahs_pos), (ahs_len - pdu->ahs_pos) );
+
+ if ( len < 0L ) {
conn->pdu_recv_state = ISCSI_CONNECT_PDU_RECV_STATE_ERR;
break;
}
- pdu->ahs_read_len += len;
+ pdu->ahs_pos += len;
- if ( pdu->ahs_read_len < ahs_len )
+ if ( pdu->ahs_pos < ahs_len )
return ISCSI_CONNECT_PDU_READ_OK;
}
- if ( conn->header_digest != 0 ) {
- if ( pdu->header_digest == NULL ) {
- pdu->header_digest = (iscsi_header_digest *) iscsi_append_header_digest_packet( pdu->bhs_pkt, ISCSI_DIGEST_SIZE );
+ if ( pdu->header_digest != NULL ) {
+ if ( (int) pdu->header_digest_pos < pdu->header_digest_size ) {
+ const int32_t len = iscsi_connection_read( conn, (((uint8_t *) pdu->header_digest) + pdu->header_digest_pos), (pdu->header_digest_size - pdu->header_digest_pos) );
- if ( pdu->header_digest == NULL )
- return ISCSI_CONNECT_PDU_READ_ERR_FATAL;
-
- pdu->header_digest = (iscsi_header_digest *) (((uint8_t *) pdu->bhs_pkt) + sizeof(struct iscsi_bhs_packet) + ahs_len);
- }
-
- if ( pdu->header_digest_read_len < (uint) conn->header_digest ) {
- const int len = iscsi_connection_read( conn, (((uint8_t *) pdu->header_digest) + pdu->header_digest_read_len), (conn->header_digest - pdu->header_digest_read_len) );
-
- if ( len < 0 ) {
+ if ( len < 0L ) {
conn->pdu_recv_state = ISCSI_CONNECT_PDU_RECV_STATE_ERR;
break;
}
- pdu->header_digest_read_len += len;
+ pdu->header_digest_pos += len;
- if ( pdu->header_digest_read_len < (uint) conn->header_digest )
+ if ( (int) pdu->header_digest_pos < pdu->header_digest_size )
return ISCSI_CONNECT_PDU_READ_OK;
}
- if ( iscsi_validate_header_digest( pdu->bhs_pkt ) == 0 ) {
+ if ( !iscsi_connection_pdu_digest_header_verify( pdu->header_digest, bhs_pkt, ahs_len ) ) {
conn->pdu_recv_state = ISCSI_CONNECT_PDU_RECV_STATE_ERR;
break;
}
}
- conn->pdu_recv_state = (iscsi_connection_pdu_header_handle( conn, pdu ) < 0L) ? ISCSI_CONNECT_PDU_RECV_STATE_ERR : ISCSI_CONNECT_PDU_RECV_STATE_WAIT_PDU_DATA;
+ conn->pdu_recv_state = ((iscsi_connection_pdu_header_handle( conn, pdu ) < 0) ? ISCSI_CONNECT_PDU_RECV_STATE_ERR : ISCSI_CONNECT_PDU_RECV_STATE_WAIT_PDU_DATA);
break;
}
case ISCSI_CONNECT_PDU_RECV_STATE_WAIT_PDU_DATA : {
- if ( pdu->ds_len != 0 ) {
+ if ( pdu->ds_len != 0UL ) {
const int len = iscsi_connection_pdu_data_read( conn, pdu );
if ( len < 0 ) {
@@ -6027,28 +13718,52 @@ static int iscsi_connection_pdu_read(iscsi_connection *conn)
#define ISCSI_PDU_HANDLE_COUNT 16
/**
- * @brief Handle incoming PDU data, read up to 16 fragments at once.
+ * @brief Handles incoming PDU data, read up to 16 fragments at once.
*
* Until iSCSI processing has been stopped or a
* complete iSCSI packet has been read, this
* function will read, parse and process
* incoming iSCSI protocol data.
*
- * @param[in] iSCSI connection to handle.
+ * @param[in] conn Pointer to iSCSI connection to handle.
* @return Number of proccessed fragments or return
* code of iscsi_connection_pdu_read in case of a
* fatal error.
*/
int iscsi_connection_pdu_handle(iscsi_connection *conn)
{
- uint i;
+ int i;
for ( i = 0; i < ISCSI_PDU_HANDLE_COUNT; i++ ) {
- int rc = iscsi_connection_pdu_read(conn);
+ const int rc = iscsi_connection_pdu_read( conn );
+
+ while ( !iscsi_list_empty( &conn->exec_queue ) ) {
+ iscsi_connection_exec_queue *exec_queue = (iscsi_connection_exec_queue *) iscsi_list_peek( &conn->exec_queue );
+
+ iscsi_list_remove( &exec_queue->node );
+
+ switch ( exec_queue->type ) {
+ case ISCSI_CONNECT_EXEC_QUEUE_TYPE_SCSI_EMU_IO : {
+ exec_queue->data.io.callback( exec_queue->data.io.image, exec_queue->data.io.user_data, exec_queue->data.io.success );
+
+ break;
+ }
+ case ISCSI_CONNECT_EXEC_QUEUE_TYPE_PDU_WRITE : {
+ exec_queue->data.pdu_write.callback( exec_queue->data.pdu_write.user_data, exec_queue->data.pdu_write.err );
- if ( rc == 0 )
+ break;
+ }
+ default : {
+ break;
+ }
+ }
+
+ free( exec_queue );
+ }
+
+ if ( rc == ISCSI_CONNECT_PDU_READ_OK )
break;
- else if ( rc < 0 )
+ else if ( rc == ISCSI_CONNECT_PDU_READ_ERR_FATAL )
return rc;
if ( (conn->flags & ISCSI_CONNECT_FLAGS_STOPPED) != 0 )
@@ -6057,3 +13772,243 @@ int iscsi_connection_pdu_handle(iscsi_connection *conn)
return i;
}
+
+/**
+ * @brief Handles an iSCSI connection until connection is closed.
+ *
+ * This function creates an iSCSI portal group
+ * and iSCSI portal with connection data
+ * delivered from the DNBD3 client and
+ * request data.
+ *
+ * @param[in] client Pointer to DNBD3 client structure,
+ * may NOT be NULL, so be careful.
+ * @param[in] request Pointer to DNBD3 request packet data.
+ * NULL is not allowed here, take caution.
+ * @param[in] len Length of already read DNBD3 request data.
+ */
+void iscsi_connection_handle(dnbd3_client_t *client, const dnbd3_request_t *request, const int len)
+{
+ _Static_assert( sizeof(dnbd3_request_t) <= sizeof(struct iscsi_bhs_packet), "DNBD3 request size larger than iSCSI BHS packet data size - Manual intervention required!" );
+ sock_setTimeout( client->sock, 1000L * 3600L ); // TODO: Remove after finishing iSCSI implementation
+
+ pthread_rwlock_rdlock( &iscsi_globvec_rwlock );
+
+ if ( iscsi_globvec == NULL )
+ return;
+
+ uint64_t *hash_key;
+ iscsi_portal_group *portal_group = NULL;
+
+ pthread_rwlock_wrlock( &iscsi_globvec->portal_groups_rwlock );
+
+ int rc = iscsi_hashmap_get( iscsi_globvec->portal_groups, (uint8_t *) &iscsi_globvec->portal_groups->last_insert_id, sizeof(iscsi_globvec->portal_groups->last_insert_id), (uint8_t **) &portal_group );
+
+ if ( portal_group == NULL ) {
+ hash_key = (uint64_t *) malloc( sizeof(uint64_t) );
+
+ if ( hash_key == NULL ) {
+ logadd( LOG_ERROR, "iscsi_connection_handle: Out of memory while allocating iSCSI portal group" );
+
+ pthread_rwlock_unlock( &iscsi_globvec->portal_groups_rwlock );
+ pthread_rwlock_unlock( &iscsi_globvec_rwlock );
+
+ return;
+ }
+
+ iscsi_hashmap_key_create_id( iscsi_globvec->portal_groups, hash_key );
+ portal_group = iscsi_portal_group_create( *hash_key, 0 );
+
+ if ( portal_group == NULL ) {
+ logadd( LOG_ERROR, "iscsi_connection_handle: Out of memory while allocating iSCSI portal group" );
+
+ iscsi_hashmap_key_destroy( (uint8_t *) hash_key );
+ pthread_rwlock_unlock( &iscsi_globvec->portal_groups_rwlock );
+ pthread_rwlock_unlock( &iscsi_globvec_rwlock );
+
+ return;
+ }
+
+ portal_group->tag = *hash_key;
+
+ iscsi_hashmap_key_destroy( (uint8_t *) hash_key );
+
+ rc = iscsi_hashmap_put( iscsi_globvec->portal_groups, (uint8_t *) &portal_group->tag, sizeof(portal_group->tag), (uint8_t *) portal_group );
+
+ if ( rc < 0 ) {
+ iscsi_portal_group_destroy( portal_group );
+ pthread_rwlock_unlock( &iscsi_globvec->portal_groups_rwlock );
+ pthread_rwlock_unlock( &iscsi_globvec_rwlock );
+
+ return;
+ }
+ }
+
+ pthread_rwlock_unlock( &iscsi_globvec->portal_groups_rwlock );
+ host_to_string( &client->host, client->hostName, HOSTNAMELEN );
+
+ const uint8_t *port = memchr( client->hostName, ':', HOSTNAMELEN );
+ const uint host_len = ((port != NULL) ? (uint) (port++ - (uint8_t *) client->hostName) : (uint) strlen( client->hostName ));
+ uint8_t *host = malloc( (host_len + 1U) );
+
+ if ( host == NULL ) {
+ logadd( LOG_ERROR, "iscsi_connection_handle: Out of memory while allocating iSCSI portal host name" );
+
+ pthread_rwlock_unlock( &iscsi_globvec_rwlock );
+
+ return;
+ }
+
+ memcpy( host, client->hostName, host_len );
+ host[host_len] = '\0';
+
+ uint8_t *tmp_buf;
+
+ if ( port != NULL )
+ tmp_buf = iscsi_sprintf_alloc( "%s:%s", host, port );
+ else
+ tmp_buf = iscsi_sprintf_alloc( "%s:%u", host, PORT );
+
+ if ( tmp_buf == NULL ) {
+ logadd( LOG_ERROR, "iscsi_connection_handle: Out of memory while allocating temporarily iSCSI portal name" );
+
+ free( host );
+ pthread_rwlock_unlock( &iscsi_globvec_rwlock );
+
+ return;
+ }
+
+ const uint key_len = (uint) (strlen( (char *) tmp_buf ) + 1U);
+
+ hash_key = (uint64_t *) iscsi_hashmap_key_create( tmp_buf, key_len );
+
+ free( tmp_buf );
+
+ if ( hash_key == NULL ) {
+ logadd( LOG_ERROR, "iscsi_connection_handle: Out of memory while allocating temporarily iSCSI portal name hash key" );
+
+ free( host );
+ pthread_rwlock_unlock( &iscsi_globvec_rwlock );
+
+ return;
+ }
+
+ iscsi_portal *portal = NULL;
+
+ pthread_rwlock_wrlock( &iscsi_globvec->portal_groups_rwlock );
+ rc = iscsi_hashmap_get( portal_group->portals, (uint8_t *) hash_key, key_len, (uint8_t **) &portal );
+
+ if ( portal == NULL ) {
+ if ( port == NULL ) {
+ port = (uint8_t *) strchr( (char *) hash_key, ':' );
+ port++;
+ }
+
+ portal = iscsi_portal_create( host, port );
+
+ if ( portal == NULL ) {
+ logadd( LOG_ERROR, "iscsi_connection_handle: Out of memory while allocating iSCSI portal" );
+
+ pthread_rwlock_unlock( &iscsi_globvec->portal_groups_rwlock );
+ iscsi_hashmap_key_destroy( (uint8_t *) hash_key );
+ free( host );
+ pthread_rwlock_unlock( &iscsi_globvec_rwlock );
+
+ return;
+ }
+
+ rc = iscsi_portal_group_add_portal( portal_group, portal );
+
+ if ( rc < 0 ) {
+ pthread_rwlock_unlock( &iscsi_globvec->portal_groups_rwlock );
+ iscsi_portal_destroy( portal );
+ iscsi_hashmap_key_destroy( (uint8_t *) hash_key );
+ free( host );
+ pthread_rwlock_unlock( &iscsi_globvec_rwlock );
+
+ return;
+ }
+ }
+
+ iscsi_hashmap_key_destroy( (uint8_t *) hash_key );
+ free( host );
+
+ iscsi_connection *conn = iscsi_connection_create( portal, client->sock );
+
+ if ( conn == NULL ) {
+ logadd( LOG_ERROR, "iscsi_connection_handle: Out of memory while allocating iSCSI connection" );
+
+ iscsi_portal_group_del_portal( portal_group, portal );
+ iscsi_portal_destroy( portal );
+ pthread_rwlock_unlock( &iscsi_globvec_rwlock );
+
+ return;
+ }
+
+ pthread_rwlock_unlock( &iscsi_globvec->portal_groups_rwlock );
+
+ conn->pdu_processing = iscsi_connection_pdu_create( conn, 0U, 0, 0UL, 0 );
+
+ if ( conn->pdu_processing == NULL ) {
+ iscsi_connection_destroy( conn );
+ pthread_rwlock_wrlock( &iscsi_globvec->portal_groups_rwlock );
+ iscsi_portal_group_del_portal( portal_group, portal );
+ pthread_rwlock_unlock( &iscsi_globvec->portal_groups_rwlock );
+ iscsi_portal_destroy( portal );
+ pthread_rwlock_unlock( &iscsi_globvec_rwlock );
+
+ return;
+ }
+
+ memcpy( conn->pdu_processing->bhs_pkt, request, len );
+
+ conn->pdu_processing->bhs_pos = len;
+ conn->pdu_recv_state = ISCSI_CONNECT_PDU_RECV_STATE_WAIT_PDU_HDR;
+
+ logadd( LOG_INFO, "" );
+ logadd( LOG_INFO, "iSCSI connection opened for device %s from initiator %s using port %s and portal %s:%s", (conn->device != NULL ? (char *) conn->device->name : "(null)"), (char *) conn->init_name, ((conn->init_port != NULL) ? (char *) conn->init_port->name : "(null)"), (char *) portal->host, (char *) portal->port );
+
+ while ( iscsi_connection_pdu_handle( conn ) >= ISCSI_CONNECT_PDU_READ_OK ) {
+ }
+
+ iscsi_hashmap_bucket *stat_bucket;
+
+ logadd( LOG_INFO, "iSCSI connection closed for device %s from initiator %s using port %s and portal %s:%s", (conn->device != NULL ? (char *) conn->device->name : "(null)"), (char *) conn->init_name, ((conn->init_port != NULL) ? (char *) conn->init_port->name : "(null)"), (char *) portal->host, (char *) portal->port );
+
+ iscsi_list_foreach_node ( &conn->stat_iscsi_opcodes->list, stat_bucket ) {
+ uint64_t *stat_opcode = (uint64_t *) stat_bucket->value;
+
+ logadd( LOG_INFO, "iSCSI opcode usage statistics for device %s from initiator %s using port %s and portal %s:%s: Opcode 0x%02" PRIX64 " has been received %" PRIu64 " times until connection drop.", (conn->device != NULL ? (char *) conn->device->name : "(null)"), (char *) conn->init_name, ((conn->init_port != NULL) ? (char *) conn->init_port->name : "(null)"), (char *) portal->host, (char *) portal->port, *(uint64_t *) stat_bucket->key, *stat_opcode );
+ }
+
+ iscsi_list_foreach_node ( &conn->stat_scsi_opcodes->list, stat_bucket ) {
+ uint64_t *stat_opcode = (uint64_t *) stat_bucket->value;
+
+ logadd( LOG_INFO, "iSCSI SCSI CDB opcode usage statistics for device %s from initiator %s using port %s and portal %s:%s: SCSI CDB opcode 0x%02" PRIX64 " has been received %" PRIu64 " times until connection drop.", (conn->device != NULL ? (char *) conn->device->name : "(null)"), (char *) conn->init_name, ((conn->init_port != NULL) ? (char *) conn->init_port->name : "(null)"), (char *) portal->host, (char *) portal->port, *(uint64_t *) stat_bucket->key, *stat_opcode );
+ }
+
+ iscsi_session *session = conn->session;
+
+ if ( session != NULL ) {
+ pthread_rwlock_wrlock( &iscsi_globvec->sessions_rwlock );
+ iscsi_list_remove( &conn->node );
+
+ if ( --session->conns == 0UL ) {
+ const uint64_t tsih = session->tsih;
+
+ iscsi_hashmap_remove( iscsi_globvec->sessions, (uint8_t *) &tsih, sizeof(tsih) );
+ iscsi_session_destroy( session );
+ }
+
+ pthread_rwlock_unlock( &iscsi_globvec->sessions_rwlock );
+ }
+
+ iscsi_connection_destroy( conn );
+
+ pthread_rwlock_wrlock( &iscsi_globvec->portal_groups_rwlock );
+ iscsi_portal_group_del_portal( portal_group, portal );
+ iscsi_portal_destroy( portal );
+ pthread_rwlock_unlock( &iscsi_globvec->portal_groups_rwlock );
+
+ pthread_rwlock_unlock( &iscsi_globvec_rwlock );
+}
diff --git a/src/server/iscsi.h b/src/server/iscsi.h
index 5990d69..193b292 100644
--- a/src/server/iscsi.h
+++ b/src/server/iscsi.h
@@ -32,7 +32,40 @@
#ifndef DNBD3_ISCSI_H_
#define DNBD3_ISCSI_H_
-#include <inttypes.h>
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <limits.h>
+#include <stdbool.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <sys/types.h>
+#include <dnbd3/types.h>
+#include <pthread.h>
+
+#include "globals.h"
+#include "image.h"
+
+#if defined(__GNUC__) && (defined(__x86_64__) || defined(__i386__))
+ // GCC-compatible compiler, targeting x86/x86-64
+ #include <x86intrin.h>
+#elif defined(__GNUC__) && defined(__ARM_NEON__)
+ // GCC-compatible compiler, targeting ARM with NEON
+ #include <arm_neon.h>
+#elif defined(__GNUC__) && defined(__IWMMXT__)
+ // GCC-compatible compiler, targeting ARM with WMMX
+ #include <mmintrin.h>
+#elif (defined(__GNUC__) || defined(__xlC__)) && (defined(__VEC__) || defined(__ALTIVEC__))
+ // XLC or GCC-compatible compiler, targeting PowerPC with VMX/VSX
+ #include <altivec.h>
+#elif defined(__GNUC__) && defined(__SPE__)
+ // GCC-compatible compiler, targeting PowerPC with SPE
+ #include <spe.h>
+#elif defined(_MSC_VER)
+ // Microsoft C/C++-compatible compiler
+ #include <intrin.h>
+#endif
#if defined(__BIG_ENDIAN__) || (defined(__BYTE_ORDER) && defined(__BIG_ENDIAN) && __BYTE_ORDER == __BIG_ENDIAN) || (defined(__BYTE_ORDER__) && defined(__ORDER_BIG_ENDIAN__) && __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__)
#define iscsi_get_be16(x) (x)
@@ -40,25 +73,74 @@
#define iscsi_get_be32(x) (x)
#define iscsi_get_be64(x) (x)
-static inline void iscsi_put_be16(uint8_t *data, const uint16_t val)
+static inline void iscsi_put_be16(uint8_t *data, const uint16_t value)
{
- (*(uint16_t *) data) = val;
+ (*(uint16_t *) data) = value;
}
-static inline void iscsi_put_be24(uint8_t *data, const uint32_t val)
+static inline void iscsi_put_be24(uint8_t *data, const uint32_t value)
{
- (*(uint16_t *) data) = (uint16_t) (val >> 8U);
- data[2] = (uint8_t) val;
+ data--;
+
+ (*(uint32_t *) data) = (((uint32_t ) *data << 24UL) | (value & 0xFFFFFFUL));
}
-static inline void iscsi_put_be32(uint8_t *data, const uint32_t val)
+static inline void iscsi_put_be32(uint8_t *data, const uint32_t value)
{
- (*(uint32_t *) *data) = val;
+ (*(uint32_t *) data) = value;
}
-static inline void iscsi_put_be64(uint8_t *data, const uint64_t val)
+static inline void iscsi_put_be64(uint8_t *data, const uint64_t value)
{
- (*(uint64_t *) data) = val;
+ (*(uint64_t *) data) = value;
+}
+
+#if defined(__clang__) || defined(__GNUC__) || defined(__GNUG__)
+// GCC or CLang
+#define iscsi_get_le16(x) (__builtin_bswap16(x))
+#define iscsi_get_le24(x) (iscsi_get_le32((*(uint32_t *) ((uint8_t *) x - 1))) & 0xFFFFFFUL)
+#define iscsi_get_le32(x) (__builtin_bswap32(x))
+#define iscsi_get_le64(x) (__builtin_bswap64(x))
+#elif defined(_MSC_VER)
+// MSVC
+#define iscsi_get_le16(x) (_byteswap_ushort(x))
+#define iscsi_get_le24(x) (iscsi_get_le32((*(uint32_t *) ((uint8_t *) x - 1))) & 0xFFFFFFUL)
+#define iscsi_get_le32(x) (_byteswap_ulong(x))
+#define iscsi_get_le64(x) (_byteswap_uint64(x))
+#elif defined(__INTEL_COMPILER) || defined(__ECC)
+// Intel Compiler
+#define iscsi_get_le16(x) (_bswap16(x))
+#define iscsi_get_le24(x) (iscsi_get_le32((*(uint32_t *) ((uint8_t *) x - 1))) & 0xFFFFFFUL)
+#define iscsi_get_le32(x) (_bswap(x))
+#define iscsi_get_le64(x) (_bswap64(x))
+#else
+// Other compilers (use slow conversion method with bit rotation, bit shift and logcal AND)
+#define iscsi_get_le16(x) ((((uint16_t) (x)) << 8U) | (((uint16_t) (x)) >> 8U))
+#define iscsi_get_le24(x) (iscsi_get_le32((*(uint32_t *) ((uint8_t *) x - 1))) & 0xFFFFFFUL)
+#define iscsi_get_le32(x) ((((uint32_t) (x) & 0xFFUL) << 24UL) | (((uint32_t) (x) & 0xFF00UL) << 8UL) | (((uint32_t) (x) & 0xFF0000UL) >> 8UL) | (((uint32_t) (x) >> 24UL)))
+#define iscsi_get_le64(x) ((uint64_t)((((x) & 0xFFULL) << 56ULL) | (((x) & 0xFF00ULL) << 40ULL) | (((x) & 0xFF0000ull) << 24ULL) | (((x) & 0xFF000000ULL) << 8ULL) | (((x) & 0xFF00000000ULL) >> 8ULL) | (((x) & 0xFF0000000000ULL) >> 24ULL) | (((x) & 0xFF000000000000ULL) >> 40ULL) | (((x) & 0xFF00000000000000ULL) >> 56ULL)))
+#endif
+
+static inline void iscsi_put_le16(uint8_t *data, const uint16_t value)
+{
+ (*(uint16_t *) data) = iscsi_get_le16(value);
+}
+
+static inline void iscsi_put_le24(uint8_t *data, const uint32_t value)
+{
+ data--;
+
+ (*(uint32_t *) data) = ((uint32_t ) *data | (iscsi_get_le32(value) & 0xFFFFFF00UL));
+}
+
+static inline void iscsi_put_le32(uint8_t *data, const uint32_t value)
+{
+ (*(uint32_t *) data) = iscsi_get_le32(value);
+}
+
+static inline void iscsi_put_le64(uint8_t *data, const uint64_t value)
+{
+ (*(uint64_t *) data) = iscsi_get_le64(value);
}
#elif defined(__LITTLE_ENDIAN__) || (defined(__BYTE_ORDER) && defined(__LITTLE_ENDIAN) && __BYTE_ORDER == __LITTLE_ENDIAN) || (defined(__BYTE_ORDER__) && defined(__ORDER_LITTLE_ENDIAN__) && __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__) || defined(__i386__) || defined(__i386) || defined(__x86_64)
#if defined(__clang__) || defined(__GNUC__) || defined(__GNUG__)
@@ -68,12 +150,17 @@ static inline void iscsi_put_be64(uint8_t *data, const uint64_t val)
#define iscsi_get_be32(x) (__builtin_bswap32(x))
#define iscsi_get_be64(x) (__builtin_bswap64(x))
#elif defined(_MSC_VER)
-#include <intrin.h>
-// MVSC
+// MSVC
#define iscsi_get_be16(x) (_byteswap_ushort(x))
#define iscsi_get_be24(x) (iscsi_get_be32((*(uint32_t *) ((uint8_t *) x - 1))) & 0xFFFFFFUL)
#define iscsi_get_be32(x) (_byteswap_ulong(x))
#define iscsi_get_be64(x) (_byteswap_uint64(x))
+#elif defined(__INTEL_COMPILER) || defined(__ECC)
+// Intel Compiler
+#define iscsi_get_be16(x) (_bswap16(x))
+#define iscsi_get_be24(x) (iscsi_get_be32((*(uint32_t *) ((uint8_t *) x - 1))) & 0xFFFFFFUL)
+#define iscsi_get_be32(x) (_bswap(x))
+#define iscsi_get_be64(x) (_bswap64(x))
#else
// Other compilers (use slow conversion method with bit rotation, bit shift and logcal AND)
#define iscsi_get_be16(x) ((((uint16_t) (x)) << 8U) | (((uint16_t) (x)) >> 8U))
@@ -81,49 +168,211 @@ static inline void iscsi_put_be64(uint8_t *data, const uint64_t val)
#define iscsi_get_be32(x) ((((uint32_t) (x) & 0xFFUL) << 24UL) | (((uint32_t) (x) & 0xFF00UL) << 8UL) | (((uint32_t) (x) & 0xFF0000UL) >> 8UL) | (((uint32_t) (x) >> 24UL)))
#define iscsi_get_be64(x) ((uint64_t)((((x) & 0xFFULL) << 56ULL) | (((x) & 0xFF00ULL) << 40ULL) | (((x) & 0xFF0000ull) << 24ULL) | (((x) & 0xFF000000ULL) << 8ULL) | (((x) & 0xFF00000000ULL) >> 8ULL) | (((x) & 0xFF0000000000ULL) >> 24ULL) | (((x) & 0xFF000000000000ULL) >> 40ULL) | (((x) & 0xFF00000000000000ULL) >> 56ULL)))
#endif
-static inline void iscsi_put_be16(uint8_t *data, const uint16_t val)
+
+static inline void iscsi_put_be16(uint8_t *data, const uint16_t value)
+{
+ (*(uint16_t *) data) = iscsi_get_be16(value);
+}
+
+static inline void iscsi_put_be24(uint8_t *data, const uint32_t value)
+{
+ data--;
+
+ (*(uint32_t *) data) = ((uint32_t ) *data | (iscsi_get_be32(value) & 0xFFFFFF00UL));
+}
+
+static inline void iscsi_put_be32(uint8_t *data, const uint32_t value)
+{
+ (*(uint32_t *) data) = iscsi_get_be32(value);
+}
+
+static inline void iscsi_put_be64(uint8_t *data, const uint64_t value)
{
- data[0] = (uint8_t) (val >> 8U);
- data[1] = (uint8_t) val;
+ (*(uint64_t *) data) = iscsi_get_be64(value);
}
-static inline void iscsi_put_be24(uint8_t *data, const uint32_t val)
+#define iscsi_get_le16(x) (x)
+#define iscsi_get_le24(x) (iscsi_get_le32((*(uint32_t *) ((uint8_t *) x - 1))) & 0xFFFFFFUL)
+#define iscsi_get_le32(x) (x)
+#define iscsi_get_le64(x) (x)
+
+static inline void iscsi_put_le16(uint8_t *data, const uint16_t value)
{
- data[0] = (uint8_t) (val >> 16UL);
- data[1] = (uint8_t) (val >> 8UL);
- data[2] = (uint8_t) val;
+ (*(uint16_t *) data) = value;
+}
+
+static inline void iscsi_put_le24(uint8_t *data, const uint32_t value)
+{
+ data--;
+
+ (*(uint32_t *) data) = (((uint32_t ) *data << 24UL) | (value & 0xFFFFFFUL));
}
-static inline void iscsi_put_be32(uint8_t *data, const uint32_t val)
+static inline void iscsi_put_le32(uint8_t *data, const uint32_t value)
{
- data[0] = (uint8_t) (val >> 24UL);
- data[1] = (uint8_t) (val >> 16UL);
- data[2] = (uint8_t) (val >> 8UL);
- data[3] = (uint8_t) val;
+ (*(uint32_t *) data) = value;
}
-static inline void iscsi_put_be64(uint8_t *data, const uint64_t val)
+static inline void iscsi_put_le64(uint8_t *data, const uint64_t value)
{
- data[0] = (uint8_t) (val >> 56ULL);
- data[1] = (uint8_t) (val >> 48ULL);
- data[2] = (uint8_t) (val >> 40ULL);
- data[3] = (uint8_t) (val >> 32ULL);
- data[4] = (uint8_t) (val >> 24ULL);
- data[5] = (uint8_t) (val >> 16ULL);
- data[6] = (uint8_t) (val >> 8ULL);
- data[7] = (uint8_t) val;
+ (*(uint64_t *) data) = value;
}
#else
#error "Unknown CPU endianness"
#endif
+
+/**
+ * @brief Checks whether a specified 32-bit integer value is a power of two.
+ *
+ * This function is used to determine
+ * if shift operations can be used for
+ * calculating instead of very slow
+ * multiplication, division and modulo
+ * operations.
+ *
+ * @param[in] value Value to check for a power of two.
+ * @retval true Value is a power of two.
+ * @retval false Value is NOT a power of two,
+ * hence slow division is required.
+ */
+static inline bool iscsi_is_pow2(const uint32_t value)
+{
+ return ((value & (value - 1UL)) == 0UL);
+}
+
+/**
+ * @brief Rounds up a positive 32-bit integer value to the nearest power of two.
+ *
+ * This function is used to ensure that
+ * a value is always a power of two by
+ * rounding up.\n
+ * An input value of zero is NOT
+ * handled correctly.
+ *
+ * @param[in] value Positive value to round up to
+ * the nearest power of two.
+ * @return Rounded up nearest power of two.
+ */
+static inline uint32_t iscsi_align_pow2_ceil(const uint32_t value)
+{
+ uint32_t num_value = (value - 1UL); // 1UL << (lg(value - 1UL) + 1UL)
+
+ num_value |= (num_value >> 1UL);
+ num_value |= (num_value >> 2UL);
+ num_value |= (num_value >> 4UL);
+ num_value |= (num_value >> 8UL);
+ num_value |= (num_value >> 16UL);
+
+ return ++num_value;
+}
+
+/**
+ * @brief Calculates the shift factor for a power of two value.
+ *
+ * This function is used to determine
+ * the shift factor to use instead of
+ * using very slow multiplication,
+ * division and modulo operations.
+ *
+ * @param[in] value Value to retrieve the
+ * the shift factor for. May NOT be
+ * zero in which case the result is
+ * undefined.
+ * @return The shift count to use as a
+ * replacement for multiplication
+ * and division.
+ */
+static inline uint32_t iscsi_get_log2_of_pow2(const uint32_t value)
+{
+#if defined(__clang__) || defined(__GNUC__) || defined(__GNUG__) || defined(__INTEL_COMPILER) || defined(__ECC)
+// GCC, CLang or Intel Compiler
+ return (((sizeof(uint32_t) * CHAR_BIT) - 1UL) - (uint32_t) __builtin_clz( value ));
+#elif defined(_MSC_VER)
+// MSVC
+ uint32_t shift;
+
+ _BitScanReverse( &shift, value );
+
+ return (((sizeof(uint32_t) * CHAR_BIT) - 1UL) - shift);
+#else
+// Other compilers (use slow parallel calculation method with logical OR, bit shift, logcal AND)
+ uint32_t shift = ((value & 0xAAAAAAAAUL) != 0UL);
+
+ shift |= ((value & 0xCCCCCCCCUL) != 0UL) << 1UL;
+ shift |= ((value & 0xF0F0F0F0UL) != 0UL) << 2UL;
+ shift |= ((value & 0xFF00FF00UL) != 0UL) << 3UL;
+ shift |= ((value & 0xFFFF0000UL) != 0UL) << 4UL;
+
+ return shift;
+#endif
+}
+
+
+/// Determines the container of member b in struct a of type x.
+#define ISCSI_CONTAINER(x, a, b) ((x *) (((uint8_t *) (a)) - offsetof(x, b)))
+
+/// Determines the next offset after member b of struct a.
+#define ISCSI_NEXT_OFFSET(a, b) (offsetof(struct a, b) + sizeof(((struct a *) 0)->b))
+
+
+/// Bit sequence manipulation double word (32 bits) mask bits: Gets mask for filtering out a bit range between a and b, b may NOT exceed 30 bits range.
+#define ISCSI_BITS_GET_MASK(a, b) (((1U << (a)) - 1U) ^ ((1U << ((b) + 1U)) - 1U))
+
+/// Bit sequence manipulation double word (32 bits) test bits: Tests value x in of a bit range between a and b, b may NOT exceed 30 bits range.
+#define ISCSI_BITS_TST(x, a, b) ((x) & ISCSI_BITS_GET_MASK(a, b))
+
+/// Bit sequence manipulation double word (32 bits) clear bits: Clears all bits in range between a and b out of x, b may NOT exceed 30 bits range.
+#define ISCSI_BITS_CLR(x, a, b) ((x) & ~ISCSI_BITS_GET_MASK(a, b))
+
+/// Bit sequence manipulation double word (32 bits) set bits: Sets all bits in range between a and b of x, b may NOT exceed 30 bits range.
+#define ISCSI_BITS_SET(x, a, b) ((x) | ISCSI_BITS_GET_MASK(a, b))
+
+/// Bit sequence manipulation double word (32 bits) change bits: Flips all bits in range between a and b of x, b may NOT exceed 30 bits range.
+#define ISCSI_BITS_CHG(x, a, b) ((x) ^ ISCSI_BITS_GET_MASK(a, b))
+
+/// Bit sequence manipulation double word (32 bits) get bits: Extracts a value x out of a bit range between a and b, b may NOT exceed 30 bits range.
+#define ISCSI_BITS_GET(x, a, b) (ISCSI_BITS_TST(x, a, b) >> (a))
+
+/// Bit sequence manipulation double word (32 bits) get bits: Puts a value x into a bit range between a and b, b may NOT exceed 30 bits range.
+#define ISCSI_BITS_PUT(x, a, b) (((x) << (a)) & ISCSI_BITS_GET_MASK(a, b))
+
+
+/// Bit sequence manipulation quad word (64 bits) mask bits: Gets mask for filtering out a bit range between a and b, b may NOT exceed 62 bits range.
+#define ISCSI_QBITS_GET_MASK(a, b) (((1ULL << (a)) - 1ULL) ^ ((1ULL << ((b) + 1ULL)) - 1ULL))
+
+/// Bit sequence manipulation quad word (64 bits) test bits: Tests value x in of a bit range between a and b, b may NOT exceed 62 bits range.
+#define ISCSI_QBITS_TST(x, a, b) ((x) & ISCSI_QBITS_GET_MASK(a, b))
+
+/// Bit sequence manipulation quad word (64 bits) clear bits: Clears bits in range between a and b out of x, b may NOT exceed 62 bits range.
+#define ISCSI_QBITS_CLR(x, a, b) ((x) & ~ISCSI_QBITS_GET_MASK(a, b))
+
+/// Bit sequence manipulation quad word (64 bits) set bits: Sets all bits in range between a and b of x, b may NOT exceed 62 bits range.
+#define ISCSI_QBITS_SET(x, a, b) ((x) | ISCSI_QBITS_GET_MASK(a, b))
+
+/// Bit sequence manipulation quad word (64 bits) change bits: Flips all bits in range between a and b of x, b may NOT exceed 62 bits range.
+#define ISCSI_QBITS_CHG(x, a, b) ((x) ^ ISCSI_QBITS_GET_MASK(a, b))
+
+/// Bit sequence manipulation quad word (64 bits) get bits: Extracts a value x out of a bit range between a and b, b may NOT exceed 62 bits range.
+#define ISCSI_QBITS_GET(x, a, b) (ISCSI_QBITS_TST(x, a, b) >> (a))
+
+/// Bit sequence manipulation quad word (64 bits) get bits: Puts a value x into a bit range between a and b, b may NOT exceed 62 bits range.
+#define ISCSI_QBITS_PUT(x, a, b) (((x) << (a)) & ISCSI_QBITS_GET_MASK(a, b))
+
+
/// Aligns value x by rounding up, so it's evenly divisable by n.
-#define iscsi_align(x, n) (((x) + (n) - 1) & ~((n) - 1))
+#define ISCSI_ALIGN(x, n) (((x) + (n) - 1) & ~((n) - 1))
+
+
+/// Determines the length of a zero terminated string at compile time.
+#define ISCSI_STRLEN(x) ((sizeof(x) / sizeof(uint8_t)) - sizeof(uint8_t))
+
uint8_t *iscsi_vsprintf_append_realloc(char *buf, const char *format, va_list args); // Allocates and appends a buffer and sprintf's it
uint8_t *iscsi_sprintf_append_realloc(char *buf, const char *format, ...); // Allocates and appends a buffer and sprintf's it
uint8_t *iscsi_vsprintf_alloc(const char *format, va_list args); // Allocates a buffer and sprintf's it
uint8_t *iscsi_sprintf_alloc(const char *format, ... ); // Allocates a buffer and sprintf's it
+void iscsi_strcpy_pad(char *dst, const char *src, const size_t size, const int pad); // Copies a string with additional padding character to fill in a specified size
/// Shift factor for default capacity.
@@ -138,15 +387,9 @@ uint8_t *iscsi_sprintf_alloc(const char *format, ... ); // Allocates a buffer an
/// Key data shift value for alignment enforcement.
#define ISCSI_HASHMAP_KEY_ALIGN_SHIFT 3UL
-/// Key data size must be multiple of 8 bytes by now.
+/// Key data size MUST be multiple of 8 bytes by now.
#define ISCSI_HASHMAP_KEY_ALIGN (1UL << (ISCSI_HASHMAP_KEY_ALIGN_SHIFT))
-/// Value data shift value for alignment enforcement.
-#define ISCSI_HASHMAP_VALUE_ALIGN_SHIFT 4UL
-
-/// Value data size is a multiple of 16 bytes for key and value pairs, which allows us to change their integer values without reallocation of memory.
-#define ISCSI_HASHMAP_VALUE_ALIGN (1UL << (ISCSI_HASHMAP_VALUE_ALIGN_SHIFT))
-
/// Initial hash code.
#define ISCSI_HASHMAP_HASH_INITIAL 0x811C9DC5UL
@@ -155,6 +398,303 @@ uint8_t *iscsi_sprintf_alloc(const char *format, ... ); // Allocates a buffer an
#define ISCSI_HASHMAP_HASH_MUL 0xBF58476D1CE4E5B9ULL
+typedef struct iscsi_node iscsi_node;
+
+
+/**
+ * @brief Doubly linked list node structure.
+ *
+ * This structure is used by the iSCSI doubly linked list
+ * implementation in order to maintain the elements.
+ */
+typedef struct iscsi_node {
+ /// Successor node in node list. Must be first element.
+ iscsi_node *succ;
+
+ /// Predecessor node in node list. Must be second element.
+ iscsi_node *pred;
+} iscsi_node;
+
+
+/**
+ * @brief Doubly linked list structure.
+ *
+ * This structure is used by the iSCSI doubly linked list
+ * implementation in order to maintain the elements.
+ */
+typedef struct iscsi_list {
+ /// Head of linked list. Must be first element.
+ iscsi_node *head;
+
+ /// Tail of linked list. Must be second element and always be NULL.
+ iscsi_node *tail;
+
+ /// Tail predecessor of linked list. Must be third element.
+ iscsi_node *pred;
+} iscsi_list;
+
+
+/// foreach( ( list => entry ) usage style forward iterator over all nodes in a doubly linked list.
+#define iscsi_list_foreach(list, entry) for ( (entry) = (list)->head; (entry)->succ != NULL; (entry) = (entry)->succ )
+
+/// foreach( ( list => (typeof(entry)) as field ) usage style forward iterator over all nodes in a doubly linked list embedded into another structure with default node field name.
+#define iscsi_list_foreach_field(list, entry, field) for ( (entry) = (__typeof__(entry)) (list)->head; (__typeof__(entry)) (entry)->field.succ != NULL; (entry) = (__typeof__(entry)) (entry)->field.succ )
+
+/// foreach( ( list => (typeof(entry)) entry->node.succ ) usage style forward iterator over all nodes in a doubly linked list embedded into another structure with default node field name.
+#define iscsi_list_foreach_node(list, entry) iscsi_list_foreach_field(list, entry, node)
+
+/// foreach( ( list => entry ) usage style forward iterator over all nodes in a doubly linked list.
+#define iscsi_list_foreach_safe(list, entry, tmp) for ( (entry) = (list)->head; ((entry)->succ != NULL) && ((tmp) = (entry)->succ, true); (entry) = (tmp) )
+
+/// foreach( ( list => (typeof(entry)) as field ) usage style forward iterator over all nodes in a doubly linked list embedded into another structure with default node field name.
+#define iscsi_list_foreach_safe_field(list, entry, field, tmp) for ( (entry) = (__typeof__(entry)) (list)->head; ((entry)->field.succ != NULL) && ((tmp) = (__typeof__(entry)) (entry)->field.succ, true); (entry) = (tmp) )
+
+/// foreach( ( list => (typeof(entry)) entry->node.succ ) usage style forward iterator over all nodes in a doubly linked list embedded into another structure with default node field name.
+#define iscsi_list_foreach_safe_node(list, entry, tmp) iscsi_list_foreach_safe_field(list, entry, node, tmp)
+
+
+/**
+ * @brief Initializes a doubly linked list for usage.
+ *
+ * This function sets the head of the list to
+ * the pointer of the list's tail, the tail
+ * itself to NULL and the predecessor to the
+ * pointer of the list's head.
+ *
+ * @param[in] list Pointer to idoubly linked list to
+ * initialize. May NOT be NULL, so be careful.
+ * */
+static inline void iscsi_list_create(iscsi_list *list)
+{
+ list->head = (iscsi_node *) &list->tail;
+ list->tail = NULL;
+ list->pred = (iscsi_node *) &list->head;
+}
+
+/**
+ * @brief Clears an already initialized doubly linked list.
+ *
+ * This function sets the head of the list to
+ * the pointer of the list's tail and the
+ * predecessor to the pointer of the list's
+ * head.
+ *
+ * @param[in] list Pointer to idoubly linked list to
+ * initialize. May NOT be NULL, so be careful.
+ * */
+static inline void iscsi_list_clear(iscsi_list *list)
+{
+ list->head = (iscsi_node *) &list->tail;
+ list->pred = (iscsi_node *) &list->head;
+}
+
+/**
+ * @brief Adds a node at the head of a doubly linked list.
+ *
+ * This function sets the head of the list to
+ * the node and adjusts the list and node
+ * pointers accordingly.
+ *
+ * @param[in] list Pointer to doubly linked list to add to
+ * the head. May NOT be NULL, so be careful.
+ * @param[in] node Pointer to node to add to the head of
+ * the list. NULL is NOT allowed here, take
+ * caution.
+ */
+static inline void iscsi_list_push(iscsi_list *list, iscsi_node *node)
+{
+ iscsi_node *head = list->head;
+
+ list->head = node;
+ head->pred = node;
+
+ node->succ = head;
+ node->pred = (iscsi_node *) &list->head;
+}
+
+/**
+ * @brief Adds a node at the tail of a doubly linked list.
+ *
+ * This function sets the tail of the list to
+ * the node and adjusts the list and node
+ * pointers accordingly.
+ *
+ * @param[in] list Pointer to doubly linked list to add to
+ * the tail. May NOT be NULL, so be careful.
+ * @param[in] node Pointer to node to add to the tail of
+ * the list. NULL is NOT allowed here, take
+ * caution.
+ */
+static inline void iscsi_list_enqueue(iscsi_list *list, iscsi_node *node)
+{
+ iscsi_node *tail = list->pred;
+
+ list->pred = node;
+ tail->succ = node;
+
+ node->succ = (iscsi_node *) &list->tail;
+ node->pred = tail;
+}
+
+/**
+ * @brief Inserts a node into a doubly linked list before an already existing node.
+ *
+ * This function sets the successor of the
+ * new node to the successor of the
+ * existing predecessor node and the
+ * predecessor of the new node to the
+ * the existing predecessor node itself
+ * and adjusts the list pointers
+ * accordingly.
+ *
+ * @param[in] list Pointer to doubly linked list to insert the
+ * node into. May NOT be NULL, so be careful.
+ * @param[in] node Pointer to node to be inserted into the
+ * list. NULL is NOT allowed here, take
+ * caution.
+ * @param[in] pred Pointer to node which should be the
+ * previous node of the new inserted node.
+ * May be NULL in which case the new node
+ * is inserted at the head of the list.
+ */
+static inline void iscsi_list_insert(iscsi_list *list, iscsi_node *node, iscsi_node *pred)
+{
+ if ( pred == NULL ) {
+ iscsi_node *head = list->head;
+
+ list->head = node;
+ head->pred = node;
+
+ node->succ = head;
+ node->pred = (iscsi_node *) &list->head;
+
+ return;
+ }
+
+ iscsi_node *tail = pred->succ;
+
+ if ( tail == NULL ) {
+ tail = pred->pred;
+
+ node->succ = pred;
+ node->pred = tail;
+
+ pred->pred = node;
+ tail->succ = node;
+
+ return;
+ }
+
+ node->succ = tail;
+ node->pred = pred;
+
+ tail->pred = node;
+ pred->succ = node;
+}
+
+/**
+ * @brief Removes the node from the head of a doubly linked list.
+ *
+ * This function sets the head of the list to
+ * its successor and adjusts the list and
+ * node pointers accordingly.
+ *
+ * @param[in] list Pointer to doubly linked list to remove the
+ * head from. May NOT be NULL, so be careful.
+ */
+static inline void iscsi_list_pop(iscsi_list *list)
+{
+ iscsi_node *head = list->head;
+ iscsi_node *node = head->succ;
+
+ if ( node == NULL )
+ return;
+
+ list->head = node;
+
+ node->pred = (iscsi_node *) &list->head;
+}
+
+/**
+ * @brief Removes the node from the tail of a doubly linked list.
+ *
+ * This function sets the tail of the list to
+ * its predecessor and adjusts the list and
+ * node pointers accordingly.
+ *
+ * @param[in] list Pointer to doubly linked list to remove the
+ * tail from. May NOT be NULL, so be careful.
+ */
+static inline void iscsi_list_dequeue(iscsi_list *list)
+{
+ iscsi_node *tail = list->pred;
+ iscsi_node *node = tail->pred;
+
+ if ( node == NULL )
+ return;
+
+ list->pred = node;
+
+ node->succ = (iscsi_node *) &list->tail;
+}
+
+/**
+ * @brief Removes a specified node from a doubly linked list.
+ *
+ * This function sets the successor of the
+ * node's predecessor and the predecessor
+ * of the node's successor by adjusting
+ * the list and node pointers accordingly.
+ *
+ * @param[in] node Pointer to node to be removed from
+ * the list. May NOT be NULL, so
+ * be careful.
+ */
+static inline void iscsi_list_remove(iscsi_node *node)
+{
+ iscsi_node *succ = node->succ;
+ iscsi_node *pred = node->pred;
+
+ pred->succ = succ;
+ succ->pred = pred;
+}
+
+/**
+ * @brief Checks whether a doubly linked list is empty.
+ *
+ * Whenever this function returns false,
+ * iscsi_list_peek will return a pointer
+ * to the first node in the list.
+ *
+ * @param[in] list Pointer to doubly linked list to check if
+ * empty. May NOT be NULL, so be careful.
+ * @retval true The doubly linked list is empty.
+ * @retval false The doubly linked list contains nodes.
+ */
+static inline bool iscsi_list_empty(const iscsi_list *list)
+{
+ return (list->head->succ == NULL);
+}
+
+/**
+ * @brief Gets the node from the head of a doubly linked list.
+ *
+ * This function returns NULL if the list is
+ * empty.
+ *
+ * @param[in] list Pointer to doubly linked list to get the
+ * head from. May NOT be NULL, so be careful.
+ * @return Pointer to doubly linked list node of the
+ * head or NULL if the list is empty.
+ */
+static inline iscsi_node *iscsi_list_peek(const iscsi_list *list)
+{
+ iscsi_node *head = list->head;
+
+ return (head->succ != NULL) ? head : NULL;
+}
+
+
/**
* @brief Hash map bucket containing key, value and hash code.
*
@@ -162,13 +702,13 @@ uint8_t *iscsi_sprintf_alloc(const char *format, ... ); // Allocates a buffer an
* in order to maintain the elements.
*/
typedef struct iscsi_hashmap_bucket {
- /// Next bucket, must be first element.
- struct iscsi_hashmap_bucket *next;
+ /// Next bucket, MUST be first element.
+ iscsi_node node;
- /// Data used as key, must be aligned to 8 bytes and zero padded.
+ /// Data used as key, MUST be aligned to 8 bytes and zero padded.
uint8_t *key;
- /// Size of key, must be a multiple of 8 bytes.
+ /// Size of key.
size_t key_size;
/// Hash code for the key.
@@ -183,34 +723,30 @@ typedef struct iscsi_hashmap_bucket {
*
* This structure is used by the ultra performant hash map
* implementation. It uses a linked list allowing fast
- * insertions. Elements can be removed and are marked for
- * deletion until a resize operation is necessary.
+ * insertions. Elements can be removed.
*/
typedef struct iscsi_hashmap {
/// Linked list containing the hash map buckets.
iscsi_hashmap_bucket *buckets;
+ /// Doubly linked list for fast insertion.
+ iscsi_list list;
+
+ /// Last inserted unique identifier (primary key).
+ uint64_t last_insert_id;
+
/// Current bucket capacity, MUST be a power of two.
uint capacity;
/// Current capacity threshold triggering resize operation.
- uint cap_load; // Capacity load threshold before next resize
+ uint cap_load;
- /// Current count of buckets including ones marked for removal.
+ /// Current count of buckets.
uint count;
-
- /// Number of buckets marked for removal.
- uint removed_count;
-
- /// First linked list bucket for fast insertion.
- iscsi_hashmap_bucket *first;
-
- /// Last linked list bucket for faster traversion.
- iscsi_hashmap_bucket *last;
} iscsi_hashmap;
/**
- * @brief A Callback for iterating over map, freeing and removing entries. user_data is free for personal use.
+ * @brief Callback for iterating over map, freeing and removing entries. user_data is free for personal use.
*
* Callback function. This is a pointer to a
* function for various purposes like iterating
@@ -239,22 +775,25 @@ void iscsi_hashmap_destroy(iscsi_hashmap *map); // Deallocates the hash map obje
// Use iscsi_hashmap_iterate to deallocate the elements themselves
uint8_t *iscsi_hashmap_key_create(const uint8_t *data, const size_t len); // Creates a key suitable for hashmap usage (ensures 8-byte boundary and zero padding)
+void iscsi_hashmap_key_create_id(iscsi_hashmap *map, uint64_t *key); // Creates an unique key identifier suitable for hashmap usage (ensures 8-byte boundary and zero padding)
void iscsi_hashmap_key_destroy(uint8_t *key); // Deallocates all resources acquired by iscsi_hashmap_create_key
-int iscsi_hashmap_key_destroy_value_callback(uint8_t *key, const size_t key_size, uint8_t *value, uint8_t *user_data); // Deallocates all key / value pairs in a hash map by calling free (default destructor)
-
-int iscsi_hashmap_put(iscsi_hashmap *map, uint8_t *key, const size_t key_size, uint8_t *value); // Assigns key / value pair to hash map without making copies
-int iscsi_hashmap_get_put(iscsi_hashmap *map, uint8_t *key, const size_t key_size, uint8_t **out_in_value); // Assigns key / value pair to hash map without making copies
-int iscsi_hashmap_put_free(iscsi_hashmap *map, uint8_t *key, const size_t key_size, uint8_t *value, iscsi_hashmap_callback callback, uint8_t *user_data); // Assigns key / value pair to hash map without making copies
- // with callback function in case the key already exists
-int iscsi_hashmap_contains(iscsi_hashmap *map, const uint8_t *key, const size_t key_size); // Checks whether a specified key exists
+int iscsi_hashmap_key_destroy_callback(uint8_t *key, const size_t key_size, uint8_t *value, uint8_t *user_data); // Deallocates a key in a hash map
+int iscsi_hashmap_destroy_value_callback(uint8_t *key, const size_t key_size, uint8_t *value, uint8_t *user_data); // Deallocates a value in a hash map
+int iscsi_hashmap_key_destroy_value_callback(uint8_t *key, const size_t key_size, uint8_t *value, uint8_t *user_data); // Deallocates a key / value pair in a hash map by calling free (default destructor)
+
+int iscsi_hashmap_put(iscsi_hashmap *map, uint8_t *key, const size_t key_size, uint8_t *value); // Assigns key / value pair to hash map at the tail of doubly linked list without making copies
+int iscsi_hashmap_get_put(iscsi_hashmap *map, uint8_t *key, const size_t key_size, uint8_t **out_in_value); // Assigns key / value pair to hash map at the tail of doubly linked list without making copies
+int iscsi_hashmap_put_free(iscsi_hashmap *map, uint8_t *key, const size_t key_size, uint8_t *value, iscsi_hashmap_callback callback, uint8_t *user_data); // Assigns key / value pair to hash map without making copies with callback function in case the key already exists
+bool iscsi_hashmap_contains(iscsi_hashmap *map, const uint8_t *key, const size_t key_size); // Checks whether a specified key exists
int iscsi_hashmap_get(iscsi_hashmap *map, const uint8_t *key, const size_t key_size, uint8_t **out_value); // Retrieves the value of a specified key
-void iscsi_hashmap_remove(iscsi_hashmap *map, const uint8_t *key, const size_t key_size); // Marks an element for removal by setting key and value both to NULL
-void iscsi_hashmap_remove_free(iscsi_hashmap *map, const uint8_t *key, const size_t key_size, iscsi_hashmap_callback callback, uint8_t *user_data); // Marks an element for removal by setting key and value both to NULL,
- // but invokes a callback function before actual marking for removal.
-uint iscsi_hashmap_size(const iscsi_hashmap *map); // Retrieves the number of elements of the hash map, ignoring elements marked for removal
+void iscsi_hashmap_remove(iscsi_hashmap *map, const uint8_t *key, const size_t key_size); // Removes an element both from the doubly linked list and by setting the key to NULL
+void iscsi_hashmap_remove_free(iscsi_hashmap *map, const uint8_t *key, const size_t key_size, iscsi_hashmap_callback callback, uint8_t *user_data); // Removes an element both from the doubly linked list and by setting the key to NULL and invokes a callback function before actual removal
+
+uint iscsi_hashmap_size(const iscsi_hashmap *map); // Retrieves the number of elements of the hash map
+
+int iscsi_hashmap_iterate(iscsi_hashmap *map, iscsi_hashmap_callback callback, uint8_t *user_data); // Iterator with callback function invoked on each element
-int iscsi_hashmap_iterate(iscsi_hashmap *map, iscsi_hashmap_callback callback, uint8_t *user_data); // Iterator with callback function invoked on each element which has not been removed
/* iSCSI protocol stuff (all WORD/DWORD/QWORD values are big endian by default
unless specified otherwise). */
@@ -278,10 +817,17 @@ int iscsi_hashmap_iterate(iscsi_hashmap *map, iscsi_hashmap_callback callback, u
/// iSCSI Default receive DataSegment (DS) size in bytes.
#define ISCSI_DEFAULT_RECV_DS_LEN 8192UL
-/// iSCSI default maximum DataSegment receive length in bytes
+/// iSCSI default maximum DataSegment receive length in bytes.
#define ISCSI_DEFAULT_MAX_RECV_DS_LEN 65536UL
-/// iSCSI default maximum DataSegment receive length in bytes
+
+/// iSCSI default maximum Ready To Transfer (R2T) active tasks.
+#define ISCSI_DEFAULT_MAX_R2T_PER_CONNECTION 4UL
+
+/// iSCSI default maximum DataSegment receive length in bytes.
+#define ISCSI_DEFAULT_MAX_DATA_IN_PER_CONNECTION 64UL
+
+/// iSCSI default maximum DataSegment send length in bytes.
#define ISCSI_DEFAULT_MAX_DATA_OUT_PER_CONNECTION 16UL
@@ -300,98 +846,101 @@ int iscsi_hashmap_iterate(iscsi_hashmap *map, iscsi_hashmap_callback callback, u
/// iSCSI initiator (client) command opcode: NOP-Out.
-#define ISCSI_CLIENT_NOP_OUT 0x00
+#define ISCSI_OPCODE_CLIENT_NOP_OUT 0x00
/// iSCSI initiator (client) command opcode: SCSI Command (encapsulates a SCSI Command Descriptor Block).
-#define ISCSI_CLIENT_SCSI_CMD 0x01
+#define ISCSI_OPCODE_CLIENT_SCSI_CMD 0x01
/// iSCSI initiator (client) command opcode: SCSI Task Management Function Request.
-#define ISCSI_CLIENT_TASK_FUNC_REQ 0x02
+#define ISCSI_OPCODE_CLIENT_TASK_FUNC_REQ 0x02
/// iSCSI initiator (client) command opcode: Login Request.
-#define ISCSI_CLIENT_LOGIN_REQ 0x03
+#define ISCSI_OPCODE_CLIENT_LOGIN_REQ 0x03
/// iSCSI initiator (client) command opcode: Text Request.
-#define ISCSI_CLIENT_TEXT_REQ 0x04
+#define ISCSI_OPCODE_CLIENT_TEXT_REQ 0x04
/// iSCSI initiator (client) command opcode: SCSI Data-Out (for write operations).
-#define ISCSI_CLIENT_SCSI_DATA_OUT 0x05
+#define ISCSI_OPCODE_CLIENT_SCSI_DATA_OUT 0x05
/// iSCSI initiator (client) command opcode: Logout Request.
-#define ISCSI_CLIENT_LOGOUT_REQ 0x06
+#define ISCSI_OPCODE_CLIENT_LOGOUT_REQ 0x06
/// iSCSI initiator (client) command opcode: Selective Negative / Sequence Number Acknowledgment (SNACK) Request.
-#define ISCSI_CLIENT_SNACK_REQ 0x10
+#define ISCSI_OPCODE_CLIENT_SNACK_REQ 0x10
/// iSCSI initiator (client) command opcode: Vendor-specific code #1.
-#define ISCSI_CLIENT_VENDOR_CODE1 0x1C
+#define ISCSI_OPCODE_CLIENT_VENDOR_CODE1 0x1C
/// iSCSI initiator (client) command opcode: Vendor-specific code #2.
-#define ISCSI_CLIENT_VENDOR_CODE2 0x1D
+#define ISCSI_OPCODE_CLIENT_VENDOR_CODE2 0x1D
/// iSCSI initiator (client) command opcode: Vendor-specific code #3.
-#define ISCSI_CLIENT_VENDOR_CODE3 0x1E
+#define ISCSI_OPCODE_CLIENT_VENDOR_CODE3 0x1E
/// First iSCSI initiator (client) command opcode.
-#define ISCSI_CLIENT_FIRST_OPCODE 0x00
+#define ISCSI_OPCODE_CLIENT_FIRST 0x00
/// Last iSCSI initiator (client) command opcode.
-#define ISCSI_CLIENT_LAST_OPCODE 0x1F
+#define ISCSI_OPCODE_CLIENT_LAST 0x1F
/// iSCSI target (server) command opcode: NOP-In.
-#define ISCSI_SERVER_NOP_IN 0x20
+#define ISCSI_OPCODE_SERVER_NOP_IN 0x20
/// iSCSI target (server) command opcode: SCSI Response - contains SCSI status and possibly sense information or other response information.
-#define ISCSI_SERVER_SCSI_RESPONSE 0x21
+#define ISCSI_OPCODE_SERVER_SCSI_RESPONSE 0x21
/// iSCSI target (server) command opcode: SCSI Task Management Function Response.
-#define ISCSI_SERVER_TASK_FUNC_RES 0x22
+#define ISCSI_OPCODE_SERVER_TASK_FUNC_RES 0x22
/// iSCSI target (server) command opcode: Login Response.
-#define ISCSI_SERVER_LOGIN_RES 0x23
+#define ISCSI_OPCODE_SERVER_LOGIN_RES 0x23
/// iSCSI target (server) command opcode: Text Response.
-#define ISCSI_SERVER_TEXT_RES 0x24
+#define ISCSI_OPCODE_SERVER_TEXT_RES 0x24
/// iSCSI target (server) command opcode: SCSI Data-In (for read operations).
-#define ISCSI_SERVER_SCSI_DATA_IN 0x25
+#define ISCSI_OPCODE_SERVER_SCSI_DATA_IN 0x25
/// iSCSI target (server) command opcode: Logout Response.
-#define ISCSI_SERVER_LOGOUT_RES 0x26
+#define ISCSI_OPCODE_SERVER_LOGOUT_RES 0x26
/// iSCSI target (server) command opcode: Ready To Transfer (R2T) - sent by target when it is ready to receive data.
-#define ISCSI_SERVER_READY_XFER 0x31
+#define ISCSI_OPCODE_SERVER_READY_XFER 0x31
/// iSCSI target (server) command opcode: Asynchronous Message - sent by target to indicate certain special conditions.
-#define ISCSI_SERVER_ASYNC_MSG 0x32
+#define ISCSI_OPCODE_SERVER_ASYNC_MSG 0x32
/// iSCSI target (server) command opcode: Vendor-specific code #1.
-#define ISCSI_SERVER_VENDOR_CODE1 0x3C
+#define ISCSI_OPCODE_SERVER_VENDOR_CODE1 0x3C
/// iSCSI target (server) command opcode: Vendor-specific code #2.
-#define ISCSI_SERVER_VENDOR_CODE2 0x3D
+#define ISCSI_OPCODE_SERVER_VENDOR_CODE2 0x3D
/// iSCSI target (server) command opcode: Vendor-specific code #3.
-#define ISCSI_SERVER_VENDOR_CODE3 0x3E
+#define ISCSI_OPCODE_SERVER_VENDOR_CODE3 0x3E
/// iSCSI target (server) command opcode: Reject.
-#define ISCSI_SERVER_REJECT 0x3F
+#define ISCSI_OPCODE_SERVER_REJECT 0x3F
/// First iSCSI target (server) command opcode.
-#define ISCSI_SERVER_FIRST_OPCODE 0x20
+#define ISCSI_OPCODE_SERVER_FIRST 0x20
/// Last iSCSI target (server) command opcode.
-#define ISCSI_SERVER_LAST_OPCODE 0x3F
+#define ISCSI_OPCODE_SERVER_LAST 0x3F
/// iSCSI opcode bit mask (bits 0-5 used).
#define ISCSI_OPCODE_MASK 0x3F
-/// Macro which extracts iSCSI packet data opcode out of opcode byte
+/// Macro which extracts iSCSI packet data opcode out of opcode byte.
#define ISCSI_GET_OPCODE(x) ((x) & ISCSI_OPCODE_MASK)
+/// iSCSI opcode flags (I) Immediate bit: For Request PDUs, the I bit set to 1 is an immediate delivery marker.
+#define ISCSI_OPCODE_FLAGS_IMMEDIATE (1 << 6L)
+
/**
* @brief iSCSI Basic Header Segment packet data.
@@ -459,16 +1008,6 @@ typedef struct __attribute__((packed)) iscsi_ahs_packet {
uint8_t data[0];
} iscsi_ahs_packet;
-/**
- * @brief iSCSI CDB packet data structure.
- *
- * There are 16 bytes in the CDB field to accommodate the commonly used
- * CDBs. Whenever the CDB is larger than 16 bytes, an Extended CDB AHS
- * MUST be used to contain the CDB spillover.
- */
-typedef struct __attribute__((packed)) iscsi_cdb {
- uint8_t data[16];
-} iscsi_cdb;
/**
* @brief iSCSI Extended CDB AHS packet data structure.
@@ -497,10 +1036,10 @@ typedef struct __attribute__((packed)) iscsi_ext_cdb_ahs_packet {
* expected data transfer length.
*/
typedef struct __attribute__((packed)) iscsi_bidi_read_exp_xfer_ahs_packet {
- /// AHSLength: Always 5 according to ISCSI specifications for now.
+ /// AHSLength: Always 5 according to iSCSI specifications for now.
uint16_t len;
- /// AHSType: Always 2 according to ISCSI specifications for now.
+ /// AHSType: Always 2 according to iSCSI specifications for now.
uint8_t type; // Identifier (always 0x02 according to specs)
/// Reserved for future usage, always MUST be 0.
@@ -629,8 +1168,1027 @@ typedef struct __attribute__((packed)) iscsi_data_digest {
uint32_t crc32c;
} iscsi_data_digest;
+
+/**
+ * @brief iSCSI SCSI CDB packet data structure.
+ *
+ * There are 16 bytes in the CDB field to accommodate the commonly used
+ * CDBs. Whenever the CDB is larger than 16 bytes, an Extended CDB AHS
+ * MUST be used to contain the CDB spillover.
+ */
+typedef struct __attribute__((packed)) iscsi_scsi_cdb {
+ /// SCSI opcode.
+ uint8_t opcode;
+
+ /// Additional op-code specific data.
+ uint8_t data[0];
+} iscsi_scsi_cdb;
+
+
+/// iSCSI SCSI Command Descriptor Block (CDB) for INQUIRY command flags: Enable Vital Product Data (EVPD).
+#define ISCSI_SCSI_CDB_INQUIRY_FLAGS_EVPD (1 << 0)
+
+/// iSCSI SCSI Command Descriptor Block (CDB) for INQUIRY command flags: Command Support Data (CMDDT).
+#define ISCSI_SCSI_CDB_INQUIRY_FLAGS_CMDDT (1 << 1)
+
+
+/**
+ * @brief iSCSI SCSI CDB packet data structure for SCSI INQUIRY command.
+ *
+ * There are 6 bytes in the CDB field for this command.
+ */
+typedef struct __attribute__((packed)) iscsi_scsi_cdb_inquiry {
+ /// SCSI opcode.
+ iscsi_scsi_cdb cdb;
+
+ /// Logical Unit Number (LUN), CMMDT and EVPD.
+ uint8_t lun_flags;
+
+ /// Page code.
+ uint8_t page_code;
+
+ /// Allocation length in bytes.
+ uint16_t alloc_len;
+
+ /// Control.
+ uint8_t control;
+} iscsi_scsi_cdb_inquiry;
+
+
+/**
+ * @brief iSCSI SCSI CDB packet data structure for SCSI READ(6) and WRITE(6) commands.
+ *
+ * There are 6 bytes in the CDB field for this command.
+ */
+typedef struct __attribute__((packed)) iscsi_scsi_cdb_read_write_6 {
+ /// SCSI opcode.
+ iscsi_scsi_cdb cdb;
+
+ /// Logical Block Address (LBA).
+ uint8_t lba[3];
+
+ /// Transfer length in bytes.
+ uint8_t xfer_len;
+
+ /// Control.
+ uint8_t control;
+} iscsi_scsi_cdb_read_write_6;
+
+
+/**
+ * @brief iSCSI SCSI CDB packet data structure for SCSI READ(10) and WRITE(10) commands.
+ *
+ * There are 10 bytes in the CDB field for this command.
+ */
+typedef struct __attribute__((packed)) iscsi_scsi_cdb_read_write_10 {
+ /// SCSI opcode.
+ iscsi_scsi_cdb cdb;
+
+ /// Flags.
+ int8_t flags;
+
+ /// Logical Block Address (LBA).
+ uint32_t lba;
+
+ /// Group number.
+ int8_t group_num;
+
+ /// Transfer length in bytes.
+ uint16_t xfer_len;
+
+ /// Control.
+ uint8_t control;
+} iscsi_scsi_cdb_read_write_10;
+
+
+/**
+ * @brief iSCSI SCSI CDB packet data structure for SCSI READ(12) and WRITE(12) commands.
+ *
+ * There are 12 bytes in the CDB field for this command.
+ */
+typedef struct __attribute__((packed)) iscsi_scsi_cdb_read_write_12 {
+ /// SCSI opcode.
+ iscsi_scsi_cdb cdb;
+
+ /// Flags.
+ int8_t flags;
+
+ /// Logical Block Address (LBA).
+ uint32_t lba;
+
+ /// Transfer length in bytes.
+ uint32_t xfer_len;
+
+ /// Restricted for MMC-6 and group number.
+ int8_t restrict_group_num;
+
+ /// Control.
+ uint8_t control;
+} iscsi_scsi_cdb_read_write_12;
+
+
+/**
+ * @brief iSCSI SCSI CDB packet data structure for SCSI READ(16) and WRITE(16) commands.
+ *
+ * There are 16 bytes in the CDB field for this command.
+ */
+typedef struct __attribute__((packed)) iscsi_scsi_cdb_read_write_16 {
+ /// SCSI opcode.
+ iscsi_scsi_cdb cdb;
+
+ /// Flags.
+ int8_t flags;
+
+ /// Logical Block Address (LBA).
+ uint64_t lba;
+
+ /// Transfer length in bytes.
+ uint32_t xfer_len;
+
+ /// Restricted for MMC-6 and group number.
+ int8_t restrict_group_num;
+
+ /// Control.
+ uint8_t control;
+} iscsi_scsi_cdb_read_write_16;
+
+
+/// iSCSI SCSI Command Descriptor Block (CDB) for REPORT LUNS command select report: Logical unit with addressing method.
+#define ISCSI_SCSI_CDB_REPORT_LUNS_SELECT_REPORT_LU_ADDR_METHOD 0x00
+
+/// iSCSI SCSI Command Descriptor Block (CDB) for REPORT LUNS command select report: Well known logical unit.
+#define ISCSI_SCSI_CDB_REPORT_LUNS_SELECT_REPORT_LU_KNOWN 0x01
+
+/// iSCSI SCSI Command Descriptor Block (CDB) for REPORT LUNS command select report: Logical unit.
+#define ISCSI_SCSI_CDB_REPORT_LUNS_SELECT_REPORT_LU_ALL 0x02
+
+
+/**
+ * @brief iSCSI SCSI CDB packet data structure for REPORT LUNS command.
+ *
+ * There are 12 bytes in the CDB field for this command.
+ */
+typedef struct __attribute__((packed)) iscsi_scsi_cdb_report_luns {
+ /// SCSI opcode.
+ iscsi_scsi_cdb cdb;
+
+ /// Reserved for future usage (always MUST be 0 for now).
+ uint8_t reserved;
+
+ /// Select report.
+ uint8_t select_report;
+
+ /// Reserved for future usage (always MUST be 0 for now).
+ uint16_t reserved2;
+
+ /// Reserved for future usage (always MUST be 0 for now).
+ uint8_t reserved3;
+
+ /// Allocation length in bytes.
+ uint32_t alloc_len;
+
+ /// Reserved for future usage (always MUST be 0 for now).
+ uint8_t reserved4;
+
+ /// Control.
+ uint8_t control;
+} iscsi_scsi_cdb_report_luns;
+
+
+/// iSCSI SCSI Command Descriptor Block (CDB) for COMPARE AND WRITE command flags: Force Unit Access (FUA).
+#define ISCSI_SCSI_CDB_CMP_WRITE_FLAGS_FUA (1 << 3L)
+
+/// iSCSI SCSI Command Descriptor Block (CDB) for COMPARE AND WRITE command flags: Disable Page Out (DPO).
+#define ISCSI_SCSI_CDB_CMP_WRITE_FLAGS_DPO (1 << 4L)
+
+/// iSCSI SCSI Command Descriptor Block (CDB) for COMPARE AND WRITE command write protect flags: First bit of the three bits.
+#define ISCSI_SCSI_CDB_CMP_WRITE_FLAGS_WRPROTECT_FIRST_BIT 5
+
+/// iSCSI SCSI Command Descriptor Block (CDB) for COMPARE AND WRITE command write protect flags: Last bit of the three bits.
+#define ISCSI_SCSI_CDB_CMP_WRITE_FLAGS_WRPROTECT_LAST_BIT ((ISCSI_SCSI_CDB_CMP_WRITE_FLAGS_WRPROTECT_FIRST_BIT) + 3 - 1)
+
+/// iSCSI SCSI Command Descriptor Block (CDB) for COMPARE AND WRITE command write protect flags: Bit mask.
+#define ISCSI_SCSI_CDB_CMP_WRITE_FLAGS_WRPROTECT_MASK (ISCSI_BITS_GET_MASK(ISCSI_SCSI_CDB_CMP_WRITE_FLAGS_WRPROTECT_FIRST_BIT, ISCSI_SCSI_CDB_CMP_WRITE_FLAGS_WRPROTECT_LAST_BIT))
+
+/// iSCSI SCSI Command Descriptor Block (CDB) for COMPARE AND WRITE command write protect flags: Extracts the write protect bits.
+#define ISCSI_SCSI_CDB_CMP_WRITE_FLAGS_GET_WRPROTECT(x) (ISCSI_BITS_GET((x), ISCSI_SCSI_CDB_CMP_WRITE_FLAGS_WRPROTECT_FIRST_BIT, ISCSI_SCSI_CDB_CMP_WRITE_FLAGS_WRPROTECT_LAST_BIT))
+
+/// iSCSI SCSI Command Descriptor Block (CDB) for COMPARE AND WRITE command write protect flags: Stores into the write protect bits.
+#define ISCSI_SCSI_CDB_CMP_WRITE_FLAGS_PUT_WRPROTECT(x) (ISCSI_BITS_PUT((x), ISCSI_SCSI_CDB_CMP_WRITE_FLAGS_WRPROTECT_FIRST_BIT, ISCSI_SCSI_CDB_CMP_WRITE_FLAGS_WRPROTECT_LAST_BIT))
+
+
+/**
+ * @brief iSCSI SCSI CDB packet data structure for SCSI COMPARE AND WRITE command.
+ *
+ * There are 16 bytes in the CDB field for this command.
+ */
+typedef struct __attribute__((packed)) iscsi_scsi_cdb_cmp_write {
+ /// SCSI opcode.
+ iscsi_scsi_cdb cdb;
+
+ /// Flags.
+ int8_t flags;
+
+ /// Logical Block Address (LBA).
+ uint64_t lba;
+
+ /// Reserved for future usage (always MUST be 0 for now).
+ uint16_t reserved;
+
+ /// Reserved for future usage (always MUST be 0 for now).
+ uint8_t reserved2;
+
+ /// Number of blocks in bytes.
+ uint8_t num_blocks;
+
+ /// Restricted for MMC-6 and group number.
+ int8_t restrict_group_num;
+
+ /// Control.
+ uint8_t control;
+} iscsi_scsi_cdb_cmp_write;
+
+
+/// iSCSI SCSI Command Descriptor Block (CDB) for SERVICE ACTION IN(16) command service action: READ CAPACITY(16).
+#define ISCSI_SCSI_CDB_SERVICE_ACTION_IN_16_ACTION_READ_CAPACITY_16 0x10
+
+/// iSCSI SCSI Command Descriptor Block (CDB) for SERVICE ACTION IN(16) command service action: READ LONG(16).
+#define ISCSI_SCSI_CDB_SERVICE_ACTION_IN_16_ACTION_READ_LONG_16 0x11
+
+/// iSCSI SCSI Command Descriptor Block (CDB) for SERVICE ACTION IN(16) command service action: First bit of the five bits.
+#define ISCSI_SCSI_CDB_SERVICE_ACTION_IN_16_ACTION_FIRST_BIT 0
+
+/// iSCSI SCSI Command Descriptor Block (CDB) for SERVICE ACTION IN(16) command service action: Last bit of the five bits.
+#define ISCSI_SCSI_CDB_SERVICE_ACTION_IN_16_ACTION_LAST_BIT ((ISCSI_SCSI_CDB_SERVICE_ACTION_IN_16_ACTION_FIRST_BIT) + 5 - 1)
+
+/// iSCSI SCSI Command Descriptor Block (CDB) for SERVICE ACTION IN(16) command service action: Bit mask.
+#define ISCSI_SCSI_CDB_SERVICE_ACTION_IN_16_ACTION_MASK (ISCSI_BITS_GET_MASK(ISCSI_SCSI_CDB_SERVICE_ACTION_IN_16_ACTION_FIRST_BIT, ISCSI_SCSI_CDB_SERVICE_ACTION_IN_16_ACTION_LAST_BIT))
+
+/// iSCSI SCSI Command Descriptor Block (CDB) for SERVICE ACTION IN(16) command service action: Extracts the service action bits.
+#define ISCSI_SCSI_CDB_SERVICE_ACTION_IN_16_GET_ACTION(x) (ISCSI_BITS_GET((x), ISCSI_SCSI_CDB_SERVICE_ACTION_IN_16_ACTION_FIRST_BIT, ISCSI_SCSI_CDB_SERVICE_ACTION_IN_16_ACTION_LAST_BIT))
+
+/// iSCSI SCSI Command Descriptor Block (CDB) for SERVICE ACTION IN(16) command service action: Stores into the service action bits.
+#define ISCSI_SCSI_CDB_SERVICE_ACTION_IN_16_PUT_ACTION(x) (ISCSI_BITS_PUT((x), ISCSI_SCSI_CDB_SERVICE_ACTION_IN_16_ACTION_FIRST_BIT, ISCSI_SCSI_CDB_SERVICE_ACTION_IN_16_ACTION_LAST_BIT))
+
+
+/**
+ * @brief iSCSI SCSI CDB packet data structure for SCSI SERVICE IN ACTION(16) command.
+ *
+ * There are 16 bytes in the CDB field for this command.
+ */
+typedef struct __attribute__((packed)) iscsi_scsi_cdb_service_action_in_16 {
+ /// SCSI opcode.
+ iscsi_scsi_cdb cdb;
+
+ /// Service action.
+ uint8_t action;
+
+ /// Logical Block Address (LBA), obselete by now.
+ uint64_t lba;
+
+ /// Allocation length in bytes.
+ uint32_t alloc_len;
+
+ /// Reserved for future usage (always MUST be 0 for now).
+ uint8_t reserved;
+
+ /// Control.
+ uint8_t control;
+} iscsi_scsi_cdb_service_action_in_16;
+
+
+/**
+ * @brief iSCSI SCSI CDB packet data structure for SCSI SYNCHRONIZE CACHE(10) command.
+ *
+ * There are 10 bytes in the CDB field for this command.
+ */
+typedef struct __attribute__((packed)) iscsi_scsi_cdb_sync_cache_10 {
+ /// SCSI opcode.
+ iscsi_scsi_cdb cdb;
+
+ /// Flags.
+ int8_t flags;
+
+ /// Logical Block Address (LBA).
+ uint32_t lba;
+
+ /// Group number.
+ int8_t group_num;
+
+ /// Transfer length in bytes.
+ uint16_t xfer_len;
+
+ /// Control.
+ uint8_t control;
+} iscsi_scsi_cdb_sync_cache_10;
+
+
+/**
+ * @brief iSCSI SCSI CDB packet data structure for SCSI SYNCHRONIZE CACHE(16) command.
+ *
+ * There are 16 bytes in the CDB field for this command.
+ */
+typedef struct __attribute__((packed)) iscsi_scsi_cdb_sync_cache_16 {
+ /// SCSI opcode.
+ iscsi_scsi_cdb cdb;
+
+ /// Flags.
+ int8_t flags;
+
+ /// Logical Block Address (LBA).
+ uint64_t lba;
+
+ /// Transfer length in bytes.
+ uint32_t xfer_len;
+
+ /// Group number.
+ int8_t group_num;
+
+ /// Control.
+ uint8_t control;
+} iscsi_scsi_cdb_sync_cache_16;
+
+
+/**
+ * @brief iSCSI SCSI CDB packet data structure for SCSI WRITE SAME(10) command.
+ *
+ * There are 10 bytes in the CDB field for this command.
+ */
+typedef struct __attribute__((packed)) iscsi_scsi_cdb_write_same_10 {
+ /// SCSI opcode.
+ iscsi_scsi_cdb cdb;
+
+ /// Flags.
+ int8_t flags;
+
+ /// Logical Block Address (LBA).
+ uint32_t lba;
+
+ /// Group number.
+ int8_t group_num;
+
+ /// Transfer length in bytes.
+ uint16_t xfer_len;
+
+ /// Control.
+ uint8_t control;
+} iscsi_scsi_cdb_write_same_10;
+
+
+/**
+ * @brief iSCSI SCSI CDB packet data structure for SCSI WRITE SAME(16) command.
+ *
+ * There are 16 bytes in the CDB field for this command.
+ */
+typedef struct __attribute__((packed)) iscsi_scsi_cdb_write_same_16 {
+ /// SCSI opcode.
+ iscsi_scsi_cdb cdb;
+
+ /// Flags.
+ int8_t flags;
+
+ /// Logical Block Address (LBA).
+ uint64_t lba;
+
+ /// Transfer length in bytes.
+ uint32_t xfer_len;
+
+ /// Group number.
+ int8_t group_num;
+
+ /// Control.
+ uint8_t control;
+} iscsi_scsi_cdb_write_same_16;
+
+
+/// iSCSI SCSI Command Descriptor Block (CDB) for MODE SELECT(6) command flags: Save Pages (SP).
+#define ISCSI_SCSI_CDB_MODE_SELECT_6_FLAGS_SP (1 << 0)
+
+/// iSCSI SCSI Command Descriptor Block (CDB) for MODE SELECT(6) command flags: Revert To Defaults (RTD).
+#define ISCSI_SCSI_CDB_MODE_SELECT_6_FLAGS_RTD (1 << 1)
+
+/// iSCSI SCSI Command Descriptor Block (CDB) for MODE SELECT(6) command flags: Page Format (PF).
+#define ISCSI_SCSI_CDB_MODE_SELECT_6_FLAGS_PF (1 << 4)
+
+
+/**
+ * @brief iSCSI SCSI CDB packet data structure for SCSI MODE SELECT(6) command.
+ *
+ * There are 6 bytes in the CDB field for this command.
+ */
+typedef struct __attribute__((packed)) iscsi_scsi_cdb_mode_select_6 {
+ /// SCSI opcode.
+ iscsi_scsi_cdb cdb;
+
+ /// Flags.
+ int8_t flags;
+
+ /// Reserved for future usage (always MUST be 0 for now).
+ uint16_t reserved;
+
+ /// Parameter list length in bytes.
+ uint8_t param_list_len;
+
+ /// Control.
+ uint8_t control;
+} iscsi_scsi_cdb_mode_select_6;
+
+
+/// iSCSI SCSI Command Descriptor Block (CDB) for MODE SELECT(10) command flags: Save Pages (SP).
+#define ISCSI_SCSI_CDB_MODE_SELECT_10_FLAGS_SP (1 << 0)
+
+/// iSCSI SCSI Command Descriptor Block (CDB) for MODE SELECT(10) command flags: Page Format (PF).
+#define ISCSI_SCSI_CDB_MODE_SELECT_10_FLAGS_PF (1 << 4)
+
+
+/**
+ * @brief iSCSI SCSI CDB packet data structure for SCSI MODE SELECT(10) command.
+ *
+ * There are 10 bytes in the CDB field for this command.
+ */
+typedef struct __attribute__((packed)) iscsi_scsi_cdb_mode_select_10 {
+ /// SCSI opcode.
+ iscsi_scsi_cdb cdb;
+
+ /// Flags.
+ int8_t flags;
+
+ /// Reserved for future usage (always MUST be 0 for now).
+ uint32_t reserved;
+
+ /// Reserved for future usage (always MUST be 0 for now).
+ uint8_t reserved2;
+
+ /// Parameter list length in bytes.
+ uint16_t param_list_len;
+
+ /// Control.
+ uint8_t control;
+} iscsi_scsi_cdb_mode_select_10;
+
+
+/// iSCSI SCSI Command Descriptor Block (CDB) for MODE SENSE(6) command flags: Disable Block Descriptors (DBD).
+#define ISCSI_SCSI_CDB_MODE_SENSE_6_FLAGS_DBD (1 << 3)
+
+
+/// iSCSI SCSI Command Descriptor Block (CDB) for MODE SENSE(6) command page code: First bit of the six bits.
+#define ISCSI_SCSI_CDB_MODE_SENSE_6_PAGE_CODE_FIRST_BIT 0
+
+/// iSCSI SCSI Command Descriptor Block (CDB) for MODE SENSE(6) command page code: Last bit of the six bits.
+#define ISCSI_SCSI_CDB_MODE_SENSE_6_PAGE_CODE_LAST_BIT ((ISCSI_SCSI_CDB_MODE_SENSE_6_PAGE_CODE_FIRST_BIT) + 6 - 1)
+
+/// iSCSI SCSI Command Descriptor Block (CDB) for MODE SENSE(6) command page code: Bit mask.
+#define ISCSI_SCSI_CDB_MODE_SENSE_6_PAGE_CODE_MASK (ISCSI_BITS_GET_MASK(ISCSI_SCSI_CDB_MODE_SENSE_6_PAGE_CODE_FIRST_BIT, ISCSI_SCSI_CDB_MODE_SENSE_6_PAGE_CODE_LAST_BIT))
+
+/// iSCSI SCSI Command Descriptor Block (CDB) for MODE SENSE(6) command page code: Extracts the page code bits.
+#define ISCSI_SCSI_CDB_MODE_SENSE_6_GET_PAGE_CODE(x) (ISCSI_BITS_GET((x), ISCSI_SCSI_CDB_MODE_SENSE_6_PAGE_CODE_FIRST_BIT, ISCSI_SCSI_CDB_MODE_SENSE_6_PAGE_CODE_LAST_BIT))
+
+/// iSCSI SCSI Command Descriptor Block (CDB) for MODE SENSE(6) command page code: Stores into the page code bits.
+#define ISCSI_SCSI_CDB_MODE_SENSE_6_PUT_PAGE_CODE(x) (ISCSI_BITS_PUT((x), ISCSI_SCSI_CDB_MODE_SENSE_6_PAGE_CODE_FIRST_BIT, ISCSI_SCSI_CDB_MODE_SENSE_6_PAGE_CODE_LAST_BIT))
+
+/// iSCSI SCSI Command Descriptor Block (CDB) for MODE SENSE(6) command page control: Current values.
+#define ISCSI_SCSI_CDB_MODE_SENSE_6_PAGE_CONTROL_CURRENT_VALUES 0x0
+
+/// iSCSI SCSI Command Descriptor Block (CDB) for MODE SENSE(6) command page control: Changeable values.
+#define ISCSI_SCSI_CDB_MODE_SENSE_6_PAGE_CONTROL_CHG_VALUES 0x1
+
+/// iSCSI SCSI Command Descriptor Block (CDB) for MODE SENSE(6) command page control: Default values.
+#define ISCSI_SCSI_CDB_MODE_SENSE_6_PAGE_CONTROL_DEFAULT_VALUES 0x2
+
+/// iSCSI SCSI Command Descriptor Block (CDB) for MODE SENSE(6) command page control: Saved values.
+#define ISCSI_SCSI_CDB_MODE_SENSE_6_PAGE_CONTROL_SAVED_VALUES 0x3
+
+/// iSCSI SCSI Command Descriptor Block (CDB) for MODE SENSE(6) command page control: First bit of the two bits.
+#define ISCSI_SCSI_CDB_MODE_SENSE_6_PAGE_CONTROL_FIRST_BIT 6
+
+/// iSCSI SCSI Command Descriptor Block (CDB) for MODE SENSE(6) command page control: Last bit of the two bits.
+#define ISCSI_SCSI_CDB_MODE_SENSE_6_PAGE_CONTROL_LAST_BIT ((ISCSI_SCSI_CDB_MODE_SENSE_6_PAGE_CONTROL_FIRST_BIT) + 2 - 1)
+
+/// iSCSI SCSI Command Descriptor Block (CDB) for MODE SENSE(6) command page control: Bit mask.
+#define ISCSI_SCSI_CDB_MODE_SENSE_6_PAGE_CONTROL_MASK (ISCSI_BITS_GET_MASK(ISCSI_SCSI_CDB_MODE_SENSE_6_PAGE_CONTROL_FIRST_BIT, ISCSI_SCSI_CDB_MODE_SENSE_6_PAGE_CONTROL_LAST_BIT))
+
+/// iSCSI SCSI Command Descriptor Block (CDB) for MODE SENSE(6) command page control: Extracts the page control bits.
+#define ISCSI_SCSI_CDB_MODE_SENSE_6_GET_PAGE_CONTROL(x) (ISCSI_BITS_GET((x), ISCSI_SCSI_CDB_MODE_SENSE_6_PAGE_CONTROL_FIRST_BIT, ISCSI_SCSI_CDB_MODE_SENSE_6_PAGE_CONTROL_LAST_BIT))
+
+/// iSCSI SCSI Command Descriptor Block (CDB) for MODE SENSE(6) command page control: Stores into the page control bits.
+#define ISCSI_SCSI_CDB_MODE_SENSE_6_PUT_PAGE_CONTROL(x) (ISCSI_BITS_PUT((x), ISCSI_SCSI_CDB_MODE_SENSE_6_PAGE_CONTROL_FIRST_BIT, ISCSI_SCSI_CDB_MODE_SENSE_6_PAGE_CONTROL_LAST_BIT))
+
+
+/**
+ * @brief iSCSI SCSI CDB packet data structure for SCSI MODE SENSE(6) command.
+ *
+ * There are 6 bytes in the CDB field for this command.
+ */
+typedef struct __attribute__((packed)) iscsi_scsi_cdb_mode_sense_6 {
+ /// SCSI opcode.
+ iscsi_scsi_cdb cdb;
+
+ /// Flags.
+ int8_t flags;
+
+ /// Page code and page control.
+ uint8_t page_code_control;
+
+ /// Sub page code.
+ uint8_t sub_page_code;
+
+ /// Allocation length in bytes.
+ uint8_t alloc_len;
+
+ /// Control.
+ uint8_t control;
+} iscsi_scsi_cdb_mode_sense_6;
+
+
+/// iSCSI SCSI Command Descriptor Block (CDB) for MODE SENSE(10) command flags: Disable Block Descriptors (DBD).
+#define ISCSI_SCSI_CDB_MODE_SENSE_10_FLAGS_DBD (1 << 3)
+
+/// iSCSI SCSI Command Descriptor Block (CDB) for MODE SENSE(10) command flags: Long LBA Accepted (LLBAA).
+#define ISCSI_SCSI_CDB_MODE_SENSE_10_FLAGS_LLBAA (1 << 4)
+
+
+/// iSCSI SCSI Command Descriptor Block (CDB) for MODE SENSE(10) command page code: First bit of the six bits.
+#define ISCSI_SCSI_CDB_MODE_SENSE_10_PAGE_CODE_FIRST_BIT 0
+
+/// iSCSI SCSI Command Descriptor Block (CDB) for MODE SENSE(10) command page code: Last bit of the six bits.
+#define ISCSI_SCSI_CDB_MODE_SENSE_10_PAGE_CODE_LAST_BIT ((ISCSI_SCSI_CDB_MODE_SENSE_10_PAGE_CODE_FIRST_BIT) + 6 - 1)
+
+/// iSCSI SCSI Command Descriptor Block (CDB) for MODE SENSE(10) command page code: Bit mask.
+#define ISCSI_SCSI_CDB_MODE_SENSE_10_PAGE_CODE_MASK (ISCSI_BITS_GET_MASK(ISCSI_SCSI_CDB_MODE_SENSE_10_PAGE_CODE_FIRST_BIT, ISCSI_SCSI_CDB_MODE_SENSE_10_PAGE_CODE_LAST_BIT))
+
+/// iSCSI SCSI Command Descriptor Block (CDB) for MODE SENSE(10) command page code: Extracts the page code bits.
+#define ISCSI_SCSI_CDB_MODE_SENSE_10_GET_PAGE_CODE(x) (ISCSI_BITS_GET((x), ISCSI_SCSI_CDB_MODE_SENSE_10_PAGE_CODE_FIRST_BIT, ISCSI_SCSI_CDB_MODE_SENSE_10_PAGE_CODE_LAST_BIT))
+
+/// iSCSI SCSI Command Descriptor Block (CDB) for MODE SENSE(10) command page code: Stores into the page code bits.
+#define ISCSI_SCSI_CDB_MODE_SENSE_10_PUT_PAGE_CODE(x) (ISCSI_BITS_PUT((x), ISCSI_SCSI_CDB_MODE_SENSE_10_PAGE_CODE_FIRST_BIT, ISCSI_SCSI_CDB_MODE_SENSE_10_PAGE_CODE_LAST_BIT))
+
+/// iSCSI SCSI Command Descriptor Block (CDB) for MODE SENSE(10) command page control: Current values.
+#define ISCSI_SCSI_CDB_MODE_SENSE_10_PAGE_CONTROL_CURRENT_VALUES 0x0
+
+/// iSCSI SCSI Command Descriptor Block (CDB) for MODE SENSE(10) command page control: Changeable values.
+#define ISCSI_SCSI_CDB_MODE_SENSE_10_PAGE_CONTROL_CHG_VALUES 0x1
+
+/// iSCSI SCSI Command Descriptor Block (CDB) for MODE SENSE(10) command page control: Default values.
+#define ISCSI_SCSI_CDB_MODE_SENSE_10_PAGE_CONTROL_DEFAULT_VALUES 0x2
+
+/// iSCSI SCSI Command Descriptor Block (CDB) for MODE SENSE(10) command page control: Saved values.
+#define ISCSI_SCSI_CDB_MODE_SENSE_10_PAGE_CONTROL_SAVED_VALUES 0x3
+
+/// iSCSI SCSI Command Descriptor Block (CDB) for MODE SENSE(10) command page control: First bit of the two bits.
+#define ISCSI_SCSI_CDB_MODE_SENSE_10_PAGE_CONTROL_FIRST_BIT 6
+
+/// iSCSI SCSI Command Descriptor Block (CDB) for MODE SENSE(10) command page control: Last bit of the two bits.
+#define ISCSI_SCSI_CDB_MODE_SENSE_10_PAGE_CONTROL_LAST_BIT ((ISCSI_SCSI_CDB_MODE_SENSE_10_PAGE_CONTROL_FIRST_BIT) + 2 - 1)
+
+/// iSCSI SCSI Command Descriptor Block (CDB) for MODE SENSE(10) command page control: Bit mask.
+#define ISCSI_SCSI_CDB_MODE_SENSE_10_PAGE_CONTROL_MASK (ISCSI_BITS_GET_MASK(ISCSI_SCSI_CDB_MODE_SENSE_10_PAGE_CONTROL_FIRST_BIT, ISCSI_SCSI_CDB_MODE_SENSE_10_PAGE_CONTROL_LAST_BIT))
+
+/// iSCSI SCSI Command Descriptor Block (CDB) for MODE SENSE(10) command page control: Extracts the page control bits.
+#define ISCSI_SCSI_CDB_MODE_SENSE_10_GET_PAGE_CONTROL(x) (ISCSI_BITS_GET((x), ISCSI_SCSI_CDB_MODE_SENSE_10_PAGE_CONTROL_FIRST_BIT, ISCSI_SCSI_CDB_MODE_SENSE_10_PAGE_CONTROL_LAST_BIT))
+
+/// iSCSI SCSI Command Descriptor Block (CDB) for MODE SENSE(10) command page control: Stores into the page control bits.
+#define ISCSI_SCSI_CDB_MODE_SENSE_10_PUT_PAGE_CONTROL(x) (ISCSI_BITS_PUT((x), ISCSI_SCSI_CDB_MODE_SENSE_10_PAGE_CONTROL_FIRST_BIT, ISCSI_SCSI_CDB_MODE_SENSE_10_PAGE_CONTROL_LAST_BIT))
+
+
+/**
+ * @brief iSCSI SCSI CDB packet data structure for SCSI MODE SENSE(10) command.
+ *
+ * There are 10 bytes in the CDB field for this command.
+ */
+typedef struct __attribute__((packed)) iscsi_scsi_cdb_mode_sense_10 {
+ /// SCSI opcode.
+ iscsi_scsi_cdb cdb;
+
+ /// Flags.
+ int8_t flags;
+
+ /// Page code and page control.
+ uint8_t page_code_control;
+
+ /// Sub page code.
+ uint8_t sub_page_code;
+
+ /// Reserved for future usage (always MUST be 0 for now).
+ uint16_t reserved;
+
+ /// Reserved for future usage (always MUST be 0 for now).
+ uint8_t reserved2;
+
+ /// Allocation length in bytes.
+ uint16_t alloc_len;
+
+ /// Control.
+ uint8_t control;
+} iscsi_scsi_cdb_mode_sense_10;
+
+
+/// iSCSI SCSI Command Descriptor Block (CDB) for REQUEST SENSE command flags: Descriptor Format (DESC).
+#define ISCSI_SCSI_CDB_REQ_SENSE_FLAGS_DESC (1 << 0)
+
+
+/**
+ * @brief iSCSI SCSI CDB packet data structure for SCSI REQUEST SENSE command.
+ *
+ * There are 6 bytes in the CDB field for this command.
+ */
+typedef struct __attribute__((packed)) iscsi_scsi_cdb_req_sense {
+ /// SCSI opcode.
+ iscsi_scsi_cdb cdb;
+
+ /// Flags.
+ int8_t flags;
+
+ /// Reserved for future usage (always MUST be 0 for now).
+ uint16_t reserved;
+
+ /// Allocation length in bytes.
+ uint8_t alloc_len;
+
+ /// Control.
+ uint8_t control;
+} iscsi_scsi_cdb_req_sense;
+
+
+/// iSCSI SCSI Command Descriptor Block (CDB) for START STOP UNIT command execution flags: Reply immediately after CDB check (IMMED).
+#define ISCSI_SCSI_CDB_START_STOP_UNIT_EXEC_FLAGS_IMMED (1 << 0)
+
+
+/// iSCSI SCSI Command Descriptor Block (CDB) for START STOP UNIT command execution power condition modifier: Process the START and LOEJ bits.
+#define ISCSI_SCSI_CDB_START_STOP_UNIT_POWER_COND_MOD_PROC_START_LOEJ_BITS 0x0
+
+/// iSCSI SCSI Command Descriptor Block (CDB) for START STOP UNIT command execution power condition modifier: Cause the logical unit to transition to the active power condition (see SPC5).
+#define ISCSI_SCSI_CDB_START_STOP_UNIT_POWER_COND_MOD_ACTIVE 0x0
+
+/// iSCSI SCSI Command Descriptor Block (CDB) for START STOP UNIT command execution power condition modifier: Cause the logical unit to transition to the idle_a power condition (see SPC5).
+#define ISCSI_SCSI_CDB_START_STOP_UNIT_POWER_COND_MOD_IDLE_A 0x0
+
+/// iSCSI SCSI Command Descriptor Block (CDB) for START STOP UNIT command execution power condition modifier: Cause the logical unit to transition to the idle_b power condition (see SPC5).
+#define ISCSI_SCSI_CDB_START_STOP_UNIT_POWER_COND_MOD_IDLE_B 0x1
+
+/// iSCSI SCSI Command Descriptor Block (CDB) for START STOP UNIT command execution power condition modifier: Cause the logical unit to transition to the idle_c power condition (see SPC5).
+#define ISCSI_SCSI_CDB_START_STOP_UNIT_POWER_COND_MOD_IDLE_C 0x2
+
+/// iSCSI SCSI Command Descriptor Block (CDB) for START STOP UNIT command execution power condition modifier: Cause the logical unit to transition to the standby_z power condition (see SPC5).
+#define ISCSI_SCSI_CDB_START_STOP_UNIT_POWER_COND_MOD_STANDBY_Z 0x0
+
+/// iSCSI SCSI Command Descriptor Block (CDB) for START STOP UNIT command execution power condition modifier: Cause the logical unit to transition to the standby_b power condition (see SPC5).
+#define ISCSI_SCSI_CDB_START_STOP_UNIT_POWER_COND_MOD_STANDBY_Y 0x1
+
+/// iSCSI SCSI Command Descriptor Block (CDB) for START STOP UNIT command execution power condition modifier: Initialize and start all of the idle and standby condition timers that are enabled (see SPC5).
+#define ISCSI_SCSI_CDB_START_STOP_UNIT_POWER_COND_MOD_LU_CONTROL 0x0
+
+/// iSCSI SCSI Command Descriptor Block (CDB) for START STOP UNIT command execution power condition modifier: Force the idle_a condition timer to be set to zero (see SPC5).
+#define ISCSI_SCSI_CDB_START_STOP_UNIT_POWER_COND_MOD_FORCE_IDLE_A_0 0x0
+
+/// iSCSI SCSI Command Descriptor Block (CDB) for START STOP UNIT command execution power condition modifier: Force the idle_b condition timer to be set to zero (see SPC5).
+#define ISCSI_SCSI_CDB_START_STOP_UNIT_POWER_COND_MOD_FORCE_IDLE_B_0 0x1
+
+/// iSCSI SCSI Command Descriptor Block (CDB) for START STOP UNIT command execution power condition modifier: Force the idle_c condition timer to be set to zero (see SPC5).
+#define ISCSI_SCSI_CDB_START_STOP_UNIT_POWER_COND_MOD_FORCE_IDLE_C_0 0x2
+
+/// iSCSI SCSI Command Descriptor Block (CDB) for START STOP UNIT command execution power condition modifier: Force the standby_z condition timer to be set to zero (see SPC5).
+#define ISCSI_SCSI_CDB_START_STOP_UNIT_POWER_COND_MOD_FORCE_STANDBY_Z_0 0x0
+
+/// iSCSI SCSI Command Descriptor Block (CDB) for START STOP UNIT command execution power condition modifier: Force the standby_y condition timer to be set to zero (see SPC5).
+#define ISCSI_SCSI_CDB_START_STOP_UNIT_POWER_COND_MOD_FORCE_STANDBY_Y_0 0x1
+
+/// iSCSI SCSI Command Descriptor Block (CDB) for START STOP UNIT command execution power condition modifier: First bit of the four bits.
+#define ISCSI_SCSI_CDB_START_STOP_UNIT_POWER_COND_MOD_FIRST_BIT 0
+
+/// iSCSI SCSI Command Descriptor Block (CDB) for START STOP UNIT command execution power condition modifier: Last bit of the four bits.
+#define ISCSI_SCSI_CDB_START_STOP_UNIT_POWER_COND_MOD_LAST_BIT ((ISCSI_SCSI_CDB_START_STOP_UNIT_POWER_COND_MOD_FIRST_BIT) + 4 - 1)
+
+/// iSCSI SCSI Command Descriptor Block (CDB) for START STOP UNIT command execution power condition modifier: Bit mask.
+#define ISCSI_SCSI_CDB_START_STOP_UNIT_POWER_COND_MOD_MASK (ISCSI_BITS_GET_MASK(ISCSI_SCSI_CDB_START_STOP_UNIT_POWER_COND_MOD_FIRST_BIT, ISCSI_SCSI_CDB_START_STOP_UNIT_POWER_COND_MOD_LAST_BIT))
+
+/// iSCSI SCSI Command Descriptor Block (CDB) for START STOP UNIT command execution power condition modifier: Extracts the power condition modifier bits.
+#define ISCSI_SCSI_CDB_START_STOP_UNIT_GET_POWER_COND_MOD(x) (ISCSI_BITS_GET((x), ISCSI_SCSI_CDB_START_STOP_UNIT_POWER_COND_MOD_FIRST_BIT, ISCSI_SCSI_CDB_START_STOP_UNIT_POWER_COND_MOD_LAST_BIT))
+
+/// iSCSI SCSI Command Descriptor Block (CDB) for START STOP UNIT command execution power condition modifier: Stores into the power condition modifier bits.
+#define ISCSI_SCSI_CDB_START_STOP_UNIT_PUT_POWER_COND_MOD(x) (ISCSI_BITS_PUT((x), ISCSI_SCSI_CDB_START_STOP_UNIT_POWER_COND_MOD_FIRST_BIT, ISCSI_SCSI_CDB_START_STOP_UNIT_POWER_COND_MOD_LAST_BIT))
+
+
+/// iSCSI SCSI Command Descriptor Block (CDB) for START STOP UNIT command execution flags: START.
+#define ISCSI_SCSI_CDB_START_STOP_UNIT_FLAGS_START (1 << 0)
+
+/// iSCSI SCSI Command Descriptor Block (CDB) for START STOP UNIT command execution flags: LOad EJect (LOEJ).
+#define ISCSI_SCSI_CDB_START_STOP_UNIT_FLAGS_LOEJ (1 << 1)
+
+/// iSCSI SCSI Command Descriptor Block (CDB) for START STOP UNIT command execution flags: Do not flush caches until a power condition that prevents accessing the medium is entered (NO_FLUSH).
+#define ISCSI_SCSI_CDB_START_STOP_UNIT_FLAGS_NO_FLUSH (1 << 2)
+
+/// iSCSI SCSI Command Descriptor Block (CDB) for START STOP UNIT command execution flags power condition: Process the START and LOEJ bits (START_VALID).
+#define ISCSI_SCSI_CDB_START_STOP_UNIT_FLAGS_POWER_COND_START_VALID 0x0
+
+/// iSCSI SCSI Command Descriptor Block (CDB) for START STOP UNIT command execution flags power condition: Cause the logical unit to transition to the active power condition (see SPC5).
+#define ISCSI_SCSI_CDB_START_STOP_UNIT_FLAGS_POWER_COND_ACTIVE 0x1
+
+/// iSCSI SCSI Command Descriptor Block (CDB) for START STOP UNIT command execution flags power condition: Cause the logical unit to transition to the idle_a to idle_c power conditions (see SPC5).
+#define ISCSI_SCSI_CDB_START_STOP_UNIT_FLAGS_POWER_COND_IDLE 0x2
+
+/// iSCSI SCSI Command Descriptor Block (CDB) for START STOP UNIT command execution flags power condition: Cause the logical unit to transition to the standby_z and standby_y power conditions (see SPC5).
+#define ISCSI_SCSI_CDB_START_STOP_UNIT_FLAGS_POWER_COND_STANDBY 0x3
+
+/// iSCSI SCSI Command Descriptor Block (CDB) for START STOP UNIT command execution flags power condition: Obselete.
+#define ISCSI_SCSI_CDB_START_STOP_UNIT_FLAGS_POWER_COND_OBSELETE 0x5
+
+/// iSCSI SCSI Command Descriptor Block (CDB) for START STOP UNIT command execution flags power condition: Initialize and start all of the idle and standby condition timers that are enabled (see SPC5).
+#define ISCSI_SCSI_CDB_START_STOP_UNIT_FLAGS_POWER_COND_LU_CONTROL 0x7
+
+/// iSCSI SCSI Command Descriptor Block (CDB) for START STOP UNIT command execution flags power condition: Force the idle_a to idle_c condition timers to be set to zero (see SPC5).
+#define ISCSI_SCSI_CDB_START_STOP_UNIT_FLAGS_POWER_COND_FORCE_IDLE_0 0xA
+
+/// iSCSI SCSI Command Descriptor Block (CDB) for START STOP UNIT command execution flags power condition: Force the standby_z and standby_y condition timers to be set to zero (see SPC5).
+#define ISCSI_SCSI_CDB_START_STOP_UNIT_FLAGS_POWER_COND_FORCE_STANDBY_0 0xB
+
+/// iSCSI SCSI Command Descriptor Block (CDB) for START STOP UNIT command execution flags power condition: First bit of the four bits.
+#define ISCSI_SCSI_CDB_START_STOP_UNIT_FLAGS_POWER_COND_FIRST_BIT 4
+
+/// iSCSI SCSI Command Descriptor Block (CDB) for START STOP UNIT command execution flags power condition: Last bit of the four bits.
+#define ISCSI_SCSI_CDB_START_STOP_UNIT_FLAGS_POWER_COND_LAST_BIT ((ISCSI_SCSI_CDB_START_STOP_UNIT_FLAGS_POWER_COND_FIRST_BIT) + 8 - 1)
+
+/// iSCSI SCSI Command Descriptor Block (CDB) for START STOP UNIT command execution flags power condition: Bit mask.
+#define ISCSI_SCSI_CDB_START_STOP_UNIT_FLAGS_POWER_COND_MASK (ISCSI_BITS_GET_MASK(ISCSI_SCSI_CDB_START_STOP_UNIT_FLAGS_POWER_COND_FIRST_BIT, ISCSI_SCSI_CDB_START_STOP_UNIT_FLAGS_POWER_COND_LAST_BIT))
+
+/// iSCSI SCSI Command Descriptor Block (CDB) for START STOP UNIT command execution flags power condition: Extracts the power condition bits.
+#define ISCSI_SCSI_CDB_START_STOP_UNIT_FLAGS_GET_POWER_COND(x) (ISCSI_BITS_GET((x), ISCSI_SCSI_CDB_START_STOP_UNIT_FLAGS_POWER_COND_FIRST_BIT, ISCSI_SCSI_CDB_START_STOP_UNIT_FLAGS_POWER_COND_LAST_BIT))
+
+/// iSCSI SCSI Command Descriptor Block (CDB) for START STOP UNIT command execution flags power condition: Stores into the power condition bits.
+#define ISCSI_SCSI_CDB_START_STOP_UNIT_FLAGS_PUT_POWER_COND(x) (ISCSI_BITS_PUT((x), ISCSI_SCSI_CDB_START_STOP_UNIT_FLAGS_POWER_COND_FIRST_BIT, ISCSI_SCSI_CDB_START_STOP_UNIT_FLAGS_POWER_COND_LAST_BIT))
+
+
+/**
+ * @brief iSCSI SCSI CDB packet data structure for SCSI START STOP UNIT command.
+ *
+ * There are 6 bytes in the CDB field for this command.
+ */
+typedef struct __attribute__((packed)) iscsi_scsi_cdb_start_stop_unit {
+ /// SCSI opcode.
+ iscsi_scsi_cdb cdb;
+
+ /// Execution flags.
+ int8_t exec_flags;
+
+ /// Reserved for future usage (always MUST be 0 for now).
+ uint8_t reserved;
+
+ /// Power condition modifier.
+ uint8_t power_cond_mod;
+
+ /// Flags.
+ int8_t flags;
+
+ /// Control.
+ uint8_t control;
+} iscsi_scsi_cdb_start_stop_unit;
+
+
+/// iSCSI SCSI Command Descriptor Block (CDB) for PERSISTENT RESERVE OUT command service action: Register - Register a reservation key without making a reservation.
+#define ISCSI_SCSI_CDB_PR_RESERVE_OUT_ACTION_REGISTER 0x00
+
+/// iSCSI SCSI Command Descriptor Block (CDB) for PERSISTENT RESERVE OUT command service action: Reserve - Create a persistent reservation of the specified scope and type.
+#define ISCSI_SCSI_CDB_PR_RESERVE_OUT_ACTION_RESERVE 0x01
+
+/// iSCSI SCSI Command Descriptor Block (CDB) for PERSISTENT RESERVE OUT command service action: Release - Releases the selected reservation for the requesting initiator.
+#define ISCSI_SCSI_CDB_PR_RESERVE_OUT_ACTION_RELEASE 0x02
+
+/// iSCSI SCSI Command Descriptor Block (CDB) for PERSISTENT RESERVE OUT command service action: Clear – Clears all reservations keys and all persistent reservations.
+#define ISCSI_SCSI_CDB_PR_RESERVE_OUT_ACTION_CLEAR 0x03
+
+/// iSCSI SCSI Command Descriptor Block (CDB) for PERSISTENT RESERVE OUT command service action: Preempt – Preempt reservations from another initiator.
+#define ISCSI_SCSI_CDB_PR_RESERVE_OUT_ACTION_PREEMPT 0x04
+
+/// iSCSI SCSI Command Descriptor Block (CDB) for PERSISTENT RESERVE OUT command service action: Preempt reservations from another initiator and abort all tasks for all initiators with the specified reservation key.
+#define ISCSI_SCSI_CDB_PR_RESERVE_OUT_ACTION_PREEMPT_ABORT 0x05
+
+/// iSCSI SCSI Command Descriptor Block (CDB) for PERSISTENT RESERVE OUT command service action: Register and Ignore Existing Key – Register a new reservation key and discard existing reservation key.
+#define ISCSI_SCSI_CDB_PR_RESERVE_OUT_ACTION_REGISTER_IGNORE_EXIST_KEY 0x06
+
+/// iSCSI SCSI Command Descriptor Block (CDB) for PERSISTENT RESERVE OUT command service action: Register and Move Registers - Registers a reservation key for another I_T nexus and moves the persistent reservation to that I-T nexus.
+#define ISCSI_SCSI_CDB_PR_RESERVE_OUT_ACTION_REGISTER_MOVE_REGS 0x07
+
+/// iSCSI SCSI Command Descriptor Block (CDB) for PERSISTENT RESERVE OUT command service action: First bit of the five bits.
+#define ISCSI_SCSI_CDB_PR_RESERVE_OUT_ACTION_FIRST_BIT 0
+
+/// iSCSI SCSI Command Descriptor Block (CDB) for PERSISTENT RESERVE OUT command service action: Last bit of the five bits.
+#define ISCSI_SCSI_CDB_PR_RESERVE_OUT_ACTION_LAST_BIT ((ISCSI_SCSI_CDB_PR_RESERVE_OUT_ACTION_FIRST_BIT) + 5 - 1)
+
+/// iSCSI SCSI Command Descriptor Block (CDB) for PERSISTENT RESERVE OUT command service action: Bit mask.
+#define ISCSI_SCSI_CDB_PR_RESERVE_OUT_ACTION_MASK (ISCSI_BITS_GET_MASK(ISCSI_SCSI_CDB_PR_RESERVE_OUT_ACTION_FIRST_BIT, ISCSI_SCSI_CDB_PR_RESERVE_OUT_ACTION_LAST_BIT))
+
+/// iSCSI SCSI Command Descriptor Block (CDB) for PERSISTENT RESERVE OUT command service action: Extracts the service action bits.
+#define ISCSI_SCSI_CDB_PR_RESERVE_OUT_GET_ACTION(x) (ISCSI_BITS_GET((x), ISCSI_SCSI_CDB_PR_RESERVE_OUT_ACTION_FIRST_BIT, ISCSI_SCSI_CDB_PR_RESERVE_OUT_ACTION_LAST_BIT))
+
+/// iSCSI SCSI Command Descriptor Block (CDB) for PERSISTENT RESERVE OUT command service action: Stores into the service action bits.
+#define ISCSI_SCSI_CDB_PR_RESERVE_OUT_PUT_ACTION(x) (ISCSI_BITS_PUT((x), ISCSI_SCSI_CDB_PR_RESERVE_OUT_ACTION_FIRST_BIT, ISCSI_SCSI_CDB_PR_RESERVE_OUT_ACTION_LAST_BIT))
+
+
+/**
+ * @brief iSCSI SCSI CDB packet data structure for SCSI PERSISTENT RESERVE OUT command.
+ *
+ * There are 10 bytes in the CDB field for this command.
+ */
+typedef struct __attribute__((packed)) iscsi_scsi_cdb_pr_reserve_out {
+ /// SCSI opcode.
+ iscsi_scsi_cdb cdb;
+
+ /// Service action.
+ uint8_t action;
+
+ /// Scope and reservation type.
+ uint8_t scope_type;
+
+ /// Reserved for future usage (always MUST be 0 for now).
+ uint16_t reserved;
+
+ /// Parameter list length in bytes.
+ uint32_t param_list_len;
+
+ /// Control.
+ uint8_t control;
+} iscsi_scsi_cdb_pr_reserve_out;
+
+
+/// iSCSI SCSI Command Descriptor Block (CDB) for PERSISTENT RESERVE IN command service action: Read keys - Reads all registered reservation keys (i.e. registrations) as described in SPC5.
+#define ISCSI_SCSI_CDB_PR_RESERVE_IN_ACTION_READ_KEYS 0x00
+
+/// iSCSI SCSI Command Descriptor Block (CDB) for PERSISTENT RESERVE IN command service action: Read reservations - Reads the current persistent reservations as described in SPC5.
+#define ISCSI_SCSI_CDB_PR_RESERVE_IN_ACTION_READ_RESERVATIONS 0x01
+
+/// iSCSI SCSI Command Descriptor Block (CDB) for PERSISTENT RESERVE IN command service action: Report capabilities - Returns capability information.
+#define ISCSI_SCSI_CDB_PR_RESERVE_IN_ACTION_READ_REPORT_CAPABILITIES 0x02
+
+/// iSCSI SCSI Command Descriptor Block (CDB) for PERSISTENT RESERVE IN command service action: Read full status – Reads complete information about all registrations and the persistent reservations, if any.
+#define ISCSI_SCSI_CDB_PR_RESERVE_IN_ACTION_READ_FULL_STATUS 0x03
+
+/// iSCSI SCSI Command Descriptor Block (CDB) for PERSISTENT RESERVE IN command service action: First bit of the five bits.
+#define ISCSI_SCSI_CDB_PR_RESERVE_IN_ACTION_FIRST_BIT 0
+
+/// iSCSI SCSI Command Descriptor Block (CDB) for PERSISTENT RESERVE IN command service action: Last bit of the five bits.
+#define ISCSI_SCSI_CDB_PR_RESERVE_IN_ACTION_LAST_BIT ((ISCSI_SCSI_CDB_PR_RESERVE_IN_ACTION_FIRST_BIT) + 5 - 1)
+
+/// iSCSI SCSI Command Descriptor Block (CDB) for PERSISTENT RESERVE IN command service action: Bit mask.
+#define ISCSI_SCSI_CDB_PR_RESERVE_IN_ACTION_MASK (ISCSI_BITS_GET_MASK(ISCSI_SCSI_CDB_PR_RESERVE_IN_ACTION_FIRST_BIT, ISCSI_SCSI_CDB_PR_RESERVE_IN_ACTION_LAST_BIT))
+
+/// iSCSI SCSI Command Descriptor Block (CDB) for PERSISTENT RESERVE IN command service action: Extracts the service action bits.
+#define ISCSI_SCSI_CDB_PR_RESERVE_IN_GET_ACTION(x) (ISCSI_BITS_GET((x), ISCSI_SCSI_CDB_PR_RESERVE_IN_ACTION_FIRST_BIT, ISCSI_SCSI_CDB_PR_RESERVE_IN_ACTION_LAST_BIT))
+
+/// iSCSI SCSI Command Descriptor Block (CDB) for PERSISTENT RESERVE IN command service action: Stores into the service action bits.
+#define ISCSI_SCSI_CDB_PR_RESERVE_IN_PUT_ACTION(x) (ISCSI_BITS_PUT((x), ISCSI_SCSI_CDB_PR_RESERVE_IN_ACTION_FIRST_BIT, ISCSI_SCSI_CDB_PR_RESERVE_IN_ACTION_LAST_BIT))
+
+
+/**
+ * @brief iSCSI SCSI CDB packet data structure for SCSI PERSISTENT RESERVE IN command.
+ *
+ * There are 10 bytes in the CDB field for this command.
+ */
+typedef struct __attribute__((packed)) iscsi_scsi_cdb_pr_reserve_in {
+ /// SCSI opcode.
+ iscsi_scsi_cdb cdb;
+
+ /// Service action.
+ uint8_t action;
+
+ /// Reserved for future usage (always MUST be 0 for now).
+ uint32_t reserved;
+
+ /// Reserved for future usage (always MUST be 0 for now).
+ uint8_t reserved2;
+
+ /// Parameter list length in bytes.
+ uint16_t param_list_len;
+
+ /// Control.
+ uint8_t control;
+} iscsi_scsi_cdb_pr_reserve_in;
+
+
+/**
+ * @brief iSCSI SCSI CDB packet data structure for SCSI RESERVE(6) command.
+ *
+ * There are 6 bytes in the CDB field for this command.
+ */
+typedef struct __attribute__((packed)) iscsi_scsi_cdb_pr_reserve_6 {
+ /// SCSI opcode.
+ iscsi_scsi_cdb cdb;
+
+ /// Reserved for future usage (always MUST be 0 for now).
+ uint8_t reserved_obselete;
+
+ /// Obselete byte.
+ uint8_t obselete;
+
+ /// Obselete word.
+ uint16_t obselete2;
+
+ /// Control.
+ uint8_t control;
+} iscsi_scsi_cdb_pr_reserve_6;
+
+
+/// iSCSI SCSI Command Descriptor Block (CDB) for RESERVE(10) command flags: Long identifier larger than 255 (LONGID).
+#define ISCSI_SCSI_CDB_RESERVE_10_FLAGS_LONGID (1 << 1)
+
+/// iSCSI SCSI Command Descriptor Block (CDB) for RESERVE(10) command flags: Third-party reservation (3RDPTY).
+#define ISCSI_SCSI_CDB_RESERVE_10_FLAGS_3RDPTY (1 << 4)
+
+
+/**
+ * @brief iSCSI SCSI CDB packet data structure for SCSI RESERVE(10) command.
+ *
+ * There are 10 bytes in the CDB field for this command.
+ */
+typedef struct __attribute__((packed)) iscsi_scsi_cdb_pr_reserve_10 {
+ /// SCSI opcode.
+ iscsi_scsi_cdb cdb;
+
+ /// Flags.
+ int8_t flags;
+
+ /// Obselete.
+ uint8_t obselete;
+
+ /// Third-party device identifier.
+ uint8_t third_party_dev_id;
+
+ /// Reserved for future usage (always MUST be 0 for now).
+ uint16_t reserved;
+
+ /// Reserved for future usage (always MUST be 0 for now).
+ uint8_t reserved2;
+
+ /// Parameter list length in bytes.
+ uint16_t param_list_len;
+
+ /// Control.
+ uint8_t control;
+} iscsi_scsi_cdb_pr_reserve_10;
+
+
/**
- * @brief iSCSI DataSegment Command packet structure.
+ * @brief iSCSI SCSI CDB packet data structure for SCSI RELEASE(6) command.
+ *
+ * There are 6 bytes in the CDB field for this command.
+ */
+typedef struct __attribute__((packed)) iscsi_scsi_cdb_pr_release_6 {
+ /// SCSI opcode.
+ iscsi_scsi_cdb cdb;
+
+ /// Reserved for future usage (always MUST be 0 for now).
+ uint8_t reserved_obselete;
+
+ /// Obselete byte.
+ uint8_t obselete;
+
+ /// Obselete word.
+ uint16_t obselete2;
+
+ /// Control.
+ uint8_t control;
+} iscsi_scsi_cdb_pr_release_6;
+
+
+/// iSCSI SCSI Command Descriptor Block (CDB) for RELEASE(10) command flags: Long identifier larger than 255 (LONGID).
+#define ISCSI_SCSI_CDB_RELEASE_10_FLAGS_LONGID (1 << 1)
+
+/// iSCSI SCSI Command Descriptor Block (CDB) for RELEASE(10) command flags: Third-party reservation (3RDPTY).
+#define ISCSI_SCSI_CDB_RELEASE_10_FLAGS_3RDPTY (1 << 4)
+
+
+/**
+ * @brief iSCSI SCSI CDB packet data structure for SCSI RELEASE(10) command.
+ *
+ * There are 10 bytes in the CDB field for this command.
+ */
+typedef struct __attribute__((packed)) iscsi_scsi_cdb_pr_release_10 {
+ /// SCSI opcode.
+ iscsi_scsi_cdb cdb;
+
+ /// Flags.
+ int8_t flags;
+
+ /// Obselete.
+ uint8_t obselete;
+
+ /// Third-party device identifier.
+ uint8_t third_party_dev_id;
+
+ /// Reserved for future usage (always MUST be 0 for now).
+ uint16_t reserved;
+
+ /// Reserved for future usage (always MUST be 0 for now).
+ uint8_t reserved2;
+
+ /// Parameter list length in bytes.
+ uint16_t param_list_len;
+
+ /// Control.
+ uint8_t control;
+} iscsi_scsi_cdb_pr_release_10;
+
+
+/**
+ * @brief iSCSI SCSI DataSegment Command packet structure.
*
* iSCSI targets MUST support and enable Autosense. If Status is CHECK
* CONDITION (0x02), then the data segment MUST contain sense data for
@@ -640,7 +2198,7 @@ typedef struct __attribute__((packed)) iscsi_data_digest {
* response-related information (e.g., for a target failure, it may
* contain a vendor-specific detailed description of the failure).
*/
-typedef struct __attribute__((packed)) iscsi_ds_cmd_data {
+typedef struct __attribute__((packed)) iscsi_scsi_ds_cmd_data {
/// SenseLength: This field indicates the length of Sense Data.
uint16_t len;
@@ -649,14 +2207,2540 @@ typedef struct __attribute__((packed)) iscsi_ds_cmd_data {
/// Response Data.
uint8_t res_data[0];
-} iscsi_ds_cmd_data;
+} iscsi_scsi_ds_cmd_data;
+
+
+/// iSCSI SCSI Basic Inquiry Data peripheral type: Direct access device.
+#define ISCSI_SCSI_BASIC_INQUIRY_DATA_PERIPHERAL_TYPE_DIRECT 0x00
+
+/// iSCSI SCSI Basic Inquiry Data peripheral type: Sequential access device.
+#define ISCSI_SCSI_BASIC_INQUIRY_DATA_PERIPHERAL_TYPE_SEQ 0x01
+
+/// iSCSI SCSI Basic Inquiry Data peripheral type: Printer device.
+#define ISCSI_SCSI_BASIC_INQUIRY_DATA_PERIPHERAL_TYPE_PRINTER 0x02
+
+/// iSCSI SCSI Basic Inquiry Data peripheral type: Processor device.
+#define ISCSI_SCSI_BASIC_INQUIRY_DATA_PERIPHERAL_TYPE_PROCESSOR 0x03
+
+/// iSCSI SCSI Basic Inquiry Data peripheral type: Write once device.
+#define ISCSI_SCSI_BASIC_INQUIRY_DATA_PERIPHERAL_TYPE_WORM 0x04
+
+/// iSCSI SCSI Basic Inquiry Data peripheral type: Read only direct access (e.g. CD-ROM) device.
+#define ISCSI_SCSI_BASIC_INQUIRY_DATA_PERIPHERAL_TYPE_RO_DIRECT 0x05
+
+/// iSCSI SCSI Basic Inquiry Data peripheral type: Scanner device.
+#define ISCSI_SCSI_BASIC_INQUIRY_DATA_PERIPHERAL_TYPE_SCANNER 0x06
+
+/// iSCSI SCSI Basic Inquiry Data peripheral type: Optical memory device.
+#define ISCSI_SCSI_BASIC_INQUIRY_DATA_PERIPHERAL_TYPE_OPTICAL 0x07
+
+/// iSCSI SCSI Basic Inquiry Data peripheral type: Medium changer device.
+#define ISCSI_SCSI_BASIC_INQUIRY_DATA_PERIPHERAL_TYPE_CHANGER 0x08
+
+/// iSCSI SCSI Basic Inquiry Data peripheral type: Communications device.
+#define ISCSI_SCSI_BASIC_INQUIRY_DATA_PERIPHERAL_TYPE_COMM 0x09
+
+/// iSCSI SCSI Basic Inquiry Data peripheral type: Unknown or no device.
+#define ISCSI_SCSI_BASIC_INQUIRY_DATA_PERIPHERAL_TYPE_UNKNOWN 0x1F
+
+/// iSCSI SCSI Basic Inquiry Data peripheral type: First bit of the five bits.
+#define ISCSI_SCSI_BASIC_INQUIRY_DATA_PERIPHERAL_TYPE_FIRST_BIT 0
+
+/// iSCSI SCSI Basic Inquiry Data peripheral type: Last bit of the five bits.
+#define ISCSI_SCSI_BASIC_INQUIRY_DATA_PERIPHERAL_TYPE_LAST_BIT ((ISCSI_SCSI_BASIC_INQUIRY_DATA_PERIPHERAL_TYPE_FIRST_BIT) + 5 - 1)
+
+/// iSCSI SCSI Basic Inquiry Data peripheral type: Bit mask.
+#define ISCSI_SCSI_BASIC_INQUIRY_DATA_PERIPHERAL_TYPE_MASK (ISCSI_BITS_GET_MASK(ISCSI_SCSI_BASIC_INQUIRY_DATA_PERIPHERAL_TYPE_FIRST_BIT, ISCSI_SCSI_BASIC_INQUIRY_DATA_PERIPHERAL_TYPE_LAST_BIT))
+
+/// iSCSI SCSI Basic Inquiry Data peripheral type: Extracts the peripheral device type bits.
+#define ISCSI_SCSI_BASIC_INQUIRY_DATA_GET_PERIPHERAL_TYPE(x) (ISCSI_BITS_GET((x), ISCSI_SCSI_BASIC_INQUIRY_DATA_PERIPHERAL_TYPE_FIRST_BIT, ISCSI_SCSI_BASIC_INQUIRY_DATA_PERIPHERAL_TYPE_LAST_BIT))
+
+/// iSCSI SCSI Basic Inquiry Data peripheral type: Stores into the peripheral device type bits.
+#define ISCSI_SCSI_BASIC_INQUIRY_DATA_PUT_PERIPHERAL_TYPE(x) (ISCSI_BITS_PUT((x), ISCSI_SCSI_BASIC_INQUIRY_DATA_PERIPHERAL_TYPE_FIRST_BIT, ISCSI_SCSI_BASIC_INQUIRY_DATA_PERIPHERAL_TYPE_LAST_BIT))
+
+/// iSCSI SCSI Basic Inquiry Data peripheral identifier: The specified peripheral device type is currently connected to this logical unit, or connection state could not be determined.
+#define ISCSI_SCSI_BASIC_INQUIRY_DATA_PERIPHERAL_ID_POSSIBLE 0x0
+
+/// iSCSI SCSI Basic Inquiry Data peripheral identifier: The target is capable of supporting the specified peripheral device type on this logical unit, but not connected.
+#define ISCSI_SCSI_BASIC_INQUIRY_DATA_PERIPHERAL_ID_SUPPORTED 0x1
+
+/// iSCSI SCSI Basic Inquiry Data peripheral identifier: The target is not capable of supporting a physical device on this logical unit.
+#define ISCSI_SCSI_BASIC_INQUIRY_DATA_PERIPHERAL_ID_NEVER 0x3
+
+/// iSCSI SCSI Basic Inquiry Data peripheral identifier: Vendor specific.
+#define ISCSI_SCSI_BASIC_INQUIRY_DATA_PERIPHERAL_ID_VENDOR_UNIQ 0x4
+
+/// iSCSI SCSI Basic Inquiry Data peripheral identifier: First bit of the three bits.
+#define ISCSI_SCSI_BASIC_INQUIRY_DATA_PERIPHERAL_ID_FIRST_BIT 5
+
+/// iSCSI SCSI Basic Inquiry Data peripheral identifier: Last bit of the three bits.
+#define ISCSI_SCSI_BASIC_INQUIRY_DATA_PERIPHERAL_ID_LAST_BIT ((ISCSI_SCSI_BASIC_INQUIRY_DATA_PERIPHERAL_ID_FIRST_BIT) + 3 - 1)
+
+/// iSCSI SCSI Basic Inquiry Data peripheral identifier: Bit mask.
+#define ISCSI_SCSI_BASIC_INQUIRY_DATA_PERIPHERAL_ID_MASK (ISCSI_BITS_GET_MASK(ISCSI_SCSI_BASIC_INQUIRY_DATA_PERIPHERAL_ID_FIRST_BIT, ISCSI_SCSI_BASIC_INQUIRY_DATA_PERIPHERAL_ID_LAST_BIT))
+
+/// iSCSI SCSI Basic Inquiry Data peripheral identifier: Extracts the peripheral device identifier bits.
+#define ISCSI_SCSI_BASIC_INQUIRY_DATA_GET_PERIPHERAL_ID(x) (ISCSI_BITS_GET((x), ISCSI_SCSI_BASIC_INQUIRY_DATA_PERIPHERAL_ID_FIRST_BIT, ISCSI_SCSI_BASIC_INQUIRY_DATA_PERIPHERAL_ID_LAST_BIT))
+
+/// iSCSI SCSI Basic Inquiry Data peripheral identifier: Stores into the peripheral device identifier bits.
+#define ISCSI_SCSI_BASIC_INQUIRY_DATA_PUT_PERIPHERAL_ID(x) (ISCSI_BITS_PUT((x), ISCSI_SCSI_BASIC_INQUIRY_DATA_PERIPHERAL_ID_FIRST_BIT, ISCSI_SCSI_BASIC_INQUIRY_DATA_PERIPHERAL_ID_LAST_BIT))
+
+
+/// iSCSI SCSI Basic Inquiry Data peripheral type modifier: First bit of the seven bits.
+#define ISCSI_SCSI_BASIC_INQUIRY_DATA_PERIPHERAL_TYPE_MOD_FIRST_BIT 0
+
+/// iSCSI SCSI Basic Inquiry Data peripheral type modifier: Last bit of the seven bits.
+#define ISCSI_SCSI_BASIC_INQUIRY_DATA_PERIPHERAL_TYPE_MOD_LAST_BIT ((ISCSI_SCSI_BASIC_INQUIRY_DATA_PERIPHERAL_TYPE_MOD_FIRST_BIT) + 7 - 1)
+
+/// iSCSI SCSI Basic Inquiry Data peripheral type modifier: Bit mask.
+#define ISCSI_SCSI_BASIC_INQUIRY_DATA_PERIPHERAL_TYPE_MOD_MASK (ISCSI_BITS_GET_MASK(ISCSI_SCSI_BASIC_INQUIRY_DATA_PERIPHERAL_TYPE_MOD_FIRST_BIT, ISCSI_SCSI_BASIC_INQUIRY_DATA_PERIPHERAL_TYPE_MOD_LAST_BIT))
+
+/// iSCSI SCSI Basic Inquiry Data peripheral identifier: Extracts the peripheral type modifier bits.
+#define ISCSI_SCSI_BASIC_INQUIRY_DATA_GET_PERIPHERAL_TYPE_MOD(x) (ISCSI_BITS_GET((x), ISCSI_SCSI_BASIC_INQUIRY_DATA_PERIPHERAL_TYPE_MOD_FIRST_BIT, ISCSI_SCSI_BASIC_INQUIRY_DATA_PERIPHERAL_TYPE_MOD_LAST_BIT))
+
+/// iSCSI SCSI Basic Inquiry Data peripheral identifier: Stores into the peripheral type modifier bits.
+#define ISCSI_SCSI_BASIC_INQUIRY_DATA_PUT_PERIPHERAL_TYPE_MOD(x) (ISCSI_BITS_PUT((x), ISCSI_SCSI_BASIC_INQUIRY_DATA_PERIPHERAL_TYPE_MOD_FIRST_BIT, ISCSI_SCSI_BASIC_INQUIRY_DATA_PERIPHERAL_TYPE_MOD_LAST_BIT))
+
+/// iSCSI SCSI Basic Inquiry Data peripheral type modifier: Removable media.
+#define ISCSI_SCSI_BASIC_INQUIRY_DATA_PERIPHERAL_TYPE_MOD_FLAGS_REMOVABLE_MEDIA (1 << 7)
+
+
+/// iSCSI SCSI Basic Inquiry Data ANSI version: None.
+#define ISCSI_SCSI_BASIC_INQUIRY_DATA_VERSION_ANSI_NONE 0x0
+
+/// iSCSI SCSI Basic Inquiry Data ANSI version: SPC.
+#define ISCSI_SCSI_BASIC_INQUIRY_DATA_VERSION_ANSI_SPC 0x3
+
+/// iSCSI SCSI Basic Inquiry Data ANSI version: SPC2.
+#define ISCSI_SCSI_BASIC_INQUIRY_DATA_VERSION_ANSI_SPC2 0x4
+
+/// iSCSI SCSI Basic Inquiry Data ANSI version: SPC3.
+#define ISCSI_SCSI_BASIC_INQUIRY_DATA_VERSION_ANSI_SPC3 0x5
+
+/// iSCSI SCSI Basic Inquiry Data ANSI version: SPC4.
+#define ISCSI_SCSI_BASIC_INQUIRY_DATA_VERSION_ANSI_SPC4 0x6
+
+/// iSCSI SCSI Basic Inquiry Data ANSI version: SPC5.
+#define ISCSI_SCSI_BASIC_INQUIRY_DATA_VERSION_ANSI_SPC5 0x7
+
+/// iSCSI SCSI Basic Inquiry Data ANSI version: First bit of the three bits.
+#define ISCSI_SCSI_BASIC_INQUIRY_DATA_VERSION_ANSI_FIRST_BIT 0
+
+/// iSCSI SCSI Basic Inquiry Data ANSI version: Last bit of the three bits.
+#define ISCSI_SCSI_BASIC_INQUIRY_DATA_VERSION_ANSI_LAST_BIT ((ISCSI_SCSI_BASIC_INQUIRY_DATA_VERSION_ANSI_FIRST_BIT) + 3 - 1)
+
+/// iSCSI SCSI Basic Inquiry Data ANSI version: Bit mask.
+#define ISCSI_SCSI_BASIC_INQUIRY_DATA_VERSION_ANSI_MASK (ISCSI_BITS_GET_MASK(ISCSI_SCSI_BASIC_INQUIRY_DATA_VERSION_ANSI_FIRST_BIT, ISCSI_SCSI_BASIC_INQUIRY_DATA_VERSION_ANSI_LAST_BIT))
+
+/// iSCSI SCSI Basic Inquiry Data ANSI version: Extracts the ANSI version bits.
+#define ISCSI_SCSI_BASIC_INQUIRY_DATA_GET_VERSION_ANSI(x) (ISCSI_BITS_GET((x), ISCSI_SCSI_BASIC_INQUIRY_DATA_VERSION_ANSI_FIRST_BIT, ISCSI_SCSI_BASIC_INQUIRY_DATA_VERSION_ANSI_LAST_BIT))
+
+/// iSCSI SCSI Basic Inquiry Data ANSI version: Stores into the ANSI version bits.
+#define ISCSI_SCSI_BASIC_INQUIRY_DATA_PUT_VERSION_ANSI(x) (ISCSI_BITS_PUT((x), ISCSI_SCSI_BASIC_INQUIRY_DATA_VERSION_ANSI_FIRST_BIT, ISCSI_SCSI_BASIC_INQUIRY_DATA_VERSION_ANSI_LAST_BIT))
+
+/// iSCSI SCSI Basic Inquiry Data ECMA version: First bit of the three bits.
+#define ISCSI_SCSI_BASIC_INQUIRY_DATA_VERSION_ECMA_FIRST_BIT 3
+
+/// iSCSI SCSI Basic Inquiry Data ECMA version: Last bit of the three bits.
+#define ISCSI_SCSI_BASIC_INQUIRY_DATA_VERSION_ECMA_LAST_BIT ((ISCSI_SCSI_BASIC_INQUIRY_DATA_VERSION_ECMA_FIRST_BIT) + 3 - 1)
+
+/// iSCSI SCSI Basic Inquiry Data ECMA version: Bit mask.
+#define ISCSI_SCSI_BASIC_INQUIRY_DATA_VERSION_ECMA_MASK (ISCSI_BITS_GET_MASK(ISCSI_SCSI_BASIC_INQUIRY_DATA_VERSION_ECMA_FIRST_BIT, ISCSI_SCSI_BASIC_INQUIRY_DATA_VERSION_ECMA_LAST_BIT))
+
+/// iSCSI SCSI Basic Inquiry Data ECMA version: Extracts the ECMA version bits.
+#define ISCSI_SCSI_BASIC_INQUIRY_DATA_GET_VERSION_ECMA(x) (ISCSI_BITS_GET((x), ISCSI_SCSI_BASIC_INQUIRY_DATA_VERSION_ECMA_FIRST_BIT, ISCSI_SCSI_BASIC_INQUIRY_DATA_VERSION_ECMA_LAST_BIT))
+
+/// iSCSI SCSI Basic Inquiry Data ECMA version: Stores into the ECMA version bits.
+#define ISCSI_SCSI_BASIC_INQUIRY_DATA_PUT_VERSION_ECMA(x) (ISCSI_BITS_PUT((x), ISCSI_SCSI_BASIC_INQUIRY_DATA_VERSION_ECMA_FIRST_BIT, ISCSI_SCSI_BASIC_INQUIRY_DATA_VERSION_ECMA_LAST_BIT))
+
+/// iSCSI SCSI Basic Inquiry Data ISO version: First bit of the two bits.
+#define ISCSI_SCSI_BASIC_INQUIRY_DATA_VERSION_ISO_FIRST_BIT 6
+
+/// iSCSI SCSI Basic Inquiry Data ISO version: Last bit of the two bits.
+#define ISCSI_SCSI_BASIC_INQUIRY_DATA_VERSION_ISO_LAST_BIT ((ISCSI_SCSI_BASIC_INQUIRY_DATA_VERSION_ISO_FIRST_BIT) + 2 - 1)
+
+/// iSCSI SCSI Basic Inquiry Data ISO version: Bit mask.
+#define ISCSI_SCSI_BASIC_INQUIRY_DATA_VERSION_ISO_MASK (ISCSI_BITS_GET_MASK(ISCSI_SCSI_BASIC_INQUIRY_DATA_VERSION_ISO_FIRST_BIT, ISCSI_SCSI_BASIC_INQUIRY_DATA_VERSION_ISO_LAST_BIT))
+
+/// iSCSI SCSI Basic Inquiry Data ISO version: Extracts the ISO version bits.
+#define ISCSI_SCSI_BASIC_INQUIRY_DATA_GET_VERSION_ISO(x) (ISCSI_BITS_GET((x), ISCSI_SCSI_BASIC_INQUIRY_DATA_VERSION_ISO_FIRST_BIT, ISCSI_SCSI_BASIC_INQUIRY_DATA_VERSION_ISO_LAST_BIT))
+
+/// iSCSI SCSI Basic Inquiry Data ISO version: Stores into the ISO version bits.
+#define ISCSI_SCSI_BASIC_INQUIRY_DATA_PUT_VERSION_ISO(x) (ISCSI_BITS_PUT((x), ISCSI_SCSI_BASIC_INQUIRY_DATA_VERSION_ISO_FIRST_BIT, ISCSI_SCSI_BASIC_INQUIRY_DATA_VERSION_ISO_LAST_BIT))
+
+
+/// iSCSI SCSI Basic Inquiry Data response data format flags: This structure complies with SCSI-1 specifications.
+#define ISCSI_SCSI_BASIC_INQUIRY_DATA_RESPONSE_DATA_FMT_FLAGS_LEVEL_0 0x00
+
+/// iSCSI SCSI Basic Inquiry Data response data format flags: This structure complies with CCS pseudo specifications.
+#define ISCSI_SCSI_BASIC_INQUIRY_DATA_RESPONSE_DATA_FMT_FLAGS_CCS 0x01
+
+/// iSCSI SCSI Basic Inquiry Data response data format flags: This structure complies with SCSI-2/3 specifications.
+#define ISCSI_SCSI_BASIC_INQUIRY_DATA_RESPONSE_DATA_FMT_FLAGS_SCSI_2 0x02
+
+/// iSCSI SCSI Basic Inquiry Data response data format flags: First bit of the four bits.
+#define ISCSI_SCSI_BASIC_INQUIRY_DATA_RESPONSE_DATA_FMT_FLAGS_FIRST_BIT 0
+
+/// iSCSI SCSI Basic Inquiry Data response data format flags: Last bit of the four bits.
+#define ISCSI_SCSI_BASIC_INQUIRY_DATA_RESPONSE_DATA_FMT_FLAGS_LAST_BIT ((ISCSI_SCSI_BASIC_INQUIRY_DATA_RESPONSE_DATA_FMT_FLAGS_FIRST_BIT) + 4 - 1)
+
+/// iSCSI SCSI Basic Inquiry Data response data format flags: Bit mask.
+#define ISCSI_SCSI_BASIC_INQUIRY_DATA_RESPONSE_DATA_FMT_FLAGS_MASK (ISCSI_BITS_GET_MASK(ISCSI_SCSI_BASIC_INQUIRY_DATA_RESPONSE_DATA_FMT_FLAGS_FIRST_BIT, ISCSI_SCSI_BASIC_INQUIRY_DATA_RESPONSE_DATA_FMT_FLAGS_LAST_BIT))
+
+/// iSCSI SCSI Basic Inquiry Data response data format flags: Extracts the response data format flags bits.
+#define ISCSI_SCSI_BASIC_INQUIRY_DATA_GET_RESPONSE_DATA_FMT_FLAGS(x) (ISCSI_BITS_GET((x), ISCSI_SCSI_BASIC_INQUIRY_DATA_RESPONSE_DATA_FMT_FLAGS_FIRST_BIT, ISCSI_SCSI_BASIC_INQUIRY_DATA_RESPONSE_DATA_FMT_FLAGS_LAST_BIT))
+
+/// iSCSI SCSI Basic Inquiry Data response data format flags: Stores into the response data format flags bits.
+#define ISCSI_SCSI_BASIC_INQUIRY_DATA_PUT_RESPONSE_DATA_FMT_FLAGS(x) (ISCSI_BITS_PUT((x), ISCSI_SCSI_BASIC_INQUIRY_DATA_RESPONSE_DATA_FMT_FLAGS_FIRST_BIT, ISCSI_SCSI_BASIC_INQUIRY_DATA_RESPONSE_DATA_FMT_FLAGS_LAST_BIT))
+
+/// iSCSI SCSI Basic Inquiry Data response data format flags: Hierarchical Support.
+#define ISCSI_SCSI_BASIC_INQUIRY_DATA_RESPONSE_DATA_FMT_FLAGS_HISUP (1 << 4)
+
+/// iSCSI SCSI Basic Inquiry Data response data format flags: Normal ACA Supported.
+#define ISCSI_SCSI_BASIC_INQUIRY_DATA_RESPONSE_DATA_FMT_FLAGS_NORMACA (1 << 5)
+
+/// iSCSI SCSI Basic Inquiry Data response data format flags: TERMINATE I/O PROCESS message device support.
+#define ISCSI_SCSI_BASIC_INQUIRY_DATA_RESPONSE_DATA_FMT_FLAGS_TERMINATE_IO_PROC_MSG (1 << 6)
+
+/// iSCSI SCSI Basic Inquiry Data response data format flags: Asynchronous Event Notification device support.
+#define ISCSI_SCSI_BASIC_INQUIRY_DATA_RESPONSE_DATA_FMT_FLAGS_ASYNC_EVENT_NOTIFY (1 << 7)
+
+
+/**
+ * @brief iSCSI SCSI basic inquiry data packet.
+ *
+ * This structure is used by the SCSI INQUIRY command
+ * in order to fill in the result if the EVPD bit is
+ * cleared.
+ */
+typedef struct __attribute__((packed)) iscsi_scsi_basic_inquiry_data_packet {
+ /// Peripheral device type and qualifier.
+ uint8_t peripheral_type_id;
+
+ /// Peripheral device type modifier and removable media bit.
+ int8_t peripheral_type_mod_flags;
+
+ /// ANSI-Approved, ECMA and ISO version.
+ uint8_t version;
+
+ /// Response data format, HISUP, NORMACA, AENC and TrmIOP flags.
+ int8_t response_data_fmt_flags;
+
+ /// Additional length in bytes.
+ uint8_t add_len;
+} iscsi_scsi_basic_inquiry_data_packet;
+
+
+/// iSCSI SCSI Standard Inquiry Data vendor identifier for disk.
+#define ISCSI_SCSI_STD_INQUIRY_DATA_DISK_VENDOR_ID "UNI FRBG"
+
+
+/// iSCSI SCSI Standard Inquiry Data TPGS flags: Protect.
+#define ISCSI_SCSI_STD_INQUIRY_DATA_TPGS_FLAGS_PROTECT (1 << 0)
+
+/// iSCSI SCSI Standard Inquiry Data TPGS flags: Third-Party Copy (3PC).
+#define ISCSI_SCSI_STD_INQUIRY_DATA_TPGS_FLAGS_3PC (1 << 3)
+
+/// iSCSI SCSI Standard Inquiry Data TPGS flags: First bit of the two bits.
+#define ISCSI_SCSI_STD_INQUIRY_DATA_TPGS_FLAGS_TPGS_FIRST_BIT 4
+
+/// iSCSI SCSI Standard Inquiry Data TPGS flags: Last bit of the two bits.
+#define ISCSI_SCSI_STD_INQUIRY_DATA_TPGS_FLAGS_TPGS_LAST_BIT ((ISCSI_SCSI_STD_INQUIRY_DATA_TPGS_FLAGS_TPGS_FIRST_BIT) + 2 - 1)
+
+/// iSCSI SCSI Standard Inquiry Data TPGS flags: Bit mask.
+#define ISCSI_SCSI_STD_INQUIRY_DATA_TPGS_FLAGS_TPGS_MASK (ISCSI_BITS_GET_MASK(ISCSI_SCSI_STD_INQUIRY_DATA_TPGS_FLAGS_TPGS_FIRST_BIT, ISCSI_SCSI_STD_INQUIRY_DATA_TPGS_FLAGS_TPGS_LAST_BIT))
+
+/// iSCSI SCSI Standard Inquiry Data TPGS flags: Extracts the Target Port Group Support (TPGS) bits.
+#define ISCSI_SCSI_STD_INQUIRY_DATA_TPGS_FLAGS_GET_TPGS(x) (ISCSI_BITS_GET((x), ISCSI_SCSI_STD_INQUIRY_DATA_TPGS_FLAGS_TPGS_FIRST_BIT, ISCSI_SCSI_STD_INQUIRY_DATA_TPGS_FLAGS_TPGS_LAST_BIT))
+
+/// iSCSI SCSI Standard Inquiry Data TPGS flags: Stores into the Target Port Group Support (TPGS) bits.
+#define ISCSI_SCSI_STD_INQUIRY_DATA_TPGS_FLAGS_PUT_TPGS(x) (ISCSI_BITS_PUT((x), ISCSI_SCSI_STD_INQUIRY_DATA_TPGS_FLAGS_TPGS_FIRST_BIT, ISCSI_SCSI_STD_INQUIRY_DATA_TPGS_FLAGS_TPGS_LAST_BIT))
+
+/// iSCSI SCSI Standard Inquiry Data TPGS flags: Access Controls Coordinator (ACC).
+#define ISCSI_SCSI_STD_INQUIRY_DATA_TPGS_FLAGS_ACC (1 << 6)
+
+/// iSCSI SCSI Standard Inquiry Data TPGS flags: SCC Supported (SCCS).
+#define ISCSI_SCSI_STD_INQUIRY_DATA_TPGS_FLAGS_SCCS (1 << 7)
+
+
+/// iSCSI SCSI Standard Inquiry Data services flags: Multi Port (MULTIP).
+#define ISCSI_SCSI_STD_INQUIRY_DATA_SERVICES_FLAGS_MULTIP (1 << 4)
+
+/// iSCSI SCSI Standard Inquiry Data services flags: VS.
+#define ISCSI_SCSI_STD_INQUIRY_DATA_SERVICES_FLAGS_VS (1 << 5)
+
+/// iSCSI SCSI Standard Inquiry Data services flags: Enclosure Services (ENCSERV).
+#define ISCSI_SCSI_STD_INQUIRY_DATA_SERVICES_FLAGS_ENCSERV (1 << 6)
+
+
+/// iSCSI SCSI Standard Inquiry Data flags: Device responds with soft reset instead of hard reset to reset condition.
+#define ISCSI_SCSI_STD_INQUIRY_DATA_FLAGS_SOFT_RESET (1 << 0)
+
+/// iSCSI SCSI Standard Inquiry Data flags: Device supports tagged command queueing.
+#define ISCSI_SCSI_STD_INQUIRY_DATA_FLAGS_COMMAND_QUEUE (1 << 1)
+
+/// iSCSI SCSI Standard Inquiry Data flags: Device supports linked commands for this logical unit.
+#define ISCSI_SCSI_STD_INQUIRY_DATA_FLAGS_LINKED_CMDS (1 << 3)
+
+/// iSCSI SCSI Standard Inquiry Data flags: Device supports synchronous data transfers.
+#define ISCSI_SCSI_STD_INQUIRY_DATA_FLAGS_SYNC (1 << 4)
+
+/// iSCSI SCSI Standard Inquiry Data flags: Device supports 16-bit wide data transfers.
+#define ISCSI_SCSI_STD_INQUIRY_DATA_FLAGS_WIDE_16_BIT (1 << 5)
+
+/// iSCSI SCSI Standard Inquiry Data flags: Device supports 32-bit wide data transfers.
+#define ISCSI_SCSI_STD_INQUIRY_DATA_FLAGS_WIDE_32_BIT (1 << 6)
+
+/// iSCSI SCSI Standard Inquiry Data flags: Device supports relative addressing mode of this logical unit.
+#define ISCSI_SCSI_STD_INQUIRY_DATA_FLAGS_REL_ADDR (1 << 7)
+
+
+/**
+ * @brief iSCSI SCSI standard inquiry data packet.
+ *
+ * This structure is used by the SCSI INQUIRY command
+ * in order to fill in the result if the EVPD bit is
+ * cleared.
+ */
+typedef struct __attribute__((packed)) iscsi_scsi_std_inquiry_data_packet {
+ /// iSCSI SCSI basic inquiry data packet.
+ iscsi_scsi_basic_inquiry_data_packet basic_inquiry;
+
+ /// PROTECT, 3PC, TPGS, ACC and SCCS.
+ uint8_t tpgs_flags;
+
+ /// MULTIP, VS and ENCSERV.
+ int8_t services_flags;
+
+ /// Flags.
+ int8_t flags;
+
+ /// Vendor identification.
+ uint8_t vendor_id[8];
+
+ /// Product identification.
+ uint8_t product_id[16];
+
+ /// Product revision level.
+ uint8_t product_rev_level[4];
+} iscsi_scsi_std_inquiry_data_packet;
+
+
+/// iSCSI SCSI Extended Inquiry Data vendor specific.
+#define ISCSI_SCSI_EXT_INQUIRY_DATA_VENDOR_SPEC_ID "UNI FREIBURG DNBD3"
+
+
+/// iSCSI SCSI Extended Inquiry Data version descriptor: iSCSI (no version claimed).
+#define ISCSI_SCSI_EXT_INQUIRY_DATA_VERSION_DESC_ISCSI_NO_VERSION 0x0960
+
+/// iSCSI SCSI Extended Inquiry Data version descriptor: SPC3 (no version claimed).
+#define ISCSI_SCSI_EXT_INQUIRY_DATA_VERSION_DESC_SPC3_NO_VERSION 0x0300
+
+/// iSCSI SCSI Extended Inquiry Data version descriptor: SBC2 (no version claimed).
+#define ISCSI_SCSI_EXT_INQUIRY_DATA_VERSION_DESC_SBC2_NO_VERSION 0x0320
+
+/// iSCSI SCSI Extended Inquiry Data version descriptor: SAM2 (no version claimed).
+#define ISCSI_SCSI_EXT_INQUIRY_DATA_VERSION_DESC_SAM2_NO_VERSION 0x0040
+
+
+/**
+ * @brief iSCSI SCSI extended inquiry data packet.
+ *
+ * This structure is used by the SCSI INQUIRY command
+ * in order to fill in the result if the EVPD bit is
+ * cleared.
+ */
+typedef struct __attribute__((packed)) iscsi_scsi_ext_inquiry_data_packet {
+ /// iSCSI SCSI standard inquiry data packet.
+ iscsi_scsi_std_inquiry_data_packet std_inquiry;
+
+ /// Vendor specific.
+ uint8_t vendor_spec[20];
+
+ /// Flags.
+ int8_t flags;
+
+ /// Reserved for future usage (always MUST be 0).
+ uint8_t reserved;
+
+ /// Version descriptors.
+ uint16_t version_desc[8];
+
+ /// Reserved for future usage (always MUST be 0).
+ uint64_t reserved2[2];
+
+ /// Reserved for future usage (always MUST be 0).
+ uint32_t reserved3;
+
+ /// Reserved for future usage (always MUST be 0).
+ uint16_t reserved4;
+} iscsi_scsi_ext_inquiry_data_packet;
+
+
+/// iSCSI SCSI Vital Product Data (VPD) Page Inquiry Data peripheral type: Direct access device.
+#define ISCSI_SCSI_VPD_PAGE_INQUIRY_DATA_PERIPHERAL_TYPE_DIRECT 0x00
+
+/// iSCSI SCSI Vital Product Data (VPD) Page Inquiry Data peripheral type: Sequential access device.
+#define ISCSI_SCSI_VPD_PAGE_INQUIRY_DATA_PERIPHERAL_TYPE_SEQ 0x01
+
+/// iSCSI SCSI Vital Product Data (VPD) Page Inquiry Data peripheral type: Printer device.
+#define ISCSI_SCSI_VPD_PAGE_INQUIRY_DATA_PERIPHERAL_TYPE_PRINTER 0x02
+
+/// iSCSI SCSI Vital Product Data (VPD) Page Inquiry Data peripheral type: Processor device.
+#define ISCSI_SCSI_VPD_PAGE_INQUIRY_DATA_PERIPHERAL_TYPE_PROCESSOR 0x03
+
+/// iSCSI SCSI Vital Product Data (VPD) Page Inquiry Data peripheral type: Write once device.
+#define ISCSI_SCSI_VPD_PAGE_INQUIRY_DATA_PERIPHERAL_TYPE_WORM 0x04
+
+/// iSCSI SCSI Vital Product Data (VPD) Page Inquiry Data peripheral type: Read only direct access (e.g. CD-ROM) device.
+#define ISCSI_SCSI_VPD_PAGE_INQUIRY_DATA_PERIPHERAL_TYPE_RO_DIRECT 0x05
+
+/// iSCSI SCSI Vital Product Data (VPD) Page Inquiry Data peripheral type: Scanner device.
+#define ISCSI_SCSI_VPD_PAGE_INQUIRY_DATA_PERIPHERAL_TYPE_SCANNER 0x06
+
+/// iSCSI SCSI Vital Product Data (VPD) Page Inquiry Data peripheral type: Optical memory device.
+#define ISCSI_SCSI_VPD_PAGE_INQUIRY_DATA_PERIPHERAL_TYPE_OPTICAL 0x07
+
+/// iSCSI SCSI Vital Product Data (VPD) Page Inquiry Data peripheral type: Medium changer device.
+#define ISCSI_SCSI_VPD_PAGE_INQUIRY_DATA_PERIPHERAL_TYPE_CHANGER 0x08
+
+/// iSCSI SCSI Vital Product Data (VPD) Page Inquiry Data peripheral type: Communications device.
+#define ISCSI_SCSI_VPD_PAGE_INQUIRY_DATA_PERIPHERAL_TYPE_COMM 0x09
+
+/// iSCSI SCSI Vital Product Data (VPD) Page Inquiry Data peripheral type: Unknown or no device.
+#define ISCSI_SCSI_VPD_PAGE_INQUIRY_DATA_PERIPHERAL_TYPE_UNKNOWN 0x1F
+
+/// iSCSI SCSI Vital Product Data (VPD) Page Inquiry Data peripheral type: First bit of the five bits.
+#define ISCSI_SCSI_VPD_PAGE_INQUIRY_DATA_PERIPHERAL_TYPE_FIRST_BIT 0
+
+/// iSCSI SCSI Vital Product Data (VPD) Page Inquiry Data peripheral type: Last bit of the five bits.
+#define ISCSI_SCSI_VPD_PAGE_INQUIRY_DATA_PERIPHERAL_TYPE_LAST_BIT ((ISCSI_SCSI_VPD_PAGE_INQUIRY_DATA_PERIPHERAL_TYPE_FIRST_BIT) + 5 - 1)
+
+/// iSCSI SCSI Vital Product Data (VPD) Page Inquiry Data peripheral type: Bit mask.
+#define ISCSI_SCSI_VPD_PAGE_INQUIRY_DATA_PERIPHERAL_TYPE_MASK (ISCSI_BITS_GET_MASK(ISCSI_SCSI_VPD_PAGE_INQUIRY_DATA_PERIPHERAL_TYPE_FIRST_BIT, ISCSI_SCSI_VPD_PAGE_INQUIRY_DATA_PERIPHERAL_TYPE_LAST_BIT))
+
+/// iSCSI SCSI Vital Product Data (VPD) Page Inquiry Data peripheral type: Extracts the peripheral device type bits.
+#define ISCSI_SCSI_VPD_PAGE_INQUIRY_DATA_GET_PERIPHERAL_TYPE(x) (ISCSI_BITS_GET((x), ISCSI_SCSI_VPD_PAGE_INQUIRY_DATA_PERIPHERAL_TYPE_FIRST_BIT, ISCSI_SCSI_VPD_PAGE_INQUIRY_DATA_PERIPHERAL_TYPE_LAST_BIT))
+
+/// iSCSI SCSI Vital Product Data (VPD) Page Inquiry Data peripheral type: Stores into the peripheral device type bits.
+#define ISCSI_SCSI_VPD_PAGE_INQUIRY_DATA_PUT_PERIPHERAL_TYPE(x) (ISCSI_BITS_PUT((x), ISCSI_SCSI_VPD_PAGE_INQUIRY_DATA_PERIPHERAL_TYPE_FIRST_BIT, ISCSI_SCSI_VPD_PAGE_INQUIRY_DATA_PERIPHERAL_TYPE_LAST_BIT))
+
+/// iSCSI SCSI Vital Product Data (VPD) Page Inquiry Data peripheral identifier: The specified peripheral device type is currently connected to this logical unit, or connection state could not be determined.
+#define ISCSI_SCSI_VPD_PAGE_INQUIRY_DATA_PERIPHERAL_ID_POSSIBLE 0x0
+
+/// iSCSI SCSI Vital Product Data (VPD) Page Inquiry Data peripheral identifier: The target is capable of supporting the specified peripheral device type on this logical unit, but not connected.
+#define ISCSI_SCSI_VPD_PAGE_INQUIRY_DATA_PERIPHERAL_ID_SUPPORTED 0x1
+
+/// iSCSI SCSI Vital Product Data (VPD) Page Inquiry Data peripheral identifier: The target is not capable of supporting a physical device on this logical unit.
+#define ISCSI_SCSI_VPD_PAGE_INQUIRY_DATA_PERIPHERAL_ID_NEVER 0x3
+
+/// iSCSI SCSI Vital Product Data (VPD) Page Inquiry Data peripheral identifier: Vendor specific.
+#define ISCSI_SCSI_VPD_PAGE_INQUIRY_DATA_PERIPHERAL_ID_VENDOR_UNIQ 0x4
+
+/// iSCSI SCSI Vital Product Data (VPD) Page Inquiry Data peripheral identifier: First bit of the three bits.
+#define ISCSI_SCSI_VPD_PAGE_INQUIRY_DATA_PERIPHERAL_ID_FIRST_BIT 5
+
+/// iSCSI SCSI Vital Product Data (VPD) Page Inquiry Data peripheral identifier: Last bit of the three bits.
+#define ISCSI_SCSI_VPD_PAGE_INQUIRY_DATA_PERIPHERAL_ID_LAST_BIT ((ISCSI_SCSI_VPD_PAGE_INQUIRY_DATA_PERIPHERAL_ID_FIRST_BIT) + 3 - 1)
+
+/// iSCSI SCSI Vital Product Data (VPD) Page Inquiry Data peripheral identifier: Bit mask.
+#define ISCSI_SCSI_VPD_PAGE_INQUIRY_DATA_PERIPHERAL_ID_MASK (ISCSI_BITS_GET_MASK(ISCSI_SCSI_VPD_PAGE_INQUIRY_DATA_PERIPHERAL_ID_FIRST_BIT, ISCSI_SCSI_VPD_PAGE_INQUIRY_DATA_PERIPHERAL_ID_LAST_BIT))
+
+/// iSCSI SCSI Vital Product Data (VPD) Page Inquiry Data peripheral identifier: Extracts the peripheral device identifier bits.
+#define ISCSI_SCSI_VPD_PAGE_INQUIRY_DATA_GET_PERIPHERAL_ID(x) (ISCSI_BITS_GET((x), ISCSI_SCSI_VPD_PAGE_INQUIRY_DATA_PERIPHERAL_ID_FIRST_BIT, ISCSI_SCSI_VPD_PAGE_INQUIRY_DATA_PERIPHERAL_ID_LAST_BIT))
+
+/// iSCSI SCSI Vital Product Data (VPD) Page Inquiry Data peripheral identifier: Stores into the peripheral device identifier bits.
+#define ISCSI_SCSI_VPD_PAGE_INQUIRY_DATA_PUT_PERIPHERAL_ID(x) (ISCSI_BITS_PUT((x), ISCSI_SCSI_VPD_PAGE_INQUIRY_DATA_PERIPHERAL_ID_FIRST_BIT, ISCSI_SCSI_VPD_PAGE_INQUIRY_DATA_PERIPHERAL_ID_LAST_BIT))
+
+
+/// iSCSI SCSI Vital Product Data (VPD) Page Inquiry Data page code: Supported VPD pages.
+#define ISCSI_SCSI_VPD_PAGE_INQUIRY_DATA_PAGE_CODE_SUPPORTED_VPD_PAGES 0x00
+
+/// iSCSI SCSI Vital Product Data (VPD) Page Inquiry Data page code: Unit serial number.
+#define ISCSI_SCSI_VPD_PAGE_INQUIRY_DATA_PAGE_CODE_UNIT_SERIAL_NUMBER 0x80
+
+/// iSCSI SCSI Vital Product Data (VPD) Page Inquiry Data page code: Device identification.
+#define ISCSI_SCSI_VPD_PAGE_INQUIRY_DATA_PAGE_CODE_DEVICE_ID 0x83
+
+/// iSCSI SCSI Vital Product Data (VPD) Page Inquiry Data page code: Software interface identification.
+#define ISCSI_SCSI_VPD_PAGE_INQUIRY_DATA_PAGE_CODE_SOFTWARE_IFACE_ID 0x84
+
+/// iSCSI SCSI Vital Product Data (VPD) Page Inquiry Data page code: Management network addresses.
+#define ISCSI_SCSI_VPD_PAGE_INQUIRY_DATA_PAGE_CODE_MANAGEMENT_NETWORK_ADDRS 0x85
+
+/// iSCSI SCSI Vital Product Data (VPD) Page Inquiry Data page code: Extended inquiry data.
+#define ISCSI_SCSI_VPD_PAGE_INQUIRY_DATA_PAGE_CODE_EXTENDED_INQUIRY_DATA 0x86
+
+/// iSCSI SCSI Vital Product Data (VPD) Page Inquiry Data page code: Mode page policy.
+#define ISCSI_SCSI_VPD_PAGE_INQUIRY_DATA_PAGE_CODE_MODE_PAGE_POLICY 0x87
+
+/// iSCSI SCSI Vital Product Data (VPD) Page Inquiry Data page code: SCSI ports.
+#define ISCSI_SCSI_VPD_PAGE_INQUIRY_DATA_PAGE_CODE_SCSI_PORTS 0x88
+
+/// iSCSI SCSI Vital Product Data (VPD) Page Inquiry Data page code: Block limits.
+#define ISCSI_SCSI_VPD_PAGE_INQUIRY_DATA_PAGE_CODE_BLOCK_LIMITS 0xB0
+
+/// iSCSI SCSI Vital Product Data (VPD) Page Inquiry Data page code: Block device characteristics.
+#define ISCSI_SCSI_VPD_PAGE_INQUIRY_DATA_PAGE_CODE_BLOCK_DEV_CHARS 0xB1
+
+/// iSCSI SCSI Vital Product Data (VPD) Page Inquiry Data page code: Thin provisioning.
+#define ISCSI_SCSI_VPD_PAGE_INQUIRY_DATA_PAGE_CODE_THIN_PROVISION 0xB2
+
+
+/// iSCSI SCSI Vital Product Data (VPD) Page Inquiry Data page code: Maximum serial string length in bytes.
+#define ISCSI_SCSI_VPD_PAGE_INQUIRY_DATA_PAGE_CODE_MAX_SERIAL_STRING 32
+
+
+/**
+ * @brief iSCSI SCSI Vital Product Data (VPD) Page Inquiry data packet.
+ *
+ * This structure is used by the SCSI INQUIRY command
+ * in order to fill in the result if the EVPD bit is
+ * set.
+ */
+typedef struct __attribute__((packed)) iscsi_scsi_vpd_page_inquiry_data_packet {
+ /// Peripheral device type and qualifier.
+ uint8_t peripheral_type_id;
+
+ /// Page code.
+ uint8_t page_code;
+
+ /// Allocation length in bytes.
+ uint16_t alloc_len;
+
+ /// Parameters.
+ uint8_t params[0];
+} iscsi_scsi_vpd_page_inquiry_data_packet;
+
+
+/// iSCSI SCSI Vital Product Data (VPD) Page Designation Descriptor Inquiry data protocol identifier: iSCSI.
+#define ISCSI_SCSI_VPD_PAGE_DESIGN_DESC_INQUIRY_DATA_PROTOCOL_ID_ISCSI 0x05
+
+/// iSCSI SCSI Vital Product Data (VPD) Page Designation Descriptor Inquiry data protocol identifier: First bit of the four bits.
+#define ISCSI_SCSI_VPD_PAGE_DESIGN_DESC_INQUIRY_DATA_PROTOCOL_ID_FIRST_BIT 0
+
+/// iSCSI SCSI Vital Product Data (VPD) Page Designation Descriptor Inquiry data protocol identifier: Last bit of the four bits.
+#define ISCSI_SCSI_VPD_PAGE_DESIGN_DESC_INQUIRY_DATA_PROTOCOL_ID_LAST_BIT ((ISCSI_SCSI_VPD_PAGE_DESIGN_DESC_INQUIRY_DATA_PROTOCOL_ID_FIRST_BIT) + 4 - 1)
+
+/// iSCSI SCSI Vital Product Data (VPD) Page Designation Descriptor Inquiry data protocol identifier: Bit mask.
+#define ISCSI_SCSI_VPD_PAGE_DESIGN_DESC_INQUIRY_DATA_PROTOCOL_ID_MASK (ISCSI_BITS_GET_MASK(ISCSI_SCSI_VPD_PAGE_DESIGN_DESC_INQUIRY_DATA_PROTOCOL_ID_FIRST_BIT, ISCSI_SCSI_VPD_PAGE_DESIGN_DESC_INQUIRY_DATA_PROTOCOL_ID_LAST_BIT))
+
+/// iSCSI SCSI Vital Product Data (VPD) Page Designation Descriptor Inquiry data protocol identifier: Extracts the protocol identifier bits.
+#define ISCSI_SCSI_VPD_PAGE_DESIGN_DESC_INQUIRY_DATA_GET_PROTOCOL_ID(x) (ISCSI_BITS_GET((x), ISCSI_SCSI_VPD_PAGE_DESIGN_DESC_INQUIRY_DATA_PROTOCOL_ID_FIRST_BIT, ISCSI_SCSI_VPD_PAGE_DESIGN_DESC_INQUIRY_DATA_PROTOCOL_ID_LAST_BIT))
+
+/// iSCSI SCSI Vital Product Data (VPD) Page Designation Descriptor Inquiry data protocol identifier: Stores into the protocol identifier bits.
+#define ISCSI_SCSI_VPD_PAGE_DESIGN_DESC_INQUIRY_DATA_PUT_PROTOCOL_ID(x) (ISCSI_BITS_PUT((x), ISCSI_SCSI_VPD_PAGE_DESIGN_DESC_INQUIRY_DATA_PROTOCOL_ID_FIRST_BIT, ISCSI_SCSI_VPD_PAGE_DESIGN_DESC_INQUIRY_DATA_PROTOCOL_ID_LAST_BIT))
+
+/// iSCSI SCSI Vital Product Data (VPD) Page Designation Descriptor Inquiry data code set: Binary encoding.
+#define ISCSI_SCSI_VPD_PAGE_DESIGN_DESC_INQUIRY_DATA_CODE_SET_BINARY 0x01
+
+/// iSCSI SCSI Vital Product Data (VPD) Page Designation Descriptor Inquiry data code set: ASCII encoding.
+#define ISCSI_SCSI_VPD_PAGE_DESIGN_DESC_INQUIRY_DATA_CODE_SET_ASCII 0x02
+
+/// iSCSI SCSI Vital Product Data (VPD) Page Designation Descriptor Inquiry data code set: UTF-8 encoding.
+#define ISCSI_SCSI_VPD_PAGE_DESIGN_DESC_INQUIRY_DATA_CODE_SET_UTF8 0x03
+
+/// iSCSI SCSI Vital Product Data (VPD) Page Designation Descriptor Inquiry data code set: First bit of the four bits.
+#define ISCSI_SCSI_VPD_PAGE_DESIGN_DESC_INQUIRY_DATA_CODE_SET_FIRST_BIT 4
+
+/// iSCSI SCSI Vital Product Data (VPD) Page Designation Descriptor Inquiry data code set: Last bit of the four bits.
+#define ISCSI_SCSI_VPD_PAGE_DESIGN_DESC_INQUIRY_DATA_CODE_SET_LAST_BIT ((ISCSI_SCSI_VPD_PAGE_DESIGN_DESC_INQUIRY_DATA_CODE_SET_FIRST_BIT) + 8 - 1)
+
+/// iSCSI SCSI Vital Product Data (VPD) Page Designation Descriptor Inquiry data code set: Bit mask.
+#define ISCSI_SCSI_VPD_PAGE_DESIGN_DESC_INQUIRY_DATA_CODE_SET_MASK (ISCSI_BITS_GET_MASK(ISCSI_SCSI_VPD_PAGE_DESIGN_DESC_INQUIRY_DATA_CODE_SET_FIRST_BIT, ISCSI_SCSI_VPD_PAGE_DESIGN_DESC_INQUIRY_DATA_CODE_SET_LAST_BIT))
+
+/// iSCSI SCSI Vital Product Data (VPD) Page Designation Descriptor Inquiry data code set: Extracts the protocol identifier bits.
+#define ISCSI_SCSI_VPD_PAGE_DESIGN_DESC_INQUIRY_DATA_GET_CODE_SET(x) (ISCSI_BITS_GET((x), ISCSI_SCSI_VPD_PAGE_DESIGN_DESC_INQUIRY_DATA_CODE_SET_FIRST_BIT, ISCSI_SCSI_VPD_PAGE_DESIGN_DESC_INQUIRY_DATA_CODE_SET_LAST_BIT))
+
+/// iSCSI SCSI Vital Product Data (VPD) Page Designation Descriptor Inquiry data code set: Stores into the protocol identifier bits.
+#define ISCSI_SCSI_VPD_PAGE_DESIGN_DESC_INQUIRY_DATA_PUT_CODE_SET(x) (ISCSI_BITS_PUT((x), ISCSI_SCSI_VPD_PAGE_DESIGN_DESC_INQUIRY_DATA_CODE_SET_FIRST_BIT, ISCSI_SCSI_VPD_PAGE_DESIGN_DESC_INQUIRY_DATA_CODE_SET_LAST_BIT))
+
+
+/// iSCSI SCSI Vital Product Data (VPD) Page Designation Descriptor Inquiry data flags type: Vendor specific.
+#define ISCSI_SCSI_VPD_PAGE_DESIGN_DESC_INQUIRY_DATA_FLAGS_TYPE_VENDOR_SPEC 0x00
+
+/// iSCSI SCSI Vital Product Data (VPD) Page Designation Descriptor Inquiry data flags type: T10 vendor identifier.
+#define ISCSI_SCSI_VPD_PAGE_DESIGN_DESC_INQUIRY_DATA_FLAGS_TYPE_T10_VENDOR_ID 0x01
+
+/// iSCSI SCSI Vital Product Data (VPD) Page Designation Descriptor Inquiry data flags type: EUI64.
+#define ISCSI_SCSI_VPD_PAGE_DESIGN_DESC_INQUIRY_DATA_FLAGS_TYPE_EUI64 0x02
+
+/// iSCSI SCSI Vital Product Data (VPD) Page Designation Descriptor Inquiry data flags type: NAA.
+#define ISCSI_SCSI_VPD_PAGE_DESIGN_DESC_INQUIRY_DATA_FLAGS_TYPE_NAA 0x03
+
+/// iSCSI SCSI Vital Product Data (VPD) Page Designation Descriptor Inquiry data flags type: Relative target port.
+#define ISCSI_SCSI_VPD_PAGE_DESIGN_DESC_INQUIRY_DATA_FLAGS_TYPE_REL_TARGET_PORT 0x04
+
+/// iSCSI SCSI Vital Product Data (VPD) Page Designation Descriptor Inquiry data flags type: Target port group.
+#define ISCSI_SCSI_VPD_PAGE_DESIGN_DESC_INQUIRY_DATA_FLAGS_TYPE_TARGET_PORT_GROUP 0x05
+
+/// iSCSI SCSI Vital Product Data (VPD) Page Designation Descriptor Inquiry data flags type: Logical unit group.
+#define ISCSI_SCSI_VPD_PAGE_DESIGN_DESC_INQUIRY_DATA_FLAGS_TYPE_LOGICAL_UNIT_GROUP 0x06
+
+/// iSCSI SCSI Vital Product Data (VPD) Page Designation Descriptor Inquiry data flags type: MD5 logical unit.
+#define ISCSI_SCSI_VPD_PAGE_DESIGN_DESC_INQUIRY_DATA_FLAGS_TYPE_MD5_LOGICAL_UNIT 0x07
+
+/// iSCSI SCSI Vital Product Data (VPD) Page Designation Descriptor Inquiry data flags type: SCSI name.
+#define ISCSI_SCSI_VPD_PAGE_DESIGN_DESC_INQUIRY_DATA_FLAGS_TYPE_SCSI_NAME 0x08
+
+/// iSCSI SCSI Vital Product Data (VPD) Page Designation Descriptor Inquiry data flags type: First bit of the four bits.
+#define ISCSI_SCSI_VPD_PAGE_DESIGN_DESC_INQUIRY_DATA_FLAGS_TYPE_FIRST_BIT 0
+
+/// iSCSI SCSI Vital Product Data (VPD) Page Designation Descriptor Inquiry data flags type: Last bit of the four bits.
+#define ISCSI_SCSI_VPD_PAGE_DESIGN_DESC_INQUIRY_DATA_FLAGS_TYPE_LAST_BIT ((ISCSI_SCSI_VPD_PAGE_DESIGN_DESC_INQUIRY_DATA_FLAGS_TYPE_FIRST_BIT) + 4 - 1)
+
+/// iSCSI SCSI Vital Product Data (VPD) Page Designation Descriptor Inquiry data flags type: Bit mask.
+#define ISCSI_SCSI_VPD_PAGE_DESIGN_DESC_INQUIRY_DATA_FLAGS_TYPE_MASK (ISCSI_BITS_GET_MASK(ISCSI_SCSI_VPD_PAGE_DESIGN_DESC_INQUIRY_DATA_FLAGS_TYPE_FIRST_BIT, ISCSI_SCSI_VPD_PAGE_DESIGN_DESC_INQUIRY_DATA_FLAGS_TYPE_LAST_BIT))
+
+/// iSCSI SCSI Vital Product Data (VPD) Page Designation Descriptor Inquiry data flags type: Extracts the type bits.
+#define ISCSI_SCSI_VPD_PAGE_DESIGN_DESC_INQUIRY_DATA_FLAGS_GET_TYPE(x) (ISCSI_BITS_GET((x), ISCSI_SCSI_VPD_PAGE_DESIGN_DESC_INQUIRY_DATA_FLAGS_TYPE_FIRST_BIT, ISCSI_SCSI_VPD_PAGE_DESIGN_DESC_INQUIRY_DATA_FLAGS_TYPE_LAST_BIT))
+
+/// iSCSI SCSI Vital Product Data (VPD) Page Designation Descriptor Inquiry data flags type: Stores into the type bits.
+#define ISCSI_SCSI_VPD_PAGE_DESIGN_DESC_INQUIRY_DATA_FLAGS_PUT_TYPE(x) (ISCSI_BITS_PUT((x), ISCSI_SCSI_VPD_PAGE_DESIGN_DESC_INQUIRY_DATA_FLAGS_TYPE_FIRST_BIT, ISCSI_SCSI_VPD_PAGE_DESIGN_DESC_INQUIRY_DATA_FLAGS_TYPE_LAST_BIT))
+
+/// iSCSI SCSI Vital Product Data (VPD) Page Designation Descriptor Inquiry data flags association: Logical unit.
+#define ISCSI_SCSI_VPD_PAGE_DESIGN_DESC_INQUIRY_DATA_FLAGS_ASSOC_LOGICAL_UNIT 0x0
+
+/// iSCSI SCSI Vital Product Data (VPD) Page Designation Descriptor Inquiry data flags association: Target port.
+#define ISCSI_SCSI_VPD_PAGE_DESIGN_DESC_INQUIRY_DATA_FLAGS_ASSOC_TARGET_PORT 0x1
+
+/// iSCSI SCSI Vital Product Data (VPD) Page Designation Descriptor Inquiry data flags association: Target device.
+#define ISCSI_SCSI_VPD_PAGE_DESIGN_DESC_INQUIRY_DATA_FLAGS_ASSOC_TARGET_DEVICE 0x2
+
+/// iSCSI SCSI Vital Product Data (VPD) Page Designation Descriptor Inquiry data flags association: First bit of the two bits.
+#define ISCSI_SCSI_VPD_PAGE_DESIGN_DESC_INQUIRY_DATA_FLAGS_ASSOC_FIRST_BIT 4
+
+/// iSCSI SCSI Vital Product Data (VPD) Page Designation Descriptor Inquiry data flags association: Last bit of the two bits.
+#define ISCSI_SCSI_VPD_PAGE_DESIGN_DESC_INQUIRY_DATA_FLAGS_ASSOC_LAST_BIT ((ISCSI_SCSI_VPD_PAGE_DESIGN_DESC_INQUIRY_DATA_FLAGS_ASSOC_FIRST_BIT) + 6 - 1)
+
+/// iSCSI SCSI Vital Product Data (VPD) Page Designation Descriptor Inquiry data flags association: Bit mask.
+#define ISCSI_SCSI_VPD_PAGE_DESIGN_DESC_INQUIRY_DATA_FLAGS_ASSOC_MASK (ISCSI_BITS_GET_MASK(ISCSI_SCSI_VPD_PAGE_DESIGN_DESC_INQUIRY_DATA_FLAGS_ASSOC_FIRST_BIT, ISCSI_SCSI_VPD_PAGE_DESIGN_DESC_INQUIRY_DATA_FLAGS_ASSOC_LAST_BIT))
+
+/// iSCSI SCSI Vital Product Data (VPD) Page Designation Descriptor Inquiry data flags association: Extracts the association bits.
+#define ISCSI_SCSI_VPD_PAGE_DESIGN_DESC_INQUIRY_DATA_FLAGS_GET_ASSOC(x) (ISCSI_BITS_GET((x), ISCSI_SCSI_VPD_PAGE_DESIGN_DESC_INQUIRY_DATA_FLAGS_ASSOC_FIRST_BIT, ISCSI_SCSI_VPD_PAGE_DESIGN_DESC_INQUIRY_DATA_FLAGS_ASSOC_LAST_BIT))
+
+/// iSCSI SCSI Vital Product Data (VPD) Page Designation Descriptor Inquiry data flags association: Stores into the association bits.
+#define ISCSI_SCSI_VPD_PAGE_DESIGN_DESC_INQUIRY_DATA_FLAGS_PUT_ASSOC(x) (ISCSI_BITS_PUT((x), ISCSI_SCSI_VPD_PAGE_DESIGN_DESC_INQUIRY_DATA_FLAGS_ASSOC_FIRST_BIT, ISCSI_SCSI_VPD_PAGE_DESIGN_DESC_INQUIRY_DATA_FLAGS_ASSOC_LAST_BIT))
+
+/// iSCSI SCSI Vital Product Data (VPD) Page Designation Descriptor Inquiry data flags: Protocol Identifier Valid (PIV).
+#define ISCSI_SCSI_VPD_PAGE_DESIGN_DESC_INQUIRY_DATA_FLAGS_PIV (1 << 7)
+
+
+/**
+ * @brief iSCSI SCSI Vital Product Data (VPD) Page Designation Descriptor Inquiry data packet.
+ *
+ * This structure is used by the SCSI INQUIRY command
+ * in order to fill in the result if the EVPD bit is
+ * set.
+ */
+typedef struct __attribute__((packed)) iscsi_scsi_vpd_page_design_desc_inquiry_data_packet {
+ /// Protocol identifier and code set.
+ uint8_t protocol_id_code_set;
+
+ /// Flags.
+ int8_t flags;
+
+ /// Reserved for future usage (always MUST be 0).
+ uint8_t reserved;
+
+ /// Length in bytes.
+ uint8_t len;
+
+ /// Designation descriptor.
+ uint8_t desc[0];
+} iscsi_scsi_vpd_page_design_desc_inquiry_data_packet;
+
+
+/**
+ * @brief iSCSI SCSI Vital Product Data (VPD) Page Designation Descriptor IEEE NAA Extended Inquiry data packet.
+ *
+ * This structure is used by the SCSI INQUIRY command
+ * in order to fill in the result if the EVPD bit is
+ * set.
+ */
+typedef struct __attribute__((packed)) iscsi_scsi_vpd_page_design_desc_ieee_naa_ext_inquiry_data_packet {
+ /// IEEE NAA Extended.
+ uint64_t ieee_naa_ext;
+} iscsi_scsi_vpd_page_design_desc_ieee_naa_ext_inquiry_data_packet;
+
+
+/**
+ * @brief iSCSI SCSI Vital Product Data (VPD) Page Designation Descriptor T10 Vendor ID Inquiry data packet.
+ *
+ * This structure is used by the SCSI INQUIRY command
+ * in order to fill in the result if the EVPD bit is
+ * set.
+ */
+typedef struct __attribute__((packed)) iscsi_scsi_vpd_page_design_desc_t10_vendor_id_inquiry_data_packet {
+ /// Vendor identification.
+ uint8_t vendor_id[8];
+
+ /// Product identification.
+ uint8_t product_id[16];
+
+ /// Unit serial number.
+ uint8_t unit_serial_num[32];
+} iscsi_scsi_vpd_page_design_desc_t10_vendor_id_inquiry_data_packet;
+
+
+/**
+ * @brief iSCSI SCSI Vital Product Data (VPD) Page Designation Descriptor Relative Target Port Inquiry data packet.
+ *
+ * This structure is used by the SCSI INQUIRY command
+ * in order to fill in the result if the EVPD bit is
+ * set.
+ */
+typedef struct __attribute__((packed)) iscsi_scsi_vpd_page_design_desc_rel_target_port_inquiry_data_packet {
+ /// Reserved for future usage (always MUST be 0).
+ uint16_t reserved;
+
+ /// Port index.
+ uint16_t index;
+} iscsi_scsi_vpd_page_design_desc_rel_target_port_inquiry_data_packet;
+
+
+/**
+ * @brief iSCSI SCSI Vital Product Data (VPD) Page Designation Descriptor Target Port Group Inquiry data packet.
+ *
+ * This structure is used by the SCSI INQUIRY command
+ * in order to fill in the result if the EVPD bit is
+ * set.
+ */
+typedef struct __attribute__((packed)) iscsi_scsi_vpd_page_design_desc_target_port_group_inquiry_data_packet {
+ /// Reserved for future usage (always MUST be 0).
+ uint16_t reserved;
+
+ /// Port group index.
+ uint16_t index;
+} iscsi_scsi_vpd_page_design_desc_target_port_group_inquiry_data_packet;
+
+
+/**
+ * @brief iSCSI SCSI Vital Product Data (VPD) Page Designation Descriptor Logical Unit Group Inquiry data packet.
+ *
+ * This structure is used by the SCSI INQUIRY command
+ * in order to fill in the result if the EVPD bit is
+ * set.
+ */
+typedef struct __attribute__((packed)) iscsi_scsi_vpd_page_design_desc_logical_unit_group_inquiry_data_packet {
+ /// Reserved for future usage (always MUST be 0).
+ uint16_t reserved;
+
+ /// Logical unit identifier.
+ uint16_t id;
+} iscsi_scsi_vpd_page_design_desc_logical_unit_group_inquiry_data_packet;
+
+
+/// iSCSI SCSI Vital Product Data (VPD) Page Extended Inquiry Data peripheral type: Direct access device.
+#define ISCSI_SCSI_VPD_PAGE_EXT_INQUIRY_DATA_PERIPHERAL_TYPE_DIRECT 0x00
+
+/// iSCSI SCSI Vital Product Data (VPD) Page Extended Inquiry Data peripheral type: Sequential access device.
+#define ISCSI_SCSI_VPD_PAGE_EXT_INQUIRY_DATA_PERIPHERAL_TYPE_SEQ 0x01
+
+/// iSCSI SCSI Vital Product Data (VPD) Page Extended Inquiry Data peripheral type: Printer device.
+#define ISCSI_SCSI_VPD_PAGE_EXT_INQUIRY_DATA_PERIPHERAL_TYPE_PRINTER 0x02
+
+/// iSCSI SCSI Vital Product Data (VPD) Page Extended Inquiry Data peripheral type: Processor device.
+#define ISCSI_SCSI_VPD_PAGE_EXT_INQUIRY_DATA_PERIPHERAL_TYPE_PROCESSOR 0x03
+
+/// iSCSI SCSI Vital Product Data (VPD) Page Extended Inquiry Data peripheral type: Write once device.
+#define ISCSI_SCSI_VPD_PAGE_EXT_INQUIRY_DATA_PERIPHERAL_TYPE_WORM 0x04
+
+/// iSCSI SCSI Vital Product Data (VPD) Page Extended Inquiry Data peripheral type: Read only direct access (e.g. CD-ROM) device.
+#define ISCSI_SCSI_VPD_PAGE_EXT_INQUIRY_DATA_PERIPHERAL_TYPE_RO_DIRECT 0x05
+
+/// iSCSI SCSI Vital Product Data (VPD) Page Extended Inquiry Data peripheral type: Scanner device.
+#define ISCSI_SCSI_VPD_PAGE_EXT_INQUIRY_DATA_PERIPHERAL_TYPE_SCANNER 0x06
+
+/// iSCSI SCSI Vital Product Data (VPD) Page Extended Inquiry Data peripheral type: Optical memory device.
+#define ISCSI_SCSI_VPD_PAGE_EXT_INQUIRY_DATA_PERIPHERAL_TYPE_OPTICAL 0x07
+
+/// iSCSI SCSI Vital Product Data (VPD) Page Extended Inquiry Data peripheral type: Medium changer device.
+#define ISCSI_SCSI_VPD_PAGE_EXT_INQUIRY_DATA_PERIPHERAL_TYPE_CHANGER 0x08
+
+/// iSCSI SCSI Vital Product Data (VPD) Page Extended Inquiry Data peripheral type: Communications device.
+#define ISCSI_SCSI_VPD_PAGE_EXT_INQUIRY_DATA_PERIPHERAL_TYPE_COMM 0x09
+
+/// iSCSI SCSI Vital Product Data (VPD) Page Extended Inquiry Data peripheral type: Unknown or no device.
+#define ISCSI_SCSI_VPD_PAGE_EXT_INQUIRY_DATA_PERIPHERAL_TYPE_UNKNOWN 0x1F
+
+/// iSCSI SCSI Vital Product Data (VPD) Page Extended Inquiry Data peripheral type: First bit of the five bits.
+#define ISCSI_SCSI_VPD_PAGE_EXT_INQUIRY_DATA_PERIPHERAL_TYPE_FIRST_BIT 0
+
+/// iSCSI SCSI Vital Product Data (VPD) Page Extended Inquiry Data peripheral type: Last bit of the five bits.
+#define ISCSI_SCSI_VPD_PAGE_EXT_INQUIRY_DATA_PERIPHERAL_TYPE_LAST_BIT ((ISCSI_SCSI_VPD_PAGE_EXT_INQUIRY_DATA_PERIPHERAL_TYPE_FIRST_BIT) + 5 - 1)
+
+/// iSCSI SCSI Vital Product Data (VPD) Page Extended Inquiry Data peripheral type: Bit mask.
+#define ISCSI_SCSI_VPD_PAGE_EXT_INQUIRY_DATA_PERIPHERAL_TYPE_MASK (ISCSI_BITS_GET_MASK(ISCSI_SCSI_VPD_PAGE_EXT_INQUIRY_DATA_PERIPHERAL_TYPE_FIRST_BIT, ISCSI_SCSI_VPD_PAGE_EXT_INQUIRY_DATA_PERIPHERAL_TYPE_LAST_BIT))
+
+/// iSCSI SCSI Vital Product Data (VPD) Page Extended Inquiry Data peripheral type: Extracts the peripheral device type bits.
+#define ISCSI_SCSI_VPD_PAGE_EXT_INQUIRY_DATA_GET_PERIPHERAL_TYPE(x) (ISCSI_BITS_GET((x), ISCSI_SCSI_VPD_PAGE_EXT_INQUIRY_DATA_PERIPHERAL_TYPE_FIRST_BIT, ISCSI_SCSI_VPD_PAGE_EXT_INQUIRY_DATA_PERIPHERAL_TYPE_LAST_BIT))
+
+/// iSCSI SCSI Vital Product Data (VPD) Page Extended Inquiry Data peripheral type: Stores into the peripheral device type bits.
+#define ISCSI_SCSI_VPD_PAGE_EXT_INQUIRY_DATA_PUT_PERIPHERAL_TYPE(x) (ISCSI_BITS_PUT((x), ISCSI_SCSI_VPD_PAGE_EXT_INQUIRY_DATA_PERIPHERAL_TYPE_FIRST_BIT, ISCSI_SCSI_VPD_PAGE_EXT_INQUIRY_DATA_PERIPHERAL_TYPE_LAST_BIT))
+
+/// iSCSI SCSI Vital Product Data (VPD) Page Extended Inquiry Data peripheral identifier: The specified peripheral device type is currently connected to this logical unit, or connection state could not be determined.
+#define ISCSI_SCSI_VPD_PAGE_EXT_INQUIRY_DATA_PERIPHERAL_ID_POSSIBLE 0x0
+
+/// iSCSI SCSI Vital Product Data (VPD) Page Extended Inquiry Data peripheral identifier: The target is capable of supporting the specified peripheral device type on this logical unit, but not connected.
+#define ISCSI_SCSI_VPD_PAGE_EXT_INQUIRY_DATA_PERIPHERAL_ID_SUPPORTED 0x1
+
+/// iSCSI SCSI Vital Product Data (VPD) Page Extended Inquiry Data peripheral identifier: The target is not capable of supporting a physical device on this logical unit.
+#define ISCSI_SCSI_VPD_PAGE_EXT_INQUIRY_DATA_PERIPHERAL_ID_NEVER 0x3
+
+/// iSCSI SCSI Vital Product Data (VPD) Page Extended Inquiry Data peripheral identifier: Vendor specific.
+#define ISCSI_SCSI_VPD_PAGE_EXT_INQUIRY_DATA_PERIPHERAL_ID_VENDOR_UNIQ 0x4
+
+/// iSCSI SCSI Vital Product Data (VPD) Page Extended Inquiry Data peripheral identifier: First bit of the three bits.
+#define ISCSI_SCSI_VPD_PAGE_EXT_INQUIRY_DATA_PERIPHERAL_ID_FIRST_BIT 5
+
+/// iSCSI SCSI Vital Product Data (VPD) Page Extended Inquiry Data peripheral identifier: Last bit of the three bits.
+#define ISCSI_SCSI_VPD_PAGE_EXT_INQUIRY_DATA_PERIPHERAL_ID_LAST_BIT ((ISCSI_SCSI_VPD_PAGE_EXT_INQUIRY_DATA_PERIPHERAL_ID_FIRST_BIT) + 3 - 1)
+
+/// iSCSI SCSI Vital Product Data (VPD) Page Extended Inquiry Data peripheral identifier: Bit mask.
+#define ISCSI_SCSI_VPD_PAGE_EXT_INQUIRY_DATA_PERIPHERAL_ID_MASK (ISCSI_BITS_GET_MASK(ISCSI_SCSI_VPD_PAGE_EXT_INQUIRY_DATA_PERIPHERAL_ID_FIRST_BIT, ISCSI_SCSI_VPD_PAGE_EXT_INQUIRY_DATA_PERIPHERAL_ID_LAST_BIT))
+
+/// iSCSI SCSI Vital Product Data (VPD) Page Extended Inquiry Data peripheral identifier: Extracts the peripheral device identifier bits.
+#define ISCSI_SCSI_VPD_PAGE_EXT_INQUIRY_DATA_GET_PERIPHERAL_ID(x) (ISCSI_BITS_GET((x), ISCSI_SCSI_VPD_PAGE_EXT_INQUIRY_DATA_PERIPHERAL_ID_FIRST_BIT, ISCSI_SCSI_VPD_PAGE_EXT_INQUIRY_DATA_PERIPHERAL_ID_LAST_BIT))
+
+/// iSCSI SCSI Vital Product Data (VPD) Page Extended Inquiry Data peripheral identifier: Stores into the peripheral device identifier bits.
+#define ISCSI_SCSI_VPD_PAGE_EXT_INQUIRY_DATA_PUT_PERIPHERAL_ID(x) (ISCSI_BITS_PUT((x), ISCSI_SCSI_VPD_PAGE_EXT_INQUIRY_DATA_PERIPHERAL_ID_FIRST_BIT, ISCSI_SCSI_VPD_PAGE_EXT_INQUIRY_DATA_PERIPHERAL_ID_LAST_BIT))
+
+
+/// iSCSI SCSI Vital Product Data (VPD) Page Extended Inquiry Data page code: Supported VPD pages.
+#define ISCSI_SCSI_VPD_PAGE_EXT_INQUIRY_DATA_PAGE_CODE_SUPPORTED_VPD_PAGES 0x00
+
+/// iSCSI SCSI Vital Product Data (VPD) Page Extended Inquiry Data page code: Unit serial number.
+#define ISCSI_SCSI_VPD_PAGE_EXT_INQUIRY_DATA_PAGE_CODE_UNIT_SERIAL_NUMBER 0x80
+
+/// iSCSI SCSI Vital Product Data (VPD) Page Extended Inquiry Data page code: Device identification.
+#define ISCSI_SCSI_VPD_PAGE_EXT_INQUIRY_DATA_PAGE_CODE_DEVICE_ID 0x83
+
+/// iSCSI SCSI Vital Product Data (VPD) Page Extended Inquiry Data page code: Software interface identification.
+#define ISCSI_SCSI_VPD_PAGE_EXT_INQUIRY_DATA_PAGE_CODE_SOFTWARE_IFACE_ID 0x84
+
+/// iSCSI SCSI Vital Product Data (VPD) Page Extended Inquiry Data page code: Management network addresses.
+#define ISCSI_SCSI_VPD_PAGE_EXT_INQUIRY_DATA_PAGE_CODE_MANAGEMENT_NETWORK_ADDRS 0x85
+
+/// iSCSI SCSI Vital Product Data (VPD) Page Extended Inquiry Data page code: Extended inquiry data.
+#define ISCSI_SCSI_VPD_PAGE_EXT_INQUIRY_DATA_PAGE_CODE_EXTENDED_INQUIRY_DATA 0x86
+
+/// iSCSI SCSI Vital Product Data (VPD) Page Extended Inquiry Data page code: Mode page policy.
+#define ISCSI_SCSI_VPD_PAGE_EXT_INQUIRY_DATA_PAGE_CODE_MODE_PAGE_POLICY 0x87
+
+/// iSCSI SCSI Vital Product Data (VPD) Page Extended Inquiry Data page code: SCSI ports.
+#define ISCSI_SCSI_VPD_PAGE_EXT_INQUIRY_DATA_PAGE_CODE_SCSI_PORTS 0x88
+
+/// iSCSI SCSI Vital Product Data (VPD) Page Extended Inquiry Data page code: Block limits.
+#define ISCSI_SCSI_VPD_PAGE_EXT_INQUIRY_DATA_PAGE_CODE_BLOCK_LIMITS 0xB0
+
+/// iSCSI SCSI Vital Product Data (VPD) Page Extended Inquiry Data page code: Block device characteristics.
+#define ISCSI_SCSI_VPD_PAGE_EXT_INQUIRY_DATA_PAGE_CODE_BLOCK_DEV_CHARS 0xB1
+
+/// iSCSI SCSI Vital Product Data (VPD) Page Extended Inquiry Data page code: Thin provisioning.
+#define ISCSI_SCSI_VPD_PAGE_EXT_INQUIRY_DATA_PAGE_CODE_THIN_PROVISION 0xB2
+
+
+/// iSCSI SCSI Vital Product Data (VPD) Page Extended Inquiry Data check flags: RFTG check.
+#define ISCSI_SCSI_VPD_PAGE_EXT_INQUIRY_DATA_CHECK_FLAGS_RFTG_CHK (1 << 0)
+
+/// iSCSI SCSI Vital Product Data (VPD) Page Extended Inquiry Data check flags: APTG check.
+#define ISCSI_SCSI_VPD_PAGE_EXT_INQUIRY_DATA_CHECK_FLAGS_APTG_CHK (1 << 1)
+
+/// iSCSI SCSI Vital Product Data (VPD) Page Extended Inquiry Data check flags: GRD check.
+#define ISCSI_SCSI_VPD_PAGE_EXT_INQUIRY_DATA_CHECK_FLAGS_GRD_CHK (1 << 2)
+
+
+/// iSCSI SCSI Vital Product Data (VPD) Page Extended Inquiry Data support flags: SIMP support.
+#define ISCSI_SCSI_VPD_PAGE_EXT_INQUIRY_DATA_SUPPORT_FLAGS_SIMPSUP (1 << 0)
+
+/// iSCSI SCSI Vital Product Data (VPD) Page Extended Inquiry Data support flags: ORD support.
+#define ISCSI_SCSI_VPD_PAGE_EXT_INQUIRY_DATA_SUPPORT_FLAGS_ORDSUP (1 << 1)
+
+/// iSCSI SCSI Vital Product Data (VPD) Page Extended Inquiry Data support flags: HEAD support.
+#define ISCSI_SCSI_VPD_PAGE_EXT_INQUIRY_DATA_SUPPORT_FLAGS_HEADSUP (1 << 2)
+
+/// iSCSI SCSI Vital Product Data (VPD) Page Extended Inquiry Data support flags: PRIOR support.
+#define ISCSI_SCSI_VPD_PAGE_EXT_INQUIRY_DATA_SUPPORT_FLAGS_PRIOR_SUP (1 << 3)
+
+/// iSCSI SCSI Vital Product Data (VPD) Page Extended Inquiry Data support flags: GROUP support.
+#define ISCSI_SCSI_VPD_PAGE_EXT_INQUIRY_DATA_SUPPORT_FLAGS_GROUP_SUP (1 << 4)
+
+
+/**
+ * @brief iSCSI SCSI Vital Product Data (VPD) Extended Inquiry data packet.
+ *
+ * This structure is used by the SCSI INQUIRY command
+ * in order to fill in the result if the EVPD bit is
+ * set.
+ */
+typedef struct __attribute__((packed)) iscsi_scsi_vpd_page_ext_inquiry_data_packet {
+ /// Peripheral device type and qualifier.
+ uint8_t peripheral_type_id;
+
+ /// Page code.
+ uint8_t page_code;
+
+ /// Reserved for future usage (always MUST be 0).
+ uint8_t reserved;
+
+ /// Page length in bytes.
+ uint8_t page_len;
+
+ /// Check flags.
+ int8_t check_flags;
+
+ /// Support flags.
+ int8_t support_flags;
+
+ /// More support flags.
+ int8_t support_flags_2;
+
+ /// LUICLR.
+ uint8_t luiclr;
+
+ /// CBCS.
+ uint8_t cbcs;
+
+ /// Micro DL.
+ uint8_t micro_dl;
+
+ /// Reserved for future usage (always MUST be 0).
+ uint64_t reserved2[6];
+
+ /// Reserved for future usage (always MUST be 0).
+ uint32_t reserved3;
+
+ /// Reserved for future usage (always MUST be 0).
+ uint16_t reserved4;
+} iscsi_scsi_vpd_page_ext_inquiry_data_packet;
+
+
+/// iSCSI SCSI Vital Product Data (VPD) Mode Page Policy Descriptor Inquiry Data policy page code: First bit of the six bits.
+#define ISCSI_SCSI_VPD_MODE_PAGE_POLICY_DESC_INQUIRY_DATA_POLICY_PAGE_CODE_FIRST_BIT 0
+
+/// iSCSI SCSI Vital Product Data (VPD) Mode Page Policy Descriptor Inquiry Data policy page code: Last bit of the six bits.
+#define ISCSI_SCSI_VPD_MODE_PAGE_POLICY_DESC_INQUIRY_DATA_POLICY_PAGE_CODE_LAST_BIT ((ISCSI_SCSI_VPD_MODE_PAGE_POLICY_DESC_INQUIRY_DATA_POLICY_PAGE_CODE_FIRST_BIT) + 6 - 1)
+
+/// iSCSI SCSI Vital Product Data (VPD) Mode Page Policy Descriptor Inquiry Data policy page code: Bit mask.
+#define ISCSI_SCSI_VPD_MODE_PAGE_POLICY_DESC_INQUIRY_DATA_POLICY_PAGE_CODE_MASK (ISCSI_BITS_GET_MASK(ISCSI_SCSI_VPD_MODE_PAGE_POLICY_DESC_INQUIRY_DATA_POLICY_PAGE_CODE_FIRST_BIT, ISCSI_SCSI_VPD_MODE_PAGE_POLICY_DESC_INQUIRY_DATA_POLICY_PAGE_CODE_LAST_BIT))
+
+/// iSCSI SCSI Vital Product Data (VPD) Mode Page Policy Descriptor Inquiry Data policy page code: Extracts the policy page code bits.
+#define ISCSI_SCSI_VPD_MODE_PAGE_POLICY_DESC_INQUIRY_DATA_GET_POLICY_PAGE_CODE(x) (ISCSI_BITS_GET((x), ISCSI_SCSI_VPD_MODE_PAGE_POLICY_DESC_INQUIRY_DATA_POLICY_PAGE_CODE_FIRST_BIT, ISCSI_SCSI_VPD_MODE_PAGE_POLICY_DESC_INQUIRY_DATA_POLICY_PAGE_CODE_LAST_BIT))
+
+/// iSCSI SCSI Vital Product Data (VPD) Mode Page Policy Descriptor Inquiry Data policy page code: Stores into the policy page code bits.
+#define ISCSI_SCSI_VPD_MODE_PAGE_POLICY_DESC_INQUIRY_DATA_PUT_POLICY_PAGE_CODE(x) (ISCSI_BITS_PUT((x), ISCSI_SCSI_VPD_MODE_PAGE_POLICY_DESC_INQUIRY_DATA_POLICY_PAGE_CODE_FIRST_BIT, ISCSI_SCSI_VPD_MODE_PAGE_POLICY_DESC_INQUIRY_DATA_POLICY_PAGE_CODE_LAST_BIT))
+
+
+/// iSCSI SCSI Vital Product Data (VPD) Mode Page Policy Descriptor Inquiry Data flags mode page policy: First bit of the two bits.
+#define ISCSI_SCSI_VPD_MODE_PAGE_POLICY_DESC_INQUIRY_DATA_FLAGS_MODE_PAGE_POLICY_FIRST_BIT 0
+
+/// iSCSI SCSI Vital Product Data (VPD) Mode Page Policy Descriptor Inquiry Data flags mode page policy: Last bit of the two bits.
+#define ISCSI_SCSI_VPD_MODE_PAGE_POLICY_DESC_INQUIRY_DATA_FLAGS_MODE_PAGE_POLICY_LAST_BIT ((ISCSI_SCSI_VPD_MODE_PAGE_POLICY_DESC_INQUIRY_DATA_FLAGS_MODE_PAGE_POLICY_FIRST_BIT) + 2 - 1)
+
+/// iSCSI SCSI Vital Product Data (VPD) Mode Page Policy Descriptor Inquiry Data flags mode page policy: Bit mask.
+#define ISCSI_SCSI_VPD_MODE_PAGE_POLICY_DESC_INQUIRY_DATA_FLAGS_MODE_PAGE_POLICY_MASK (ISCSI_BITS_GET_MASK(ISCSI_SCSI_VPD_MODE_PAGE_POLICY_DESC_INQUIRY_DATA_FLAGS_MODE_PAGE_POLICY_FIRST_BIT, ISCSI_SCSI_VPD_MODE_PAGE_POLICY_DESC_INQUIRY_DATA_FLAGS_MODE_PAGE_POLICY_LAST_BIT))
+
+/// iSCSI SCSI Vital Product Data (VPD) Mode Page Policy Descriptor Inquiry Data flags mode page policy: Extracts the mode page policy bits.
+#define ISCSI_SCSI_VPD_MODE_PAGE_POLICY_DESC_INQUIRY_DATA_FLAGS_GET_MODE_PAGE_POLICY(x) (ISCSI_BITS_GET((x), ISCSI_SCSI_VPD_MODE_PAGE_POLICY_DESC_INQUIRY_DATA_FLAGS_MODE_PAGE_POLICY_FIRST_BIT, ISCSI_SCSI_VPD_MODE_PAGE_POLICY_DESC_INQUIRY_DATA_FLAGS_MODE_PAGE_POLICY_LAST_BIT))
+
+/// iSCSI SCSI Vital Product Data (VPD) Mode Page Policy Descriptor Inquiry Data flags mode page policy: Stores into the mode page policy bits.
+#define ISCSI_SCSI_VPD_MODE_PAGE_POLICY_DESC_INQUIRY_DATA_FLAGS_PUT_MODE_PAGE_POLICY(x) (ISCSI_BITS_PUT((x), ISCSI_SCSI_VPD_MODE_PAGE_POLICY_DESC_INQUIRY_DATA_FLAGS_MODE_PAGE_POLICY_FIRST_BIT, ISCSI_SCSI_VPD_MODE_PAGE_POLICY_DESC_INQUIRY_DATA_FLAGS_MODE_PAGE_POLICY_LAST_BIT))
+
+/// iSCSI SCSI Vital Product Data (VPD) Mode Page Policy Descriptor Inquiry Data flag: Multiple Logical Units Share (MLUS).
+#define ISCSI_SCSI_VPD_MODE_PAGE_POLICY_DESC_INQUIRY_DATA_FLAGS_MLUS (1 << 7)
+
+
+/**
+ * @brief iSCSI SCSI Vital Product Data (VPD) Mode Page Policy Descriptor Inquiry data packet.
+ *
+ * This structure is used by the SCSI INQUIRY command
+ * in order to fill in the result if the EVPD bit is
+ * set.
+ */
+typedef struct __attribute__((packed)) iscsi_scsi_vpd_mode_page_policy_desc_inquiry_data_packet {
+ /// Policy page code.
+ uint8_t page_code;
+
+ /// Policy sub page code.
+ uint8_t sub_page_code;
+
+ /// Policy flags.
+ int8_t flags;
+
+ /// Reserved for future usage (always MUST be 0).
+ uint8_t reserved;
+} iscsi_scsi_vpd_mode_page_policy_desc_inquiry_data_packet;
+
+
+/// iSCSI SCSI Vital Product Data (VPD) SCSI Target Port Designation Descriptor Inquiry data protocol identifier: iSCSI.
+#define ISCSI_SCSI_VPD_SCSI_TARGET_PORT_DESIGN_DESC_INQUIRY_DATA_PROTOCOL_ID_ISCSI 0x05
+
+/// iSCSI SCSI Vital Product Data (VPD) SCSI Target Port Designation Descriptor Inquiry data protocol identifier: First bit of the four bits.
+#define ISCSI_SCSI_VPD_SCSI_TARGET_PORT_DESIGN_DESC_INQUIRY_DATA_PROTOCOL_ID_FIRST_BIT 0
+
+/// iSCSI SCSI Vital Product Data (VPD) SCSI Target Port Designation Descriptor Inquiry data protocol identifier: Last bit of the four bits.
+#define ISCSI_SCSI_VPD_SCSI_TARGET_PORT_DESIGN_DESC_INQUIRY_DATA_PROTOCOL_ID_LAST_BIT ((ISCSI_SCSI_VPD_SCSI_TARGET_PORT_DESIGN_DESC_INQUIRY_DATA_PROTOCOL_ID_FIRST_BIT) + 4 - 1)
+
+/// iSCSI SCSI Vital Product Data (VPD) SCSI Target Port Designation Descriptor Inquiry data protocol identifier: Bit mask.
+#define ISCSI_SCSI_VPD_SCSI_TARGET_PORT_DESIGN_DESC_INQUIRY_DATA_PROTOCOL_ID_MASK (ISCSI_BITS_GET_MASK(ISCSI_SCSI_VPD_SCSI_TARGET_PORT_DESIGN_DESC_INQUIRY_DATA_PROTOCOL_ID_FIRST_BIT, ISCSI_SCSI_VPD_SCSI_TARGET_PORT_DESIGN_DESC_INQUIRY_DATA_PROTOCOL_ID_LAST_BIT))
+
+/// iSCSI SCSI Vital Product Data (VPD) SCSI Target Port Designation Descriptor Inquiry data protocol identifier: Extracts the protocol identifier bits.
+#define ISCSI_SCSI_VPD_SCSI_TARGET_PORT_DESIGN_DESC_INQUIRY_DATA_GET_PROTOCOL_ID(x) (ISCSI_BITS_GET((x), ISCSI_SCSI_VPD_SCSI_TARGET_PORT_DESIGN_DESC_INQUIRY_DATA_PROTOCOL_ID_FIRST_BIT, ISCSI_SCSI_VPD_SCSI_TARGET_PORT_DESIGN_DESC_INQUIRY_DATA_PROTOCOL_ID_LAST_BIT))
+
+/// iSCSI SCSI Vital Product Data (VPD) SCSI Target Port Designation Descriptor Inquiry data protocol identifier: Stores into the protocol identifier bits.
+#define ISCSI_SCSI_VPD_SCSI_TARGET_PORT_DESIGN_DESC_INQUIRY_DATA_PUT_PROTOCOL_ID(x) (ISCSI_BITS_PUT((x), ISCSI_SCSI_VPD_SCSI_TARGET_PORT_DESIGN_DESC_INQUIRY_DATA_PROTOCOL_ID_FIRST_BIT, ISCSI_SCSI_VPD_SCSI_TARGET_PORT_DESIGN_DESC_INQUIRY_DATA_PROTOCOL_ID_LAST_BIT))
+
+/// iSCSI SCSI Vital Product Data (VPD) SCSI Target Port Designation Descriptor Inquiry data code set: Binary encoding.
+#define ISCSI_SCSI_VPD_SCSI_TARGET_PORT_DESIGN_DESC_INQUIRY_DATA_CODE_SET_BINARY 0x01
+
+/// iSCSI SCSI Vital Product Data (VPD) SCSI Target Port Designation Descriptor Inquiry data code set: ASCII encoding.
+#define ISCSI_SCSI_VPD_SCSI_TARGET_PORT_DESIGN_DESC_INQUIRY_DATA_CODE_SET_ASCII 0x02
+
+/// iSCSI SCSI Vital Product Data (VPD) SCSI Target Port Designation Descriptor Inquiry data code set: UTF-8 encoding.
+#define ISCSI_SCSI_VPD_SCSI_TARGET_PORT_DESIGN_DESC_INQUIRY_DATA_CODE_SET_UTF8 0x03
+
+/// iSCSI SCSI Vital Product Data (VPD) SCSI Target Port Designation Descriptor Inquiry data code set: First bit of the four bits.
+#define ISCSI_SCSI_VPD_SCSI_TARGET_PORT_DESIGN_DESC_INQUIRY_DATA_CODE_SET_FIRST_BIT 4
+
+/// iSCSI SCSI Vital Product Data (VPD) SCSI Target Port Designation Descriptor Inquiry data code set: Last bit of the four bits.
+#define ISCSI_SCSI_VPD_SCSI_TARGET_PORT_DESIGN_DESC_INQUIRY_DATA_CODE_SET_LAST_BIT ((ISCSI_SCSI_VPD_SCSI_TARGET_PORT_DESIGN_DESC_INQUIRY_DATA_CODE_SET_FIRST_BIT) + 8 - 1)
+
+/// iSCSI SCSI Vital Product Data (VPD) SCSI Target Port Designation Descriptor Inquiry data code set: Bit mask.
+#define ISCSI_SCSI_VPD_SCSI_TARGET_PORT_DESIGN_DESC_INQUIRY_DATA_CODE_SET_MASK (ISCSI_BITS_GET_MASK(ISCSI_SCSI_VPD_SCSI_TARGET_PORT_DESIGN_DESC_INQUIRY_DATA_CODE_SET_FIRST_BIT, ISCSI_SCSI_VPD_SCSI_TARGET_PORT_DESIGN_DESC_INQUIRY_DATA_CODE_SET_LAST_BIT))
+
+/// iSCSI SCSI Vital Product Data (VPD) SCSI Target Port Designation Descriptor Inquiry data code set: Extracts the protocol identifier bits.
+#define ISCSI_SCSI_VPD_SCSI_TARGET_PORT_DESIGN_DESC_INQUIRY_DATA_GET_CODE_SET(x) (ISCSI_BITS_GET((x), ISCSI_SCSI_VPD_SCSI_TARGET_PORT_DESIGN_DESC_INQUIRY_DATA_CODE_SET_FIRST_BIT, ISCSI_SCSI_VPD_SCSI_TARGET_PORT_DESIGN_DESC_INQUIRY_DATA_CODE_SET_LAST_BIT))
+
+/// iSCSI SCSI Vital Product Data (VPD) SCSI Target Port Designation Descriptor Inquiry data code set: Stores into the protocol identifier bits.
+#define ISCSI_SCSI_VPD_SCSI_TARGET_PORT_DESIGN_DESC_INQUIRY_DATA_PUT_CODE_SET(x) (ISCSI_BITS_PUT((x), ISCSI_SCSI_VPD_SCSI_TARGET_PORT_DESIGN_DESC_INQUIRY_DATA_CODE_SET_FIRST_BIT, ISCSI_SCSI_VPD_SCSI_TARGET_PORT_DESIGN_DESC_INQUIRY_DATA_CODE_SET_LAST_BIT))
+
+
+/// iSCSI SCSI Vital Product Data (VPD) SCSI Target Port Designation Descriptor Inquiry data flags type: Vendor specific.
+#define ISCSI_SCSI_VPD_SCSI_TARGET_PORT_DESIGN_DESC_INQUIRY_DATA_FLAGS_TYPE_VENDOR_SPEC 0x00
+
+/// iSCSI SCSI Vital Product Data (VPD) SCSI Target Port Designation Descriptor Inquiry data flags type: T10 vendor identifier.
+#define ISCSI_SCSI_VPD_SCSI_TARGET_PORT_DESIGN_DESC_INQUIRY_DATA_FLAGS_TYPE_T10_VENDOR_ID 0x01
+
+/// iSCSI SCSI Vital Product Data (VPD) SCSI Target Port Designation Descriptor Inquiry data flags type: EUI64.
+#define ISCSI_SCSI_VPD_SCSI_TARGET_PORT_DESIGN_DESC_INQUIRY_DATA_FLAGS_TYPE_EUI64 0x02
+
+/// iSCSI SCSI Vital Product Data (VPD) SCSI Target Port Designation Descriptor Inquiry data flags type: NAA.
+#define ISCSI_SCSI_VPD_SCSI_TARGET_PORT_DESIGN_DESC_INQUIRY_DATA_FLAGS_TYPE_NAA 0x03
+
+/// iSCSI SCSI Vital Product Data (VPD) SCSI Target Port Designation Descriptor Inquiry data flags type: Relative target port.
+#define ISCSI_SCSI_VPD_SCSI_TARGET_PORT_DESIGN_DESC_INQUIRY_DATA_FLAGS_TYPE_REL_TARGET_PORT 0x04
+
+/// iSCSI SCSI Vital Product Data (VPD) SCSI Target Port Designation Descriptor Inquiry data flags type: Target port group.
+#define ISCSI_SCSI_VPD_SCSI_TARGET_PORT_DESIGN_DESC_INQUIRY_DATA_FLAGS_TYPE_TARGET_PORT_GROUP 0x05
+
+/// iSCSI SCSI Vital Product Data (VPD) SCSI Target Port Designation Descriptor Inquiry data flags type: Logical unit group.
+#define ISCSI_SCSI_VPD_SCSI_TARGET_PORT_DESIGN_DESC_INQUIRY_DATA_FLAGS_TYPE_LOGICAL_UNIT_GROUP 0x06
+
+/// iSCSI SCSI Vital Product Data (VPD) SCSI Target Port Designation Descriptor Inquiry data flags type: MD5 logical unit.
+#define ISCSI_SCSI_VPD_SCSI_TARGET_PORT_DESIGN_DESC_INQUIRY_DATA_FLAGS_TYPE_MD5_LOGICAL_UNIT 0x07
+
+/// iSCSI SCSI Vital Product Data (VPD) SCSI Target Port Designation Descriptor Inquiry data flags type: SCSI name.
+#define ISCSI_SCSI_VPD_SCSI_TARGET_PORT_DESIGN_DESC_INQUIRY_DATA_FLAGS_TYPE_SCSI_NAME 0x08
+
+/// iSCSI SCSI Vital Product Data (VPD) SCSI Target Port Designation Descriptor Inquiry data flags type: First bit of the four bits.
+#define ISCSI_SCSI_VPD_SCSI_TARGET_PORT_DESIGN_DESC_INQUIRY_DATA_FLAGS_TYPE_FIRST_BIT 0
+
+/// iSCSI SCSI Vital Product Data (VPD) SCSI Target Port Designation Descriptor Inquiry data flags type: Last bit of the four bits.
+#define ISCSI_SCSI_VPD_SCSI_TARGET_PORT_DESIGN_DESC_INQUIRY_DATA_FLAGS_TYPE_LAST_BIT ((ISCSI_SCSI_VPD_SCSI_TARGET_PORT_DESIGN_DESC_INQUIRY_DATA_FLAGS_TYPE_FIRST_BIT) + 4 - 1)
+
+/// iSCSI SCSI Vital Product Data (VPD) SCSI Target Port Designation Descriptor Inquiry data flags type: Bit mask.
+#define ISCSI_SCSI_VPD_SCSI_TARGET_PORT_DESIGN_DESC_INQUIRY_DATA_FLAGS_TYPE_MASK (ISCSI_BITS_GET_MASK(ISCSI_SCSI_VPD_SCSI_TARGET_PORT_DESIGN_DESC_INQUIRY_DATA_FLAGS_TYPE_FIRST_BIT, ISCSI_SCSI_VPD_SCSI_TARGET_PORT_DESIGN_DESC_INQUIRY_DATA_FLAGS_TYPE_LAST_BIT))
+
+/// iSCSI SCSI Vital Product Data (VPD) SCSI Target Port Designation Descriptor Inquiry data flags type: Extracts the type bits.
+#define ISCSI_SCSI_VPD_SCSI_TARGET_PORT_DESIGN_DESC_INQUIRY_DATA_FLAGS_GET_TYPE(x) (ISCSI_BITS_GET((x), ISCSI_SCSI_VPD_SCSI_TARGET_PORT_DESIGN_DESC_INQUIRY_DATA_FLAGS_TYPE_FIRST_BIT, ISCSI_SCSI_VPD_SCSI_TARGET_PORT_DESIGN_DESC_INQUIRY_DATA_FLAGS_TYPE_LAST_BIT))
+
+/// iSCSI SCSI Vital Product Data (VPD) SCSI Target Port Designation Descriptor Inquiry data flags type: Stores into the type bits.
+#define ISCSI_SCSI_VPD_SCSI_TARGET_PORT_DESIGN_DESC_INQUIRY_DATA_FLAGS_PUT_TYPE(x) (ISCSI_BITS_PUT((x), ISCSI_SCSI_VPD_SCSI_TARGET_PORT_DESIGN_DESC_INQUIRY_DATA_FLAGS_TYPE_FIRST_BIT, ISCSI_SCSI_VPD_SCSI_TARGET_PORT_DESIGN_DESC_INQUIRY_DATA_FLAGS_TYPE_LAST_BIT))
+
+/// iSCSI SCSI Vital Product Data (VPD) SCSI Target Port Designation Descriptor Inquiry data flags association: Logical unit.
+#define ISCSI_SCSI_VPD_SCSI_TARGET_PORT_DESIGN_DESC_INQUIRY_DATA_FLAGS_ASSOC_LOGICAL_UNIT 0x0
+
+/// iSCSI SCSI Vital Product Data (VPD) SCSI Target Port Designation Descriptor Inquiry data flags association: Target port.
+#define ISCSI_SCSI_VPD_SCSI_TARGET_PORT_DESIGN_DESC_INQUIRY_DATA_FLAGS_ASSOC_TARGET_PORT 0x1
+
+/// iSCSI SCSI Vital Product Data (VPD) SCSI Target Port Designation Descriptor Inquiry data flags association: Target device.
+#define ISCSI_SCSI_VPD_SCSI_TARGET_PORT_DESIGN_DESC_INQUIRY_DATA_FLAGS_ASSOC_TARGET_DEVICE 0x2
+
+/// iSCSI SCSI Vital Product Data (VPD) SCSI Target Port Designation Descriptor Inquiry data flags association: First bit of the two bits.
+#define ISCSI_SCSI_VPD_SCSI_TARGET_PORT_DESIGN_DESC_INQUIRY_DATA_FLAGS_ASSOC_FIRST_BIT 4
+
+/// iSCSI SCSI Vital Product Data (VPD) SCSI Target Port Designation Descriptor Inquiry data flags association: Last bit of the two bits.
+#define ISCSI_SCSI_VPD_SCSI_TARGET_PORT_DESIGN_DESC_INQUIRY_DATA_FLAGS_ASSOC_LAST_BIT ((ISCSI_SCSI_VPD_SCSI_TARGET_PORT_DESIGN_DESC_INQUIRY_DATA_FLAGS_ASSOC_FIRST_BIT) + 6 - 1)
+
+/// iSCSI SCSI Vital Product Data (VPD) SCSI Target Port Designation Descriptor Inquiry data flags association: Bit mask.
+#define ISCSI_SCSI_VPD_SCSI_TARGET_PORT_DESIGN_DESC_INQUIRY_DATA_FLAGS_ASSOC_MASK (ISCSI_BITS_GET_MASK(ISCSI_SCSI_VPD_SCSI_TARGET_PORT_DESIGN_DESC_INQUIRY_DATA_FLAGS_ASSOC_FIRST_BIT, ISCSI_SCSI_VPD_SCSI_TARGET_PORT_DESIGN_DESC_INQUIRY_DATA_FLAGS_ASSOC_LAST_BIT))
+
+/// iSCSI SCSI Vital Product Data (VPD) SCSI Target Port Designation Descriptor Inquiry data flags association: Extracts the association bits.
+#define ISCSI_SCSI_VPD_SCSI_TARGET_PORT_DESIGN_DESC_INQUIRY_DATA_FLAGS_GET_ASSOC(x) (ISCSI_BITS_GET((x), ISCSI_SCSI_VPD_SCSI_TARGET_PORT_DESIGN_DESC_INQUIRY_DATA_FLAGS_ASSOC_FIRST_BIT, ISCSI_SCSI_VPD_SCSI_TARGET_PORT_DESIGN_DESC_INQUIRY_DATA_FLAGS_ASSOC_LAST_BIT))
+
+/// iSCSI SCSI Vital Product Data (VPD) SCSI Target Port Designation Descriptor Inquiry data flags association: Stores into the association bits.
+#define ISCSI_SCSI_VPD_SCSI_TARGET_PORT_DESIGN_DESC_INQUIRY_DATA_FLAGS_PUT_ASSOC(x) (ISCSI_BITS_PUT((x), ISCSI_SCSI_VPD_SCSI_TARGET_PORT_DESIGN_DESC_INQUIRY_DATA_FLAGS_ASSOC_FIRST_BIT, ISCSI_SCSI_VPD_SCSI_TARGET_PORT_DESIGN_DESC_INQUIRY_DATA_FLAGS_ASSOC_LAST_BIT))
+
+/// iSCSI SCSI Vital Product Data (VPD) SCSI Target Port Designation Descriptor Inquiry data flags: Protocol Identifier Valid (PIV).
+#define ISCSI_SCSI_VPD_SCSI_TARGET_PORT_DESIGN_DESC_INQUIRY_DATA_FLAGS_PIV (1 << 7)
+
+
+/**
+ * @brief iSCSI SCSI Vital Product Data (VPD) SCSI Target Port Designation Descriptor Inquiry data packet.
+ *
+ * This structure is used by the SCSI INQUIRY command
+ * in order to fill in the result if the EVPD bit is
+ * set.
+ */
+typedef struct __attribute__((packed)) iscsi_scsi_vpd_scsi_target_port_design_dec_inquiry_data_packet {
+ /// Protocol identifier and code set.
+ uint8_t protocol_id_code_set;
+
+ /// Flags.
+ int8_t flags;
+
+ /// Reserved for future usage (always MUST be 0).
+ uint8_t reserved;
+
+ /// Length in bytes.
+ uint8_t len;
+
+ /// Designator.
+ uint8_t design[0];
+} iscsi_scsi_vpd_scsi_target_port_design_dec_inquiry_data_packet;
+
+
+/**
+ * @brief iSCSI SCSI Vital Product Data (VPD) SCSI Port Designation Descriptor Inquiry data packet.
+ *
+ * This structure is used by the SCSI INQUIRY command
+ * in order to fill in the result if the EVPD bit is
+ * set.
+ */
+typedef struct __attribute__((packed)) iscsi_scsi_vpd_scsi_port_design_dec_inquiry_data_packet {
+ /// Reserved for future usage (always MUST be 0).
+ uint16_t reserved;
+
+ /// Relative port identifier.
+ uint16_t rel_port_id;
+
+ /// Reserved for future usage (always MUST be 0).
+ uint16_t reserved2;
+
+ /// Initiator port length in bytes.
+ uint16_t init_port_len;
+
+ /// Initiator port identifier.
+ uint16_t init_port_id[0];
+
+ /// Reserved for future usage (always MUST be 0).
+ uint16_t reserved3;
+
+ /// SCSI Target Port Designation Descriptor length in bytes.
+ uint16_t target_desc_len;
+
+ /// SCSI Target Port Designation Descriptor.
+ iscsi_scsi_vpd_scsi_target_port_design_dec_inquiry_data_packet target_desc[0];
+} iscsi_scsi_vpd_scsi_port_design_dec_inquiry_data_packet;
+
+
+/**
+ * @brief iSCSI SCSI command INQUIRY Vital Product Data (VPD) SCSI Port Designation Descriptor entry fill.
+ *
+ * This structure is used by iterating through
+ * all iSCSI device ports in order to fill in
+ * the INQUIRY Vital Product Data (VPD) SCSI
+ * Port Designation Descriptor structure.
+ */
+typedef struct iscsi_scsi_emu_primary_inquiry_ports_fill {
+ /// Pointer to current Vital Product Data (VPD) SCSI Port Designation Descriptor entry packet data.
+ iscsi_scsi_vpd_scsi_port_design_dec_inquiry_data_packet *port_entry;
+
+ /// Total length of Vital Product Data (VPD) SCSI Port Designation Descriptor entry packet data in bytes.
+ uint alloc_len;
+
+ /// Total remaining allocation length for packet data in bytes.
+ uint len;
+} iscsi_scsi_emu_primary_inquiry_ports_fill;
+
+
+/// iSCSI SCSI Vital Product Data (VPD) Page Block Limits Inquiry data UNMAP Granularity Alignment: First bit of the thirty one bits.
+#define ISCSI_SCSI_VPD_PAGE_BLOCK_LIMITS_INQUIRY_DATA_UNMAP_GRANULARITY_ALIGN_FIRST_BIT 0L
+
+/// iSCSI SCSI Vital Product Data (VPD) Page Block Limits Inquiry data UNMAP Granularity Alignment: Last bit of the thirty one bits.
+#define ISCSI_SCSI_VPD_PAGE_BLOCK_LIMITS_INQUIRY_DATA_UNMAP_GRANULARITY_ALIGN_LAST_BIT ((ISCSI_SCSI_VPD_PAGE_BLOCK_LIMITS_INQUIRY_DATA_UNMAP_GRANULARITY_ALIGN_FIRST_BIT) + 31L - 1L)
+
+/// iSCSI SCSI Vital Product Data (VPD) Page Block Limits Inquiry data UNMAP Granularity Alignment: Bit mask.
+#define ISCSI_SCSI_VPD_PAGE_BLOCK_LIMITS_INQUIRY_DATA_UNMAP_GRANULARITY_ALIGN_MASK (ISCSI_BITS_GET_MASK(ISCSI_SCSI_VPD_PAGE_BLOCK_LIMITS_INQUIRY_DATA_UNMAP_GRANULARITY_ALIGN_FIRST_BIT, ISCSI_SCSI_VPD_PAGE_BLOCK_LIMITS_INQUIRY_DATA_UNMAP_GRANULARITY_ALIGN_LAST_BIT))
+
+/// iSCSI SCSI Vital Product Data (VPD) Page Block Limits Inquiry data UNMAP Granularity Alignment: Extracts the UNMAP granularity alignment bits.
+#define ISCSI_SCSI_VPD_PAGE_BLOCK_LIMITS_INQUIRY_DATA_GET_UNMAP_GRANULARITY_ALIGN(x) (ISCSI_BITS_GET((x), ISCSI_SCSI_VPD_PAGE_BLOCK_LIMITS_INQUIRY_DATA_UNMAP_GRANULARITY_ALIGN_FIRST_BIT, ISCSI_SCSI_VPD_PAGE_BLOCK_LIMITS_INQUIRY_DATA_UNMAP_GRANULARITY_ALIGN_LAST_BIT))
+
+/// iSCSI SCSI Vital Product Data (VPD) Page Block Limits Inquiry data UNMAP Granularity Alignment: Stores into the UNMAP granularity alignment bits.
+#define ISCSI_SCSI_VPD_PAGE_BLOCK_LIMITS_INQUIRY_DATA_PUT_UNMAP_GRANULARITY_ALIGN(x) (ISCSI_BITS_PUT((x), ISCSI_SCSI_VPD_PAGE_BLOCK_LIMITS_INQUIRY_DATA_UNMAP_GRANULARITY_ALIGN_FIRST_BIT, ISCSI_SCSI_VPD_PAGE_BLOCK_LIMITS_INQUIRY_DATA_UNMAP_GRANULARITY_ALIGN_LAST_BIT))
+
+/// iSCSI SCSI Vital Product Data (VPD) Page Block Limits Inquiry data UNMAP Granularity Alignment: UNMAP Granularity Alignment Valid (UGVALID).
+#define ISCSI_SCSI_VPD_PAGE_BLOCK_LIMITS_INQUIRY_DATA_UNMAP_GRANULARITY_ALIGN_UGAVALID (1L << 31L)
+
+
+/**
+ * @brief iSCSI SCSI Vital Product Data (VPD) Page Block Limits Inquiry data packet.
+ *
+ * This structure is used by the SCSI INQUIRY command
+ * in order to fill in the result if the EVPD bit is
+ * set.
+ */
+typedef struct __attribute__((packed)) iscsi_scsi_vpd_page_block_limits_inquiry_data_packet {
+ /// Flags.
+ int8_t flags;
+
+ /// Maximum COMPARE AND WRITE length in logical blocks.
+ uint8_t max_cmp_write_len;
+
+ /// Optimal transfer length granularity in logical blocks.
+ uint16_t optimal_granularity_xfer_len;
+
+ /// Maximum transfer length in logical blocks.
+ uint32_t max_xfer_len;
+
+ /// Optimal transfer length in logical blocks.
+ uint32_t optimal_xfer_len;
+
+ /// Maximum prefetch length in logical blocks.
+ uint32_t max_prefetch_len;
+
+ /// Maximum UNMAP LBA count in LBAs.
+ uint32_t max_unmap_lba_cnt;
+
+ /// Maximum UNMAP block descriptor count in block descriptors.
+ uint32_t max_unmap_block_desc_cnt;
+
+ /// Optimal UNMAP granularity in logical blocks.
+ uint32_t optimal_unmap_granularity;
+
+ /// UNMAP granularity alignment (first LBA) and UGAVALID bit.
+ uint32_t unmap_granularity_align_ugavalid;
+
+ /// Maximum WRITE SAME length in logical blocks.
+ uint64_t max_write_same_len;
+
+ /// Reserved for future usage (always MUST be 0).
+ uint64_t reserved[2];
+
+ /// Reserved for future usage (always MUST be 0).
+ uint32_t reserved2;
+} iscsi_scsi_vpd_page_block_limits_inquiry_data_packet;
+
+/// iSCSI SCSI Vital Product Data (VPD) Page Block Device Characteristics Inquiry data medium rotation rate: Medium rotation rate is not reported.
+#define ISCSI_SCSI_VPD_PAGE_BLOCK_DEV_CHARS_INQUIRY_DATA_MEDIUM_ROTATION_RATE_NOT_REPORTED 0x0000
+
+/// iSCSI SCSI Vital Product Data (VPD) Page Block Device Characteristics Inquiry data medium rotation rate: Non-rotating medium (e.g., solid state).
+#define ISCSI_SCSI_VPD_PAGE_BLOCK_DEV_CHARS_INQUIRY_DATA_MEDIUM_ROTATION_RATE_NONE 0x0001
+
+
+/// iSCSI SCSI Vital Product Data (VPD) Page Block Device Characteristics Inquiry data product type: Not indicated.
+#define ISCSI_SCSI_VPD_PAGE_BLOCK_DEV_CHARS_INQUIRY_DATA_PRODUCT_TYPE_NOT_INDICATED 0x00
+
+/// iSCSI SCSI Vital Product Data (VPD) Page Block Device Characteristics Inquiry data product type: Not specified first value.
+#define ISCSI_SCSI_VPD_PAGE_BLOCK_DEV_CHARS_INQUIRY_DATA_PRODUCT_TYPE_NOT_SPECIFIED_FIRST 0xF0
+
+/// iSCSI SCSI Vital Product Data (VPD) Page Block Device Characteristics Inquiry data product type: Not specified last value.
+#define ISCSI_SCSI_VPD_PAGE_BLOCK_DEV_CHARS_INQUIRY_DATA_PRODUCT_TYPE_NOT_SPECIFIED_LAST 0xFF
+
+
+/// iSCSI SCSI Vital Product Data (VPD) Page Block Device Characteristics Inquiry data flags nominal form factor: Nominal form factor is not reported.
+#define ISCSI_SCSI_VPD_PAGE_BLOCK_DEV_CHARS_INQUIRY_DATA_FLAGS_NOMINAL_FORM_FACTOR_NOT_REPORTED 0x0
+
+/// iSCSI SCSI Vital Product Data (VPD) Page Block Device Characteristics Inquiry data flags nominal form factor: 5.25 inch.
+#define ISCSI_SCSI_VPD_PAGE_BLOCK_DEV_CHARS_INQUIRY_DATA_FLAGS_NOMINAL_FORM_FACTOR_525_INCH 0x1
+
+/// iSCSI SCSI Vital Product Data (VPD) Page Block Device Characteristics Inquiry data flags nominal form factor: 3.5 inch.
+#define ISCSI_SCSI_VPD_PAGE_BLOCK_DEV_CHARS_INQUIRY_DATA_FLAGS_NOMINAL_FORM_FACTOR_35_INCH 0x2
+
+/// iSCSI SCSI Vital Product Data (VPD) Page Block Device Characteristics Inquiry data flags nominal form factor: 2.5 inch.
+#define ISCSI_SCSI_VPD_PAGE_BLOCK_DEV_CHARS_INQUIRY_DATA_FLAGS_NOMINAL_FORM_FACTOR_25_INCH 0x3
+
+/// iSCSI SCSI Vital Product Data (VPD) Page Block Device Characteristics Inquiry data flags nominal form factor: 1.8 inch.
+#define ISCSI_SCSI_VPD_PAGE_BLOCK_DEV_CHARS_INQUIRY_DATA_FLAGS_NOMINAL_FORM_FACTOR_18_INCH 0x4
+
+/// iSCSI SCSI Vital Product Data (VPD) Page Block Device Characteristics Inquiry data flags nominal form factor: Less than 1.8 inch.
+#define ISCSI_SCSI_VPD_PAGE_BLOCK_DEV_CHARS_INQUIRY_DATA_FLAGS_NOMINAL_FORM_FACTOR_LT_18_INCH 0x5
+
+/// iSCSI SCSI Vital Product Data (VPD) Page Block Device Characteristics Inquiry data flags nominal form factor: First bit of the four bits.
+#define ISCSI_SCSI_VPD_PAGE_BLOCK_DEV_CHARS_INQUIRY_DATA_FLAGS_NOMINAL_FORM_FACTOR_FIRST_BIT 0
+
+/// iSCSI SCSI Vital Product Data (VPD) Page Block Device Characteristics Inquiry data flags nominal form factor: Last bit of the four bits.
+#define ISCSI_SCSI_VPD_PAGE_BLOCK_DEV_CHARS_INQUIRY_DATA_FLAGS_NOMINAL_FORM_FACTOR_LAST_BIT ((ISCSI_SCSI_VPD_PAGE_BLOCK_DEV_CHARS_INQUIRY_DATA_FLAGS_NOMINAL_FORM_FACTOR_FIRST_BIT) + 4 - 1)
+
+/// iSCSI SCSI Vital Product Data (VPD) Page Block Device Characteristics Inquiry data flags nominal form factor: Bit mask.
+#define ISCSI_SCSI_VPD_PAGE_BLOCK_DEV_CHARS_INQUIRY_DATA_FLAGS_NOMINAL_FORM_FACTOR_MASK (ISCSI_BITS_GET_MASK(ISCSI_SCSI_VPD_PAGE_BLOCK_DEV_CHARS_INQUIRY_DATA_FLAGS_NOMINAL_FORM_FACTOR_FIRST_BIT, ISCSI_SCSI_VPD_PAGE_BLOCK_DEV_CHARS_INQUIRY_DATA_FLAGS_NOMINAL_FORM_FACTOR_LAST_BIT))
+
+/// iSCSI SCSI Vital Product Data (VPD) Page Block Device Characteristics Inquiry data flags nominal form factor: Extracts the nominal form factor bits.
+#define ISCSI_SCSI_VPD_PAGE_BLOCK_DEV_CHARS_INQUIRY_DATA_FLAGS_GET_NOMINAL_FORM_FACTOR(x) (ISCSI_BITS_GET((x), ISCSI_SCSI_VPD_PAGE_BLOCK_DEV_CHARS_INQUIRY_DATA_FLAGS_NOMINAL_FORM_FACTOR_FIRST_BIT, ISCSI_SCSI_VPD_PAGE_BLOCK_DEV_CHARS_INQUIRY_DATA_FLAGS_NOMINAL_FORM_FACTOR_LAST_BIT))
+
+/// iSCSI SCSI Vital Product Data (VPD) Page Block Device Characteristics Inquiry data flags nominal form factor: Stores into the nominal form factor bits.
+#define ISCSI_SCSI_VPD_PAGE_BLOCK_DEV_CHARS_INQUIRY_DATA_FLAGS_PUT_NOMINAL_FORM_FACTOR(x) (ISCSI_BITS_PUT((x), ISCSI_SCSI_VPD_PAGE_BLOCK_DEV_CHARS_INQUIRY_DATA_FLAGS_NOMINAL_FORM_FACTOR_FIRST_BIT, ISCSI_SCSI_VPD_PAGE_BLOCK_DEV_CHARS_INQUIRY_DATA_FLAGS_NOMINAL_FORM_FACTOR_LAST_BIT))
+
+/// iSCSI SCSI Vital Product Data (VPD) Page Block Device Characteristics Inquiry data flags Write After Cryptographic Erase REQuired (WACEREQ): First bit of the two bits.
+#define ISCSI_SCSI_VPD_PAGE_BLOCK_DEV_CHARS_INQUIRY_DATA_FLAGS_WACEREQ_FIRST_BIT 4
+
+/// iSCSI SCSI Vital Product Data (VPD) Page Block Device Characteristics Inquiry data flags Write After Cryptographic Erase REQuired (WACEREQ): Last bit of the two bits.
+#define ISCSI_SCSI_VPD_PAGE_BLOCK_DEV_CHARS_INQUIRY_DATA_FLAGS_WACEREQ_LAST_BIT ((ISCSI_SCSI_VPD_PAGE_BLOCK_DEV_CHARS_INQUIRY_DATA_FLAGS_WACEREQ_FIRST_BIT) + 6 - 1)
+
+/// iSCSI SCSI Vital Product Data (VPD) Page Block Device Characteristics Inquiry data flags Write After Cryptographic Erase REQuired (WACEREQ): Bit mask.
+#define ISCSI_SCSI_VPD_PAGE_BLOCK_DEV_CHARS_INQUIRY_DATA_FLAGS_WACEREQ_MASK (ISCSI_BITS_GET_MASK(ISCSI_SCSI_VPD_PAGE_BLOCK_DEV_CHARS_INQUIRY_DATA_FLAGS_WACEREQ_FIRST_BIT, ISCSI_SCSI_VPD_PAGE_BLOCK_DEV_CHARS_INQUIRY_DATA_FLAGS_WACEREQ_LAST_BIT))
+
+/// iSCSI SCSI Vital Product Data (VPD) Page Block Device Characteristics Inquiry data flags Write After Cryptographic Erase REQuired (WACEREQ): Extracts the Write After Block Erase REQuired (WACEREQ) bits.
+#define ISCSI_SCSI_VPD_PAGE_BLOCK_DEV_CHARS_INQUIRY_DATA_FLAGS_GET_WACEREQ(x) (ISCSI_BITS_GET((x), ISCSI_SCSI_VPD_PAGE_BLOCK_DEV_CHARS_INQUIRY_DATA_FLAGS_WACEREQ_FIRST_BIT, ISCSI_SCSI_VPD_PAGE_BLOCK_DEV_CHARS_INQUIRY_DATA_FLAGS_WACEREQ_LAST_BIT))
+
+/// iSCSI SCSI Vital Product Data (VPD) Page Block Device Characteristics Inquiry data flags Write After Cryptographic Erase REQuired (WACEREQ): Stores into the Write After Block Erase REQuired (WACEREQ) bits.
+#define ISCSI_SCSI_VPD_PAGE_BLOCK_DEV_CHARS_INQUIRY_DATA_FLAGS_PUT_WACEREQ(x) (ISCSI_BITS_PUT((x), ISCSI_SCSI_VPD_PAGE_BLOCK_DEV_CHARS_INQUIRY_DATA_FLAGS_WACEREQ_FIRST_BIT, ISCSI_SCSI_VPD_PAGE_BLOCK_DEV_CHARS_INQUIRY_DATA_FLAGS_WACEREQ_LAST_BIT))
+
+/// iSCSI SCSI Vital Product Data (VPD) Page Block Device Characteristics Inquiry data flags Write After Block Erase REQuired (WABEREQ): First bit of the two bits.
+#define ISCSI_SCSI_VPD_PAGE_BLOCK_DEV_CHARS_INQUIRY_DATA_FLAGS_WABEREQ_FIRST_BIT 6
+
+/// iSCSI SCSI Vital Product Data (VPD) Page Block Device Characteristics Inquiry data flags Write After Block Erase REQuired (WABEREQ): Last bit of the two bits.
+#define ISCSI_SCSI_VPD_PAGE_BLOCK_DEV_CHARS_INQUIRY_DATA_FLAGS_WABEREQ_LAST_BIT ((ISCSI_SCSI_VPD_PAGE_BLOCK_DEV_CHARS_INQUIRY_DATA_FLAGS_WABEREQ_FIRST_BIT) + 8 - 1)
+
+/// iSCSI SCSI Vital Product Data (VPD) Page Block Device Characteristics Inquiry data flags Write After Block Erase REQuired (WABEREQ): Bit mask.
+#define ISCSI_SCSI_VPD_PAGE_BLOCK_DEV_CHARS_INQUIRY_DATA_FLAGS_WABEREQ_MASK (ISCSI_BITS_GET_MASK(ISCSI_SCSI_VPD_PAGE_BLOCK_DEV_CHARS_INQUIRY_DATA_FLAGS_WABEREQ_FIRST_BIT, ISCSI_SCSI_VPD_PAGE_BLOCK_DEV_CHARS_INQUIRY_DATA_FLAGS_WABEREQ_LAST_BIT))
+
+/// iSCSI SCSI Vital Product Data (VPD) Page Block Device Characteristics Inquiry data flags Write After Block Erase REQuired (WABEREQ): Extracts the Write After Block Erase REQuired (WABEREQ) bits.
+#define ISCSI_SCSI_VPD_PAGE_BLOCK_DEV_CHARS_INQUIRY_DATA_FLAGS_GET_WABEREQ(x) (ISCSI_BITS_GET((x), ISCSI_SCSI_VPD_PAGE_BLOCK_DEV_CHARS_INQUIRY_DATA_FLAGS_WABEREQ_FIRST_BIT, ISCSI_SCSI_VPD_PAGE_BLOCK_DEV_CHARS_INQUIRY_DATA_FLAGS_WABEREQ_LAST_BIT))
+
+/// iSCSI SCSI Vital Product Data (VPD) Page Block Device Characteristics Inquiry data flags Write After Block Erase REQuired (WABEREQ): Stores into the Write After Block Erase REQuired (WABEREQ) bits.
+#define ISCSI_SCSI_VPD_PAGE_BLOCK_DEV_CHARS_INQUIRY_DATA_FLAGS_PUT_WABEREQ(x) (ISCSI_BITS_PUT((x), ISCSI_SCSI_VPD_PAGE_BLOCK_DEV_CHARS_INQUIRY_DATA_FLAGS_WABEREQ_FIRST_BIT, ISCSI_SCSI_VPD_PAGE_BLOCK_DEV_CHARS_INQUIRY_DATA_FLAGS_WABEREQ_LAST_BIT))
+
+
+/// iSCSI SCSI Vital Product Data (VPD) Page Block Device Characteristics Inquiry data support flags: Verify Byte Check Unmapped LBA Supported (VBULS).
+#define ISCSI_SCSI_VPD_PAGE_BLOCK_DEV_CHARS_INQUIRY_DATA_SUPPORT_FLAGS_VBULS (1 << 0)
+
+
+/**
+ * @brief iSCSI SCSI Vital Product Data (VPD) Page Block Device Characteristics Inquiry data packet.
+ *
+ * This structure is used by the SCSI INQUIRY command
+ * in order to fill in the result if the EVPD bit is
+ * set.
+ */
+typedef struct __attribute__((packed)) iscsi_scsi_vpd_page_block_dev_chars_inquiry_data_packet {
+ /// Medium rotation rate.
+ uint16_t medium_rotation_rate;
+
+ /// Product type.
+ uint8_t product_type;
+
+ /// Flags.
+ int8_t flags;
+
+ /// Support flags.
+ uint8_t support_flags;
+
+ /// Reserved for future usage (always MUST be 0).
+ uint64_t reserved[6];
+
+ /// Reserved for future usage (always MUST be 0).
+ uint32_t reserved2;
+
+ /// Reserved for future usage (always MUST be 0).
+ uint16_t reserved3;
+
+ /// Reserved for future usage (always MUST be 0).
+ uint8_t reserved4;
+} iscsi_scsi_vpd_page_block_dev_chars_inquiry_data_packet;
+
+
+/// iSCSI SCSI Vital Product Data (VPD) Page Thin Provision Inquiry data flags: Descriptor Present (DP).
+#define ISCSI_SCSI_VPD_PAGE_THIN_PROVISION_INQUIRY_DATA_FLAGS_DP (1 << 0)
+
+/// iSCSI SCSI Vital Product Data (VPD) Page Thin Provision Inquiry data flags: Anchor Supported (ANC_SUP).
+#define ISCSI_SCSI_VPD_PAGE_THIN_PROVISION_INQUIRY_DATA_FLAGS_ANC_SUP (1 << 1)
+
+/// iSCSI SCSI Vital Product Data (VPD) Page Thin Provision Inquiry data flags: Logical Block Provisioning Read Zeros (LBPRZ).
+#define ISCSI_SCSI_VPD_PAGE_THIN_PROVISION_INQUIRY_DATA_FLAGS_LBPRZ (1 << 2)
+
+/// iSCSI SCSI Vital Product Data (VPD) Page Thin Provision Inquiry data flags: Logical Block Provisioning WRITE SAME(10) (LBPWS10).
+#define ISCSI_SCSI_VPD_PAGE_THIN_PROVISION_INQUIRY_DATA_FLAGS_LBPWS10 (1 << 5)
+
+/// iSCSI SCSI Vital Product Data (VPD) Page Thin Provision Inquiry data flags: Logical Block Provisioning WRITE SAME (LBPWS).
+#define ISCSI_SCSI_VPD_PAGE_THIN_PROVISION_INQUIRY_DATA_FLAGS_LBPWS (1 << 6)
+
+/// iSCSI SCSI Vital Product Data (VPD) Page Thin Provision Inquiry data flags: Logical Block Provisioning UNMAP (LBPU).
+#define ISCSI_SCSI_VPD_PAGE_THIN_PROVISION_INQUIRY_DATA_FLAGS_LBPU (1 << 7)
+
+
+/// iSCSI SCSI Vital Product Data (VPD) Page Thin Provision Inquiry data provision type: The device server does NOT report a provisioning type.
+#define ISCSI_SCSI_VPD_PAGE_THIN_PROVISION_INQUIRY_DATA_PROVISION_TYPE_PROVISIONING_NOT_REPORTED 0x0
+
+/// iSCSI SCSI Vital Product Data (VPD) Page Thin Provision Inquiry data provision type: The logical unit is resource provisioned (see SBC3).
+#define ISCSI_SCSI_VPD_PAGE_THIN_PROVISION_INQUIRY_DATA_PROVISION_TYPE_RESOURCE_PROVISIONING 0x1
+
+/// iSCSI SCSI Vital Product Data (VPD) Page Thin Provision Inquiry data provision type: The logical unit is thin provisioned (see SBC3).
+#define ISCSI_SCSI_VPD_PAGE_THIN_PROVISION_INQUIRY_DATA_PROVISION_TYPE_THIN_PROVISIONING 0x2
+
+/// iSCSI SCSI Vital Product Data (VPD) Page Thin Provision Inquiry data provision type: First bit of the three bits.
+#define ISCSI_SCSI_VPD_PAGE_THIN_PROVISION_INQUIRY_DATA_PROVISION_TYPE_FIRST_BIT 0
+
+/// iSCSI SCSI Vital Product Data (VPD) Page Thin Provision Inquiry data provision type: Last bit of the three bits.
+#define ISCSI_SCSI_VPD_PAGE_THIN_PROVISION_INQUIRY_DATA_PROVISION_TYPE_LAST_BIT ((ISCSI_SCSI_VPD_PAGE_THIN_PROVISION_INQUIRY_DATA_PROVISION_TYPE_FIRST_BIT) + 3 - 1)
+
+/// iSCSI SCSI Vital Product Data (VPD) Page Thin Provision Inquiry data provision type: Bit mask.
+#define ISCSI_SCSI_VPD_PAGE_THIN_PROVISION_INQUIRY_DATA_PROVISION_TYPE_MASK (ISCSI_BITS_GET_MASK(ISCSI_SCSI_VPD_PAGE_THIN_PROVISION_INQUIRY_DATA_PROVISION_TYPE_FIRST_BIT, ISCSI_SCSI_VPD_PAGE_THIN_PROVISION_INQUIRY_DATA_PROVISION_TYPE_LAST_BIT))
+
+/// iSCSI SCSI Vital Product Data (VPD) Page Thin Provision Inquiry data provision type: Extracts the provision type bits.
+#define ISCSI_SCSI_VPD_PAGE_THIN_PROVISION_INQUIRY_DATA_GET_PROVISION_TYPE(x) (ISCSI_BITS_GET((x), ISCSI_SCSI_VPD_PAGE_THIN_PROVISION_INQUIRY_DATA_PROVISION_TYPE_FIRST_BIT, ISCSI_SCSI_VPD_PAGE_THIN_PROVISION_INQUIRY_DATA_PROVISION_TYPE_LAST_BIT))
+
+/// iSCSI SCSI Vital Product Data (VPD) Page Thin Provision Inquiry data provision type: Stores into the provision type bits.
+#define ISCSI_SCSI_VPD_PAGE_THIN_PROVISION_INQUIRY_DATA_PUT_PROVISION_TYPE(x) (ISCSI_BITS_PUT((x), ISCSI_SCSI_VPD_PAGE_THIN_PROVISION_INQUIRY_DATA_PROVISION_TYPE_FIRST_BIT, ISCSI_SCSI_VPD_PAGE_THIN_PROVISION_INQUIRY_DATA_PROVISION_TYPE_LAST_BIT))
+
+
+/**
+ * @brief iSCSI SCSI Vital Product Data (VPD) Page Thin Provision Inquiry data packet.
+ *
+ * This structure is used by the SCSI INQUIRY command
+ * in order to fill in the result if the EVPD bit is
+ * set.
+ */
+typedef struct __attribute__((packed)) iscsi_scsi_vpd_page_thin_provision_inquiry_data_packet {
+ /// Threshold exponent.
+ uint8_t threshold_exponent;
+
+ /// Flags.
+ int8_t flags;
+
+ /// Provision type.
+ uint8_t provision_type;
+
+ /// Reserved for future usage (always MUST be 0).
+ uint8_t reserved;
+
+ /// Provision group descriptors.
+ uint8_t provision_group_desc[0];
+} iscsi_scsi_vpd_page_thin_provision_inquiry_data_packet;
+
+
+/**
+ * @brief iSCSI SCSI Sense Event data packet.
+ *
+ * For a SCSI event, this data accompanies the report in the data
+ * segment and identifies the condition.
+ *
+ * For an iSCSI event, additional vendor-unique data MAY accompany the
+ * Async event. Initiators MAY ignore the data when not understood,
+ * while processing the rest of the PDU.
+ *
+ * If the DataSegmentLength is not 0, the format of the DataSegment is
+ * as follows:
+ */
+typedef struct __attribute__((packed)) iscsi_scsi_sense_event_data_packet {
+ /**
+ * @brief SenseLength.
+ *
+ * This is the length of Sense Data. When the Sense Data field is empty
+ * (e.g., the event is not a SCSI event), SenseLength is 0.
+ */
+ uint16_t sense_len;
+
+ /// Sense Data.
+ uint16_t sense_data[0];
+
+ /// iSCSI Event Data.
+ uint16_t event_data[0];
+} iscsi_scsi_sense_event_data_packet;
+
+
+/// iSCSI SCSI sense data response code: Current format.
+#define ISCSI_SCSI_SENSE_DATA_RESPONSE_CODE_CURRENT_FMT 0x70
+
+/// iSCSI SCSI sense data response code: Deferred format.
+#define ISCSI_SCSI_SENSE_DATA_RESPONSE_CODE_DEFERRED_FMT 0x71
+
+/// iSCSI SCSI sense data response code: First bit of the seven bits.
+#define ISCSI_SCSI_SENSE_DATA_RESPONSE_CODE_FIRST_BIT 0
+
+/// iSCSI SCSI sense data response code: Last bit of the seven bits.
+#define ISCSI_SCSI_SENSE_DATA_RESPONSE_CODE_LAST_BIT ((ISCSI_SCSI_SENSE_DATA_RESPONSE_CODE_FIRST_BIT) + 7 - 1)
+
+/// iSCSI SCSI sense data response code: Bit mask.
+#define ISCSI_SCSI_SENSE_DATA_RESPONSE_CODE_MASK (ISCSI_BITS_GET_MASK(ISCSI_SCSI_SENSE_DATA_RESPONSE_CODE_FIRST_BIT, ISCSI_SCSI_SENSE_DATA_RESPONSE_CODE_LAST_BIT))
+
+/// iSCSI SCSI sense data response code: Extracts the response code bits.
+#define ISCSI_SCSI_SENSE_DATA_GET_RESPONSE_CODE(x) (ISCSI_BITS_GET((x), ISCSI_SCSI_SENSE_DATA_RESPONSE_CODE_FIRST_BIT, ISCSI_SCSI_SENSE_DATA_RESPONSE_CODE_LAST_BIT))
+
+/// iSCSI SCSI sense data response code: Stores into the response code bits.
+#define ISCSI_SCSI_SENSE_DATA_PUT_RESPONSE_CODE(x) (ISCSI_BITS_PUT((x), ISCSI_SCSI_SENSE_DATA_RESPONSE_CODE_FIRST_BIT, ISCSI_SCSI_SENSE_DATA_RESPONSE_CODE_LAST_BIT))
+
+/// iSCSI SCSI sense data response code: Valid.
+#define ISCSI_SCSI_SENSE_DATA_RESPONSE_CODE_VALID (1 << 7)
+
+
+/// iSCSI SCSI sense data sense key: First bit of the four bits.
+#define ISCSI_SCSI_SENSE_DATA_SENSE_KEY_FIRST_BIT 0
+
+/// iSCSI SCSI sense data sense key: Last bit of the four bits.
+#define ISCSI_SCSI_SENSE_DATA_SENSE_KEY_LAST_BIT ((ISCSI_SCSI_SENSE_DATA_SENSE_KEY_FIRST_BIT) + 4 - 1)
+
+/// iSCSI SCSI sense data sense key: Bit mask.
+#define ISCSI_SCSI_SENSE_DATA_SENSE_KEY_MASK (ISCSI_BITS_GET_MASK(ISCSI_SCSI_SENSE_DATA_SENSE_KEY_FIRST_BIT, ISCSI_SCSI_SENSE_DATA_SENSE_KEY_LAST_BIT))
+
+/// iSCSI SCSI sense data sense key: Extracts the Sense Key (SK) bits.
+#define ISCSI_SCSI_SENSE_DATA_GET_SENSE_KEY(x) (ISCSI_BITS_GET((x), ISCSI_SCSI_SENSE_DATA_SENSE_KEY_FIRST_BIT, ISCSI_SCSI_SENSE_DATA_SENSE_KEY_LAST_BIT))
+
+/// iSCSI SCSI sense data sense key: Stores into the Sense Key (SK) bits.
+#define ISCSI_SCSI_SENSE_DATA_PUT_SENSE_KEY(x) (ISCSI_BITS_PUT((x), ISCSI_SCSI_SENSE_DATA_SENSE_KEY_FIRST_BIT, ISCSI_SCSI_SENSE_DATA_SENSE_KEY_LAST_BIT))
+
+// iSCSI SCSI sense data sense key flags: ILI.
+#define ISCSI_SCSI_SENSE_DATA_SENSE_KEY_FLAGS_ILI (1 << 5)
+
+// iSCSI SCSI sense data sense key flags: EOM.
+#define ISCSI_SCSI_SENSE_DATA_SENSE_KEY_FLAGS_EOM (1 << 6)
+
+// iSCSI SCSI sense data sense key flags: FILEMARK.
+#define ISCSI_SCSI_SENSE_DATA_SENSE_KEY_FLAGS_FILEMARK (1 << 7)
+
+
+/**
+ * @brief iSCSI SCSI basic sense data packet data.
+ *
+ * This is the basic SCSI sense data shared by
+ * all SCSI sense data.
+ */
+typedef struct __attribute__((packed)) iscsi_scsi_sense_data_packet {
+ /// Response code.
+ int8_t response_code;
+
+ /// Reserved for future usage (always MUST be 0).
+ uint8_t reserved;
+
+ /// Sense key and flags.
+ int8_t sense_key_flags;
+
+ /// Information.
+ uint32_t info;
+
+ /// Additional sense length in bytes.
+ uint8_t add_len;
+} iscsi_scsi_sense_data_packet;
+
+/// iSCSI SCSI maximum sense data length.
+#define ISCSI_SCSI_MAX_SENSE_DATA_LEN (sizeof(struct iscsi_scsi_sense_data_packet) + 255U)
+
+
+/// iSCSI SCSI sense data check condition sense key specific: First bit of the six bits.
+#define ISCSI_SCSI_SENSE_DATA_CHECK_COND_SENSE_KEY_SPEC_FIRST_BIT 0
+
+/// iSCSI SCSI sense data check condition sense key specific: Last bit of the six bits.
+#define ISCSI_SCSI_SENSE_DATA_CHECK_COND_SENSE_KEY_SPEC_LAST_BIT ((ISCSI_SCSI_SENSE_DATA_CHECK_COND_SENSE_KEY_SPEC_FIRST_BIT) + 6 - 1)
+
+/// iSCSI SCSI sense data check condition sense key specific: Bit mask.
+#define ISCSI_SCSI_SENSE_DATA_CHECK_COND_SENSE_KEY_SPEC_MASK (ISCSI_BITS_GET_MASK(ISCSI_SCSI_SENSE_DATA_CHECK_COND_SENSE_KEY_SPEC_FIRST_BIT, ISCSI_SCSI_SENSE_DATA_CHECK_COND_SENSE_KEY_SPEC_LAST_BIT))
+
+/// iSCSI SCSI sense data check condition sense key specific: Extracts the sense key specific bits.
+#define ISCSI_SCSI_SENSE_DATA_CHECK_COND_GET_SENSE_KEY_SPEC(x) (ISCSI_BITS_GET((x), ISCSI_SCSI_SENSE_DATA_SENSE_KEY_FIRST_BIT, ISCSI_SCSI_SENSE_DATA_SENSE_KEY_LAST_BIT))
+
+/// iSCSI SCSI sense data check condition sense key specific: Stores into the sense key specific bits.
+#define ISCSI_SCSI_SENSE_DATA_CHECK_COND_PUT_SENSE_KEY_SPEC(x) (ISCSI_BITS_PUT((x), ISCSI_SCSI_SENSE_DATA_SENSE_KEY_FIRST_BIT, ISCSI_SCSI_SENSE_DATA_SENSE_KEY_LAST_BIT))
+
+// iSCSI SCSI sense data check condition sense key specific flags: SKSV.
+#define ISCSI_SCSI_SENSE_DATA_CHECK_COND_SENSE_KEY_SPEC_FLAGS_SKSV (1 << 7)
+
+
+/**
+ * @brief iSCSI SCSI sense data check condition packet data.
+ *
+ * This is the additional SCSI sense data used by
+ * the check condition status code.
+ */
+typedef struct __attribute__((packed)) iscsi_scsi_sense_data_check_cond_packet {
+ /// Basic SCSI sense data packet.
+ iscsi_scsi_sense_data_packet sense_data;
+
+ /// Information.
+ uint32_t cmd_spec_info;
+
+ /// Additional Sense Code (ASC).
+ uint8_t asc;
+
+ /// Additional Sense Code Qualifier (ASCQ).
+ uint8_t ascq;
+
+ /// Field replaceable unit code.
+ uint8_t field_rep_unit_code;
+
+ /// Sense key specific.
+ uint8_t sense_key_spec_flags;
+
+ /// Sense key specific.
+ uint16_t sense_key_spec;
+} iscsi_scsi_sense_data_check_cond_packet;
+
+
+/**
+ * @brief iSCSI SCSI command READ CAPACITY(10) parameter data packet data.
+ *
+ * This returns the Logical Block Address (LBA)
+ * and block length in bytes.
+ */
+typedef struct __attribute__((packed)) iscsi_scsi_read_capacity_10_parameter_data_packet {
+ /// Last valid Logical Block Address (LBA).
+ uint32_t lba;
+
+ /// Block length in bytes.
+ uint32_t block_len;
+} iscsi_scsi_read_capacity_10_parameter_data_packet;
+
+
+/// iSCSI SCSI command SERVICE ACTION IN(16) parameter data flags: Protection enabled (PROT_EN).
+#define ISCSI_SCSI_SERVICE_ACTION_IN_16_PARAM_DATA_FLAGS_PROT_EN (1 << 0)
+
+/// iSCSI SCSI command SERVICE ACTION IN(16) parameter data protection type flags: First bit of the three bits.
+#define ISCSI_SCSI_SERVICE_ACTION_IN_16_PARAM_DATA_FLAGS_PROTECT_TYPE_FIRST_BIT 1
+
+/// iSCSI SCSI command SERVICE ACTION IN(16) parameter data protection type flags: Last bit of the three bits.
+#define ISCSI_SCSI_SERVICE_ACTION_IN_16_PARAM_DATA_FLAGS_PROTECT_TYPE_LAST_BIT ((ISCSI_SCSI_SERVICE_ACTION_IN_16_PARAM_DATA_FLAGS_PROTECT_TYPE_FIRST_BIT) + 3 - 1)
+
+/// iSCSI SCSI command SERVICE ACTION IN(16) parameter data protection type flags: Bit mask.
+#define ISCSI_SCSI_SERVICE_ACTION_IN_16_PARAM_DATA_FLAGS_PROTECT_TYPE_MASK (ISCSI_BITS_GET_MASK(ISCSI_SCSI_SERVICE_ACTION_IN_16_PARAM_DATA_FLAGS_PROTECT_TYPE_FIRST_BIT, ISCSI_SCSI_SERVICE_ACTION_IN_16_PARAM_DATA_FLAGS_PROTECT_TYPE_LAST_BIT))
+
+/// iSCSI SCSI command SERVICE ACTION IN(16) parameter data protection type flags: Extracts the protection type bits.
+#define ISCSI_SCSI_SERVICE_ACTION_IN_16_PARAM_DATA_FLAGS_GET_PROTECT_TYPE(x) (ISCSI_BITS_GET((x), ISCSI_SCSI_SERVICE_ACTION_IN_16_PARAM_DATA_FLAGS_PROTECT_TYPE_FIRST_BIT, ISCSI_SCSI_SERVICE_ACTION_IN_16_PARAM_DATA_FLAGS_PROTECT_TYPE_LAST_BIT))
+
+/// iSCSI SCSI command SERVICE ACTION IN(16) parameter data protection type flags: Stores into the protection type bits.
+#define ISCSI_SCSI_SERVICE_ACTION_IN_16_PARAM_DATA_FLAGS_PUT_PROTECT_TYPE(x) (ISCSI_BITS_PUT((x), ISCSI_SCSI_SERVICE_ACTION_IN_16_PARAM_DATA_FLAGS_PROTECT_TYPE_FIRST_BIT, ISCSI_SCSI_SERVICE_ACTION_IN_16_PARAM_DATA_FLAGS_PROTECT_TYPE_LAST_BIT))
+
+/// iSCSI SCSI command SERVICE ACTION IN(16) parameter data RC basis flags: First bit of the two bits.
+#define ISCSI_SCSI_SERVICE_ACTION_IN_16_PARAM_DATA_FLAGS_RC_BASIS_FIRST_BIT 4
+
+/// iSCSI SCSI command SERVICE ACTION IN(16) parameter data RC basis flags: Last bit of the two bits.
+#define ISCSI_SCSI_SERVICE_ACTION_IN_16_PARAM_DATA_FLAGS_RC_BASIS_LAST_BIT ((ISCSI_SCSI_SERVICE_ACTION_IN_16_PARAM_DATA_FLAGS_RC_BASIS_FIRST_BIT) + 2 - 1)
+
+/// iSCSI SCSI command SERVICE ACTION IN(16) parameter data RC basis flags: Bit mask.
+#define ISCSI_SCSI_SERVICE_ACTION_IN_16_PARAM_DATA_FLAGS_RC_BASIS_MASK (ISCSI_BITS_GET_MASK(ISCSI_SCSI_SERVICE_ACTION_IN_16_PARAM_DATA_FLAGS_RC_BASIS_FIRST_BIT, ISCSI_SCSI_SERVICE_ACTION_IN_16_PARAM_DATA_FLAGS_RC_BASIS_LAST_BIT))
+
+/// iSCSI SCSI command SERVICE ACTION IN(16) parameter data RC basis flags: Extracts the RC basis bits.
+#define ISCSI_SCSI_SERVICE_ACTION_IN_16_PARAM_DATA_FLAGS_GET_RC_BASIS(x) (ISCSI_BITS_GET((x), ISCSI_SCSI_SERVICE_ACTION_IN_16_PARAM_DATA_FLAGS_RC_BASIS_FIRST_BIT, ISCSI_SCSI_SERVICE_ACTION_IN_16_PARAM_DATA_FLAGS_RC_BASIS_LAST_BIT))
+
+/// iSCSI SCSI command SERVICE ACTION IN(16) parameter data RC basis flags: Stores into the RC basis bits.
+#define ISCSI_SCSI_SERVICE_ACTION_IN_16_PARAM_DATA_FLAGS_PUT_RC_BASIS(x) (ISCSI_BITS_PUT((x), ISCSI_SCSI_SERVICE_ACTION_IN_16_PARAM_DATA_FLAGS_RC_BASIS_FIRST_BIT, ISCSI_SCSI_SERVICE_ACTION_IN_16_PARAM_DATA_FLAGS_RC_BASIS_LAST_BIT))
+
+
+/// iSCSI SCSI command SERVICE ACTION IN(16) parameter data logical blocks per physical block exponent: First bit of the four bits.
+#define ISCSI_SCSI_SERVICE_ACTION_IN_16_PARAM_DATA_LBPPB_EXPONENT_FIRST_BIT 0
+
+/// iSCSI SCSI command SERVICE ACTION IN(16) parameter data logical blocks per physical block exponent: Last bit of the four bits.
+#define ISCSI_SCSI_SERVICE_ACTION_IN_16_PARAM_DATA_LBPPB_EXPONENT_LAST_BIT ((ISCSI_SCSI_SERVICE_ACTION_IN_16_PARAM_DATA_LBPPB_EXPONENT_FIRST_BIT) + 4 - 1)
+
+/// iSCSI SCSI command SERVICE ACTION IN(16) parameter data logical blocks per physical block exponent: Bit mask.
+#define ISCSI_SCSI_SERVICE_ACTION_IN_16_PARAM_DATA_LBPPB_EXPONENT_MASK (ISCSI_BITS_GET_MASK(ISCSI_SCSI_SERVICE_ACTION_IN_16_PARAM_DATA_LBPPB_EXPONENT_FIRST_BIT, ISCSI_SCSI_SERVICE_ACTION_IN_16_PARAM_DATA_LBPPB_EXPONENT_LAST_BIT))
+
+/// iSCSI SCSI command SERVICE ACTION IN(16) parameter data logical blocks per physical block exponent: Extracts the logical blocks per physical block bits.
+#define ISCSI_SCSI_SERVICE_ACTION_IN_16_PARAM_DATA_GET_LBPPB_EXPONENT(x) (ISCSI_BITS_GET((x), ISCSI_SCSI_SERVICE_ACTION_IN_16_PARAM_DATA_LBPPB_EXPONENT_FIRST_BIT, ISCSI_SCSI_SERVICE_ACTION_IN_16_PARAM_DATA_LBPPB_EXPONENT_LAST_BIT))
+
+/// iSCSI SCSI command SERVICE ACTION IN(16) parameter data logical blocks per physical block exponent: Stores into the logical blocks per physical block bits.
+#define ISCSI_SCSI_SERVICE_ACTION_IN_16_PARAM_DATA_PUT_LBPPB_EXPONENT(x) (ISCSI_BITS_PUT((x), ISCSI_SCSI_SERVICE_ACTION_IN_16_PARAM_DATA_LBPPB_EXPONENT_FIRST_BIT, ISCSI_SCSI_SERVICE_ACTION_IN_16_PARAM_DATA_LBPPB_EXPONENT_LAST_BIT))
+
+/// iSCSI SCSI command SERVICE ACTION IN(16) parameter data protection information intervals exponent: First bit of the four bits.
+#define ISCSI_SCSI_SERVICE_ACTION_IN_16_PARAM_DATA_P_I_EXPONENT_FIRST_BIT 4
+
+/// iSCSI SCSI command SERVICE ACTION IN(16) parameter data protection information intervals exponent: Last bit of the four bits.
+#define ISCSI_SCSI_SERVICE_ACTION_IN_16_PARAM_DATA_P_I_EXPONENT_LAST_BIT ((ISCSI_SCSI_SERVICE_ACTION_IN_16_PARAM_DATA_P_I_EXPONENT_FIRST_BIT) + 4 - 1)
+
+/// iSCSI SCSI command SERVICE ACTION IN(16) parameter data protection information intervals exponent: Bit mask.
+#define ISCSI_SCSI_SERVICE_ACTION_IN_16_PARAM_DATA_P_I_EXPONENT_MASK (ISCSI_BITS_GET_MASK(ISCSI_SCSI_SERVICE_ACTION_IN_16_PARAM_DATA_P_I_EXPONENT_FIRST_BIT, ISCSI_SCSI_SERVICE_ACTION_IN_16_PARAM_DATA_P_I_EXPONENT_LAST_BIT))
+
+/// iSCSI SCSI command SERVICE ACTION IN(16) parameter data protection information intervals exponent: Extracts the protection information intervals bits.
+#define ISCSI_SCSI_SERVICE_ACTION_IN_16_PARAM_DATA_GET_P_I_EXPONENT(x) (ISCSI_BITS_GET((x), ISCSI_SCSI_SERVICE_ACTION_IN_16_PARAM_DATA_P_I_EXPONENT_FIRST_BIT, ISCSI_SCSI_SERVICE_ACTION_IN_16_PARAM_DATA_P_I_EXPONENT_LAST_BIT))
+
+/// iSCSI SCSI command SERVICE ACTION IN(16) parameter data protection information intervals exponent: Stores into the protection information intervals bits.
+#define ISCSI_SCSI_SERVICE_ACTION_IN_16_PARAM_DATA_PUT_P_I_EXPONENT(x) (ISCSI_BITS_PUT((x), ISCSI_SCSI_SERVICE_ACTION_IN_16_PARAM_DATA_P_I_EXPONENT_FIRST_BIT, ISCSI_SCSI_SERVICE_ACTION_IN_16_PARAM_DATA_P_I_EXPONENT_LAST_BIT))
+
+
+/// iSCSI SCSI command SERVICE ACTION IN(16) parameter logical block provisioning Lowest Aligned Logical Block Address (LALBA): First bit of the fourteen bits.
+#define ISCSI_SCSI_SERVICE_ACTION_IN_16_PARAM_DATA_LALBA_FIRST_BIT 0
+
+/// iSCSI SCSI command SERVICE ACTION IN(16) parameter logical block provisioning Lowest Aligned Logical Block Address (LALBA): Last bit of the fourteen bits.
+#define ISCSI_SCSI_SERVICE_ACTION_IN_16_PARAM_DATA_LALBA_LAST_BIT ((ISCSI_SCSI_SERVICE_ACTION_IN_16_PARAM_DATA_LALBA_FIRST_BIT) + 14 - 1)
+
+/// iSCSI SCSI command SERVICE ACTION IN(16) parameter logical block provisioning Lowest Aligned Logical Block Address (LALBA): Bit mask.
+#define ISCSI_SCSI_SERVICE_ACTION_IN_16_PARAM_DATA_LALBA_MASK (ISCSI_BITS_GET_MASK(ISCSI_SCSI_SERVICE_ACTION_IN_16_PARAM_DATA_LALBA_FIRST_BIT, ISCSI_SCSI_SERVICE_ACTION_IN_16_PARAM_DATA_LALBA_LAST_BIT))
+
+/// iSCSI SCSI command SERVICE ACTION IN(16) parameter logical block provisioning Lowest Aligned Logical Block Address (LALBA): Extracts the Lowest Aligned Logical Block Address (LALBA) bits.
+#define ISCSI_SCSI_SERVICE_ACTION_IN_16_PARAM_DATA_GET_LABLA(x) (ISCSI_BITS_GET((x), ISCSI_SCSI_SERVICE_ACTION_IN_16_PARAM_DATA_LALBA_FIRST_BIT, ISCSI_SCSI_SERVICE_ACTION_IN_16_PARAM_DATA_LALBA_LAST_BIT))
+
+/// iSCSI SCSI command SERVICE ACTION IN(16) parameter logical block provisioning Lowest Aligned Logical Block Address (LALBA): Stores into the Lowest Aligned Logical Block Address (LALBA) bits.
+#define ISCSI_SCSI_SERVICE_ACTION_IN_16_PARAM_DATA_PUT_LABLA(x) (ISCSI_BITS_PUT((x), ISCSI_SCSI_SERVICE_ACTION_IN_16_PARAM_DATA_LALBA_FIRST_BIT, ISCSI_SCSI_SERVICE_ACTION_IN_16_PARAM_DATA_LALBA_LAST_BIT))
+
+/// iSCSI SCSI command SERVICE ACTION IN(16) parameter logical block provisioning: Logical Block Provisioning Read Zeros (LBPRZ).
+#define ISCSI_SCSI_SERVICE_ACTION_IN_16_PARAM_DATA_LBPRZ (1 << 14)
+
+/// iSCSI SCSI command SERVICE ACTION IN(16) parameter logical block provisioning: Logical Block Provisioning Management Enabled (LBPME).
+#define ISCSI_SCSI_SERVICE_ACTION_IN_16_PARAM_DATA_LBPME (1 << 15)
+
+
+/**
+ * @brief iSCSI SCSI command SERVICE ACTION IN(16) parameter data packet data.
+ *
+ * This returns the Logical Block Address (LBA),
+ * block length in bytes and LBP information.
+ */
+typedef struct __attribute__((packed)) iscsi_scsi_service_action_in_16_parameter_data_packet {
+ /// Last valid Logical Block Address (LBA).
+ uint64_t lba;
+
+ /// Block length in bytes.
+ uint32_t block_len;
+
+ /// Flags: RC_BASIS, P_TYPE and PROT_EN.
+ int8_t flags;
+
+ /// P_I_EXPONENT and logical blocks per physical block exponent.
+ uint8_t exponents;
+
+ /// Logical Block Provisioning Management Enabled (LBPME), Logical Block Provisioning Read Zeros (LBPRZ) and Lowest Aligned Logical Block Address (LALBA).
+ uint16_t lbp_lalba;
+
+ /// Reserved for future usage (always MUST be 0 for now).
+ uint64_t reserved[2];
+} iscsi_scsi_service_action_in_16_parameter_data_packet;
+
+
+/**
+ * @brief iSCSI SCSI command REPORT LUNS parameter data LUN list packet data.
+ *
+ * This returns the number of entries in the
+ * LUN list in bytes.
+ */
+typedef struct __attribute__((packed)) iscsi_scsi_report_luns_parameter_data_lun_list_packet {
+ /// Number of LUN's following this packet in bytes.
+ uint32_t lun_list_len;
+
+ /// Reserved for future usage (always MUST be 0 for now).
+ uint32_t reserved;
+} iscsi_scsi_report_luns_parameter_data_lun_list_packet;
+
+
+/**
+ * @brief iSCSI SCSI command REPORT LUNS parameter data LUN entry packet data.
+ *
+ * This returns a single LUN entry of the
+ * LUN list.
+ */
+typedef struct __attribute__((packed)) iscsi_scsi_report_luns_parameter_data_lun_entry_packet {
+ /// Logical Unit Number (LUN).
+ uint64_t lun;
+} iscsi_scsi_report_luns_parameter_data_lun_entry_packet;
+
+
+/**
+ * @brief iSCSI SCSI command REPORT LUNS parameter data LUN entry fill.
+ *
+ * This structure is used by iterating through
+ * all iSCSI LUNs in order to fill in the
+ * REPORT LUNS parameter data structure.
+ */
+typedef struct iscsi_scsi_emu_primary_report_luns_fill {
+ /// Pointer to LUN list packet data.
+ iscsi_scsi_report_luns_parameter_data_lun_list_packet *lun_list;
+
+ /// Pointer to current LUN entry packet data.
+ iscsi_scsi_report_luns_parameter_data_lun_entry_packet *lun_entry;
+
+ /// Total length of LUN entry packet data in bytes.
+ uint32_t len;
+
+ /// Total remaining allocation length for packet data in bytes.
+ uint alloc_len;
+
+ /// Select report.
+ uint select_report;
+} iscsi_scsi_emu_primary_report_luns_fill;
+
+
+/**
+ * @brief iSCSI SCSI command MODE SELECT(6) parameter list packet data.
+ *
+ * This returns 32-bit vendor specific data.
+ */
+typedef struct __attribute__((packed)) iscsi_scsi_mode_select_6_parameter_list_packet {
+ /// Vendor specific data.
+ uint32_t vendor_data;
+} iscsi_scsi_mode_select_6_parameter_list_packet;
+
+
+/**
+ * @brief iSCSI SCSI command MODE SELECT(10) parameter list packet data.
+ *
+ * This returns 64-bit vendor specific data.
+ */
+typedef struct __attribute__((packed)) iscsi_scsi_mode_select_10_parameter_list_packet {
+ /// Vendor specific data.
+ uint64_t vendor_data;
+} iscsi_scsi_mode_select_10_parameter_list_packet;
+
+
+/// iSCSI SCSI command MODE SENSE(6) parameter header data flags: DPO and FUA support (DPOFUA).
+#define ISCSI_SCSI_MODE_SENSE_6_PARAM_HDR_DATA_FLAGS_DPOFUA (1 << 4)
+
+/// iSCSI SCSI command MODE SENSE(6) parameter header data flags: Write Protect (WP).
+#define ISCSI_SCSI_MODE_SENSE_6_PARAM_HDR_DATA_FLAGS_WP (1 << 7)
+
+
+/**
+ * @brief iSCSI SCSI command MODE SENSE(6) parameter header packet data.
+ *
+ * This returns the mode parameter header
+ * data.
+ */
+typedef struct __attribute__((packed)) iscsi_scsi_mode_sense_6_parameter_header_data_packet {
+ /// Mode data length in bytes.
+ uint8_t mode_data_len;
+
+ /// Medium type.
+ uint8_t medium_type;
+
+ /// Flags.
+ int8_t flags;
+
+ /// Block descriptor length in bytes.
+ uint8_t block_desc_len;
+} iscsi_scsi_mode_sense_6_parameter_header_data_packet;
+
+
+/// iSCSI SCSI command MODE SENSE(10) parameter header data flags: DPO and FUA support (DPOFUA).
+#define ISCSI_SCSI_MODE_SENSE_10_PARAM_HDR_DATA_FLAGS_DPOFUA (1 << 4)
+
+/// iSCSI SCSI command MODE SENSE(10) parameter header data flags: Write Protect (WP).
+#define ISCSI_SCSI_MODE_SENSE_10_PARAM_HDR_DATA_FLAGS_WP (1 << 7)
+
+
+/// iSCSI SCSI command MODE SENSE(10) parameter header data Long Logical Block Address (LONGLBA).
+#define ISCSI_SCSI_MODE_SENSE_10_PARAM_HDR_DATA_LONGLBA (1 << 0)
+
+
+/**
+ * @brief iSCSI SCSI command MODE SENSE(10) parameter header packet data.
+ *
+ * This returns the mode parameter header
+ * data.
+ */
+typedef struct __attribute__((packed)) iscsi_scsi_mode_sense_10_parameter_header_data_packet {
+ /// Mode data length in bytes.
+ uint16_t mode_data_len;
+
+ /// Medium type.
+ uint8_t medium_type;
+
+ /// Flags.
+ int8_t flags;
+
+ /// Long Logical Block Address (LONGLBA).
+ uint8_t long_lba;
+
+ /// Reserved for future usage (always MUST be 0 for now).
+ uint8_t reserved;
+
+ /// Block descriptor length in bytes.
+ uint16_t block_desc_len;
+} iscsi_scsi_mode_sense_10_parameter_header_data_packet;
+
+
+/**
+ * @brief iSCSI SCSI command MODE SENSE(6) short LBA mode parameter block descriptor packet data.
+ *
+ * This returns the short Logical Block
+ * Address (LBA) mode parameter block
+ * descriptor data.
+ */
+typedef struct __attribute__((packed)) iscsi_scsi_mode_sense_lba_parameter_block_desc_data_packet {
+ /// Number of blocks in logical blocks.
+ uint32_t num_blocks;
+
+ /// Reserved for future usage (always MUST be 0 for now).
+ uint8_t reserved;
+
+ /// Logical blcok length in bytes.
+ uint8_t block_len[3];
+} iscsi_scsi_mode_sense_lba_parameter_block_desc_data_packet;
+
+
+/**
+ * @brief iSCSI SCSI command MODE SENSE(10) long LBA mode parameter block descriptor packet data.
+ *
+ * This returns the long Logical Block
+ * Address (LBA) mode parameter block
+ * descriptor data.
+ */
+typedef struct __attribute__((packed)) iscsi_scsi_mode_sense_long_lba_parameter_block_desc_data_packet {
+ /// Number of blocks in logical blocks.
+ uint64_t num_blocks;
+
+ /// Reserved for future usage (always MUST be 0 for now).
+ uint32_t reserved;
+
+ /// Logical blcok length in bytes.
+ uint32_t block_len;
+} iscsi_scsi_mode_sense_long_lba_parameter_block_desc_data_packet;
+
+
+/// iSCSI SCSI command MODE SENSE(6) and MODE SENSE(10) mode page code: Vendor specific.
+#define ISCSI_SCSI_MODE_SENSE_MODE_PAGE_CODE_VENDOR_SPEC 0x00
+
+/// iSCSI SCSI command MODE SENSE(6) and MODE SENSE(10) mode page code: Read/Write error recovery.
+#define ISCSI_SCSI_MODE_SENSE_MODE_PAGE_CODE_READ_WRITE_ERR_RECOVERY 0x01
+
+/// iSCSI SCSI command MODE SENSE(6) and MODE SENSE(10) mode page code: Disconnect / Reconnect.
+#define ISCSI_SCSI_MODE_SENSE_MODE_PAGE_CODE_DISCONNECT_RECONNECT 0x02
+
+/// iSCSI SCSI command MODE SENSE(6) and MODE SENSE(10) mode page code: Format device.
+#define ISCSI_SCSI_MODE_SENSE_MODE_PAGE_CODE_FORMAT_DEVICE 0x03
+
+/// iSCSI SCSI command MODE SENSE(6) and MODE SENSE(10) mode page code: Rigid disk geometry.
+#define ISCSI_SCSI_MODE_SENSE_MODE_PAGE_CODE_RIGID_DISK_GEOMETRY 0x04
+
+/// iSCSI SCSI command MODE SENSE(6) and MODE SENSE(10) mode page code: Rigid disk geometry.
+#define ISCSI_SCSI_MODE_SENSE_MODE_PAGE_CODE_RIGID_DISK_GEOMETRY_2 0x05
+
+/// iSCSI SCSI command MODE SENSE(6) and MODE SENSE(10) mode page code: Reserved.
+#define ISCSI_SCSI_MODE_SENSE_MODE_PAGE_CODE_RESERVED 0x06
+
+/// iSCSI SCSI command MODE SENSE(6) and MODE SENSE(10) mode page code: Verify error recovery.
+#define ISCSI_SCSI_MODE_SENSE_MODE_PAGE_CODE_VERIFY_ERR_RECOVERY 0x07
+
+/// iSCSI SCSI command MODE SENSE(6) and MODE SENSE(10) mode page code: Caching.
+#define ISCSI_SCSI_MODE_SENSE_MODE_PAGE_CODE_CACHING 0x08
+
+/// iSCSI SCSI command MODE SENSE(6) and MODE SENSE(10) mode page code: Obselete.
+#define ISCSI_SCSI_MODE_SENSE_MODE_PAGE_CODE_OBSELETE 0x09
+
+/// iSCSI SCSI command MODE SENSE(6) and MODE SENSE(10) mode page code: Control.
+#define ISCSI_SCSI_MODE_SENSE_MODE_PAGE_CODE_CONTROL 0x0A
+
+/// iSCSI SCSI command MODE SENSE(6) and MODE SENSE(10) mode page code: Medium types supported.
+#define ISCSI_SCSI_MODE_SENSE_MODE_PAGE_CODE_MEDIUM_TYPES_SUPPORTED 0x0B
+
+/// iSCSI SCSI command MODE SENSE(6) and MODE SENSE(10) mode page code: Notch and partition.
+#define ISCSI_SCSI_MODE_SENSE_MODE_PAGE_CODE_NOTCH_AND_PARTITION 0x0C
+
+/// iSCSI SCSI command MODE SENSE(6) and MODE SENSE(10) mode page code: Obselete.
+#define ISCSI_SCSI_MODE_SENSE_MODE_PAGE_CODE_OBSELETE_2 0x0D
+
+/// iSCSI SCSI command MODE SENSE(6) and MODE SENSE(10) mode page code: Reserved.
+#define ISCSI_SCSI_MODE_SENSE_MODE_PAGE_CODE_RESERVED_2 0x0E
+
+/// iSCSI SCSI command MODE SENSE(6) and MODE SENSE(10) mode page code: Reserved.
+#define ISCSI_SCSI_MODE_SENSE_MODE_PAGE_CODE_RESERVED_3 0x0F
+
+/// iSCSI SCSI command MODE SENSE(6) and MODE SENSE(10) mode page code: XOR control.
+#define ISCSI_SCSI_MODE_SENSE_MODE_PAGE_CODE_XOR_CONTROL 0x10
+
+/// iSCSI SCSI command MODE SENSE(6) and MODE SENSE(10) mode page code: Reserved.
+#define ISCSI_SCSI_MODE_SENSE_MODE_PAGE_CODE_RESERVED_4 0x11
+
+/// iSCSI SCSI command MODE SENSE(6) and MODE SENSE(10) mode page code: Reserved.
+#define ISCSI_SCSI_MODE_SENSE_MODE_PAGE_CODE_RESERVED_5 0x12
+
+/// iSCSI SCSI command MODE SENSE(6) and MODE SENSE(10) mode page code: Reserved.
+#define ISCSI_SCSI_MODE_SENSE_MODE_PAGE_CODE_RESERVED_6 0x13
+
+/// iSCSI SCSI command MODE SENSE(6) and MODE SENSE(10) mode page code: Enclosure services management.
+#define ISCSI_SCSI_MODE_SENSE_MODE_PAGE_CODE_ENCLOSURE_SERVICES_MGMT 0x14
+
+/// iSCSI SCSI command MODE SENSE(6) and MODE SENSE(10) mode page code: Reserved.
+#define ISCSI_SCSI_MODE_SENSE_MODE_PAGE_CODE_RESERVED_7 0x15
+
+/// iSCSI SCSI command MODE SENSE(6) and MODE SENSE(10) mode page code: Reserved.
+#define ISCSI_SCSI_MODE_SENSE_MODE_PAGE_CODE_RESERVED_8 0x16
+
+/// iSCSI SCSI command MODE SENSE(6) and MODE SENSE(10) mode page code: Reserved.
+#define ISCSI_SCSI_MODE_SENSE_MODE_PAGE_CODE_RESERVED_9 0x17
+
+/// iSCSI SCSI command MODE SENSE(6) and MODE SENSE(10) mode page code: Protocol specific LUN.
+#define ISCSI_SCSI_MODE_SENSE_MODE_PAGE_CODE_PROTOCOL_SPEC_LUN 0x18
+
+/// iSCSI SCSI command MODE SENSE(6) and MODE SENSE(10) mode page code: Protocol specific Port.
+#define ISCSI_SCSI_MODE_SENSE_MODE_PAGE_CODE_PROTOCOL_SPEC_PORT 0x19
+
+/// iSCSI SCSI command MODE SENSE(6) and MODE SENSE(10) mode page code: Power condition.
+#define ISCSI_SCSI_MODE_SENSE_MODE_PAGE_CODE_POWER_COND 0x1A
+
+/// iSCSI SCSI command MODE SENSE(6) and MODE SENSE(10) mode page code: Reserved.
+#define ISCSI_SCSI_MODE_SENSE_MODE_PAGE_CODE_RESERVED_10 0x1B
+
+/// iSCSI SCSI command MODE SENSE(6) and MODE SENSE(10) mode page code: Informational exceptions control.
+#define ISCSI_SCSI_MODE_SENSE_MODE_PAGE_CODE_INFO_EXCEPTIOS_CONTROL 0x1C
+
+/// iSCSI SCSI command MODE SENSE(6) and MODE SENSE(10) mode page code: Reserved.
+#define ISCSI_SCSI_MODE_SENSE_MODE_PAGE_CODE_RESERVED_11 0x1D
+
+/// iSCSI SCSI command MODE SENSE(6) and MODE SENSE(10) mode page code: Reserved.
+#define ISCSI_SCSI_MODE_SENSE_MODE_PAGE_CODE_RESERVED_12 0x1E
+
+/// iSCSI SCSI command MODE SENSE(6) and MODE SENSE(10) mode page code: Reserved.
+#define ISCSI_SCSI_MODE_SENSE_MODE_PAGE_CODE_RESERVED_13 0x1F
+
+/// iSCSI SCSI command MODE SENSE(6) and MODE SENSE(10) mode page code: Vendor specific.
+#define ISCSI_SCSI_MODE_SENSE_MODE_PAGE_CODE_VENDOR_SPEC_2 0x20
+
+/// iSCSI SCSI command MODE SENSE(6) and MODE SENSE(10) mode page code: Vendor specific.
+#define ISCSI_SCSI_MODE_SENSE_MODE_PAGE_CODE_VENDOR_SPEC_3 0x21
+
+/// iSCSI SCSI command MODE SENSE(6) and MODE SENSE(10) mode page code: Vendor specific.
+#define ISCSI_SCSI_MODE_SENSE_MODE_PAGE_CODE_VENDOR_SPEC_4 0x22
+
+/// iSCSI SCSI command MODE SENSE(6) and MODE SENSE(10) mode page code: Vendor specific.
+#define ISCSI_SCSI_MODE_SENSE_MODE_PAGE_CODE_VENDOR_SPEC_5 0x23
+
+/// iSCSI SCSI command MODE SENSE(6) and MODE SENSE(10) mode page code: Vendor specific.
+#define ISCSI_SCSI_MODE_SENSE_MODE_PAGE_CODE_VENDOR_SPEC_6 0x24
+
+/// iSCSI SCSI command MODE SENSE(6) and MODE SENSE(10) mode page code: Vendor specific.
+#define ISCSI_SCSI_MODE_SENSE_MODE_PAGE_CODE_VENDOR_SPEC_7 0x25
+
+/// iSCSI SCSI command MODE SENSE(6) and MODE SENSE(10) mode page code: Vendor specific.
+#define ISCSI_SCSI_MODE_SENSE_MODE_PAGE_CODE_VENDOR_SPEC_8 0x26
+
+/// iSCSI SCSI command MODE SENSE(6) and MODE SENSE(10) mode page code: Vendor specific.
+#define ISCSI_SCSI_MODE_SENSE_MODE_PAGE_CODE_VENDOR_SPEC_9 0x27
+
+/// iSCSI SCSI command MODE SENSE(6) and MODE SENSE(10) mode page code: Vendor specific.
+#define ISCSI_SCSI_MODE_SENSE_MODE_PAGE_CODE_VENDOR_SPEC_10 0x28
+
+/// iSCSI SCSI command MODE SENSE(6) and MODE SENSE(10) mode page code: Vendor specific.
+#define ISCSI_SCSI_MODE_SENSE_MODE_PAGE_CODE_VENDOR_SPEC_11 0x29
+
+/// iSCSI SCSI command MODE SENSE(6) and MODE SENSE(10) mode page code: Vendor specific.
+#define ISCSI_SCSI_MODE_SENSE_MODE_PAGE_CODE_VENDOR_SPEC_12 0x2A
+
+/// iSCSI SCSI command MODE SENSE(6) and MODE SENSE(10) mode page code: Vendor specific.
+#define ISCSI_SCSI_MODE_SENSE_MODE_PAGE_CODE_VENDOR_SPEC_13 0x2B
+
+/// iSCSI SCSI command MODE SENSE(6) and MODE SENSE(10) mode page code: Vendor specific.
+#define ISCSI_SCSI_MODE_SENSE_MODE_PAGE_CODE_VENDOR_SPEC_14 0x2C
+
+/// iSCSI SCSI command MODE SENSE(6) and MODE SENSE(10) mode page code: Vendor specific.
+#define ISCSI_SCSI_MODE_SENSE_MODE_PAGE_CODE_VENDOR_SPEC_15 0x2D
+
+/// iSCSI SCSI command MODE SENSE(6) and MODE SENSE(10) mode page code: Vendor specific.
+#define ISCSI_SCSI_MODE_SENSE_MODE_PAGE_CODE_VENDOR_SPEC_16 0x2E
+
+/// iSCSI SCSI command MODE SENSE(6) and MODE SENSE(10) mode page code: Vendor specific.
+#define ISCSI_SCSI_MODE_SENSE_MODE_PAGE_CODE_VENDOR_SPEC_17 0x2F
+
+/// iSCSI SCSI command MODE SENSE(6) and MODE SENSE(10) mode page code: Vendor specific.
+#define ISCSI_SCSI_MODE_SENSE_MODE_PAGE_CODE_VENDOR_SPEC_18 0x30
+
+/// iSCSI SCSI command MODE SENSE(6) and MODE SENSE(10) mode page code: Vendor specific.
+#define ISCSI_SCSI_MODE_SENSE_MODE_PAGE_CODE_VENDOR_SPEC_19 0x31
+
+/// iSCSI SCSI command MODE SENSE(6) and MODE SENSE(10) mode page code: Vendor specific.
+#define ISCSI_SCSI_MODE_SENSE_MODE_PAGE_CODE_VENDOR_SPEC_20 0x32
+
+/// iSCSI SCSI command MODE SENSE(6) and MODE SENSE(10) mode page code: Vendor specific.
+#define ISCSI_SCSI_MODE_SENSE_MODE_PAGE_CODE_VENDOR_SPEC_21 0x33
+
+/// iSCSI SCSI command MODE SENSE(6) and MODE SENSE(10) mode page code: Vendor specific.
+#define ISCSI_SCSI_MODE_SENSE_MODE_PAGE_CODE_VENDOR_SPEC_22 0x34
+
+/// iSCSI SCSI command MODE SENSE(6) and MODE SENSE(10) mode page code: Vendor specific.
+#define ISCSI_SCSI_MODE_SENSE_MODE_PAGE_CODE_VENDOR_SPEC_23 0x35
+
+/// iSCSI SCSI command MODE SENSE(6) and MODE SENSE(10) mode page code: Vendor specific.
+#define ISCSI_SCSI_MODE_SENSE_MODE_PAGE_CODE_VENDOR_SPEC_24 0x36
+
+/// iSCSI SCSI command MODE SENSE(6) and MODE SENSE(10) mode page code: Vendor specific.
+#define ISCSI_SCSI_MODE_SENSE_MODE_PAGE_CODE_VENDOR_SPEC_25 0x37
+
+/// iSCSI SCSI command MODE SENSE(6) and MODE SENSE(10) mode page code: Vendor specific.
+#define ISCSI_SCSI_MODE_SENSE_MODE_PAGE_CODE_VENDOR_SPEC_26 0x38
+
+/// iSCSI SCSI command MODE SENSE(6) and MODE SENSE(10) mode page code: Vendor specific.
+#define ISCSI_SCSI_MODE_SENSE_MODE_PAGE_CODE_VENDOR_SPEC_27 0x39
+
+/// iSCSI SCSI command MODE SENSE(6) and MODE SENSE(10) mode page code: Vendor specific.
+#define ISCSI_SCSI_MODE_SENSE_MODE_PAGE_CODE_VENDOR_SPEC_28 0x3A
+
+/// iSCSI SCSI command MODE SENSE(6) and MODE SENSE(10) mode page code: Vendor specific.
+#define ISCSI_SCSI_MODE_SENSE_MODE_PAGE_CODE_VENDOR_SPEC_29 0x3B
+
+/// iSCSI SCSI command MODE SENSE(6) and MODE SENSE(10) mode page code: Vendor specific.
+#define ISCSI_SCSI_MODE_SENSE_MODE_PAGE_CODE_VENDOR_SPEC_30 0x3C
+
+/// iSCSI SCSI command MODE SENSE(6) and MODE SENSE(10) mode page code: Vendor specific.
+#define ISCSI_SCSI_MODE_SENSE_MODE_PAGE_CODE_VENDOR_SPEC_31 0x3D
+
+/// iSCSI SCSI command MODE SENSE(6) and MODE SENSE(10) mode page code: Vendor specific.
+#define ISCSI_SCSI_MODE_SENSE_MODE_PAGE_CODE_VENDOR_SPEC_32 0x3E
+
+/// iSCSI SCSI command MODE SENSE(6) and MODE SENSE(10) mode page code: Report all mode pages.
+#define ISCSI_SCSI_MODE_SENSE_MODE_PAGE_CODE_REPORT_ALL_MODE_PAGES 0x3F
+
+/// iSCSI SCSI command MODE SENSE(6) and MODE SENSE(10) mode sub page code: Control.
+#define ISCSI_SCSI_MODE_SENSE_MODE_SUB_PAGE_CODE_CONTROL 0x00
+
+/// iSCSI SCSI command MODE SENSE(6) and MODE SENSE(10) mode sub page code: Control extension.
+#define ISCSI_SCSI_MODE_SENSE_MODE_SUB_PAGE_CODE_CONTROL_EXT 0x01
+
+/// iSCSI SCSI command MODE SENSE(6) and MODE SENSE(10) mode sub page code: All sub pages.
+#define ISCSI_SCSI_MODE_SENSE_MODE_SUB_PAGE_CODE_CONTROL_ALL 0xFF
+
+/// iSCSI SCSI command MODE SENSE(6) and MODE SENSE(10) mode sub page code: Report all mode pages.
+#define ISCSI_SCSI_MODE_SENSE_MODE_SUB_PAGE_CODE_REPORT_ALL_MODE_PAGES 0x00
+
+/// iSCSI SCSI command MODE SENSE(6) and MODE SENSE(10) mode sub page code: Report all mode pages and sub pages.
+#define ISCSI_SCSI_MODE_SENSE_MODE_SUB_PAGE_CODE_REPORT_ALL_MODE_SUB_PAGES 0xFF
+
+/// iSCSI SCSI command MODE SENSE(6) and MODE SENSE(10) mode page code: First bit of the six bits.
+#define ISCSI_SCSI_MODE_SENSE_MODE_PAGE_CODE_FIRST_BIT 0
+
+/// iSCSI SCSI command MODE SENSE(6) and MODE SENSE(10) mode page code: Last bit of the six bits.
+#define ISCSI_SCSI_MODE_SENSE_MODE_PAGE_CODE_LAST_BIT ((ISCSI_SCSI_MODE_SENSE_MODE_PAGE_CODE_FIRST_BIT) + 6 - 1)
+
+/// iSCSI SCSI command MODE SENSE(6) and MODE SENSE(10) mode page code: Bit mask.
+#define ISCSI_SCSI_MODE_SENSE_MODE_PAGE_CODE_MASK (ISCSI_BITS_GET_MASK(ISCSI_SCSI_MODE_SENSE_MODE_PAGE_CODE_FIRST_BIT, ISCSI_SCSI_MODE_SENSE_MODE_PAGE_CODE_LAST_BIT))
+
+/// iSCSI SCSI command MODE SENSE(6) and MODE SENSE(10) mode page code: Extracts the page code bits.
+#define ISCSI_SCSI_MODE_SENSE_MODE_PAGE_GET_PAGE_CODE(x) (ISCSI_BITS_GET((x), ISCSI_SCSI_MODE_SENSE_MODE_PAGE_CODE_FIRST_BIT, ISCSI_SCSI_MODE_SENSE_MODE_PAGE_CODE_LAST_BIT))
+
+/// iSCSI SCSI command MODE SENSE(6) and MODE SENSE(10) mode page code: Stores into the page code bits.
+#define ISCSI_SCSI_MODE_SENSE_MODE_PAGE_PUT_PAGE_CODE(x) (ISCSI_BITS_PUT((x), ISCSI_SCSI_MODE_SENSE_MODE_PAGE_CODE_FIRST_BIT, ISCSI_SCSI_MODE_SENSE_MODE_PAGE_CODE_LAST_BIT))
+
+/// iSCSI SCSI command MODE SENSE(6) and MODE SENSE(10) mode page flags: Sub Page Format (SPF).
+#define ISCSI_SCSI_MODE_SENSE_MODE_PAGE_FLAGS_SPF (1 << 6)
+
+/// iSCSI SCSI command MODE SENSE(10) parameter header data flags: Parameters Saveable (PS).
+#define ISCSI_SCSI_MODE_SENSE_MODE_PAGE_FLAGS_PS (1 << 7)
+
+
+/**
+ * @brief iSCSI SCSI command MODE SENSE(6) and MODE SENSE(10) mode page packet data.
+ *
+ * This returns mode page specific data.
+ */
+typedef struct __attribute__((packed)) iscsi_scsi_mode_sense_mode_page_data_packet {
+ /// Page code and flags.
+ int8_t page_code_flags;
+
+ /// Page length in bytes.
+ uint8_t page_len;
+
+ /// Mode parameters.
+ uint8_t params[0];
+} iscsi_scsi_mode_sense_mode_page_data_packet;
+
+
+/**
+ * @brief iSCSI SCSI command MODE SENSE(6) and MODE SENSE(10) mode sub page packet data.
+ *
+ * This returns mode sub page specific data.
+ */
+typedef struct __attribute__((packed)) iscsi_scsi_mode_sense_mode_sub_page_data_packet {
+ /// Page code and flags.
+ int8_t page_code_flags;
+
+ /// Sub page code.
+ uint8_t sub_page_code;
+
+ /// Page length in bytes.
+ uint16_t page_len;
+
+ /// Mode parameters.
+ uint8_t params[0];
+} iscsi_scsi_mode_sense_mode_sub_page_data_packet;
+
+
+/**
+ * @brief iSCSI SCSI command MODE SENSE(6) and MODE SENSE(10) read/write error recovery mode page packet data.
+ *
+ * This returns mode page specific data.
+ */
+typedef struct __attribute__((packed)) iscsi_scsi_mode_sense_read_write_err_recovery_mode_page_data_packet {
+ /// Mode page.
+ iscsi_scsi_mode_sense_mode_page_data_packet mode_page;
+
+ /// Flags.
+ int8_t flags;
+
+ /// Read retry count.
+ uint8_t read_retry_cnt;
+
+ /// Obselete.
+ uint8_t obselete[3];
+
+ /// Restricted for MMC-6.
+ uint8_t restrict_mmc_6;
+
+ /// Write_retry count.
+ uint8_t write_retry_cnt;
+
+ /// Reserved for future usage (always MUST be 0 for now).
+ uint8_t reserved;
+
+ /// Recovery time limit.
+ uint16_t recovery_time_limit;
+} iscsi_scsi_mode_sense_read_write_err_recovery_mode_page_data_packet;
+
+
+/**
+ * @brief iSCSI SCSI command MODE SENSE(6) and MODE SENSE(10) disconnect / reconnect mode page packet data.
+ *
+ * This returns mode page specific data.
+ */
+typedef struct __attribute__((packed)) iscsi_scsi_mode_sense_disconnect_reconnect_mode_page_data_packet {
+ /// Mode page.
+ iscsi_scsi_mode_sense_mode_page_data_packet mode_page;
+
+ /// Reserved for future usage (always MUST be 0 for now).
+ uint16_t reserved;
+
+ /// Bus inactivity time limit.
+ uint16_t bus_inactivity_time_limit;
+
+ /// Reserved for future usage (always MUST be 0 for now).
+ uint16_t reserved2;
+
+ /// Maximum connect time limit.
+ uint16_t max_connect_time_limit;
+
+ /// Maximum burst size.
+ uint16_t max_burst_size;
+
+ /// Restricted.
+ uint8_t restricted;
+
+ /// Reserved for future usage (always MUST be 0 for now).
+ uint8_t reserved3;
+
+ /// First burst size.
+ uint16_t first_burst_size;
+} iscsi_scsi_mode_sense_disconnect_reconnect_mode_page_data_packet;
+
+
+/**
+ * @brief iSCSI SCSI command MODE SENSE(6) and MODE SENSE(10) verify error recovery mode page packet data.
+ *
+ * This returns mode page specific data.
+ */
+typedef struct __attribute__((packed)) iscsi_scsi_mode_sense_verify_err_recovery_mode_page_data_packet {
+ /// Mode page.
+ iscsi_scsi_mode_sense_mode_page_data_packet mode_page;
+
+ /// Flags.
+ int8_t flags;
+
+ /// Verify retry count.
+ uint8_t verify_retry_cnt;
+
+ /// Obselete.
+ uint8_t obselete;
+
+ /// Head offset count.
+ uint8_t head_offset_cnt;
+
+ /// Data strobe offset count.
+ uint8_t data_strobe_offset_cnt;
+
+ /// Reserved for future usage (always MUST be 0 for now).
+ uint8_t reserved;
+
+ /// Write retry count.
+ uint8_t write_retry_cnt;
+
+ /// Reserved for future usage (always MUST be 0 for now).
+ uint8_t reserved2;
+
+ /// Verify_recovery time limit.
+ uint16_t verify_recovery_time_limit;
+} iscsi_scsi_mode_sense_verify_err_recovery_mode_page_data_packet;
+
+
+/// iSCSI SCSI command MODE SENSE(6) and MODE SENSE(10) caching mode page flags: READ Cache Disable (RCD).
+#define ISCSI_SCSI_MODE_SENSE_CACHING_MODE_PAGE_FLAGS_RCD (1 << 0)
+
+/// iSCSI SCSI command MODE SENSE(6) and MODE SENSE(10) caching mode page flags: Multiplication factor (MF).
+#define ISCSI_SCSI_MODE_SENSE_CACHING_MODE_PAGE_FLAGS_MF (1 << 1)
+
+/// iSCSI SCSI command MODE SENSE(6) and MODE SENSE(10) caching mode page flags: Write Cache Enable (WCE).
+#define ISCSI_SCSI_MODE_SENSE_CACHING_MODE_PAGE_FLAGS_WCE (1 << 2)
+
+/// iSCSI SCSI command MODE SENSE(6) and MODE SENSE(10) caching mode page flags: Size Enable (SIZE).
+#define ISCSI_SCSI_MODE_SENSE_CACHING_MODE_PAGE_FLAGS_SIZE (1 << 3)
+
+/// iSCSI SCSI command MODE SENSE(6) and MODE SENSE(10) caching mode page flags: Discontinuity (DISC).
+#define ISCSI_SCSI_MODE_SENSE_CACHING_MODE_PAGE_FLAGS_DISC (1 << 4)
+
+/// iSCSI SCSI command MODE SENSE(6) and MODE SENSE(10) caching mode page flags: Caching Analysis Permitted (CAP).
+#define ISCSI_SCSI_MODE_SENSE_CACHING_MODE_PAGE_FLAGS_CAP (1 << 5)
+
+/// iSCSI SCSI command MODE SENSE(6) and MODE SENSE(10) caching mode page flags: Abort Prefetch (ABPF).
+#define ISCSI_SCSI_MODE_SENSE_CACHING_MODE_PAGE_FLAGS_ABPF (1 << 6)
+
+/// iSCSI SCSI command MODE SENSE(6) and MODE SENSE(10) caching mode page flags: Initiator Control (IC).
+#define ISCSI_SCSI_MODE_SENSE_CACHING_MODE_PAGE_FLAGS_IC (1 << 7)
+
+
+/**
+ * @brief iSCSI SCSI command MODE SENSE(6) and MODE SENSE(10) caching mode page packet data.
+ *
+ * This returns mode page specific data.
+ */
+typedef struct __attribute__((packed)) iscsi_scsi_mode_sense_caching_mode_page_data_packet {
+ /// Mode page.
+ iscsi_scsi_mode_sense_mode_page_data_packet mode_page;
+
+ /// Flags.
+ int8_t flags;
+
+ /// Retention priority.
+ uint8_t retention_pri;
+
+ /// Disable prefetch transfer length.
+ uint16_t disable_prefetch_xfer_len;
+
+ /// Minimum prefetch.
+ uint16_t min_prefetch;
+
+ /// Maximum prefetch.
+ uint16_t max_prefetch;
+
+ /// Maximum prefetch ceiling.
+ uint16_t max_prefetch_ceil;
+
+ /// Cache flags.
+ int8_t cache_flags;
+
+ /// Number of cache segments.
+ uint8_t num_cache_segs;
+
+ /// Cache segment size.
+ uint16_t cache_seg_size;
+
+ /// Reserved for future usage (always MUST be 0 for now).
+ uint8_t reserved;
+
+ /// Obselete.
+ uint8_t obselete[3];
+} iscsi_scsi_mode_sense_caching_mode_page_data_packet;
+
+
+/**
+ * @brief iSCSI SCSI command MODE SENSE(6) and MODE SENSE(10) control mode page packet data.
+ *
+ * This returns mode page specific data.
+ */
+typedef struct __attribute__((packed)) iscsi_scsi_mode_sense_control_mode_page_data_packet {
+ /// Mode page.
+ iscsi_scsi_mode_sense_mode_page_data_packet mode_page;
+
+ /// Flags.
+ int8_t flags;
+
+ /// Queue flags.
+ int8_t queue_flags;
+
+ /// Control flags.
+ int8_t control_flags;
+
+ /// Application task flags.
+ int8_t app_task_flags;
+
+ /// Ready AER holdoff period.
+ uint16_t ready_aer_holdoff_period;
+
+ /// Busy timeout period.
+ uint16_t busy_timeout_period;
+
+ /// Extended self-test completition time.
+ uint16_t ext_self_test_complete_time;
+} iscsi_scsi_mode_sense_control_mode_page_data_packet;
+
+
+/**
+ * @brief iSCSI SCSI command MODE SENSE(6) and MODE SENSE(10) control extension mode sub page packet data.
+ *
+ * This returns mode sub page specific data.
+ */
+typedef struct __attribute__((packed)) iscsi_scsi_mode_sense_control_ext_mode_page_data_packet {
+ /// Mode page.
+ iscsi_scsi_mode_sense_mode_sub_page_data_packet mode_sub_page;
+
+ /// Flags.
+ int8_t flags;
+
+ /// Initial command priority.
+ uint8_t init_cmd_pri;
+
+ /// Maximum sense data length in bytes.
+ uint8_t max_sense_data_len;
+
+ /// Reserved for future usage (always MUST be 0 for now).
+ uint64_t reserved[3];
+
+ /// Reserved for future usage (always MUST be 0 for now).
+ uint8_t reserved2;
+} iscsi_scsi_mode_sense_control_ext_mode_page_data_packet;
+
+
+/**
+ * @brief iSCSI SCSI command MODE SENSE(6) and MODE SENSE(10) XOR extension mode page packet data.
+ *
+ * This returns mode page specific data.
+ */
+typedef struct __attribute__((packed)) iscsi_scsi_mode_sense_xor_ext_mode_page_data_packet {
+ /// Mode page.
+ iscsi_scsi_mode_sense_mode_page_data_packet mode_page;
+
+ /// Flags.
+ int8_t flags;
+
+ /// Reserved for future usage (always MUST be 0 for now).
+ uint8_t reserved;
+
+ /// Maximum XOR write size in logical blocks.
+ uint32_t max_xor_write_size;
+
+ /// Reserved for future usage (always MUST be 0 for now).
+ uint32_t reserved2;
+
+ /// Maximum regenerate size in logical blocks.
+ uint32_t max_regenerate_size;
+
+ /// Reserved for future usage (always MUST be 0 for now).
+ uint32_t reserved3;
+
+ /// Reserved for future usage (always MUST be 0 for now).
+ uint16_t reserved4;
+
+ /// Rebuild delay.
+ uint16_t rebuild_delay;
+} iscsi_scsi_mode_sense_xor_ext_mode_page_data_packet;
+
+
+/**
+ * @brief iSCSI SCSI command MODE SENSE(6) and MODE SENSE(10) power condition mode page packet data.
+ *
+ * This returns mode page specific data.
+ */
+typedef struct __attribute__((packed)) iscsi_scsi_mode_sense_power_cond_mode_page_data_packet {
+ /// Mode page.
+ iscsi_scsi_mode_sense_mode_page_data_packet mode_page;
+
+ /// Flags.
+ int8_t flags;
+
+ /// Idle and standby flags.
+ int8_t idle_standby_flags;
+
+ /// idle_a condition timer.
+ uint32_t idle_a_cond_timer;
+
+ /// standby_z condition timer.
+ uint32_t standby_z_cond_timer;
+
+ /// idle_b condition timer.
+ uint32_t idle_b_cond_timer;
+
+ /// idle_c condition timer.
+ uint32_t idle_c_cond_timer;
+
+ /// standby_y condition timer.
+ uint32_t standby_y_cond_timer;
+
+ /// Reserved for future usage (always MUST be 0 for now).
+ uint64_t reserved;
+
+ /// Reserved for future usage (always MUST be 0 for now).
+ uint32_t reserved2;
+
+ /// Reserved for future usage (always MUST be 0 for now).
+ uint16_t reserved3;
+
+ /// Reserved for future usage (always MUST be 0 for now).
+ uint8_t reserved4;
+
+ /// Check Condition From (CCF) flags.
+ int8_t ccf_flags;
+} iscsi_scsi_mode_sense_power_cond_mode_page_data_packet;
+
+
+/**
+ * @brief iSCSI SCSI command MODE SENSE(6) and MODE SENSE(10) informational exceptions control mode page packet data.
+ *
+ * This returns mode page specific data.
+ */
+typedef struct __attribute__((packed)) iscsi_scsi_mode_sense_info_exceptions_control_mode_page_data_packet {
+ /// Mode page.
+ iscsi_scsi_mode_sense_mode_page_data_packet mode_page;
+
+ /// Flags.
+ int8_t flags;
+
+ /// Method Of Reporting Informational Exceptions (MRIE) flags.
+ uint8_t mrie;
+
+ /// Interval timer.
+ uint32_t interval_timer;
+
+ /// Report count.
+ uint32_t report_cnt;
+} iscsi_scsi_mode_sense_info_exceptions_control_mode_page_data_packet;
+
+
+/**
+ * @brief iSCSI SCSI command PERSISTENT RESERVE OUT parameter list packet data.
+ *
+ * This returns persistent storage specific data
+ * like the reservation and service action keys.
+ */
+typedef struct __attribute__((packed)) iscsi_scsi_pr_reserve_out_parameter_list_packet {
+ /// Reservation key.
+ uint64_t r_key;
+
+ /// Service action reservation key.
+ uint64_t sa_key;
+
+ /// Obselete.
+ uint32_t obselete;
+
+ /// Flags.
+ int8_t flags;
+
+ /// Reserved for future usage (always MUST be 0 for now).
+ uint8_t reserved;
+
+ /// Obselete.
+ uint16_t obselete2;
+
+} iscsi_scsi_pr_reserve_out_parameter_list_packet;
+
+
+/**
+ * @brief iSCSI SCSI command PERSISTENT RESERVE IN parameter data packet data.
+ *
+ * This returns persistent storage specific data
+ * like the reservation and service action keys.
+ */
+typedef struct __attribute__((packed)) iscsi_scsi_pr_reserve_in_parameter_data_packet {
+ /// Persistent Reservations (PR) Generation.
+ uint32_t pr_gen;
+
+ /// Additional length in bytes.
+ uint32_t add_len;
+} iscsi_scsi_pr_reserve_in_parameter_data_packet;
+
/// SCSI command opcode (embedded in iSCSI protocol): TEST UNIT READY.
#define ISCSI_SCSI_OPCODE_TESTUNITREADY 0x00
+/// SCSI command opcode (embedded in iSCSI protocol): REQUEST SENSE.
+#define ISCSI_SCSI_OPCODE_REQUESTSENSE 0x03
+
/// SCSI command opcode (embedded in iSCSI protocol): READ(6).
#define ISCSI_SCSI_OPCODE_READ6 0x08
+/// SCSI command opcode (embedded in iSCSI protocol): WRITE(6).
+#define ISCSI_SCSI_OPCODE_WRITE6 0x0A
+
/// SCSI command opcode (embedded in iSCSI protocol): INQUIRY.
#define ISCSI_SCSI_OPCODE_INQUIRY 0x12
@@ -714,9 +4798,21 @@ typedef struct __attribute__((packed)) iscsi_ds_cmd_data {
/// SCSI command opcode (embedded in iSCSI protocol): SANITIZE.
#define ISCSI_SCSI_OPCODE_SANITIZE 0x48
+/// SCSI command opcode (embedded in iSCSI protocol): LOG SELECT.
+#define ISCSI_SCSI_OPCODE_LOGSELECT 0x4C
+
+/// SCSI command opcode (embedded in iSCSI protocol): LOG SENSE.
+#define ISCSI_SCSI_OPCODE_LOGSENSE 0x4D
+
/// SCSI command opcode (embedded in iSCSI protocol): MODE SELECT(10).
#define ISCSI_SCSI_OPCODE_MODESELECT10 0x55
+/// SCSI command opcode (embedded in iSCSI protocol): RESERVE(10).
+#define ISCSI_SCSI_OPCODE_RESERVE10 0x56
+
+/// SCSI command opcode (embedded in iSCSI protocol): RELEASE(10).
+#define ISCSI_SCSI_OPCODE_RELEASE10 0x57
+
/// SCSI command opcode (embedded in iSCSI protocol): MODE SENSE(10).
#define ISCSI_SCSI_OPCODE_MODESENSE10 0x5A
@@ -763,7 +4859,7 @@ typedef struct __attribute__((packed)) iscsi_ds_cmd_data {
#define ISCSI_SCSI_OPCODE_WRITE_ATOMIC16 0x9C
/// SCSI command opcode (embedded in iSCSI protocol): SERVICE ACTION IN(16).
-#define ISCSI_SCSI_OPCODE_SERVICE_ACTION_IN 0x9E
+#define ISCSI_SCSI_OPCODE_SERVICE_ACTION_IN_16 0x9E
/// SCSI command opcode (embedded in iSCSI protocol): REPORT LUNS.
#define ISCSI_SCSI_OPCODE_REPORTLUNS 0xA0
@@ -798,6 +4894,10 @@ typedef struct __attribute__((packed)) iscsi_ds_cmd_data {
*/
#define ISCSI_SCSI_CMD_FLAGS_TASK_NO_UNSOLICITED_DATA (1 << 7)
+
+/// SCSI SCSI command flags: Final.
+#define ISCSI_SCSI_CMD_FLAGS_FINAL (1 << 7)
+
/**
* @brief iSCSI SCSI command flags: Expected input data.
*
@@ -921,7 +5021,7 @@ typedef struct __attribute__((packed)) iscsi_scsi_cmd_packet {
* CDBs. Whenever the CDB is larger than 16 bytes, an Extended CDB AHS
* MUST be used to contain the CDB spillover.
*/
- iscsi_cdb scsi_cdb;
+ iscsi_scsi_cdb scsi_cdb;
/// Optional AHS packet data.
iscsi_ahs_packet ahs;
@@ -930,7 +5030,7 @@ typedef struct __attribute__((packed)) iscsi_scsi_cmd_packet {
iscsi_header_digest hdr_digest;
/// Optional data segment, command data.
- iscsi_ds_cmd_data ds_cmd_data;
+ iscsi_scsi_ds_cmd_data ds_cmd_data;
/// Optional data digest.
iscsi_data_digest data_digest;
@@ -1112,6 +5212,7 @@ typedef struct __attribute__((packed)) iscsi_scsi_cmd_packet {
*/
#define ISCSI_SCSI_RESPONSE_STATUS_TASK_ABORTED 0x40
+
/// SCSI response code: Command Completed at Target.
#define ISCSI_SCSI_RESPONSE_CODE_OK 0x00
@@ -1256,7 +5357,7 @@ typedef struct __attribute__((packed)) iscsi_scsi_response_packet {
iscsi_header_digest hdr_digest;
/// Optional data segment, command data.
- iscsi_ds_cmd_data ds_cmd_data;
+ iscsi_scsi_ds_cmd_data ds_cmd_data;
/// Optional data digest.
iscsi_data_digest data_digest;
@@ -1433,7 +5534,7 @@ typedef struct __attribute__((packed)) iscsi_task_mgmt_func_response_packet {
/// Always 0x22 according to specification.
uint8_t opcode;
- /// Reserved for future usage (must be always 0x80 for now).
+ /// Reserved for future usage (always MUST be 0x80 for now).
int8_t flags;
/**
@@ -1617,7 +5718,7 @@ typedef struct __attribute__((packed)) iscsi_scsi_data_out_req_packet {
iscsi_header_digest hdr_digest;
/// Data segment.
- iscsi_ds_cmd_data ds_cmd_data;
+ iscsi_scsi_ds_cmd_data ds_cmd_data;
/// Optional data digest.
iscsi_data_digest data_digest;
@@ -1829,7 +5930,7 @@ typedef struct __attribute__((packed)) iscsi_scsi_data_in_response_packet {
iscsi_header_digest hdr_digest;
/// Data segment.
- iscsi_ds_cmd_data ds_cmd_data;
+ iscsi_scsi_ds_cmd_data ds_cmd_data;
/// Optional data digest.
iscsi_data_digest data_digest;
@@ -1894,7 +5995,7 @@ typedef struct __attribute__((packed)) iscsi_r2t_packet {
/// Always 0x31 according to iSCSI specification.
uint8_t opcode;
- /// Reserved for future usage (must be always 0x80 for now).
+ /// Reserved for future usage (always MUST be 0x80 for now).
int8_t flags;
/// Reserved for future usage, always MUST be 0 for now.
@@ -2087,7 +6188,7 @@ typedef struct __attribute__((packed)) iscsi_async_msg_packet {
/// Always 0x32 according to iSCSI specification.
uint8_t opcode;
- /// Reserved for future usage (must be always 0x80 for now).
+ /// Reserved for future usage (always MUST be 0x80 for now).
int8_t flags;
/// Reserved for future usage, always MUST be 0.
@@ -2144,7 +6245,7 @@ typedef struct __attribute__((packed)) iscsi_async_msg_packet {
iscsi_header_digest hdr_digest;
/// Data segment.
- iscsi_ds_cmd_data ds_cmd_data;
+ iscsi_scsi_ds_cmd_data ds_cmd_data;
/// Optional data digest.
iscsi_data_digest data_digest;
@@ -2152,36 +6253,6 @@ typedef struct __attribute__((packed)) iscsi_async_msg_packet {
/**
- * @brief iSCSI Sense Event data packet.
- *
- * For a SCSI event, this data accompanies the report in the data
- * segment and identifies the condition.
- *
- * For an iSCSI event, additional vendor-unique data MAY accompany the
- * Async event. Initiators MAY ignore the data when not understood,
- * while processing the rest of the PDU.
- *
- * If the DataSegmentLength is not 0, the format of the DataSegment is
- * as follows:
- */
-typedef struct __attribute__((packed)) iscsi_sense_event_data_packet {
- /**
- * @brief SenseLength.
- *
- * This is the length of Sense Data. When the Sense Data field is empty
- * (e.g., the event is not a SCSI event), SenseLength is 0.
- */
- uint16_t sense_len;
-
- /// Sense Data.
- uint16_t sense_data[0];
-
- /// iSCSI Event Data.
- uint16_t event_data[0];
-} iscsi_sense_event_data_packet;
-
-
-/**
* @brief Text Request flags: Continue.
*
* (C) When set to 1, this bit indicates that the text (set of key=value
@@ -2312,7 +6383,7 @@ typedef struct __attribute__((packed)) iscsi_text_req_packet {
* Text operations are usually meant for parameter setting/negotiations
* but can also be used to perform some long-lasting operations.
*/
- iscsi_ds_cmd_data ds_cmd_data;
+ iscsi_scsi_ds_cmd_data ds_cmd_data;
/// Optional data digest.
iscsi_data_digest data_digest;
@@ -2434,41 +6505,55 @@ typedef struct __attribute__((packed)) iscsi_text_response_packet {
* key=value pairs of its own as part of a sequence and not only in
* response to the initiator.
*/
- iscsi_ds_cmd_data ds_cmd_data;
+ iscsi_scsi_ds_cmd_data ds_cmd_data;
/// Optional data digest.
iscsi_data_digest data_digest;
} iscsi_text_response_packet;
-/// Initiator Session ID (ISID) type: Two bits - The T field identifies the format and usage of A, B, C, and D.
-#define ISCSI_ISID_TYPE_BITS (1 << 6)
/**
- * @brief Initiator Session ID (ISID) type: OUI-Format.
+ * @brief iSCSI Initiator Session ID (ISID) type: OUI-Format.
*
* A and B: 22-bit OUI
* (the I/G and U/L bits are omitted)
* C and D: 24-bit Qualifier.
*/
-#define ISCSI_ISID_TYPE_FORMAT_OUI 0x0
+#define ISCSI_ISID_TYPE_FORMAT_OUI 0x0
/**
- * @brief Initiator Session ID (ISID) type: EN: Format (IANA Enterprise Number).
+ * @brief iSCSI Initiator Session ID (ISID) type: EN: Format (IANA Enterprise Number).
*
* A: Reserved
* B and C: EN (IANA Enterprise Number)
* D: Qualifier
*/
-#define ISCSI_ISID_TYPE_FORMAT_EN 0x1
+#define ISCSI_ISID_TYPE_FORMAT_EN 0x1
/**
- * @brief Initiator Session ID (ISID) type: Random.
+ * @brief iSCSI Initiator Session ID (ISID) type: Random.
*
* A: Reserved
* B and C: Random
* D: Qualifier
*/
-#define ISCSI_ISID_TYPE_FORMAT_RANDOM 0x2
+#define ISCSI_ISID_TYPE_FORMAT_RANDOM 0x2
+
+/// iSCSI Initiator Session ID (ISID) type format: First bit of the two bits.
+#define ISCSI_ISID_TYPE_FORMAT_FIRST_BIT 6
+
+/// iSCSI Initiator Session ID (ISID) type format: Last bit of the two bits.
+#define ISCSI_ISID_TYPE_FORMAT_LAST_BIT ((ISCSI_ISID_TYPE_FORMAT_FIRST_BIT) + 2 - 1)
+
+/// iSCSI Initiator Session ID (ISID) type format: Bit mask.
+#define ISCSI_ISID_TYPE_FORMAT_MASK (ISCSI_BITS_GET_MASK(ISCSI_ISID_TYPE_FORMAT_FIRST_BIT, ISCSI_ISID_TYPE_FORMAT_LAST_BIT))
+
+/// iSCSI Initiator Session ID (ISID) type format: Extracts the type format.
+#define ISCSI_ISID_GET_TYPE_FORMAT(x) (ISCSI_BITS_GET((x), ISCSI_ISID_TYPE_FORMAT_FIRST_BIT, ISCSI_ISID_TYPE_FORMAT_LAST_BIT))
+
+/// iSCSI Initiator Session ID (ISID) type format: Stores into the type format.
+#define ISCSI_ISID_PUT_TYPE_FORMAT(x) (ISCSI_BITS_PUT((x), ISCSI_ISID_TYPE_FORMAT_FIRST_BIT, ISCSI_ISID_TYPE_FORMAT_LAST_BIT))
+
/**
* @brief iSCSI Initiator Session ID (ISID) packet data.
@@ -2538,7 +6623,7 @@ typedef struct __attribute__((packed)) iscsi_isid {
* SessionType key is thus going to be offered as "Discovery", it SHOULD
* be offered in the initial Login Request by the initiator.
*/
-#define ISCSI_LOGIN_AUTH_SECURITY_TEXT_KEY_SESSION_TYPE "SessionType"
+#define ISCSI_LOGIN_AUTH_SECURITY_TEXT_KEY_SESSION_TYPE ((const uint8_t *) "SessionType\0\0\0\0")
/**
* @brief Key used during SecurityNegotiation stage of Login Phase: Initiator name.
@@ -2559,7 +6644,7 @@ typedef struct __attribute__((packed)) iscsi_isid {
* itself to the remote endpoint.\n
* The InitiatorName MUST NOT be redeclared within the Login Phase.
*/
-#define ISCSI_LOGIN_AUTH_SECURITY_TEXT_KEY_INITIATOR_NAME "InitiatorName"
+#define ISCSI_LOGIN_AUTH_SECURITY_TEXT_KEY_INITIATOR_NAME ((const uint8_t *) "InitiatorName\0\0")
/**
* @brief Key used during SecurityNegotiation stage of Login Phase: Target name.
@@ -2583,7 +6668,7 @@ typedef struct __attribute__((packed)) iscsi_isid {
* Request (which is its only use when issued by a target).\n
* The TargetName MUST NOT be redeclared within the Login Phase.
*/
-#define ISCSI_LOGIN_AUTH_SECURITY_TEXT_KEY_TARGET_NAME "TargetName"
+#define ISCSI_LOGIN_AUTH_SECURITY_TEXT_KEY_TARGET_NAME ((const uint8_t *) "TargetName\0\0\0\0\0")
/**
* @brief Key used during SecurityNegotiation stage of Login Phase: Target address.
@@ -2613,7 +6698,7 @@ typedef struct __attribute__((packed)) iscsi_isid {
* The formats for the port and portal-group-tag are the same as the one
* specified in TargetPortalGroupTag.
*/
-#define ISCSI_LOGIN_AUTH_SECURITY_TEXT_KEY_TARGET_ADDRESS "TargetAddress"
+#define ISCSI_LOGIN_AUTH_SECURITY_TEXT_KEY_TARGET_ADDRESS ((const uint8_t *) "TargetAddress\0\0")
/**
* @brief Key used during SecurityNegotiation stage of Login Phase: Initiator alias.
@@ -2636,7 +6721,7 @@ typedef struct __attribute__((packed)) iscsi_isid {
* the target's user interface in a list of initiators to which it is
* connected.
*/
-#define ISCSI_LOGIN_AUTH_SECURITY_TEXT_KEY_INITIATOR_ALIAS "InitiatorAlias"
+#define ISCSI_LOGIN_AUTH_SECURITY_TEXT_KEY_INITIATOR_ALIAS ((const uint8_t *) "InitiatorAlias\0")
/**
* @brief Key used during SecurityNegotiation stage of Login Phase: Target alias.
@@ -2658,7 +6743,7 @@ typedef struct __attribute__((packed)) iscsi_isid {
* authorization decisions. It can be displayed by the initiator's user
* interface in a list of targets to which it is connected.
*/
-#define ISCSI_LOGIN_AUTH_SECURITY_TEXT_KEY_TARGET_ALIAS "TargetAlias"
+#define ISCSI_LOGIN_AUTH_SECURITY_TEXT_KEY_TARGET_ALIAS ((const uint8_t *) "TargetAlias\0\0\0\0")
/**
* @brief Key used during SecurityNegotiation stage of Login Phase: Target portal group tag.
@@ -2683,7 +6768,7 @@ typedef struct __attribute__((packed)) iscsi_isid {
* legal value for the TPGT. This discrepancy currently stands
* corrected in SAM4.
*/
-#define ISCSI_LOGIN_AUTH_SECURITY_TEXT_KEY_TARGET_PORTAL_GROUP_TAG "TargetPortalGroupTag"
+#define ISCSI_LOGIN_AUTH_SECURITY_TEXT_KEY_TARGET_PORTAL_GROUP_TAG ((const uint8_t *) "TargetPortalGroupTag\0\0\0")
/**
* @brief Key used during SecurityNegotiation stage of Login Phase: Authentication method.
@@ -2745,7 +6830,7 @@ typedef struct __attribute__((packed)) iscsi_isid {
* Support for public or private extension authentication methods is
* OPTIONAL.
*/
-#define ISCSI_LOGIN_AUTH_SECURITY_TEXT_KEY_AUTH_METHOD "AuthMethod"
+#define ISCSI_LOGIN_AUTH_SECURITY_TEXT_KEY_AUTH_METHOD ((const uint8_t *) "AuthMethod\0\0\0\0\0")
/**
@@ -2777,7 +6862,7 @@ typedef struct __attribute__((packed)) iscsi_isid {
* encoded form) MUST NOT exceed 65536 bytes. Hex or Base64 encoding
* may be used for KRB_AP_REQ and KRB_AP_REP.
*/
-#define ISCSI_LOGIN_AUTH_SECURITY_TEXT_KEY_AUTH_METHOD_KRB_AP_REQ "KRB_AP_REQ"
+#define ISCSI_LOGIN_AUTH_SECURITY_TEXT_KEY_AUTH_METHOD_KRB_AP_REQ ((const uint8_t *) "KRB_AP_REQ\0\0\0\0\0")
/**
* @brief Key used during SecurityNegotiation stage of Login Phase: Kerberos V5 (KRB5): KRB_AP_REP.
@@ -2808,7 +6893,7 @@ typedef struct __attribute__((packed)) iscsi_isid {
* encoded form) MUST NOT exceed 65536 bytes. Hex or Base64 encoding
* may be used for KRB_AP_REQ and KRB_AP_REP.
*/
-#define ISCSI_LOGIN_AUTH_SECURITY_TEXT_KEY_AUTH_METHOD_KRB_AP_REP "KRB_AP_REP"
+#define ISCSI_LOGIN_AUTH_SECURITY_TEXT_KEY_AUTH_METHOD_KRB_AP_REP ((const uint8_t *) "KRB_AP_REP\0\0\0\0\0")
/**
@@ -2862,7 +6947,7 @@ typedef struct __attribute__((packed)) iscsi_isid {
* supported by initiators and targets. To guarantee interoperability,
* targets MUST always offer "SRP-1536" as one of the proposed groups.
*/
-#define ISCSI_LOGIN_AUTH_SECURITY_TEXT_KEY_AUTH_METHOD_SRP_SRP_U "SRP_U"
+#define ISCSI_LOGIN_AUTH_SECURITY_TEXT_KEY_AUTH_METHOD_SRP_SRP_U ((const uint8_t *) "SRP_U\0\0")
/**
* @brief Key used during SecurityNegotiation stage of Login Phase: Secure Remote Password (SRP): SRP_GROUP.
@@ -2915,7 +7000,7 @@ typedef struct __attribute__((packed)) iscsi_isid {
* supported by initiators and targets. To guarantee interoperability,
* targets MUST always offer "SRP-1536" as one of the proposed groups.
*/
-#define ISCSI_LOGIN_AUTH_SECURITY_TEXT_KEY_AUTH_METHOD_SRP_SRP_GROUP "SRP_GROUP"
+#define ISCSI_LOGIN_AUTH_SECURITY_TEXT_KEY_AUTH_METHOD_SRP_SRP_GROUP ((const uint8_t *) "SRP_GROUP\0\0\0\0\0\0")
/**
* @brief Key used during SecurityNegotiation stage of Login Phase: Secure Remote Password (SRP): SRP_A.
@@ -2968,7 +7053,7 @@ typedef struct __attribute__((packed)) iscsi_isid {
* supported by initiators and targets. To guarantee interoperability,
* targets MUST always offer "SRP-1536" as one of the proposed groups.
*/
-#define ISCSI_LOGIN_AUTH_SECURITY_TEXT_KEY_AUTH_METHOD_SRP_SRP_A "SRP_A"
+#define ISCSI_LOGIN_AUTH_SECURITY_TEXT_KEY_AUTH_METHOD_SRP_SRP_A ((const uint8_t *) "SRP_A\0\0")
/**
* @brief Key used during SecurityNegotiation stage of Login Phase: Secure Remote Password (SRP): SRP_B.
@@ -3021,7 +7106,7 @@ typedef struct __attribute__((packed)) iscsi_isid {
* supported by initiators and targets. To guarantee interoperability,
* targets MUST always offer "SRP-1536" as one of the proposed groups.
*/
-#define ISCSI_LOGIN_AUTH_SECURITY_TEXT_KEY_AUTH_METHOD_SRP_SRP_B "SRP_B"
+#define ISCSI_LOGIN_AUTH_SECURITY_TEXT_KEY_AUTH_METHOD_SRP_SRP_B ((const uint8_t *) "SRP_B\0\0")
/**
* @brief Key used during SecurityNegotiation stage of Login Phase: Secure Remote Password (SRP): SRP_M.
@@ -3074,7 +7159,7 @@ typedef struct __attribute__((packed)) iscsi_isid {
* supported by initiators and targets. To guarantee interoperability,
* targets MUST always offer "SRP-1536" as one of the proposed groups.
*/
-#define ISCSI_LOGIN_AUTH_SECURITY_TEXT_KEY_AUTH_METHOD_SRP_SRP_M "SRP_M"
+#define ISCSI_LOGIN_AUTH_SECURITY_TEXT_KEY_AUTH_METHOD_SRP_SRP_M ((const uint8_t *) "SRP_M\0\0")
/**
* @brief Key used during SecurityNegotiation stage of Login Phase: Secure Remote Password (SRP): SRP_HM.
@@ -3127,7 +7212,7 @@ typedef struct __attribute__((packed)) iscsi_isid {
* supported by initiators and targets. To guarantee interoperability,
* targets MUST always offer "SRP-1536" as one of the proposed groups.
*/
-#define ISCSI_LOGIN_AUTH_SECURITY_TEXT_KEY_AUTH_METHOD_SRP_SRP_HM "SRP_HM"
+#define ISCSI_LOGIN_AUTH_SECURITY_TEXT_KEY_AUTH_METHOD_SRP_SRP_HM ((const uint8_t *) "SRP_HM\0")
/**
@@ -3184,7 +7269,7 @@ typedef struct __attribute__((packed)) iscsi_isid {
* To guarantee interoperability, initiators MUST always offer it as one
* of the proposed algorithms.
*/
-#define ISCSI_LOGIN_AUTH_SECURITY_TEXT_KEY_AUTH_METHOD_CHAP_CHAP_A "CHAP_A"
+#define ISCSI_LOGIN_AUTH_SECURITY_TEXT_KEY_AUTH_METHOD_CHAP_CHAP_A ((const uint8_t *) "CHAP_A\0")
/**
* @brief Key used during SecurityNegotiation stage of Login Phase: Challenge Handshake Authentication Protocol (CHAP): CHAP_I.
@@ -3240,7 +7325,7 @@ typedef struct __attribute__((packed)) iscsi_isid {
* To guarantee interoperability, initiators MUST always offer it as one
* of the proposed algorithms.
*/
-#define ISCSI_LOGIN_AUTH_SECURITY_TEXT_KEY_AUTH_METHOD_CHAP_CHAP_I "CHAP_I"
+#define ISCSI_LOGIN_AUTH_SECURITY_TEXT_KEY_AUTH_METHOD_CHAP_CHAP_I ((const uint8_t *) "CHAP_I\0")
/**
* @brief Key used during SecurityNegotiation stage of Login Phase: Challenge Handshake Authentication Protocol (CHAP): CHAP_C.
@@ -3296,7 +7381,7 @@ typedef struct __attribute__((packed)) iscsi_isid {
* To guarantee interoperability, initiators MUST always offer it as one
* of the proposed algorithms.
*/
-#define ISCSI_LOGIN_AUTH_SECURITY_TEXT_KEY_AUTH_METHOD_CHAP_CHAP_C "CHAP_C"
+#define ISCSI_LOGIN_AUTH_SECURITY_TEXT_KEY_AUTH_METHOD_CHAP_CHAP_C ((const uint8_t *) "CHAP_C\0")
/**
* @brief Key used during SecurityNegotiation stage of Login Phase: Challenge Handshake Authentication Protocol (CHAP): CHAP_N.
@@ -3352,7 +7437,7 @@ typedef struct __attribute__((packed)) iscsi_isid {
* To guarantee interoperability, initiators MUST always offer it as one
* of the proposed algorithms.
*/
-#define ISCSI_LOGIN_AUTH_SECURITY_TEXT_KEY_AUTH_METHOD_CHAP_CHAP_N "CHAP_N"
+#define ISCSI_LOGIN_AUTH_SECURITY_TEXT_KEY_AUTH_METHOD_CHAP_CHAP_N ((const uint8_t *) "CHAP_N\0")
/**
* @brief Key used during SecurityNegotiation stage of Login Phase: Challenge Handshake Authentication Protocol (CHAP): CHAP_R.
@@ -3408,7 +7493,7 @@ typedef struct __attribute__((packed)) iscsi_isid {
* To guarantee interoperability, initiators MUST always offer it as one
* of the proposed algorithms.
*/
-#define ISCSI_LOGIN_AUTH_SECURITY_TEXT_KEY_AUTH_METHOD_CHAP_CHAP_R "CHAP_R"
+#define ISCSI_LOGIN_AUTH_SECURITY_TEXT_KEY_AUTH_METHOD_CHAP_CHAP_R ((const uint8_t *) "CHAP_R\0")
/* Login/Text Operational Text Keys
@@ -3526,7 +7611,7 @@ typedef struct __attribute__((packed)) iscsi_isid {
* standard-label specified.\n
* Support for public or private extension digests is OPTIONAL.
*/
-#define ISCSI_LOGIN_AUTH_SESSION_TEXT_KEY_HEADER_DIGEST "HeaderDigest"
+#define ISCSI_LOGIN_AUTH_SESSION_TEXT_KEY_HEADER_DIGEST ((const uint8_t *) "HeaderDigest\0\0\0")
/**
* @brief Login/Text Operational Session Text Key: Data digest.
@@ -3608,7 +7693,7 @@ typedef struct __attribute__((packed)) iscsi_isid {
* standard-label specified.\n
* Support for public or private extension digests is OPTIONAL.
*/
-#define ISCSI_LOGIN_AUTH_SESSION_TEXT_KEY_DATA_DIGEST "DataDigest"
+#define ISCSI_LOGIN_AUTH_SESSION_TEXT_KEY_DATA_DIGEST ((const uint8_t *) "DataDigest\0\0\0\0\0")
/**
* @brief Login/Text Operational Session Text Key: New connections.
@@ -3625,7 +7710,7 @@ typedef struct __attribute__((packed)) iscsi_isid {
* The initiator and target negotiate the maximum number of connections
* requested/acceptable.
*/
-#define ISCSI_LOGIN_AUTH_SESSION_TEXT_KEY_MAX_CONNECTIONS "MaxConnections"
+#define ISCSI_LOGIN_AUTH_SESSION_TEXT_KEY_MAX_CONNECTIONS ((const uint8_t *) "MaxConnections\0")
/**
* @brief Login/Text Operational Session Text Key: Send targets.
@@ -3789,7 +7874,7 @@ typedef struct __attribute__((packed)) iscsi_isid {
* multiple connections per session; it is communicated via the
* MaxConnections text key upon login to the target.
*/
-#define ISCSI_LOGIN_AUTH_SESSION_TEXT_KEY_SEND_TARGETS "SendTargets"
+#define ISCSI_LOGIN_AUTH_SESSION_TEXT_KEY_SEND_TARGETS ((const uint8_t *) "SendTargets\0\0\0\0")
/**
* @brief Login/Text Operational Session Text Key: Initial Ready To Transfer.
@@ -3818,7 +7903,7 @@ typedef struct __attribute__((packed)) iscsi_isid {
* Only the first outgoing data burst (immediate data and/or separate
* PDUs) can be sent unsolicited (i.e., not requiring an explicit R2T).
*/
-#define ISCSI_LOGIN_AUTH_SESSION_TEXT_KEY_INITIAL_R2T "InitialR2T"
+#define ISCSI_LOGIN_AUTH_SESSION_TEXT_KEY_INITIAL_R2T ((const uint8_t *) "InitialR2T\0\0\0\0\0")
/**
* @brief Login/Text Operational Session Text Key: Immediate data.
@@ -3832,7 +7917,7 @@ typedef struct __attribute__((packed)) iscsi_isid {
* Default is Yes.
* @endverbatim
* Result function is AND.\n
- * The initiator and target negotiate support for immediate dataTo
+ * The initiator and target negotiate support for immediate data. To
* turn immediate data off, the initiator or target must state its
* desire to do soImmediateData can be turned on if both the
* initiator and target have ImmediateData=Yes.\n
@@ -3855,7 +7940,7 @@ typedef struct __attribute__((packed)) iscsi_isid {
* | Yes | No | No | No |
* | Yes | Yes | No | Yes |
*/
-#define ISCSI_LOGIN_AUTH_SESSION_TEXT_KEY_IMMEDIATE_DATA "ImmediateData"
+#define ISCSI_LOGIN_AUTH_SESSION_TEXT_KEY_IMMEDIATE_DATA ((const uint8_t *) "ImmediateData\0\0")
/**
* @brief Login/Text Operational Session Text Key: Maximum receive DataSegmentLength.
@@ -3878,7 +7963,7 @@ typedef struct __attribute__((packed)) iscsi_isid {
* unsolicited PDUs exceeding FirstBurstLength (or FirstBurstLength-
* Immediate Data Length if immediate data were sent).
*/
-#define ISCSI_LOGIN_AUTH_SESSION_TEXT_KEY_MAX_RECV_DS_LEN "MaxRecvDataSegmentLength"
+#define ISCSI_LOGIN_AUTH_SESSION_TEXT_KEY_MAX_RECV_DS_LEN ((const uint8_t *) "MaxRecvDataSegmentLength\0\0\0\0\0\0\0")
/**
* @brief Login/Text Operational Session Text Key: Maximum burst length.
@@ -3897,7 +7982,7 @@ typedef struct __attribute__((packed)) iscsi_isid {
* sequence consists of one or more consecutive Data-In or Data-Out PDUs
* that end with a Data-In or Data-Out PDU with the F bit set to 1.
*/
-#define ISCSI_LOGIN_AUTH_SESSION_TEXT_KEY_MAX_BURST_LEN "MaxBurstLength"
+#define ISCSI_LOGIN_AUTH_SESSION_TEXT_KEY_MAX_BURST_LEN ((const uint8_t *) "MaxBurstLength\0")
/**
* @brief Login/Text Operational Session Text Key: First burst length.
@@ -3919,7 +8004,7 @@ typedef struct __attribute__((packed)) iscsi_isid {
* follow the command.\n
* FirstBurstLength MUST NOT exceed MaxBurstLength.
*/
-#define ISCSI_LOGIN_AUTH_SESSION_TEXT_KEY_FIRST_BURST_LEN "FirstBurstLength"
+#define ISCSI_LOGIN_AUTH_SESSION_TEXT_KEY_FIRST_BURST_LEN ((const uint8_t *) "FirstBurstLength\0\0\0\0\0\0\0")
/**
* @brief Login/Text Operational Session Text Key: Default time to wait.
@@ -3939,7 +8024,7 @@ typedef struct __attribute__((packed)) iscsi_isid {
* A value of 0 indicates that logout or active task reassignment can be
* attempted immediately.
*/
-#define ISCSI_LOGIN_AUTH_SESSION_TEXT_KEY_DEFAULT_TIME_WAIT "DefaultTime2Wait"
+#define ISCSI_LOGIN_AUTH_SESSION_TEXT_KEY_DEFAULT_TIME_WAIT ((const uint8_t *) "DefaultTime2Wait\0\0\0\0\0\0\0")
/**
* @brief Login/Text Operational Session Text Key: Default time to retain.
@@ -3961,7 +8046,7 @@ typedef struct __attribute__((packed)) iscsi_isid {
* A value of 0 indicates that connection/task state is immediately
* discarded by the target.
*/
-#define ISCSI_LOGIN_AUTH_SESSION_TEXT_KEY_DEFAULT_TIME_RETAIN "DefaultTime2Retain"
+#define ISCSI_LOGIN_AUTH_SESSION_TEXT_KEY_DEFAULT_TIME_RETAIN ((const uint8_t *) "DefaultTime2Retain\0\0\0\0\0")
/**
* @brief Login/Text Operational Session Text Key: Maximum outstanding Ready To Transfer.
@@ -3981,7 +8066,7 @@ typedef struct __attribute__((packed)) iscsi_isid {
* PDU (with the F bit set to 1) is transferred or a sequence reception
* timeout is encountered for that data sequence.
*/
-#define ISCSI_LOGIN_AUTH_SESSION_TEXT_KEY_MAX_OUTSTANDING_R2T "MaxOutstandingR2T"
+#define ISCSI_LOGIN_AUTH_SESSION_TEXT_KEY_MAX_OUTSTANDING_R2T ((const uint8_t *) "MaxOutstandingR2T\0\0\0\0\0\0")
/**
* @brief Login/Text Operational Session Text Key: Data Protocol Data Unit (PDU) in order.
@@ -4000,7 +8085,7 @@ typedef struct __attribute__((packed)) iscsi_isid {
* sequences have to be at continuously increasing addresses and
* overlays are forbidden.
*/
-#define ISCSI_LOGIN_AUTH_SESSION_TEXT_KEY_DATA_PDU_IN_ORDER "DataPDUInOrder"
+#define ISCSI_LOGIN_AUTH_SESSION_TEXT_KEY_DATA_PDU_IN_ORDER ((const uint8_t *) "DataPDUInOrder\0")
/**
* @brief Login/Text Operational Session Text Key: Data sequence in order.
@@ -4030,7 +8115,7 @@ typedef struct __attribute__((packed)) iscsi_isid {
* not 0 and DataSequenceInOrder is set to Yes, then MaxOutstandingR2T
* MUST be set to 1.
*/
-#define ISCSI_LOGIN_AUTH_SESSION_TEXT_KEY_DATA_SEQ_IN_ORDER "DataSequenceInOrder"
+#define ISCSI_LOGIN_AUTH_SESSION_TEXT_KEY_DATA_SEQ_IN_ORDER ((const uint8_t *) "DataSequenceInOrder\0\0\0\0")
/**
* @brief Login/Text Operational Session Text Key: Error recovery level.
@@ -4050,7 +8135,7 @@ typedef struct __attribute__((packed)) iscsi_isid {
* In the description of recovery mechanisms, certain recovery classes
* are specified.
*/
-#define ISCSI_LOGIN_AUTH_SESSION_TEXT_KEY_ERR_RECOVERY_LEVEL "ErrorRecoveryLevel"
+#define ISCSI_LOGIN_AUTH_SESSION_TEXT_KEY_ERR_RECOVERY_LEVEL ((const uint8_t *) "ErrorRecoveryLevel\0\0\0\0\0")
/**
* @brief Login/Text Operational Session Text Key: X reversed vendor.
@@ -4073,7 +8158,7 @@ typedef struct __attribute__((packed)) iscsi_isid {
* Vendor-specific keys MUST ONLY be used in Normal sessions.\n
* Support for public or private extension keys is OPTIONAL.
*/
-#define ISCSI_LOGIN_AUTH_SESSION_TEXT_KEY_PRIV_EXT_KEY_FMT "X-reversed.vendor"
+#define ISCSI_LOGIN_AUTH_SESSION_TEXT_KEY_PRIV_EXT_KEY_FMT ((const uint8_t *) "X-reversed.vendor\0\0\0\0\0\0")
/**
* @brief Login/Text Operational Session Text Key: Task reporting.
@@ -4101,7 +8186,7 @@ typedef struct __attribute__((packed)) iscsi_isid {
* When TaskReporting is not negotiated to FastAbort, the
* standard multi-task abort semantics MUST be used.
*/
-#define ISCSI_LOGIN_AUTH_SESSION_TEXT_KEY_TASK_REPORTING "TaskReporting"
+#define ISCSI_LOGIN_AUTH_SESSION_TEXT_KEY_TASK_REPORTING ((const uint8_t *) "TaskReporting\0\0")
/**
* @brief Login/Text Operational Session Text Key: X Node architecture.
@@ -4150,7 +8235,7 @@ typedef struct __attribute__((packed)) iscsi_isid {
* silent (the node does not transmit any key value and simply discards
* any key values it receives without issuing a NotUnderstood response).
*/
-#define ISCSI_LOGIN_AUTH_SESSION_TEXT_KEY_X_NODE_ARCH "X#NodeArchitecture"
+#define ISCSI_LOGIN_AUTH_SESSION_TEXT_KEY_X_NODE_ARCH ((const uint8_t *) "X#NodeArchitecture\0\0\0\0\0")
/**
* @brief Login/Text Operational Session Text Key: IFMarker (obseleted).
@@ -4169,7 +8254,7 @@ typedef struct __attribute__((packed)) iscsi_isid {
* implementation MUST NOT respond with a "NotUnderstood" value for
* either of these keys.
*/
-#define ISCSI_LOGIN_AUTH_SESSION_TEXT_KEY_IF_MARKER "IFMarker"
+#define ISCSI_LOGIN_AUTH_SESSION_TEXT_KEY_IF_MARKER ((const uint8_t *) "IFMarker\0\0\0\0\0\0\0")
/**
* @brief Login/Text Operational Session Text Key: OFMarker (obseleted).
@@ -4188,7 +8273,7 @@ typedef struct __attribute__((packed)) iscsi_isid {
* implementation MUST NOT respond with a "NotUnderstood" value for
* either of these keys.
*/
-#define ISCSI_LOGIN_AUTH_SESSION_TEXT_KEY_OF_MARKER "OFMarker"
+#define ISCSI_LOGIN_AUTH_SESSION_TEXT_KEY_OF_MARKER ((const uint8_t *) "OFMarker\0\0\0\0\0\0\0")
/**
* @brief Login/Text Operational Session Text Key: OFMarkInt (obseleted).
@@ -4207,7 +8292,7 @@ typedef struct __attribute__((packed)) iscsi_isid {
* implementation MUST NOT respond with a "NotUnderstood" value for
* either of these keys.
*/
-#define ISCSI_LOGIN_AUTH_SESSION_TEXT_KEY_OF_MARK_INT "OFMarkInt"
+#define ISCSI_LOGIN_AUTH_SESSION_TEXT_KEY_OF_MARK_INT ((const uint8_t *) "OFMarkInt\0\0\0\0\0\0")
/**
* @brief Login/Text Operational Session Text Key: IFMarkInt (obseleted).
@@ -4226,7 +8311,7 @@ typedef struct __attribute__((packed)) iscsi_isid {
* implementation MUST NOT respond with a "NotUnderstood" value for
* either of these keys.
*/
-#define ISCSI_LOGIN_AUTH_SESSION_TEXT_KEY_IF_MARK_INT "IFMarkInt"
+#define ISCSI_LOGIN_AUTH_SESSION_TEXT_KEY_IF_MARK_INT ((const uint8_t *) "IFMarkInt\0\0\0\0\0\0")
/// Login request Next Stage (NSG) flags: SecurityNegotiation.
@@ -4250,10 +8335,10 @@ typedef struct __attribute__((packed)) iscsi_isid {
* next stage to which they want to move. The Next Stage value is only
* valid when the T bit is 1; otherwise, it is reserved.
*/
-#define ISCSI_LOGIN_REQ_FLAGS_NEXT_STAGE_FIRST_BIT 0
+#define ISCSI_LOGIN_REQ_FLAGS_NEXT_STAGE_FIRST_BIT 0
/**
- * @brief Login request flags: Next Stage (NSG): Second bit of the two bits.
+ * @brief Login request flags: Next Stage (NSG): Last bit of the two bits.
*
* The Login negotiation requests and responses are associated
* with a specific stage in the session (SecurityNegotiation,\n
@@ -4261,13 +8346,16 @@ typedef struct __attribute__((packed)) iscsi_isid {
* next stage to which they want to move. The Next Stage value is only
* valid when the T bit is 1; otherwise, it is reserved.
*/
-#define ISCSI_LOGIN_REQ_FLAGS_NEXT_STAGE_SECOND_BIT ((ISCSI_LOGIN_REQ_FLAGS_NEXT_STAGE_FIRST_BIT) + 1)
+#define ISCSI_LOGIN_REQ_FLAGS_NEXT_STAGE_LAST_BIT ((ISCSI_LOGIN_REQ_FLAGS_NEXT_STAGE_FIRST_BIT) + 2 - 1)
/// Login request flags: Next Stage (NSG): Bit mask.
-#define ISCSI_LOGIN_REQ_FLAGS_NEXT_STAGE_MASK ((1 << (ISCSI_LOGIN_REQ_FLAGS_NEXT_STAGE_FIRST_BIT)) | (1 << (ISCSI_LOGIN_REQ_FLAGS_NEXT_STAGE_SECOND_BIT)))
+#define ISCSI_LOGIN_REQ_FLAGS_NEXT_STAGE_MASK (ISCSI_BITS_GET_MASK(ISCSI_LOGIN_REQ_FLAGS_NEXT_STAGE_FIRST_BIT, ISCSI_LOGIN_REQ_FLAGS_NEXT_STAGE_LAST_BIT))
/// Login request flags: Extracts the Next Stage (NSG) bits.
-#define ISCSI_LOGIN_REQ_FLAGS_GET_NEXT_STAGE(x) (((x) & ISCSI_LOGIN_REQ_FLAGS_NEXT_STAGE_MASK) >> ISCSI_LOGIN_REQ_FLAGS_NEXT_STAGE_FIRST_BIT)
+#define ISCSI_LOGIN_REQ_FLAGS_GET_NEXT_STAGE(x) (ISCSI_BITS_GET((x), ISCSI_LOGIN_REQ_FLAGS_NEXT_STAGE_FIRST_BIT, ISCSI_LOGIN_REQ_FLAGS_NEXT_STAGE_LAST_BIT))
+
+/// Login request flags: Stores into the Next Stage (NSG) bits.
+#define ISCSI_LOGIN_REQ_FLAGS_PUT_NEXT_STAGE(x) (ISCSI_BITS_PUT((x), ISCSI_LOGIN_REQ_FLAGS_NEXT_STAGE_FIRST_BIT, ISCSI_LOGIN_REQ_FLAGS_NEXT_STAGE_LAST_BIT))
/// Login request Current Stage (CSG) flags: SecurityNegotiation.
@@ -4290,23 +8378,26 @@ typedef struct __attribute__((packed)) iscsi_isid {
* LoginOperationalNegotiation, FullFeaturePhase) and may indicate the
* next stage to which they want to move.
*/
-#define ISCSI_LOGIN_REQ_FLAGS_CURRENT_STAGE_FIRST_BIT 2
+#define ISCSI_LOGIN_REQ_FLAGS_CURRENT_STAGE_FIRST_BIT 2
/**
- * @brief Login request flags: Current Stage (CSG): Second bit of the two bits.
+ * @brief Login request flags: Current Stage (CSG): Last bit of the two bits.
*
* The Login negotiation requests and responses are associated
* with aspecific stage in the session (SecurityNegotiation,
* LoginOperationalNegotiation, FullFeaturePhase) and may indicate the
* next stage to which they want to move.
*/
-#define ISCSI_LOGIN_REQ_FLAGS_CURRENT_STAGE_SECOND_BIT ((ISCSI_LOGIN_REQ_FLAGS_CURRENT_STAGE_FIRST_BIT) + 1)
+#define ISCSI_LOGIN_REQ_FLAGS_CURRENT_STAGE_LAST_BIT ((ISCSI_LOGIN_REQ_FLAGS_CURRENT_STAGE_FIRST_BIT) + 2 - 1)
/// Login request flags: Current Stage (CSG): Bit mask.
-#define ISCSI_LOGIN_REQ_FLAGS_CURRENT_STAGE_MASK ((1 << (ISCSI_LOGIN_REQ_FLAGS_CURRENT_STAGE_FIRST_BIT)) | (1 << (ISCSI_LOGIN_REQ_FLAGS_CURRENT_STAGE_SECOND_BIT)))
+#define ISCSI_LOGIN_REQ_FLAGS_CURRENT_STAGE_MASK (ISCSI_BITS_GET_MASK(ISCSI_LOGIN_REQ_FLAGS_CURRENT_STAGE_FIRST_BIT, ISCSI_LOGIN_REQ_FLAGS_CURRENT_STAGE_LAST_BIT))
/// Login request flags: Extracts the Current Stage (CSG) bits.
-#define ISCSI_LOGIN_REQ_FLAGS_GET_CURRENT_STAGE(x) (((x) & ISCSI_LOGIN_REQ_FLAGS_CURRENT_STAGE_MASK) >> ISCSI_LOGIN_REQ_FLAGS_CURRENT_STAGE_FIRST_BIT)
+#define ISCSI_LOGIN_REQ_FLAGS_GET_CURRENT_STAGE(x) (ISCSI_BITS_GET((x), ISCSI_LOGIN_REQ_FLAGS_CURRENT_STAGE_FIRST_BIT, ISCSI_LOGIN_REQ_FLAGS_CURRENT_STAGE_LAST_BIT))
+
+/// Login request flags: Stores into the Current Stage (CSG) bits.
+#define ISCSI_LOGIN_REQ_FLAGS_PUT_CURRENT_STAGE(x) (ISCSI_BITS_PUT((x), ISCSI_LOGIN_REQ_FLAGS_CURRENT_STAGE_FIRST_BIT, ISCSI_LOGIN_REQ_FLAGS_CURRENT_STAGE_LAST_BIT))
/**
@@ -4457,7 +8548,7 @@ typedef struct __attribute__((packed)) iscsi_login_req_packet {
* to enable the target to determine if the initiator may use
* the target's resources and the initial text parameters for the security exchange
*/
- iscsi_ds_cmd_data ds_cmd_data;
+ iscsi_scsi_ds_cmd_data ds_cmd_data;
} iscsi_login_req_packet;
@@ -4482,10 +8573,10 @@ typedef struct __attribute__((packed)) iscsi_login_req_packet {
* next stage to which they want to move The Next Stage value is only
* valid when the T bit is 1; otherwise, it is reserved.
*/
-#define ISCSI_LOGIN_RESPONSE_FLAGS_NEXT_STAGE_FIRST_BIT 0
+#define ISCSI_LOGIN_RESPONSE_FLAGS_NEXT_STAGE_FIRST_BIT 0
/**
- * @brief Login response flags: Next Stage (NSG): First bit of the two bits.
+ * @brief Login response flags: Next Stage (NSG): Last bit of the two bits.
*
* The Login negotiation requests and responses are associated
* with a specific stage in the session (SecurityNegotiation,
@@ -4493,14 +8584,16 @@ typedef struct __attribute__((packed)) iscsi_login_req_packet {
* next stage to which they want to move The Next Stage value is only
* valid when the T bit is 1; otherwise, it is reserved.
*/
-#define ISCSI_LOGIN_RESPONSE_FLAGS_NEXT_STAGE_SECOND_BIT ((ISCSI_LOGIN_RESPONSE_FLAGS_NEXT_STAGE_FIRST_BIT) + 1)
+#define ISCSI_LOGIN_RESPONSE_FLAGS_NEXT_STAGE_LAST_BIT ((ISCSI_LOGIN_RESPONSE_FLAGS_NEXT_STAGE_FIRST_BIT) + 2 - 1)
/// Login response flags: Next Stage (NSG): Bit mask.
-#define ISCSI_LOGIN_RESPONSE_FLAGS_NEXT_STAGE_MASK ((1 << (ISCSI_LOGIN_RESPONSE_FLAGS_NEXT_STAGE_FIRST_BIT)) | (1 << (ISCSI_LOGIN_RESPONSE_FLAGS_NEXT_STAGE_SECOND_BIT)))
+#define ISCSI_LOGIN_RESPONSE_FLAGS_NEXT_STAGE_MASK (ISCSI_BITS_GET_MASK(ISCSI_LOGIN_RESPONSE_FLAGS_NEXT_STAGE_FIRST_BIT, ISCSI_LOGIN_RESPONSE_FLAGS_NEXT_STAGE_LAST_BIT))
/// Login response flags: Extracts the Next Stage (NSG) bits.
-#define ISCSI_LOGIN_RESPONSE_FLAGS_GET_NEXT_STAGE(x) (((x) & ISCSI_LOGIN_RESPONSE_FLAGS_NEXT_STAGE_MASK) >> ISCSI_LOGIN_RESPONSE_FLAGS_NEXT_STAGE_FIRST_BIT)
+#define ISCSI_LOGIN_RESPONSE_FLAGS_GET_NEXT_STAGE(x) (ISCSI_BITS_GET((x), ISCSI_LOGIN_RESPONSE_FLAGS_NEXT_STAGE_FIRST_BIT, ISCSI_LOGIN_RESPONSE_FLAGS_NEXT_STAGE_LAST_BIT))
+/// Login response flags: Stores into the Next Stage (NSG) bits.
+#define ISCSI_LOGIN_RESPONSE_FLAGS_PUT_NEXT_STAGE(x) (ISCSI_BITS_PUT((x), ISCSI_LOGIN_RESPONSE_FLAGS_NEXT_STAGE_FIRST_BIT, ISCSI_LOGIN_RESPONSE_FLAGS_NEXT_STAGE_LAST_BIT))
/// Login response Current Stage (CSG) flags: SecurityNegotiation.
@@ -4523,7 +8616,7 @@ typedef struct __attribute__((packed)) iscsi_login_req_packet {
* LoginOperationalNegotiation, FullFeaturePhase) and may indicate the
* next stage to which they want to move.
*/
-#define ISCSI_LOGIN_RESPONSE_FLAGS_CURRENT_STAGE_FIRST_BIT 2
+#define ISCSI_LOGIN_RESPONSE_FLAGS_CURRENT_STAGE_FIRST_BIT 2
/**
* @brief Login response flags: Current Stage (CSG): First bit of the two bits.
@@ -4533,13 +8626,16 @@ typedef struct __attribute__((packed)) iscsi_login_req_packet {
* LoginOperationalNegotiation, FullFeaturePhase) and may indicate the
* next stage to which they want to move.
*/
-#define ISCSI_LOGIN_RESPONSE_FLAGS_CURRENT_STAGE_SECOND_BIT ((ISCSI_LOGIN_RESPONSE_FLAGS_CURRENT_STAGE_FIRST_BIT) + 1)
+#define ISCSI_LOGIN_RESPONSE_FLAGS_CURRENT_STAGE_LAST_BIT ((ISCSI_LOGIN_RESPONSE_FLAGS_CURRENT_STAGE_FIRST_BIT) + 2 - 1)
/// Login request flags: Current Stage (CSG): Bit mask.
-#define ISCSI_LOGIN_RESPONSE_FLAGS_CURRENT_STAGE_MASK ((1 << (ISCSI_LOGIN_RESPONSE_FLAGS_CURRENT_STAGE_FIRST_BIT)) | (1 << (ISCSI_LOGIN_RESPONSE_FLAGS_CURRENT_STAGE_SECOND_BIT)))
+#define ISCSI_LOGIN_RESPONSE_FLAGS_CURRENT_STAGE_MASK (ISCSI_BITS_GET_MASK(ISCSI_LOGIN_RESPONSE_FLAGS_CURRENT_STAGE_FIRST_BIT, ISCSI_LOGIN_RESPONSE_FLAGS_CURRENT_STAGE_LAST_BIT))
/// Login request flags: Extracts the Current Stage (CSG) bits.
-#define ISCSI_LOGIN_RESPONSE_FLAGS_GET_CURRENT_STAGE(x) (((x) & ISCSI_LOGIN_RESPONSE_FLAGS_CURRENT_STAGE_MASK) >> ISCSI_LOGIN_RESPONSE_FLAGS_CURRENT_STAGE_FIRST_BIT)
+#define ISCSI_LOGIN_RESPONSE_FLAGS_GET_CURRENT_STAGE(x) (ISCSI_BITS_GET((x), ISCSI_LOGIN_RESPONSE_FLAGS_CURRENT_STAGE_FIRST_BIT, ISCSI_LOGIN_RESPONSE_FLAGS_CURRENT_STAGE_LAST_BIT))
+
+/// Login request flags: Stores into the Current Stage (CSG) bits.
+#define ISCSI_LOGIN_RESPONSE_FLAGS_PUT_CURRENT_STAGE(x) (ISCSI_BITS_PUT((x), ISCSI_LOGIN_RESPONSE_FLAGS_CURRENT_STAGE_FIRST_BIT, ISCSI_LOGIN_RESPONSE_FLAGS_CURRENT_STAGE_LAST_BIT))
/**
@@ -4799,7 +8895,7 @@ typedef struct __attribute__((packed)) iscsi_login_response_packet {
* All the rules specified for Text Responses also hold for Login
* Responses.
*/
- iscsi_ds_cmd_data ds_cmd_data;
+ iscsi_scsi_ds_cmd_data ds_cmd_data;
} iscsi_login_response_packet;
@@ -5012,7 +9108,7 @@ typedef struct __attribute__((packed)) iscsi_logout_response_packet {
/// Always 0x26 according to iSCSI specification.
uint8_t opcode;
- /// Reserved for future usage (must be always 0x80 for now).
+ /// Reserved for future usage (always MUST be 0x80 for now).
int8_t flags;
/// Response.
@@ -5344,7 +9440,7 @@ typedef struct __attribute__((packed)) iscsi_reject_packet {
/// Always 0x3F according to iSCSI specification.
uint8_t opcode;
- /// Reserved for future usage (must be always 0x80 for now).
+ /// Reserved for future usage (always MUST be 0x80 for now).
int8_t flags;
/**
@@ -5414,10 +9510,10 @@ typedef struct __attribute__((packed)) iscsi_reject_packet {
* next Data/R2T sequence number that the target would send for the
* task, if any.
*/
- uint32_t data_r2tsn_sn;
+ uint32_t data_r2t_sn;
/// Reserved for future usage, always MUST be 0.
- uint32_t reserved4[2];
+ uint64_t reserved4;
/// Optional header digest.
iscsi_header_digest hdr_digest;
@@ -5462,7 +9558,7 @@ typedef struct __attribute__((packed)) iscsi_nop_out_packet {
/// Always 0x00 according to iSCSI specification.
uint8_t opcode;
- /// Reserved for future usage (must be always 0x80 for now).
+ /// Reserved for future usage (always MUST be 0x80 for now).
int8_t flags;
/// Reserved for future usage, always MUST be 0.
@@ -5525,7 +9621,7 @@ typedef struct __attribute__((packed)) iscsi_nop_out_packet {
* ping data is indicated by the DataSegmentLength. 0 is a valid value
* for the DataSegmentLength and indicates the absence of ping data.
*/
- iscsi_ds_cmd_data ds_ping_data;
+ iscsi_scsi_ds_cmd_data ds_ping_data;
/// Optional data digest.
iscsi_data_digest data_digest;
@@ -5560,7 +9656,7 @@ typedef struct __attribute__((packed)) iscsi_nop_in_packet {
/// Always 0x20 according to iSCSI specification.
uint8_t opcode;
- /// Reserved for future usage (must be always 0x80 for now).
+ /// Reserved for future usage (always MUST be 0x80 for now).
int8_t flags;
/// Reserved for future usage, always MUST be 0.
@@ -5602,32 +9698,71 @@ typedef struct __attribute__((packed)) iscsi_nop_in_packet {
uint32_t stat_sn;
/// ExpCmdSN.
- uint32_t exp_cmd_sn; // ExpCmdSN
+ uint32_t exp_cmd_sn;
/// MaxCmdSN.
uint32_t max_cmd_sn;
/// Reserved for future usage, always MUST be 0.
- uint32_t reserved2[3];
+ uint32_t reserved2;
+
+ /// Reserved for future usage, always MUST be 0.
+ uint64_t reserved3;
/// Optional header digest.
iscsi_header_digest hdr_digest;
/// DataSegment - Return Ping Data.
- iscsi_ds_cmd_data ds_ping_data;
+ iscsi_scsi_ds_cmd_data ds_ping_data;
/// Optional data digest.
iscsi_data_digest data_digest;
} iscsi_nop_in_packet;
-/// iSCSI SCSI transport ID protocol identifier.
-#define ISCSI_TRANSPORT_ID_PROTOCOL_ID_ISCSI 0x05
+/// iSCSI SCSI transport ID protocol identifier: iSCSI.
+#define ISCSI_TRANSPORT_ID_PROTOCOL_ID_ISCSI 0x05
+
+/// iSCSI SCSI transport ID protocol identifier: First bit of the four bits.
+#define ISCSI_TRANSPORT_ID_PROTOCOL_ID_FIRST_BIT 0
+
+/// iSCSI SCSI transport ID protocol identifier: Last bit of the four bits.
+#define ISCSI_TRANSPORT_ID_PROTOCOL_ID_LAST_BIT ((ISCSI_TRANSPORT_ID_PROTOCOL_ID_FIRST_BIT) + 4 - 1)
+
+/// iSCSI SCSI transport ID protocol identifier: Bit mask.
+#define ISCSI_TRANSPORT_ID_PROTOCOL_ID_MASK (ISCSI_BITS_GET_MASK(ISCSI_TRANSPORT_ID_PROTOCOL_ID_FIRST_BIT, ISCSI_TRANSPORT_ID_PROTOCOL_ID_LAST_BIT))
+
+/// iSCSI SCSI transport ID protocol identifier: Extracts the protocol identifier bits.
+#define ISCSI_TRANSPORT_ID_GET_PROTOCOL_ID(x) (ISCSI_BITS_GET((x), ISCSI_TRANSPORT_ID_PROTOCOL_ID_FIRST_BIT, ISCSI_TRANSPORT_ID_PROTOCOL_ID_LAST_BIT))
+
+/// iSCSI SCSI transport ID protocol identifier: Stores into the protocol identifier bits.
+#define ISCSI_TRANSPORT_ID_PUT_PROTOCOL_ID(x) (ISCSI_BITS_PUT((x), ISCSI_TRANSPORT_ID_PROTOCOL_ID_FIRST_BIT, ISCSI_TRANSPORT_ID_PROTOCOL_ID_LAST_BIT))
/// iSCSI SCSI transport ID format.
-#define ISCSI_TRANSPORT_ID_FORMAT 0x01
+#define ISCSI_TRANSPORT_ID_FORMAT 0x01
+
+/// iSCSI SCSI transport ID format: First bit of the two bits.
+#define ISCSI_TRANSPORT_ID_FORMAT_FIRST_BIT 6
+
+/// iSCSI SCSI transport ID format: Last bit of the two bits.
+#define ISCSI_TRANSPORT_ID_FORMAT_LAST_BIT ((ISCSI_TRANSPORT_ID_FORMAT_FIRST_BIT) + 2 - 1)
+
+/// iSCSI SCSI transport ID format: Bit mask.
+#define ISCSI_TRANSPORT_ID_FORMAT_MASK (ISCSI_BITS_GET_MASK(ISCSI_TRANSPORT_ID_FORMAT_FIRST_BIT, ISCSI_TRANSPORT_ID_FORMAT_LAST_BIT))
+
+/// iSCSI SCSI transport ID format: Extracts the format bits.
+#define ISCSI_TRANSPORT_ID_GET_FORMAT(x) (ISCSI_BITS_GET((x), ISCSI_TRANSPORT_ID_FORMAT_FIRST_BIT, ISCSI_TRANSPORT_ID_FORMAT_LAST_BIT))
+/// iSCSI SCSI transport ID format: Stores into the format bits.
+#define ISCSI_TRANSPORT_ID_PUT_FORMAT(x) (ISCSI_BITS_PUT((x), ISCSI_TRANSPORT_ID_FORMAT_FIRST_BIT, ISCSI_TRANSPORT_ID_FORMAT_LAST_BIT))
+
+/**
+ * @brief iSCSI SCSI Transport ID structure.
+ *
+ * This structure handles the iSCSI SCSI transport
+ * identifier data.
+ */
typedef struct __attribute__((packed)) iscsi_transport_id {
/// First 4 bits are protocol ID and last 2 bits are format.
uint8_t id;
@@ -5643,115 +9778,73 @@ typedef struct __attribute__((packed)) iscsi_transport_id {
} iscsi_transport_id;
-/// iSCSI packet validation return code from iscsi_validate_packet function: Validation successful -> iSCSI packet recognized and compliance to protocol specification.
-#define ISCSI_VALIDATE_PACKET_RESULT_OK 0L
-
-/// iSCSI packet validation return code from iscsi_validate_packet function: Validation failed -> No packet data specified.
-#define ISCSI_VALIDATE_PACKET_RESULT_ERROR_NO_DATA -1L
-
-/// iSCSI packet validation return code from iscsi_validate_packet function: Validation failed -> Packet size smaller than smallest possible iSCSI packet.
-#define ISCSI_VALIDATE_PACKET_RESULT_ERROR_SIZE_TOO_SMALL -2L
-
-/// iSCSI packet validation return code from iscsi_validate_packet function: Validation failed -> Packet size doesn't match calculated lengths from BHS.
-#define ISCSI_VALIDATE_PACKET_RESULT_ERROR_SIZE_MISMATCH -3L
-
-/// iSCSI packet validation return code from iscsi_validate_packet function: Validation failed -> iSCSI protocol version not supported yet.
-#define ISCSI_VALIDATE_PACKET_RESULT_ERROR_UNSUPPORTED_VERSION -4L
-
-/// iSCSI packet validation return code from iscsi_validate_packet function: Validation failed -> Valid opcode but violates iSCSI protocol specification.
-#define ISCSI_VALIDATE_PACKET_RESULT_ERROR_PROTOCOL_SPECS -5L
-
-/// iSCSI packet validation return code from iscsi_validate_packet function: Validation failed -> Invalid opcode according to iSCSI protocol specification.
-#define ISCSI_VALIDATE_PACKET_RESULT_ERROR_INVALID_OPCODE -6L
-
-/// iSCSI packet validation return code from iscsi_validate_packet function: Validation failed -> CRC32C check failed for header (BHS and/or AHS).
-#define ISCSI_VALIDATE_PACKET_RESULT_ERROR_CRC32C_HDR_DIGEST -7L
-
-/// iSCSI packet validation return code from iscsi_validate_packet function: Validation failed -> CRC32C check failed for data segment.
-#define ISCSI_VALIDATE_PACKET_RESULT_ERROR_CRC32C_DATA_DIGEST -8L
-
-
-iscsi_bhs_packet *iscsi_create_packet(); // Allocate and initialize an iSCSI BHS packet
-void iscsi_destroy_packet(iscsi_bhs_packet *packet_data); // Free resources allocated by iscsi_create_packet
-
-iscsi_bhs_packet *iscsi_append_ahs_packet(iscsi_bhs_packet *packet_data, const uint32_t ahs_len); // Allocate and initialize an iSCSI AHS packet and append to existing data stream
-int iscsi_get_ahs_packets(const iscsi_bhs_packet *packet_data); // Counts number of AHS packets in an iSCSI data packet stream
-iscsi_ahs_packet *iscsi_get_ahs_packet(const iscsi_bhs_packet *packet_data, const int index); // Retrieves the pointer to an specific AHS packet by index
-
-iscsi_bhs_packet *iscsi_append_header_digest_packet(iscsi_bhs_packet *packet_data, const int header_digest_size); // Allocate and initialize an iSCSI header digest (CRC32C) and appends it to existing data stream
-
-iscsi_bhs_packet *iscsi_append_ds_packet(iscsi_bhs_packet *packet_data, const int header_digest_size, const uint32_t ds_len, const int data_digest_size); // Allocate and initialize an iSCSI DS packet and append to existing data stream
-
-void iscsi_calc_header_digest(const iscsi_bhs_packet *packet_data); // Calculate and store iSCSI header digest (CRC32C)
-int iscsi_validate_header_digest(const iscsi_bhs_packet *packet_data); // Validates a stored iSCSI header digest (CRC32C) with actual header data
-
-void iscsi_calc_data_digest(const iscsi_bhs_packet *packet_data, const int header_digest_size); // Calculate iSCSI data digest (CRC32C)
-int iscsi_validate_data_digest(const iscsi_bhs_packet *packet_data, const int header_digest_size); // Validates a stored iSCSI data digest (CRC32C) with actual DataSegment
-
-int iscsi_validate_packet(const struct iscsi_bhs_packet *packet_data, const uint32_t len, const int header_digest_size, const int data_digest_size); // Check if valid iSCSI packet and validate if necessarily
-
-
/// Maximum length of a key according to iSCSI specifications.
-#define ISCSI_TEXT_KEY_MAX_LEN 63UL
+#define ISCSI_TEXT_KEY_MAX_LEN 63U
/// Maximum length of value for a simple key type.
-#define ISCSI_TEXT_VALUE_MAX_SIMPLE_LEN 255UL
+#define ISCSI_TEXT_VALUE_MAX_SIMPLE_LEN 255U
/// Maximum length of value for a normal key.
-#define ISCSI_TEXT_VALUE_MAX_LEN 8192UL
+#define ISCSI_TEXT_VALUE_MAX_LEN 8192U
+
+/// Value data shift value for key value alignment enforcement.
+#define ISCSI_TEXT_VALUE_ALIGN_SHIFT 4UL
+
+/// Value alignment size is a multiple of 16 bytes for a key value for having work space when changing string representation of integer values.
+#define ISCSI_TEXT_VALUE_ALIGN (1UL << (ISCSI_TEXT_VALUE_ALIGN_SHIFT))
/// iSCSI text key=value pair type: Invalid.
-#define ISCSI_TEXT_KEY_VALUE_PAIR_TYPE_INVALID -1L
+#define ISCSI_TEXT_KEY_VALUE_PAIR_TYPE_INVALID -1
/// iSCSI text key=value pair type: Unspecified type.
-#define ISCSI_TEXT_KEY_VALUE_PAIR_TYPE_UNSPECIFIED 0L
+#define ISCSI_TEXT_KEY_VALUE_PAIR_TYPE_UNSPECIFIED 0
/// iSCSI text key=value pair type: List.
-#define ISCSI_TEXT_KEY_VALUE_PAIR_TYPE_LIST 1L
+#define ISCSI_TEXT_KEY_VALUE_PAIR_TYPE_LIST 1
/// iSCSI text key=value pair type: Numerical minimum.
-#define ISCSI_TEXT_KEY_VALUE_PAIR_TYPE_NUM_MIN 2L
+#define ISCSI_TEXT_KEY_VALUE_PAIR_TYPE_NUM_MIN 2
/// iSCSI text key=value pair type: Numerical maximum.
-#define ISCSI_TEXT_KEY_VALUE_PAIR_TYPE_NUM_MAX 3L
+#define ISCSI_TEXT_KEY_VALUE_PAIR_TYPE_NUM_MAX 3
/// iSCSI text key=value pair type: Numerical declarative.
-#define ISCSI_TEXT_KEY_VALUE_PAIR_TYPE_NUM_DECLARATIVE 4L
+#define ISCSI_TEXT_KEY_VALUE_PAIR_TYPE_NUM_DECLARATIVE 4
/// iSCSI text key=value pair type: Declarative.
-#define ISCSI_TEXT_KEY_VALUE_PAIR_TYPE_DECLARATIVE 5L
+#define ISCSI_TEXT_KEY_VALUE_PAIR_TYPE_DECLARATIVE 5
/// iSCSI text key=value pair type: Boolean OR.
-#define ISCSI_TEXT_KEY_VALUE_PAIR_TYPE_BOOL_OR 6L
+#define ISCSI_TEXT_KEY_VALUE_PAIR_TYPE_BOOL_OR 6
/// iSCSI text key=value pair type: Boolean AND.
-#define ISCSI_TEXT_KEY_VALUE_PAIR_TYPE_BOOL_AND 7L
+#define ISCSI_TEXT_KEY_VALUE_PAIR_TYPE_BOOL_AND 7
/// iSCSI key value pair flags: Discovery ignored.
-#define ISCSI_TEXT_KEY_VALUE_PAIR_FLAGS_DISCOVERY_IGNORE (1 << 0L)
+#define ISCSI_TEXT_KEY_VALUE_PAIR_FLAGS_DISCOVERY_IGNORE (1 << 0)
/// iSCSI key value pair flags: Multi negotiation.
-#define ISCSI_TEXT_KEY_VALUE_PAIR_FLAGS_MULTI_NEGOTIATION (1 << 1L)
+#define ISCSI_TEXT_KEY_VALUE_PAIR_FLAGS_MULTI_NEGOTIATION (1 << 1)
/// iSCSI key value pair flags: Target declarative.
-#define ISCSI_TEXT_KEY_VALUE_PAIR_FLAGS_TARGET_DECLARATIVE (1 << 2L)
+#define ISCSI_TEXT_KEY_VALUE_PAIR_FLAGS_TARGET_DECLARATIVE (1 << 2)
/// iSCSI key value pair flags: CHAP type.
-#define ISCSI_TEXT_KEY_VALUE_PAIR_FLAGS_CHAP_TYPE (1 << 3L)
+#define ISCSI_TEXT_KEY_VALUE_PAIR_FLAGS_CHAP_TYPE (1 << 3)
/// iSCSI key value pair flags: Requires special handling.
-#define ISCSI_TEXT_KEY_VALUE_PAIR_FLAGS_SPECIAL_HANDLING (1 << 4L)
+#define ISCSI_TEXT_KEY_VALUE_PAIR_FLAGS_SPECIAL_HANDLING (1 << 4)
/// iSCSI key value pair flags: Use previous default value.
-#define ISCSI_TEXT_KEY_VALUE_PAIR_FLAGS_USE_PREVIOUS_VALUE (1 << 5L)
+#define ISCSI_TEXT_KEY_VALUE_PAIR_FLAGS_USE_PREVIOUS_VALUE (1 << 5)
/// iSCSI key value pair flags: Override with default value.
-#define ISCSI_TEXT_KEY_VALUE_PAIR_FLAGS_OVERRIDE_DEFAULT (1 << 6L)
+#define ISCSI_TEXT_KEY_VALUE_PAIR_FLAGS_OVERRIDE_DEFAULT (1 << 6)
/// iSCSI key value pair flags: Uses maximum value depending on secondary key.
-#define ISCSI_TEXT_KEY_VALUE_PAIR_FLAGS_USE_OTHER_MAX_VALUE (1 << 7L)
+#define ISCSI_TEXT_KEY_VALUE_PAIR_FLAGS_USE_OTHER_MAX_VALUE (1 << 7)
/**
@@ -5762,7 +9855,7 @@ int iscsi_validate_packet(const struct iscsi_bhs_packet *packet_data, const uint
*/
typedef struct iscsi_key_value_pair_lut_entry {
/// Name of key.
- uint8_t *key;
+ const uint8_t *key;
/// Default value of the key, always in string representation.
uint8_t *value;
@@ -5802,6 +9895,8 @@ typedef struct iscsi_key_value_pair {
uint state_mask;
} iscsi_key_value_pair;
+typedef struct iscsi_connection iscsi_connection;
+
/**
* @brief iSCSI Text / Login key=value packet data construction helper.
*
@@ -5810,35 +9905,333 @@ typedef struct iscsi_key_value_pair {
*/
typedef struct iscsi_key_value_pair_packet {
/// Associated iSCSI connection.
- struct iscsi_connection *conn;
+ iscsi_connection *conn;
/// Current text buffer containing multiple key=value + NUL terminator pairs.
uint8_t *buf;
/// Position of output buffer for next write.
- uint pos;
+ uint32_t pos;
/// Current length of buffer including final NUL terminator without iSCSI zero padding.
- uint len;
+ uint32_t len;
/// Discovery mode.
int discovery;
} iscsi_key_value_pair_packet;
-int iscsi_parse_key_value_pairs(iscsi_hashmap *pairs, const uint8_t *packet_data, uint len, int c_bit, uint8_t **partial_pairs); // Extracts all text key / value pairs out of an iSCSI packet into a hash map
+int iscsi_parse_key_value_pairs(iscsi_hashmap *key_value_pairs, const uint8_t *packet_data, uint len, int c_bit, uint8_t **partial_pairs); // Extracts all text key / value pairs out of an iSCSI packet into a hash map
+
+
+/// iSCSI main global data: INI configuration filename.
+#define ISCSI_GLOBALS_CONFIG_FILENAME "iscsi.conf"
+
+
+/// iSCSI main global data: iSCSI INI configuration iSCSI section identifier string.
+#define ISCSI_GLOBALS_SECTION_ISCSI "iscsi"
+
+/// iSCSI main global data: iSCSI INI configuration iSCSI SCSI section identifier string.
+#define ISCSI_GLOBALS_SECTION_SCSI "scsi"
+
+
+/// iSCSI main global data: iSCSI INI configuration iSCSI section target name check key identifier string.
+#define ISCSI_GLOBALS_SECTION_ISCSI_KEY_TARGET_NAME_CHECK "TargetNameCheck"
+
+/// iSCSI main global data: iSCSI INI configuration iSCSI section maximum number of sessions allowed key identifier string.
+#define ISCSI_GLOBALS_SECTION_ISCSI_KEY_MAX_SESSIONS "MaxSessions"
+
+/// iSCSI main global data: iSCSI INI configuration iSCSI section maximum number of connections per session allowed key identifier string.
+#define ISCSI_GLOBALS_SECTION_ISCSI_MAX_CONNECTIONS_PER_SESSIONS "MaxConnectionsPerSession"
+
+
+/// iSCSI main global data: iSCSI INI configuration iSCSI SCSI section device type key identifier string.
+#define ISCSI_GLOBALS_SECTION_SCSI_KEY_DEVICE_TYPE "DeviceType"
+
+/// iSCSI main global data: iSCSI INI configuration iSCSI SCSI section physical block size key identifier string.
+#define ISCSI_GLOBALS_SECTION_SCSI_KEY_PHYSICAL_BLOCK_SIZE "PhysicalBlockSize"
+
+/// iSCSI main global data: iSCSI INI configuration iSCSI SCSI section logical block size key identifier string.
+#define ISCSI_GLOBALS_SECTION_SCSI_KEY_LOGICAL_BLOCK_SIZE "LogicalBlockSize"
+
+/// iSCSI main global data: iSCSI INI configuration iSCSI SCSI section removable device key identifier string.
+#define ISCSI_GLOBALS_SECTION_SCSI_KEY_REMOVABLE "Removable"
+
+/// iSCSI main global data: iSCSI INI configuration iSCSI SCSI section UNMAP support device key identifier string.
+#define ISCSI_GLOBALS_SECTION_SCSI_KEY_UNMAP "UNMAP"
+
+/// iSCSI main global data: iSCSI INI configuration iSCSI SCSI section no rotation device key identifier string.
+#define ISCSI_GLOBALS_SECTION_SCSI_KEY_NO_ROTATION "NoRotation"
+
+/// iSCSI main global data: iSCSI INI configuration iSCSI SCSI section physical read only device key identifier string.
+#define ISCSI_GLOBALS_SECTION_SCSI_KEY_PHYSICAL_READ_ONLY "PhysicalReadOnly"
+
+/// iSCSI main global data: iSCSI INI configuration iSCSI SCSI section write protected device key identifier string.
+#define ISCSI_GLOBALS_SECTION_SCSI_KEY_WRITE_PROTECT "WriteProtect"
+
+/// iSCSI main global data: iSCSI INI configuration iSCSI SCSI section write cache supported device key identifier string.
+#define ISCSI_GLOBALS_SECTION_SCSI_KEY_WRITE_CACHE "WriteCache"
+
+
+/// iSCSI main global data: iSCSI SCSI device specific INI configuration section prefix identifier string.
+#define ISCSI_GLOBALS_SECTION_SCSI_DEVICE_PREFIX "scsi-device-"
+
+
+/// iSCSI main global data config type: iHeader digest (CRC32), always MUST be 0 or 4 for now.
+#define ISCSI_GLOBALS_CONFIG_TYPE_HEADER_DIGEST 0
+
+/// iSCSI main global data config type: Data digest (CRC32), always MUST be 0 or 4 for now.
+#define ISCSI_GLOBALS_CONFIG_TYPE_DATA_DIGEST 1
+
+/// iSCSI main global data config type: Maximum receive DataSegment length in bytes.
+#define ISCSI_GLOBALS_CONFIG_TYPE_MAX_RECV_DS_LEN 2
+
+/// iSCSI main global data config type: Maximum number of connections per session.
+#define ISCSI_GLOBALS_CONFIG_TYPE_MAX_SESSION_CONNS 3
+
+/// iSCSI main global data config type: Ready to transfer maximum outstanding value.
+#define ISCSI_GLOBALS_CONFIG_TYPE_MAX_OUTSTANDING_R2T 4
+
+/// iSCSI main global data config type: Default time to wait.
+#define ISCSI_GLOBALS_CONFIG_TYPE_DEFAULT_TIME_TO_WAIT 5
+
+/// iSCSI main global data config type: Default time to retain.
+#define ISCSI_GLOBALS_CONFIG_TYPE_DEFAULT_TIME_TO_RETAIN 6
+
+/// iSCSI main global data config type: First burst length.
+#define ISCSI_GLOBALS_CONFIG_TYPE_FIRST_BURST_LEN 7
+
+/// iSCSI main global data config type: Maximum burst length.
+#define ISCSI_GLOBALS_CONFIG_TYPE_MAX_BURST_LEN 8
+
+/// iSCSI main global data config type: Error recovery level.
+#define ISCSI_GLOBALS_CONFIG_TYPE_ERR_RECOVERY_LEVEL 9
+
+/// iSCSI main global data config type: SCSI emulation for device type.
+#define ISCSI_GLOBALS_CONFIG_TYPE_SCSI_DEVICE_TYPE 10
+
+/// iSCSI main global data config type: SCSI emulation for physical block size.
+#define ISCSI_GLOBALS_CONFIG_TYPE_SCSI_PHYSICAL_BLOCK_SIZE 11
+
+/// iSCSI main global data config type: SCSI emulation for physical block size shift count.
+#define ISCSI_GLOBALS_CONFIG_TYPE_SCSI_PHYSICAL_BLOCK_SIZE_SHIFT 12
+
+/// iSCSI main global data config type: SCSI emulation for logical block size.
+#define ISCSI_GLOBALS_CONFIG_TYPE_SCSI_LOGICAL_BLOCK_SIZE 13
+
+/// iSCSI main global data config type: SCSI emulation for logical block size shift count.
+#define ISCSI_GLOBALS_CONFIG_TYPE_SCSI_LOGICAL_BLOCK_SIZE_SHIFT 14
+
+/// iSCSI main global data config type: Initial ready to transfer.
+#define ISCSI_GLOBALS_CONFIG_TYPE_FLAGS_INIT_R2T 15
+
+/// iSCSI main global data config type: Immediate data.
+#define ISCSI_GLOBALS_CONFIG_TYPE_FLAGS_IMMEDIATE_DATA 16
+
+/// iSCSI main global data config type: Data PDU in order.
+#define ISCSI_GLOBALS_CONFIG_TYPE_FLAGS_DATA_PDU_IN_ORDER 17
+
+/// iSCSI main global data config type: Data sequence in order.
+#define ISCSI_GLOBALS_CONFIG_TYPE_FLAGS_DATA_SEQ_IN_ORDER 18
+
+/// iSCSI main global data config type: SCSI emulation for I/O removable device.
+#define ISCSI_GLOBALS_CONFIG_TYPE_FLAGS_SCSI_IO_REMOVABLE 19
+
+/// iSCSI main global data config type: SCSI emulation for I/O UNMAP supporting device.
+#define ISCSI_GLOBALS_CONFIG_TYPE_FLAGS_SCSI_IO_UNMAP 20
+
+/// iSCSI main global data config type: SCSI emulation for I/O non-rotating device.
+#define ISCSI_GLOBALS_CONFIG_TYPE_FLAGS_SCSI_IO_NO_ROTATION 21
+
+/// iSCSI main global data config type: SCSI emulation for I/O physical read only device.
+#define ISCSI_GLOBALS_CONFIG_TYPE_FLAGS_SCSI_IO_PHYSICAL_READ_ONLY 22
+
+/// iSCSI main global data config type: SCSI emulation for I/O write protected device.
+#define ISCSI_GLOBALS_CONFIG_TYPE_FLAGS_SCSI_IO_WRITE_PROTECT 23
+
+/// iSCSI main global data config type: SCSI emulation for I/O write cache device.
+#define ISCSI_GLOBALS_CONFIG_TYPE_FLAGS_SCSI_IO_WRITE_CACHE 24
+
+
+/// iSCSI main global data SCSI device configuration flags: Initial ready to transfer.
+#define ISCSI_GLOBALS_SCSI_DEVICE_CONFIG_FLAGS_INIT_R2T (1 << 0)
+
+/// iSCSI main global data SCSI device configuration flags: Immediate data.
+#define ISCSI_GLOBALS_SCSI_DEVICE_CONFIG_FLAGS_IMMEDIATE_DATA (1 << 1)
+
+/// iSCSI main global data SCSI device configuration flags: Data PDU in order.
+#define ISCSI_GLOBALS_SCSI_DEVICE_CONFIG_FLAGS_DATA_PDU_IN_ORDER (1 << 2)
+
+/// iSCSI main global data SCSI device configuration flags: Data sequence in order.
+#define ISCSI_GLOBALS_SCSI_DEVICE_CONFIG_FLAGS_DATA_SEQ_IN_ORDER (1 << 3)
+
+/// iSCSI main global data SCSI device configuration flags: SCSI emulation for I/O removable device.
+#define ISCSI_GLOBALS_SCSI_DEVICE_CONFIG_FLAGS_SCSI_IO_REMOVABLE (1 << 4)
+
+/// iSCSI main global data SCSI device configuration flags: SCSI emulation for I/O UNMAP supporting device.
+#define ISCSI_GLOBALS_SCSI_DEVICE_CONFIG_FLAGS_SCSI_IO_UNMAP (1 << 5)
+
+/// iSCSI main global data SCSI device configuration flags: SCSI emulation for I/O non-rotating device.
+#define ISCSI_GLOBALS_SCSI_DEVICE_CONFIG_FLAGS_SCSI_IO_NO_ROTATION (1 << 6)
+
+/// iSCSI main global data SCSI device configuration flags: SCSI emulation for I/O physical read only device.
+#define ISCSI_GLOBALS_SCSI_DEVICE_CONFIG_FLAGS_SCSI_IO_PHYSICAL_READ_ONLY (1 << 7)
+
+/// iSCSI main global data SCSI device configuration flags: SCSI emulation for I/O write protected device.
+#define ISCSI_GLOBALS_SCSI_DEVICE_CONFIG_FLAGS_SCSI_IO_WRITE_PROTECT (1 << 8)
+
+/// iSCSI main global data SCSI device configuration flags: SCSI emulation for I/O write cache device.
+#define ISCSI_GLOBALS_SCSI_DEVICE_CONFIG_FLAGS_SCSI_IO_WRITE_CACHE (1 << 9)
+
+
+/**
+ * @brief iSCSI main global data SCSI device configuration.
+ *
+ * This structure is used for specific SCSI device
+ * configuration which are matched using wildcard
+ * patterns which are stored in the hash map key.
+ */
+typedef struct iscsi_scsi_device_config {
+ /// SCSI device configuration flags.
+ int flags;
+
+ /// iHeader digest (CRC32), always MUST be 0 or 4 for now.
+ int header_digest;
+
+ /// Data digest (CRC32), always MUST be 0 or 4 for now.
+ int data_digest;
+
+ /// SCSI emulation: Device type.
+ uint scsi_device_type;
+
+ /// Maximum receive DataSegment length in bytes.
+ uint32_t max_recv_ds_len;
+
+ /// Maximum number of connections per session.
+ uint32_t max_session_conns;
+
+ /// Ready to transfer maximum outstanding value.
+ uint32_t max_outstanding_r2t;
+
+ /// Default time to wait.
+ uint32_t default_time_to_wait;
+
+ /// Default time to retain.
+ uint32_t default_time_to_retain;
+
+ /// First burst length.
+ uint32_t first_burst_len;
+
+ /// Maximum burst length.
+ uint32_t max_burst_len;
+
+ /// Error recovery level.
+ uint32_t err_recovery_level;
+
+ /// SCSI emulation: Physical block size.
+ uint32_t scsi_physical_block_size;
+
+ /// SCSI emulation: Physical block size shift count.
+ uint32_t scsi_physical_block_size_shift;
+
+ /// SCSI emulation: Logical block size.
+ uint32_t scsi_logical_block_size;
+
+ /// SCSI emulation: Logical block size shift count.
+ uint32_t scsi_logical_block_size_shift;
+} iscsi_scsi_device_config;
+
+
+/**
+ * @brief iSCSI SCSI device configuration search by name.
+ *
+ * This structure is used by iterating through
+ * all iSCSI SCSI device configurations and
+ * uses wildcard matching in order to retrieve
+ * the correct SCSI configuration for a
+ * specified device name.
+ */
+typedef struct iscsi_scsi_device_config_find {
+ /// Found iSCSI SCSI device configuration is stored here, should be initialized to NULL.
+ iscsi_scsi_device_config *scsi_device_config;
+
+ /// The name to be searched for is stored here.
+ uint8_t *name;
+} iscsi_scsi_device_config_find;
/// iSCSI main global data flags: Allow duplicate ISIDs.
-#define ISCSI_GLOBALS_FLAGS_ISID_ALLOW_DUPLICATES (1 << 0L)
+#define ISCSI_GLOBALS_FLAGS_ISID_ALLOW_DUPLICATES (1 << 0)
/// iSCSI main global data flags: CHAP authentication is disabled.
-#define ISCSI_GLOBALS_FLAGS_CHAP_DISABLE (1 << 1L)
+#define ISCSI_GLOBALS_FLAGS_CHAP_DISABLE (1 << 1)
/// iSCSI main global data flags: CHAP authentication is required.
-#define ISCSI_GLOBALS_FLAGS_CHAP_REQUIRE (1 << 2L)
+#define ISCSI_GLOBALS_FLAGS_CHAP_REQUIRE (1 << 2)
/// iSCSI main global data flags: CHAP authentication is mutual.
-#define ISCSI_GLOBALS_FLAGS_CHAP_MUTUAL (1 << 3L)
+#define ISCSI_GLOBALS_FLAGS_CHAP_MUTUAL (1 << 3)
+
+/// iSCSI main global data flags: Initial ready to transfer.
+#define ISCSI_GLOBALS_FLAGS_INIT_R2T (1 << 4)
+
+/// iSCSI main global data flags: Immediate data.
+#define ISCSI_GLOBALS_FLAGS_IMMEDIATE_DATA (1 << 5)
+
+/// iSCSI main global data flags: Data PDU in order.
+#define ISCSI_GLOBALS_FLAGS_DATA_PDU_IN_ORDER (1 << 6)
+
+/// iSCSI main global data flags: Data sequence in order.
+#define ISCSI_GLOBALS_FLAGS_DATA_SEQ_IN_ORDER (1 << 7)
+
+/// iSCSI main global data flags: SCSI emulation for I/O removable device.
+#define ISCSI_GLOBALS_FLAGS_SCSI_IO_REMOVABLE (1 << 8)
+
+/// iSCSI main global data flags: SCSI emulation for I/O UNMAP supporting device.
+#define ISCSI_GLOBALS_FLAGS_SCSI_IO_UNMAP (1 << 9)
+
+/// iSCSI main global data flags: SCSI emulation for I/O non-rotating device.
+#define ISCSI_GLOBALS_FLAGS_SCSI_IO_NO_ROTATION (1 << 10)
+
+/// iSCSI main global data flags: SCSI emulation for I/O physical read only device.
+#define ISCSI_GLOBALS_FLAGS_SCSI_IO_PHYSICAL_READ_ONLY (1 << 11)
+
+/// iSCSI main global data flags: SCSI emulation for I/O write protected device.
+#define ISCSI_GLOBALS_FLAGS_SCSI_IO_WRITE_PROTECT (1 << 12)
+
+/// iSCSI main global data flags: SCSI emulation for I/O write cache device.
+#define ISCSI_GLOBALS_FLAGS_SCSI_IO_WRITE_CACHE (1 << 13)
+
+
+/// iSCSI main global data target name validation check level: None, allow everything.
+#define ISCSI_GLOBALS_TARGET_NAME_CHECK_NONE 0
+
+/// iSCSI main global data target name validation check level: Relaxed, check for maximum target name length and if target name starts with 'iqn.', 'naa.' or 'eui.' also check if target name only contains allowed characters.
+#define ISCSI_GLOBALS_TARGET_NAME_CHECK_RELAXED 1
+
+/// iSCSI main global data target name validation check level: Full, check for maximum target name length and always check target name only contains allowed characters.
+#define ISCSI_GLOBALS_TARGET_NAME_CHECK_FULL 2
+
+
+/// iSCSI main global data: Default maximum number of connections.
+#define ISCSI_GLOBALS_DEFAULT_MAX_CONNECTIONS 1UL
+
+/// iSCSI main global data: Default maximum number of outstanding ready to transfers.
+#define ISCSI_GLOBALS_DEFAULT_MAX_OUTSTANDING_R2T 1UL
+
+/// iSCSI main global data: Default time to wait in seconds.
+#define ISCSI_GLOBALS_DEFAULT_TIME_TO_WAIT 2UL
+
+/// iSCSI main global data: Default time to retain in seconds.
+#define ISCSI_GLOBALS_DEFAULT_TIME_TO_RETAIN 20UL
+
+/// iSCSI main global data: First burst length in bytes.
+#define ISCSI_GLOBALS_DEFAULT_FIRST_BURST_LEN ISCSI_DEFAULT_RECV_DS_LEN
+
+/// iSCSI main global data: Maximum burst length in bytes.
+#define ISCSI_GLOBALS_DEFAULT_MAX_BURST_LEN (ISCSI_DEFAULT_MAX_RECV_DS_LEN * ISCSI_DEFAULT_MAX_DATA_OUT_PER_CONNECTION)
+
+/// iSCSI main global data: Default error recovery level.
+#define ISCSI_GLOBALS_DEFAULT_ERR_RECOVERY_LEVEL 0UL
/**
@@ -5851,43 +10244,113 @@ typedef struct iscsi_globals {
/// Hash map containing all iSCSI devices.
iscsi_hashmap *devices;
+ /// Read/write lock for hash map containing all iSCSI devices. MUST be initialized with iscsi_create before any iSCSI functions are used.
+ pthread_rwlock_t devices_rwlock;
+
/// Hash map containing all registered iSCSI portal groups.
iscsi_hashmap *portal_groups;
+ /// Read/write lock for hash map containing all iSCSI portal_groups. MUST be initialized with iscsi_create before any iSCSI functions are used.
+ pthread_rwlock_t portal_groups_rwlock;
+
/// iSCSI target nodes.
iscsi_hashmap *target_nodes;
+ /// Read/write lock for hash map containing all iSCSI target nodes. MUST be initialized with iscsi_create before any iSCSI functions are used.
+ pthread_rwlock_t target_nodes_rwlock;
+
/// Hash map containing all iSCSI sessions.
iscsi_hashmap *sessions;
+ /// Read/write lock for hash map containing all iSCSI sessions. MUST be initialized with iscsi_create before any iSCSI functions are used.
+ pthread_rwlock_t sessions_rwlock;
+
/// Hash map containing session key and value pair types and allowed values or ranges.
iscsi_hashmap *session_key_value_pairs;
- /// Hash map containing connections not associated with an iSCSI sessions.
- iscsi_hashmap *connections;
-
/// Hash map containing connection key and value pair types and allowed values or ranges.
iscsi_hashmap *connection_key_value_pairs;
+ /// Hash map containing iSCSI SCSI device specific configuration.
+ iscsi_hashmap *scsi_device_config;
+
+ /// Mutex for hash map containing iSCSI SCSI device specific configuration.
+ pthread_mutex_t scsi_device_config_mutex;
+
/// Global flags.
int flags;
+ /// Target name validation check level.
+ int target_name_check;
+
/// Maximum number of allowed sessions.
uint max_sessions;
+ /// iHeader digest (CRC32), always MUST be 0 or 4 for now.
+ int header_digest;
+
+ /// Data digest (CRC32), always MUST be 0 or 4 for now.
+ int data_digest;
+
+ /// SCSI emulation: Device type.
+ uint scsi_device_type;
+
+ /// Maximum receive DataSegment length in bytes.
+ uint32_t max_recv_ds_len;
+
+ /// Maximum number of connections per session.
+ uint32_t max_session_conns;
+
+ /// Ready to transfer maximum outstanding value.
+ uint32_t max_outstanding_r2t;
+
+ /// Default time to wait.
+ uint32_t default_time_to_wait;
+
+ /// Default time to retain.
+ uint32_t default_time_to_retain;
+
+ /// First burst length.
+ uint32_t first_burst_len;
+
+ /// Maximum burst length.
+ uint32_t max_burst_len;
+
+ /// Error recovery level.
+ uint32_t err_recovery_level;
+
/// CHAP group id.
int32_t chap_group;
+
+ /// SCSI emulation: Physical block size.
+ uint32_t scsi_physical_block_size;
+
+ /// SCSI emulation: Physical block size shift count.
+ uint32_t scsi_physical_block_size_shift;
+
+ /// SCSI emulation: Logical block size.
+ uint32_t scsi_logical_block_size;
+
+ /// SCSI emulation: Logical block size shift count.
+ uint32_t scsi_logical_block_size_shift;
} iscsi_globals;
-/// iSCSI global vector. MUST be initialized with iscsi_create before any iSCSI functions are used.
-iscsi_globals *iscsi_globvec = NULL;
+/// Reference to iSCSI global vector. MUST be initialized with iscsi_create before any iSCSI functions are used.
+extern iscsi_globals *iscsi_globvec;
+
+/// Read/write lock for iSCSI global vector. MUST be initialized with iscsi_create before any iSCSI functions are used.
+extern pthread_rwlock_t iscsi_globvec_rwlock;
int iscsi_create(); // Allocates and initializes the iSCSI global vector structure
-int iscsi_global_key_value_pair_destroy_callback(uint8_t *key, const size_t key_size, uint8_t *value, uint8_t *user_data); // iSCSI global key and value pair destructor callback for hash map
void iscsi_destroy(); // Deallocates all resources acquired by iscsi_create
+int iscsi_config_load(iscsi_globals *globvec); // Loads iSCSI server configuration from INI file
+int iscsi_config_get_callback(uint8_t *key, const size_t key_size, uint8_t *value, uint8_t *user_data); // Finds an iSCSI SCSI device configuration by name using pattern matching
+int32_t iscsi_config_get(uint8_t *name, const int type); // Retrieves a configuration value either from the iSCSI global vector or for a specified SCSI device name
+
+
/**
* @brief iSCSI portal group: Private portal group if set, public otherwise.
*
@@ -5899,16 +10362,16 @@ void iscsi_destroy(); // Deallocates all resources acquired by iscsi_create
* Private portal groups instead do not return their portals during
* the discovery session.
*/
-#define ISCSI_PORTAL_GROUP_PRIVATE (1 << 0L)
+#define ISCSI_PORTAL_GROUP_PRIVATE (1 << 0)
/// iSCSI portal group: CHAP authentication is disabled.
-#define ISCSI_PORTAL_GROUP_CHAP_DISABLE (1 << 1L)
+#define ISCSI_PORTAL_GROUP_CHAP_DISABLE (1 << 1)
/// iSCSI portal group: CHAP authentication is required.
-#define ISCSI_PORTAL_GROUP_CHAP_REQUIRE (1 << 2L)
+#define ISCSI_PORTAL_GROUP_CHAP_REQUIRE (1 << 2)
/// iSCSI portal group: CHAP authentication is mutual.
-#define ISCSI_PORTAL_GROUP_CHAP_MUTUAL (1 << 3L)
+#define ISCSI_PORTAL_GROUP_CHAP_MUTUAL (1 << 3)
/**
@@ -5921,11 +10384,11 @@ typedef struct iscsi_portal_group {
/// Hash map containing all portals associated with this iSCSI group.
iscsi_hashmap *portals;
- /// Reference count.
- int ref_count;
-
/// Tag value for this portal group.
- int tag;
+ uint64_t tag;
+
+ /// Reference count.
+ int ref_count;
/// Portal group flags.
int flags;
@@ -5956,16 +10419,536 @@ typedef struct iscsi_portal {
} iscsi_portal;
-iscsi_portal_group *iscsi_portal_group_create(const int tag, const int flags); // Creates and initializes an iSCSI portal group
+iscsi_portal_group *iscsi_portal_group_create(const uint64_t tag, const int flags); // Creates and initializes an iSCSI portal group
+int iscsi_portal_group_destroy_callback(uint8_t *key, const size_t key_size, uint8_t *value, uint8_t *user_data); // iSCSI portal group destructor callback for hash map
void iscsi_portal_group_destroy(iscsi_portal_group *portal_group); // Deallocates resources acquired by iscsi_portal_group_create
int iscsi_portal_group_add_portal(iscsi_portal_group *portal_group, iscsi_portal *portal); // Adds an iSCSI portal to the iSCSI portal group hash map
+void iscsi_portal_group_del_portal(iscsi_portal_group *portal_group, iscsi_portal *portal); // Removes an iSCSI portal from the iSCSI portal group hash map
iscsi_portal *iscsi_portal_create(const uint8_t *host, const uint8_t *port); // Allocates and initializes an iSCSI portal structure
+int iscsi_portal_destroy_callback(uint8_t *key, const size_t key_size, uint8_t *value, uint8_t *user_data); // iSCSI portal destructor callback for hash map
void iscsi_portal_destroy(iscsi_portal *portal);
-/// ISCSI port flags: In use.
-#define ISCSI_PORT_FLAGS_IN_USE (1 << 0L)
+/// iSCSI SCSI status code: Good.
+#define ISCSI_SCSI_STATUS_GOOD 0x00
+
+/// iSCSI SCSI status code: Check condition.
+#define ISCSI_SCSI_STATUS_CHECK_COND 0x02
+
+/// iSCSI SCSI status code: Condition met.
+#define ISCSI_SCSI_STATUS_COND_MET 0x04
+
+/// iSCSI SCSI status code: Busy.
+#define ISCSI_SCSI_STATUS_BUSY 0x08
+
+/// iSCSI SCSI status code: Intermediate.
+#define ISCSI_SCSI_STATUS_INTERMEDIATE 0x10
+
+/// iSCSI SCSI status code: Intermediate condition met.
+#define ISCSI_SCSI_STATUS_INTERMEDIATE_COND_MET 0x14
+
+/// iSCSI SCSI status code: Reservation conflict.
+#define ISCSI_SCSI_STATUS_RESERVATION_CONFLICT 0x18
+
+/// iSCSI SCSI status code: Obselete.
+#define ISCSI_SCSI_STATUS_OBSELETE 0x22
+
+/// iSCSI SCSI status code: Task set full.
+#define ISCSI_SCSI_STATUS_TASK_SET_FULL 0x28
+
+/// iSCSI SCSI status code: ACA active.
+#define ISCSI_SCSI_STATUS_ACA_ACTIVE 0x30
+
+/// iSCSI SCSI status code: Task aborted.
+#define ISCSI_SCSI_STATUS_TASK_ABORTED 0x40
+
+
+/// iSCSI SCSI sense key: No sense.
+#define ISCSI_SCSI_SENSE_KEY_NO_SENSE 0x00
+
+/// iSCSI SCSI sense key: Recovered error.
+#define ISCSI_SCSI_SENSE_KEY_RECOVERED_ERR 0x01
+
+/// iSCSI SCSI sense key: Not ready.
+#define ISCSI_SCSI_SENSE_KEY_NOT_READY 0x02
+
+/// iSCSI SCSI sense key: Medium error.
+#define ISCSI_SCSI_SENSE_KEY_MEDIUM_ERR 0x03
+
+/// iSCSI SCSI sense key: Hardware error.
+#define ISCSI_SCSI_SENSE_KEY_HARDWARE_ERR 0x04
+
+/// iSCSI SCSI sense key: Illegal request.
+#define ISCSI_SCSI_SENSE_KEY_ILLEGAL_REQ 0x05
+
+/// iSCSI SCSI sense key: Unit attention.
+#define ISCSI_SCSI_SENSE_KEY_UNIT_ATTENTION 0x06
+
+/// iSCSI SCSI sense key: Data protect.
+#define ISCSI_SCSI_SENSE_KEY_DATA_PROTECT 0x07
+
+/// iSCSI SCSI sense key: Blank check.
+#define ISCSI_SCSI_SENSE_KEY_BLANK_CHECK 0x08
+
+/// iSCSI SCSI sense key: Vendor specific.
+#define ISCSI_SCSI_SENSE_KEY_VENDOR_SPECIFIC 0x09
+
+/// iSCSI SCSI sense key: Copy aborted.
+#define ISCSI_SCSI_SENSE_KEY_COPY_ABORTED 0x0A
+
+/// iSCSI SCSI sense key: Aborted command.
+#define ISCSI_SCSI_SENSE_KEY_ABORTED_COMMAND 0x0B
+
+/// iSCSI SCSI sense key: Volume overflow.
+#define ISCSI_SCSI_SENSE_KEY_VOLUME_OVERFLOW 0x0D
+
+/// iSCSI SCSI sense key: Miscompare.
+#define ISCSI_SCSI_SENSE_KEY_MISCOMPARE 0x0E
+
+
+/// iSCSI SCSI Additional Sense Code (ASC): No additional sense.
+#define ISCSI_SCSI_ASC_NO_ADDITIONAL_SENSE 0x00
+
+/// iSCSI SCSI Additional Sense Code (ASC): Peripheral device write fault.
+#define ISCSI_SCSI_ASC_PERIPHERAL_DEVICE_WRITE_FAULT 0x03
+
+/// iSCSI SCSI Additional Sense Code (ASC): Logical unit not ready.
+#define ISCSI_SCSI_ASC_LOGICAL_UNIT_NOT_READY 0x04
+
+/// iSCSI SCSI Additional Sense Code (ASC): Warning.
+#define ISCSI_SCSI_ASC_WARNING 0x0B
+
+/// iSCSI SCSI Additional Sense Code (ASC): Write error.
+#define ISCSI_SCSI_ASC_WRITE_ERR 0x0C
+
+/// iSCSI SCSI Additional Sense Code (ASC): Block guard check failed.
+#define ISCSI_SCSI_ASC_LOGICAL_BLOCK_GUARD_CHECK_FAIL 0x10
+
+/// iSCSI SCSI Additional Sense Code (ASC): Block application tag checdk failed.
+#define ISCSI_SCSI_ASC_LOGICAL_BLOCK_APP_TAG_CHECK_FAIL 0x10
+
+/// iSCSI SCSI Additional Sense Code (ASC): Block reference tag check failed.
+#define ISCSI_SCSI_ASC_LOGICAL_BLOCK_REF_TAG_CHECK_FAIL 0x10
+
+/// iSCSI SCSI Additional Sense Code (ASC): Unrecovered read error.
+#define ISCSI_SCSI_ASC_UNRECOVERED_READ_ERR 0x11
+
+/// iSCSI SCSI Additional Sense Code (ASC): Miscompare during verify operation.
+#define ISCSI_SCSI_ASC_MISCOMPARE_DURING_VERIFY_OPERATION 0x1D
+
+/// iSCSI SCSI Additional Sense Code (ASC): Invalid command operation code.
+#define ISCSI_SCSI_ASC_INVALID_COMMAND_OPERATION_CODE 0x20
+
+/// iSCSI SCSI Additional Sense Code (ASC): Access denied.
+#define ISCSI_SCSI_ASC_ACCESS_DENIED 0x20
+
+/// iSCSI SCSI Additional Sense Code (ASC): Logical block address out of range.
+#define ISCSI_SCSI_ASC_LOGICAL_BLOCK_ADDRESS_OUT_OF_RANGE 0x21
+
+/// iSCSI SCSI Additional Sense Code (ASC): Invalid field in CDB.
+#define ISCSI_SCSI_ASC_INVALID_FIELD_IN_CDB 0x24
+
+/// iSCSI SCSI Additional Sense Code (ASC): Logical unit not supported.
+#define ISCSI_SCSI_ASC_LU_NOT_SUPPORTED 0x25
+
+/// iSCSI SCSI Additional Sense Code (ASC): Write protected.
+#define ISCSI_SCSI_ASC_WRITE_PROTECTED 0x27
+
+/// iSCSI SCSI Additional Sense Code (ASC): Data has changed.
+#define ISCSI_SCSI_ASC_CAPACITY_DATA_HAS_CHANGED 0x2A
+
+/// iSCSI SCSI Additional Sense Code (ASC): Format command failed.
+#define ISCSI_SCSI_ASC_FORMAT_COMMAND_FAIL 0x31
+
+/// iSCSI SCSI Additional Sense Code (ASC): Saving parameters not supported.
+#define ISCSI_SCSI_ASC_SAVING_PARAMETERS_NOT_SUPPORTED 0x39
+
+/// iSCSI SCSI Additional Sense Code (ASC): Internal target failure.
+#define ISCSI_SCSI_ASC_INTERNAL_TARGET_FAIL 0x44
+
+
+/// iSCSI SCSI Additional Sense Code Qualifier (ASCQ): Cause not reportable.
+#define ISCSI_SCSI_ASCQ_CAUSE_NOT_REPORTABLE 0x00
+
+/// iSCSI SCSI Additional Sense Code Qualifier (ASCQ): Becoming ready.
+#define ISCSI_SCSI_ASCQ_BECOMING_READY 0x01
+
+/// iSCSI SCSI Additional Sense Code Qualifier (ASCQ): Format command failed.
+#define ISCSI_SCSI_ASCQ_FORMAT_COMMAND_FAIL 0x01
+
+/// iSCSI SCSI Additional Sense Code Qualifier (ASCQ): Block guard check failed.
+#define ISCSI_SCSI_ASCQ_LOGICAL_BLOCK_GUARD_CHECK_FAIL 0x01
+
+/// iSCSI SCSI Additional Sense Code Qualifier (ASCQ): Block application tag check failed.
+#define ISCSI_SCSI_ASCQ_LOGICAL_BLOCK_APP_TAG_CHECK_FAIL 0x02
+
+/// iSCSI SCSI Additional Sense Code Qualifier (ASCQ): No access rights.
+#define ISCSI_SCSI_ASCQ_NO_ACCESS_RIGHTS 0x02
+
+/// iSCSI SCSI Additional Sense Code Qualifier (ASCQ): Manual intervention required.
+#define ISCSI_SCSI_ASCQ_MANUAL_INTERVENTION_REQUIRED 0x03
+
+/// iSCSI SCSI Additional Sense Code Qualifier (ASCQ): Block reference tag check failed.
+#define ISCSI_SCSI_ASCQ_LOGICAL_BLOCK_REF_TAG_CHECK_FAIL 0x03
+
+/// iSCSI SCSI Additional Sense Code Qualifier (ASCQ): Power loss expected.
+#define ISCSI_SCSI_ASCQ_POWER_LOSS_EXPECTED 0x08
+
+/// iSCSI SCSI Additional Sense Code Qualifier (ASCQ): Invalid logical unit identifier.
+#define ISCSI_SCSI_ASCQ_INVALID_LU_IDENTIFIER 0x09
+
+/// iSCSI SCSI Additional Sense Code Qualifier (ASCQ): Capacity data has changed.
+#define ISCSI_SCSI_ASCQ_CAPACITY_DATA_HAS_CHANGED 0x09
+
+
+typedef struct iscsi_port iscsi_port;
+
+
+/**
+ * @brief iSCSI SCSI Persistent Reservation (PR) registrant with I_T nexus.
+ *
+ * I_T nexus is a nexus which exists between an initiator and a
+ * target.
+ */
+typedef struct iscsi_scsi_pr_registrant {
+ /// Target iSCSI port.
+ iscsi_port *target_port;
+
+ /// Target iSCSI port name.
+ uint8_t *target_name;
+
+ /// Initiator iSCSI port.
+ iscsi_port *init_port;
+
+ /// Initiator iSCSI port name.
+ uint8_t *init_name;
+
+ /// Transport ID.
+ iscsi_transport_id *transport_id;
+
+ /// Reservation key.
+ uint64_t r_key;
+
+ /// Relative target port identifier.
+ uint16_t rel_target_port_id;
+
+ /// Transport ID length.
+ uint16_t transport_id_len;
+} iscsi_scsi_pr_registrant;
+
+
+/// iSCSI SCSI Persistent Reservation (PR) reservation type: Write exclusive.
+#define ISCSI_SCSI_PR_RESERVATION_TYPE_WRITE_EXCLUSIVE 0x01
+
+/// iSCSI SCSI Persistent Reservation (PR) reservation type: Exclusive access.
+#define ISCSI_SCSI_PR_RESERVATION_TYPE_EXCLUSIVE_ACCESS 0x03
+
+/// iSCSI SCSI Persistent Reservation (PR) reservation type: Write exclusive - registrants only.
+#define ISCSI_SCSI_PR_RESERVATION_TYPE_WRITE_EXCLUSIVE_REGS_ONLY 0x05
+
+/// iSCSI SCSI Persistent Reservation (PR) reservation type: Exclusive access - registrants only.
+#define ISCSI_SCSI_PR_RESERVATION_TYPE_EXCLUSIVE_ACCESS_REGS_ONLY 0x06
+
+/// iSCSI SCSI Persistent Reservation (PR) reservation type: Write exclusive - all registrants.
+#define ISCSI_SCSI_PR_RESERVATION_TYPE_WRITE_EXCLUSIVE_ALL_REGS 0x07
+
+/// iSCSI SCSI Persistent Reservation (PR) reservation type: Exclusive access - all registrants.
+#define ISCSI_SCSI_PR_RESERVATION_TYPE_EXCLUSIVE_ACCESS_ALL_REGS 0x08
+
+
+/// iSCSI SCSI Persistent Reservation (PR) reservation flags: SPC2 reserve.
+#define ISCSI_SCSI_PR_RESERVATION_FLAGS_SPC2_RESERVE (1L << 0L)
+
+
+/**
+ * @brief iSCSI SCSI Persistent Reservation (PR) reservation with LU_SCOPE.
+ *
+ * LU_SCOPE means that Persistent Reservation (PR) scope
+ * applies to the full logical unit.
+ */
+typedef struct iscsi_scsi_pr_reservation {
+ /// Registrant for this reservation.
+ iscsi_scsi_pr_registrant *holder;
+
+ /// Current reservation key.
+ uint64_t cr_key;
+
+ /// Reservation type.
+ int type;
+
+ /// Reservation flags.
+ int32_t flags;
+} iscsi_scsi_pr_reservation;
+
+
+/**
+ * @brief iSCSI SCSI Persistent Reservation (PR) registrant search by target and initiator port.
+ *
+ * This structure is used by iterating through
+ * all iSCSI LUN Persistent Reservation (PR)
+ * registrant's finding by target and initiator
+ * port.
+ */
+typedef struct iscsi_scsi_pr_registrant_get_reg {
+ /// Found iSCSI SCSI Persistent Reservation (PR) registrant is stored here, should be initialized to NULL.
+ iscsi_scsi_pr_registrant *reg;
+
+ /// The target port to be searched for is stored here.
+ iscsi_port *target_port;
+
+ /// The initiator port to be searched for is stored here.
+ iscsi_port *init_port;
+} iscsi_scsi_pr_registrant_get_reg;
+
+
+/// iSCSI SCSI task run: Unknown.
+#define ISCSI_SCSI_TASK_RUN_UNKNOWN -1
+
+/// iSCSI SCSI task run: Completed.
+#define ISCSI_SCSI_TASK_RUN_COMPLETE 0
+
+/// iSCSI SCSI task run: Pending.
+#define ISCSI_SCSI_TASK_RUN_PENDING 1
+
+
+typedef struct iscsi_scsi_task iscsi_scsi_task;
+typedef struct iscsi_scsi_lun iscsi_scsi_lun;
+
+
+/**
+ * @brief Callback when iSCSI SCSI transfer task completed.
+ *
+ * This function is invoked when an iSCSI task
+ * finished a transfer.
+ *
+ * @param[in] scsi_task Pointer to iSCSI SCSI task which
+ * completed the transfer and may NOT be NULL,
+ * so be careful.
+ */
+typedef void (*iscsi_scsi_task_xfer_complete_callback)(iscsi_scsi_task *scsi_task);
+
+/**
+ * @brief Callback when iSCSI SCSI transfer task destruction.
+ *
+ * This function is invoked when an iSCSI task
+ * needs to be destroyed.
+ *
+ * @param[in] scsi_task Pointer to iSCSI SCSI task which
+ * is about to be destroyed and may NOT be
+ * NULL, so be careful.
+ */
+typedef void (*iscsi_scsi_task_destroy_callback)(iscsi_scsi_task *scsi_task);
+
+/**
+ * @brief Callback for I/O operation completion.
+ *
+ * This function is invoked when an I/O operation
+ * has been completed.
+ *
+ * @param[in] image Pointer to DNBD3 image which completed the
+ * I/O operation.
+ * @param[in] user_data Pointer to user data.
+ * @param[in] success true if I/O completed successfully or false
+ * if it failed instead.
+ * @return Pointer to passed user data.
+ */
+typedef uint8_t *(*iscsi_scsi_emu_io_complete_callback)(dnbd3_image_t *image, uint8_t *user_data, const bool success);
+
+/**
+ * @brief Callback for I/O wait operation.
+ *
+ * This function is invoked when an I/O
+ * operation needs waiting.
+ *
+ * @param[in] user_data Pointer to user data.
+ * @return Pointer to passed user data.
+ */
+typedef uint8_t *(*iscsi_scsi_emu_io_wait_callback)(uint8_t *user_data);
+
+
+typedef struct iscsi_scsi_emu_io_wait {
+ /// I/O task wait callback associated DNBD3 image.
+ dnbd3_image_t *image;
+
+ /// I/O task wait callback function.
+ iscsi_scsi_emu_io_wait_callback callback;
+
+ /// I/O task wait callback user data.
+ uint8_t *user_data;
+} iscsi_scsi_emu_io_wait;
+
+
+/// iSCSI SCSI task flags: Read.
+#define ISCSI_SCSI_TASK_FLAGS_XFER_READ (1 << 0)
+
+/// iSCSI SCSI task flags: Write.
+#define ISCSI_SCSI_TASK_FLAGS_XFER_WRITE (1 << 1)
+
+
+/**
+ * @brief iSCSI SCSI Task.
+ *
+ * This structure is used for the iSCSI SCSI
+ * layer task management.
+ */
+typedef struct iscsi_scsi_task {
+ /// Doubly linked list node, MUST be first element.
+ iscsi_node node;
+
+ /// SCSI LUN associated with this task.
+ iscsi_scsi_lun *lun;
+
+ /// Target iSCSI port.
+ iscsi_port *target_port;
+
+ /// Initiator iSCSI port.
+ iscsi_port *init_port;
+
+ /// SCSI Command Descriptor Block (CDB).
+ iscsi_scsi_cdb *cdb;
+
+ /// SCSI sense data.
+ iscsi_scsi_sense_data_packet *sense_data;
+
+ /// Transfer complete callback function.
+ iscsi_scsi_task_xfer_complete_callback xfer_complete_callback;
+
+ /// Task destruction callback function.
+ iscsi_scsi_task_destroy_callback destroy_callback;
+
+ /// I/O task complete callback function.
+ iscsi_scsi_emu_io_complete_callback io_complete_callback;
+
+ /// I/O task wait.
+ iscsi_scsi_emu_io_wait io_wait;
+
+ /// Output buffer.
+ uint8_t *buf;
+
+ /// Position of buffer in bytes.
+ uint32_t pos;
+
+ /// Length of buffer in bytes.
+ uint32_t len;
+
+ /// Unique identifier for this task.
+ uint64_t id;
+
+ /// Flags.
+ int flags;
+
+ /// Reference counter.
+ uint32_t ref;
+
+ /// Transfer position in bytes.
+ uint32_t xfer_pos;
+
+ /// Transfer length in bytes.
+ uint32_t xfer_len;
+
+ /// Sense data length.
+ uint8_t sense_data_len;
+
+ /// iSCSI SCSI status code.
+ uint8_t status;
+
+ /// Task management function.
+ uint8_t task_mgmt_func;
+
+ /// Task management response code.
+ uint8_t task_mgmt_response;
+} iscsi_scsi_task;
+
+
+/// iSCSI SCSI emulation physical block size in bytes.
+#define ISCSI_SCSI_EMU_PHYSICAL_BLOCK_SIZE 4096UL
+
+/// iSCSI SCSI emulation logical block size in bytes.
+#define ISCSI_SCSI_EMU_BLOCK_SIZE 512UL
+
+
+/// iSCSI SCSI emulation maximum tansfer length in logical blocks.
+#define ISCSI_SCSI_EMU_MAX_XFER_LEN (ISCSI_DEFAULT_MAX_RECV_DS_LEN * ISCSI_DEFAULT_MAX_DATA_OUT_PER_CONNECTION)
+
+/// iSCSI SCSI emulation maximum UNMAP LBA count in LBAs.
+#define ISCSI_SCSI_EMU_MAX_UNMAP_LBA_COUNT (ISCSI_DEFAULT_MAX_RECV_DS_LEN * ISCSI_DEFAULT_MAX_DATA_IN_PER_CONNECTION)
+
+/// iSCSI SCSI emulation maximum UNMAP block descriptor count in block descriptors.
+#define ISCSI_SCSI_EMU_MAX_UNMAP_BLOCK_DESC_COUNT 256UL
+
+
+/// iSCSI SCSI emulation I/O type: Removable.
+#define ISCSI_SCSI_EMU_IO_TYPE_REMOVABLE (1 << 0)
+
+/// iSCSI SCSI emulation I/O type: Unmap.
+#define ISCSI_SCSI_EMU_IO_TYPE_UNMAP (1 << 1)
+
+/// iSCSI SCSI emulation I/O type: Non-rotating medium (e.g., solid state).
+#define ISCSI_SCSI_EMU_IO_TYPE_NO_ROTATION (1 << 2)
+
+/// iSCSI SCSI emulation I/O type: Physical read only device.
+#define ISCSI_SCSI_EMU_IO_TYPE_PHYSICAL_READ_ONLY (1 << 3)
+
+/// iSCSI SCSI emulation I/O type: Device is (temporarily) write protected.
+#define ISCSI_SCSI_EMU_IO_TYPE_WRITE_PROTECT (1 << 4)
+
+/// iSCSI SCSI emulation I/O type: Write cache available.
+#define ISCSI_SCSI_EMU_IO_TYPE_WRITE_CACHE (1 << 5)
+
+
+/// iSCSI SCSI emulation block flags: Write operation.
+#define ISCSI_SCSI_EMU_BLOCK_FLAGS_WRITE (1 << 0)
+
+/// iSCSI SCSI emulation block flags: Verify operation.
+#define ISCSI_SCSI_EMU_BLOCK_FLAGS_VERIFY (1 << 1)
+
+
+void iscsi_scsi_task_create(iscsi_scsi_task *scsi_task, iscsi_scsi_task_xfer_complete_callback xfer_complete_callback, iscsi_scsi_task_destroy_callback destroy_callback); // Allocates and initializes a SCSI task
+void iscsi_scsi_task_destroy(iscsi_scsi_task *scsi_task); // Deallocates all resources acquired iscsi_scsi_task_create
+
+void iscsi_scsi_task_xfer_complete(iscsi_scsi_task *scsi_task); // Callback function when an iSCSI SCSI task completed the data transfer
+
+void iscsi_scsi_task_sense_data_check_cond_build(iscsi_scsi_task *scsi_task, const uint8_t sense_key, const uint8_t asc, const uint8_t ascq); // Allocates, if necessary and initializes SCSI sense data for check condition status code
+int iscsi_scsi_task_status_copy(iscsi_scsi_task *dst_scsi_task, const iscsi_scsi_task *src_scsi_task); // Copies iSCSI SCSI task sense data and status code
+void iscsi_scsi_task_lun_process_none(iscsi_scsi_task *scsi_task); // Processes a iSCSI SCSI task with no LUN identifier
+void iscsi_scsi_task_lun_process_abort(iscsi_scsi_task *scsi_task); // Processes a iSCSI SCSI aborted task
+
+iscsi_scsi_lun *iscsi_scsi_lun_create(const int lun_id); // Allocates and initializes an iSCSI LUN structure for linkage with a DNBD3 image
+int iscsi_scsi_lun_destroy_callback(uint8_t *key, const size_t key_size, uint8_t *value, uint8_t *user_data); // iSCSI SCSI LUN destructor callback for hash map
+void iscsi_scsi_lun_destroy(iscsi_scsi_lun *lun); // Deallocates all resources acquired by iscsi_scsi_lun_create
+
+uint64_t iscsi_scsi_lun_get_from_scsi(const int lun_id); // Converts an internal representation of a LUN identifier to an iSCSI LUN required for packet data
+int iscsi_scsi_lun_get_from_iscsi(const uint64_t lun); // Converts an iSCSI LUN from packet data to internal SCSI LUN identifier
+
+void iscsi_scsi_lun_task_append(iscsi_scsi_lun *lun, iscsi_scsi_task *scsi_task); // Appends an iSCSI SCSI task to a iSCSI SCSI LUN pending tasks doubly linked list
+void iscsi_scsi_lun_tasks_exec(iscsi_scsi_lun *lun); // Executes all iSCSI SCSI pending tasks assigned to a iSCSI SCSI LUN
+void iscsi_scsi_lun_task_run(iscsi_scsi_lun *lun, iscsi_scsi_task *scsi_task); // Runs an iSCSI SCSI task for a specified iSCSI SCSI LUN
+void iscsi_scsi_lun_task_complete(iscsi_scsi_lun *lun, iscsi_scsi_task *scsi_task); // Handles iSCSI SCSI task completition
+void iscsi_scsi_lun_task_exec(iscsi_scsi_lun *lun, iscsi_scsi_task *scsi_task); // Appends iSCSI SCSI task to pending tasks doubly linked list and / or runs it directly
+
+int iscsi_scsi_pr_check_scsi2(iscsi_scsi_task *scsi_task); // Checks the iSCSI SCSI Persistent Reservation (PR) SCSI-2 reserve of an iSCSI SCSI task
+int iscsi_scsi_pr_registrant_get_callback(uint8_t *key, const size_t key_size, uint8_t *value, uint8_t *user_data); // Finds an iSCSI SCSI Persistent Reservation (PR) registrant by target and initiator port
+int iscsi_scsi_pr_check(iscsi_scsi_task *scsi_task); // Checks the iSCSI SCSI Persistent Reservation (PR) of an iSCSI SCSI task
+int iscsi_scsi_pr_out(iscsi_scsi_task *scsi_task, iscsi_scsi_pr_reserve_out_parameter_list_packet *pr_reserve_out_parameter_list, const iscsi_scsi_cdb_pr_reserve_out *cdb_pr_reserve_out, const uint len); // Constructs an iSCSI SCSI Persistent Reservation (PR) out parameter list of an iSCSI SCSI task
+int iscsi_scsi_pr_in(iscsi_scsi_task *scsi_task, iscsi_scsi_pr_reserve_in_parameter_data_packet *pr_reserve_in_parameter_data, const iscsi_scsi_cdb_pr_reserve_in *cdb_pr_reserve_in, const uint len); // Constructs iSCSI SCSI Persistent Reservation (PR) in parameter data of an iSCSI SCSI task
+int iscsi_scsi_pr_reserve_scsi2(iscsi_scsi_task *scsi_task, const iscsi_scsi_cdb_pr_reserve_6 *cdb_pr_reserve_6); // Reserves an iSCSI SCSI Persistent Reservation (PR) of an iSCSI SCSI task
+int iscsi_scsi_pr_release_scsi2(iscsi_scsi_task *scsi_task); // Releases an iSCSI SCSI Persistent Reservation (PR) of an iSCSI SCSI task
+
+int iscsi_scsi_emu_io_block_read(iscsi_scsi_task *scsi_task, uint8_t *buf, dnbd3_image_t *image, const uint64_t offset_blocks, const uint64_t num_blocks, const uint32_t block_size, iscsi_scsi_emu_io_complete_callback callback, uint8_t *user_data); // Reads a number of blocks from a block offset of a DNBD3 image to a specified buffer
+uint8_t *iscsi_scsi_emu_block_read_complete_callback(dnbd3_image_t *image, uint8_t *user_data, const bool success); // Completes an iSCSI SCSI task after a finished I/O read operation
+int iscsi_scsi_emu_io_block_cmp_write(iscsi_scsi_task *scsi_task, uint8_t *buf, uint8_t *cmp_buf, dnbd3_image_t *image, const uint64_t offset_blocks, const uint64_t num_blocks, const uint32_t block_size, iscsi_scsi_emu_io_complete_callback callback, uint8_t *user_data); // Compares and writes a number of blocks starting from a block offset in a DNBD3 image with specified buffers
+uint8_t *iscsi_scsi_emu_block_write_complete_callback(dnbd3_image_t *image, uint8_t *user_data, const bool success); // Completes an iSCSI SCSI task after a finished I/O write operation
+int iscsi_scsi_emu_io_block_write(iscsi_scsi_task *scsi_task, uint8_t *buf, dnbd3_image_t *image, const uint64_t offset_blocks, const uint64_t num_blocks, const uint32_t block_size, iscsi_scsi_emu_io_complete_callback callback, uint8_t *user_data); // Writes a number of blocks from a block offset to a DNBD3 image of a specified buffer
+int iscsi_scsi_emu_io_queue(iscsi_scsi_emu_io_wait *io_wait); // Enqueues an I/O wait in the thread pool to execute
+uint8_t *iscsi_scsi_emu_block_resubmit_process_callback(uint8_t *user_data); // Resubmits an iSCSI SCSI task for execution
+
+int iscsi_scsi_emu_primary_inquiry_callback(uint8_t *key, const size_t key_size, uint8_t *value, uint8_t *user_data); // Fills in a single Vital Product Data (VPD) SCSI Port Designation Descriptor entry of an INQUIRY operation
+int iscsi_scsi_emu_exec(iscsi_scsi_task *scsi_task); // Executes the iSCSI SCSI emulation for an iSCSI SCSI task
+
+
+/// iSCSI port flags: In use.
+#define ISCSI_PORT_FLAGS_IN_USE (1 << 0)
/**
@@ -5997,17 +10980,87 @@ typedef struct iscsi_port {
iscsi_port *iscsi_port_create(const uint8_t *name, const uint64_t id, const uint16_t index); // Allocates and initializes an iSCSI port
+int iscsi_port_destroy_callback(uint8_t *key, const size_t key_size, uint8_t *value, uint8_t *user_data); // iSCSI port destructor callback for hash map
void iscsi_port_destroy(iscsi_port *port); // Deallocates all resource acquired iscsi_port_create
uint8_t *iscsi_port_get_name(const iscsi_port *port); // Retrieves the name of an iSCSI port
int iscsi_port_transport_id_set(iscsi_port *port, const uint8_t *name, const uint64_t isid); // Sets the SCSI transport ID of the iSCSI port
+
+/// iSCSI SCSI LUN flags: Removed.
+#define ISCSI_SCSI_LUN_FLAGS_REMOVED (1 << 0)
+
+/// iSCSI SCSI LUN flags: Resizing.
+#define ISCSI_SCSI_LUN_FLAGS_RESIZING (1 << 1)
+
+
+typedef struct iscsi_device iscsi_device;
+
+
+/**
+ * @brief iSCSI SCSI LUN.
+ *
+ * This structure managesw the SCSI
+ * LUNs attached to an iSCSI device
+ * and associates a disk image file.
+ */
+typedef struct iscsi_scsi_lun {
+ /// Doubly linked list containing associated tasks with this LUN.
+ iscsi_list tasks;
+
+ /// Mutex for accessing the associated tasks through multiple threads.
+ pthread_mutex_t tasks_mutex;
+
+ /// Doubly linked list containing associated pending tasks with this LUN.
+ iscsi_list tasks_pending;
+
+ /// Mutex for accessing the associated pending tasks through multiple threads.
+ pthread_mutex_t tasks_pending_mutex;
+
+ /// Doubly linked list containing associated management tasks with this LUN.
+ iscsi_list tasks_mgmt;
+
+ /// Mutex for accessing the associated management tasks through multiple threads.
+ pthread_mutex_t tasks_mgmt_mutex;
+
+ /// Doubly linked list containing associated management pending tasks with this LUN.
+ iscsi_list tasks_mgmt_pending;
+
+ /// Mutex for accessing the associated management pending tasks through multiple threads.
+ pthread_mutex_t tasks_mgmt_pending_mutex;
+
+ /// Doubly linked list containg Persistent Reservation (PR) registrant for I_T nexus.
+ iscsi_hashmap *pr_regs;
+
+ /// Persistent Reservation (PR) for the LUN.
+ iscsi_scsi_pr_reservation pr_reservation;
+
+ /// Persistent Reservation (PR) holder for SPC2 RESERVE(6) and RESERVE(10).
+ iscsi_scsi_pr_registrant pr_scsi2_holder;
+
+ /// iSCSI device which belongs to this LUN.
+ iscsi_device *device;
+
+ /// Assocated DNBD3 image for this LUN.
+ dnbd3_image_t *image;
+
+ /// LUN identifier (always MUST be between 0 and 7).
+ int id;
+
+ /// Flags.
+ int flags;
+
+ /// Persistent Reservation (PR) generation.
+ uint32_t pr_gen;
+} iscsi_scsi_lun;
+
+
/// iSCSI device flags: Allocated.
-#define ISCSI_DEVICE_FLAGS_ALLOCATED (1 << 0L)
+#define ISCSI_DEVICE_FLAGS_ALLOCATED (1 << 0)
/// iSCSI device flags: Removed.
-#define ISCSI_DEVICE_FLAGS_REMOVED (1 << 1L)
+#define ISCSI_DEVICE_FLAGS_REMOVED (1 << 1)
/**
@@ -6024,6 +11077,9 @@ typedef struct iscsi_device {
/// LUNs associated with this device.
iscsi_hashmap *luns;
+ /// Read/write lock for hash map containing all LUNs associated with this device. MUST be initialized with iscsi_create before any iSCSI functions are used.
+ pthread_rwlock_t luns_rwlock;
+
/// Ports associated with this device.
iscsi_hashmap *ports;
@@ -6033,31 +11089,43 @@ typedef struct iscsi_device {
/// Flags.
int flags;
- /// Number of ports.
- int num_ports;
+ /// Number of active connections for this device.
+ uint32_t active_conns;
- /// Portocol identifier.
+ /// Protocol identifier.
uint8_t protocol_id;
} iscsi_device;
-/// iSCSI target node flags: Header digest.
-#define ISCSI_TARGET_NODE_FLAGS_DIGEST_HEADER (1 << 0L)
+/// iSCSI target node maximum length.
+#define ISCSI_TARGET_NODE_MAX_NAME_LEN 223U
+
+
+/// iSCSI target node IQN identifier prefix string.
+#define ISCSI_TARGET_NODE_NAME_IQN_PREFIX "iqn."
+
+/// iSCSI target node IEEE NAA identifier prefix string.
+#define ISCSI_TARGET_NODE_NAME_NAA_PREFIX "naa."
+
+/// iSCSI target node EUI identifier prefix string.
+#define ISCSI_TARGET_NODE_NAME_EUI_PREFIX "eui."
+
+
+/// iSCSI target node WWN identifier prefix string.
+#define ISCSI_TARGET_NODE_NAME_WWN_PREFIX "wwn-0x"
-/// iSCSI target node flags: Data digest.
-#define ISCSI_TARGET_NODE_FLAGS_DIGEST_DATA (1 << 1L)
/// iSCSI target node flags: CHAP authentication disabled.
-#define ISCSI_TARGET_NODE_FLAGS_CHAP_DISABLE (1 << 2L)
+#define ISCSI_TARGET_NODE_FLAGS_CHAP_DISABLE (1 << 0)
/// iSCSI target node flags: CHAP authentication required.
-#define ISCSI_TARGET_NODE_FLAGS_CHAP_REQUIRE (1 << 3L)
+#define ISCSI_TARGET_NODE_FLAGS_CHAP_REQUIRE (1 << 1)
/// iSCSI target node flags: CHAP authentication mutual.
-#define ISCSI_TARGET_NODE_FLAGS_CHAP_MUTUAL (1 << 4L)
+#define ISCSI_TARGET_NODE_FLAGS_CHAP_MUTUAL (1 << 2)
/// iSCSI target node flags: Destroyed.
-#define ISCSI_TARGET_NODE_FLAGS_DESTROYED (1 << 5L)
+#define ISCSI_TARGET_NODE_FLAGS_DESTROYED (1 << 3)
/**
@@ -6086,10 +11154,10 @@ typedef struct iscsi_target_node {
/// Flags.
int flags;
- /// Header digest size (always must be 0 or 4 for now).
+ /// Header digest size (always MUST be 0 or 4 for now).
int header_digest;
- /// Data digest size (always must be 0 or 4 for now).
+ /// Data digest size (always MUST be 0 or 4 for now).
int data_digest;
/// CHAP group ID.
@@ -6116,16 +11184,16 @@ typedef struct iscsi_target_node_find_name {
/// iSCSI authentication CHAP phase: None.
-#define ISCSI_AUTH_CHAP_PHASE_NONE 0L
+#define ISCSI_AUTH_CHAP_PHASE_NONE 0
/// iSCSI authentication CHAP phase: Wait A.
-#define ISCSI_AUTH_CHAP_PHASE_WAIT_A 1L
+#define ISCSI_AUTH_CHAP_PHASE_WAIT_A 1
/// iSCSI authentication CHAP phase: Wait NR.
-#define ISCSI_AUTH_CHAP_PHASE_WAIT_NR 2L
+#define ISCSI_AUTH_CHAP_PHASE_WAIT_NR 2
/// iSCSI authentication CHAP phase: End.
-#define ISCSI_AUTH_CHAP_PHASE_END 3L
+#define ISCSI_AUTH_CHAP_PHASE_END 3
/**
@@ -6140,48 +11208,27 @@ typedef struct iscsi_auth_chap {
} iscsi_auth_chap;
-/// iSCSI session: Default maximum number of connections.
-#define ISCSI_SESSION_DEFAULT_MAX_CONNECTIONS 2UL
+/// iSCSI session flags: Initial ready to transfer.
+#define ISCSI_SESSION_FLAGS_INIT_R2T (1 << 0)
-/// iSCSI session: Default maximum number of outstanding ready to transfers.
-#define ISCSI_SESSION_DEFAULT_MAX_OUTSTANDING_R2T 1UL
+/// iSCSI session flags: Immediate data.
+#define ISCSI_SESSION_FLAGS_IMMEDIATE_DATA (1 << 1)
-/// iSCSI session: Default time to wait in seconds.
-#define ISCSI_SESSION_DEFAULT_TIME_TO_WAIT 2UL
+/// iSCSI session flags: Data PDU in order.
+#define ISCSI_SESSION_FLAGS_DATA_PDU_IN_ORDER (1 << 2)
-/// iSCSI session: Default time to retain in seconds.
-#define ISCSI_SESSION_DEFAULT_TIME_TO_RETAIN 20UL
-
-/// iSCSI session: First burst length in bytes.
-#define ISCSI_SESSION_DEFAULT_FIRST_BURST_LEN ISCSI_DEFAULT_RECV_DS_LEN
-
-/// iSCSI session: Maximum burst length in bytes.
-#define ISCSI_SESSION_DEFAULT_MAX_BURST_LEN (ISCSI_DEFAULT_MAX_RECV_DS_LEN * ISCSI_DEFAULT_MAX_DATA_OUT_PER_CONNECTION)
-
-/// iSCSI session: Default initial ready to transfer state.
-#define ISCSI_SESSION_DEFAULT_INIT_R2T true
-
-/// iSCSI session: Default immediate data state.
-#define ISCSI_SESSION_DEFAULT_IMMEDIATE_DATA true
-
-/// iSCSI session: Default data PDU in order state.
-#define ISCSI_SESSION_DEFAULT_DATA_PDU_IN_ORDER true
-
-/// iSCSI session: Default data sequence in order state.
-#define ISCSI_SESSION_DEFAULT_DATA_SEQ_IN_ORDER true
-
-/// iSCSI session: Default error recovery level.
-#define ISCSI_SESSION_DEFAULT_ERR_RECOVERY_LEVEL 0L
+/// iSCSI session flags: Data sequence in order.
+#define ISCSI_SESSION_FLAGS_DATA_SEQ_IN_ORDER (1 << 3)
/// iSCSI session type: Invalid.
-#define ISCSI_SESSION_TYPE_INVALID 0L
+#define ISCSI_SESSION_TYPE_INVALID 0
/// iSCSI session type: Normal.
-#define ISCSI_SESSION_TYPE_NORMAL 1L
+#define ISCSI_SESSION_TYPE_NORMAL 1
/// iSCSI session type: Discovery.
-#define ISCSI_SESSION_TYPE_DISCOVERY 2L
+#define ISCSI_SESSION_TYPE_DISCOVERY 2
/**
@@ -6192,26 +11239,29 @@ typedef struct iscsi_auth_chap {
* login phase.
*/
typedef struct iscsi_session {
- /// TCP/IP Connections associated with this session.
- iscsi_hashmap *connections;
+ /// List of iSCSI connections associated with this session.
+ iscsi_list conn_list;
/// Initiator port.
- iscsi_port *initiator_port;
+ iscsi_port *init_port;
- /// Login key / value pairs negotiated with this session.
+ /// Hash map of login key / value pairs negotiated with this session.
iscsi_hashmap *key_value_pairs;
+ /// iSCSI target node.
+ iscsi_target_node *target;
+
/// Portal group tag.
- int tag;
+ uint64_t tag;
/// Initiator Session ID (ISID).
uint64_t isid;
/// Target Session Identifying Handle (TSIH).
- uint16_t tsih;
+ uint64_t tsih;
- /// iSCSI target node.
- iscsi_target_node *target;
+ /// Flags (extracted from key and value pairs).
+ int flags;
/// Queue depth.
uint queue_depth;
@@ -6219,38 +11269,29 @@ typedef struct iscsi_session {
/// iSCSI session type.
int type;
+ /// Number of active connections linked to this session.
+ uint32_t conns;
+
/// Maximum number of connections.
- uint max_conns;
+ uint32_t max_conns;
/// Ready to transfer maximum outstanding value.
- uint max_outstanding_r2t;
+ uint32_t max_outstanding_r2t;
/// Default time to wait.
- uint default_time_to_wait;
+ uint32_t default_time_to_wait;
/// Default time to retain.
- uint default_time_to_retain;
+ uint32_t default_time_to_retain;
/// First burst length.
- uint first_burst_len;
+ uint32_t first_burst_len;
/// Maximum burst length.
- uint max_burst_len;
-
- /// Initial ready to transfer bit.
- int init_r2t;
-
- /// Immediate data bit.
- int immediate_data;
-
- /// Data PDU in order bit.
- int data_pdu_in_order;
-
- /// Data sequence in order bit.
- int data_seq_in_order;
+ uint32_t max_burst_len;
/// Error recovery level.
- uint err_recovery_level;
+ uint32_t err_recovery_level;
/// ExpCmdSN.
uint32_t exp_cmd_sn;
@@ -6263,77 +11304,84 @@ typedef struct iscsi_session {
} iscsi_session;
+typedef struct iscsi_pdu iscsi_pdu;
+
+
/// iSCSI connection read packet data return code from iscsi_connection_pdu_read function: Packet parsed successfully.
-#define ISCSI_CONNECT_PDU_READ_OK 0L
+#define ISCSI_CONNECT_PDU_READ_OK 0
/// iSCSI connection read packet data return code from iscsi_connection_pdu_read function: Packet processed successfully.
-#define ISCSI_CONNECT_PDU_READ_PROCESSED 1L
+#define ISCSI_CONNECT_PDU_READ_PROCESSED 1
/// iSCSI connection read packet data return code from iscsi_connection_pdu_read function: Fatail error during packet parsing.
-#define ISCSI_CONNECT_PDU_READ_ERR_FATAL -1L
+#define ISCSI_CONNECT_PDU_READ_ERR_FATAL -1
/// iSCSI connection read packet data return code from iscsi_connection_pdu_read function: Login error response.
-#define ISCSI_CONNECT_PDU_READ_ERR_LOGIN_RESPONSE -2L
+#define ISCSI_CONNECT_PDU_READ_ERR_LOGIN_RESPONSE -2
/// iSCSI connection read packet data return code from iscsi_connection_pdu_read function: Login parameter error.
-#define ISCSI_CONNECT_PDU_READ_ERR_LOGIN_PARAMETER -3L
+#define ISCSI_CONNECT_PDU_READ_ERR_LOGIN_PARAMETER -3
/// iSCSI connection read packet data return code from iscsi_connection_pdu_read function: Login parameter not exchanged once error.
-#define ISCSI_CONNECT_PDU_READ_ERR_LOGIN_PARAMETER_XCHG_NOT_ONCE -4L
+#define ISCSI_CONNECT_PDU_READ_ERR_LOGIN_PARAMETER_XCHG_NOT_ONCE -4
/// iSCSI connection flags: Stopped.
-#define ISCSI_CONNECT_FLAGS_STOPPED (1 << 0L)
+#define ISCSI_CONNECT_FLAGS_STOPPED (1 << 0)
/// iSCSI connection flags: Rejected.
-#define ISCSI_CONNECT_FLAGS_REJECTED (1 << 1L)
+#define ISCSI_CONNECT_FLAGS_REJECTED (1 << 1)
/// iSCSI connection flags: Logged out.
-#define ISCSI_CONNECT_FLAGS_LOGGED_OUT (1 << 2L)
+#define ISCSI_CONNECT_FLAGS_LOGGED_OUT (1 << 2)
/// iSCSI connection flags: Full feature.
-#define ISCSI_CONNECT_FLAGS_FULL_FEATURE (1 << 3L)
+#define ISCSI_CONNECT_FLAGS_FULL_FEATURE (1 << 3)
/// iSCSI connection flags: CHAP authentication is disabled.
-#define ISCSI_CONNECT_FLAGS_CHAP_DISABLE (1 << 4L)
+#define ISCSI_CONNECT_FLAGS_CHAP_DISABLE (1 << 4)
/// iSCSI connection flags: CHAP authentication is required.
-#define ISCSI_CONNECT_FLAGS_CHAP_REQUIRE (1 << 5L)
+#define ISCSI_CONNECT_FLAGS_CHAP_REQUIRE (1 << 5)
/// iSCSI connection flags: CHAP authentication is mutual.
-#define ISCSI_CONNECT_FLAGS_CHAP_MUTUAL (1 << 6L)
+#define ISCSI_CONNECT_FLAGS_CHAP_MUTUAL (1 << 6)
/// iSCSI connection flags: Authenticated.
-#define ISCSI_CONNECT_FLAGS_AUTH (1 << 7L)
+#define ISCSI_CONNECT_FLAGS_AUTH (1 << 7)
/// iSCSI connection flags: Oustanding NOP.
-#define ISCSI_CONNECT_FLAGS_NOP_OUTSTANDING (1 << 8L)
+#define ISCSI_CONNECT_FLAGS_NOP_OUTSTANDING (1 << 8)
/// Ready to wait for PDU.
-#define ISCSI_CONNECT_PDU_RECV_STATE_WAIT_PDU_READY 0L
+#define ISCSI_CONNECT_PDU_RECV_STATE_WAIT_PDU_READY 0
/// Active connection waiting for any PDU header.
-#define ISCSI_CONNECT_PDU_RECV_STATE_WAIT_PDU_HDR 1L
+#define ISCSI_CONNECT_PDU_RECV_STATE_WAIT_PDU_HDR 1
/// Active connection waiting for data.
-#define ISCSI_CONNECT_PDU_RECV_STATE_WAIT_PDU_DATA 2L
+#define ISCSI_CONNECT_PDU_RECV_STATE_WAIT_PDU_DATA 2
/// Active connection does not wait for data.
-#define ISCSI_CONNECT_PDU_RECV_STATE_ERR 3L
+#define ISCSI_CONNECT_PDU_RECV_STATE_ERR 3
/// iSCSI connection state: Invalid.
-#define ISCSI_CONNECT_STATE_INVALID 0L
+#define ISCSI_CONNECT_STATE_INVALID 0
/// iSCSI connection state: Running.
-#define ISCSI_CONNECT_STATE_RUNNING 1L
+#define ISCSI_CONNECT_STATE_RUNNING 1
/// iSCSI connection state: Exiting.
-#define ISCSI_CONNECT_STATE_EXITING 2L
+#define ISCSI_CONNECT_STATE_EXITING 2
/// iSCSI connection state: Invalid.
-#define ISCSI_CONNECT_STATE_EXITED 3L
+#define ISCSI_CONNECT_STATE_EXITED 3
+
+
+/// Number of attempts for writing to iSCSI connection socket.
+#define ISCSI_CONNECT_SOCKET_WRITE_RETRIES 3
/**
@@ -6345,15 +11393,24 @@ typedef struct iscsi_session {
* and iSCSI portals.
*/
typedef struct iscsi_connection {
+ /// Doubly linked list node, MUST be first element.
+ iscsi_node node;
+
/// iSCSI session associated with this connection.
iscsi_session *session;
- /// Hash map containing text key / value pairs associated to this connection.
+ /// Hash map containing login text key / value pairs associated to this connection.
iscsi_hashmap *key_value_pairs;
- /// Temporarily storage for partially received parameter.
+ /// Temporarily storage for partially received login parameter.
uint8_t *partial_pairs;
+ /// Hash map containing text key / value pairs associated to this connection.
+ iscsi_hashmap *text_key_value_pairs;
+
+ /// Temporarily storage for partially received text parameter.
+ uint8_t *text_partial_pairs;
+
/// iSCSI device.
iscsi_device *device;
@@ -6381,18 +11438,48 @@ typedef struct iscsi_connection {
/// iSCSI portal host port.
uint8_t *portal_port;
+ /// Current PDU being processed.
+ iscsi_pdu *pdu_processing;
+
+ /// Login response PDU.
+ iscsi_pdu *login_response_pdu;
+
+ /// Doubly linked list containing enqueued SCSI Data In tasks.
+ iscsi_list scsi_data_in_queued_tasks;
+
+ /// Doubly linked list containing writing PDU's associated with this connection.
+ iscsi_list pdus_write;
+
+ /// Doubly linked list containing SNACK PDU's associated with this connection.
+ iscsi_list pdus_snack;
+
+ /// Doubly linked list containing active Ready To Transfer (R2T) tasks.
+ iscsi_list r2t_tasks_active;
+
+ /// Doubly linked list containing queued Ready To Transfer (R2T) tasks.
+ iscsi_list r2t_tasks_queue;
+
+ /// iSCSI SendTargets total number of bytes completed.
+ uint target_send_total_size;
+
+ /// iSCSI SCSI Data In count.
+ uint scsi_data_in_cnt;
+
+ /// iSCSI SCSI Data Out count.
+ uint scsi_data_out_cnt;
+
+ /// iSCSI tasks pending count.
+ uint task_cnt;
+
+ /// Pending Ready To Transfer (R2T) tasks.
+ uint r2t_pending;
+
/// iSCSI connection contains a header digest (CRC32), always MUST be 0 or 4 for now.
int header_digest;
/// iSCSI connection contains a data digest (CRC32), always MUST be 0 or 4 for now.
int data_digest;
- /// Current PDU being processed.
- struct iscsi_pdu *pdu_processing;
-
- /// Login response PDU.
- struct iscsi_pdu *login_response_pdu;
-
/// Internal connection identifier (key of iSCSI global vector hash map).
int id;
@@ -6412,10 +11499,10 @@ typedef struct iscsi_connection {
int login_phase;
/// Maximum receive DataSegment length in bytes.
- uint max_recv_ds_len;
+ uint32_t max_recv_ds_len;
/// Portal group tag.
- int pg_tag;
+ uint64_t pg_tag;
/// Initiator Session ID (ISID).
iscsi_isid isid;
@@ -6435,6 +11522,9 @@ typedef struct iscsi_connection {
/// Initiator Task Tag (ITT).
uint32_t init_task_tag;
+ /// Targer Transfer Tag (TTT).
+ uint32_t target_xfer_tag;
+
/// CHAP authentication.
iscsi_auth_chap auth_chap;
@@ -6446,11 +11536,120 @@ typedef struct iscsi_connection {
/// ExpStatSN.
uint32_t exp_stat_sn;
+
+ /// Execution queue to run to invoke callback functions after asynchronous I/O has been finished.
+ iscsi_list exec_queue;
+
+ // TODO: Remove after test finish
+ iscsi_hashmap *stat_iscsi_opcodes;
+
+ // TODO: Remove after test finish
+ iscsi_hashmap *stat_scsi_opcodes;
} iscsi_connection;
+/**
+ * @brief iSCSI transfer completed callback function.
+ *
+ * This function is invoked when the response PDU
+ * write to the TCP/IP socket has been completed.
+ *
+ * @param[in] user_data Pointer to user data.
+ */
+typedef void (*iscsi_connection_xfer_complete_callback)(uint8_t *user_data);
+
+
+/**
+ * @brief Callback for iSCSI connection write TCP/IP write operation completion.
+ *
+ * This function is invoked when the sending
+ * TCP/IP transfer has been finished.
+ *
+ * @param[in] user_data Pointer to user data.
+ * @param[in] err 0 if I/O completed successfully or an
+ * error code indicating the problem.
+ */
+typedef void (*iscsi_connection_write_complete_callback)(uint8_t *user_data, int err);
+
+
+/// iSCSI connection asynchronous execution queue: SCSI emulation I/O.
+#define ISCSI_CONNECT_EXEC_QUEUE_TYPE_SCSI_EMU_IO 0U
+
+/// iSCSI connection asynchronous execution queue: PDU write I/O.
+#define ISCSI_CONNECT_EXEC_QUEUE_TYPE_PDU_WRITE 1U
+
+
+/**
+ * @brief iSCSI connection execution queue.
+ *
+ * This structure is used for invoking the
+ * callback functions after processing has
+ * been completed.\n
+ * Currently, PDU writes and SCSI emulation
+ * invoke I/O callbacks after finishing
+ * their operations.
+ */
+typedef struct iscsi_connection_exec_queue {
+ /// Doubly linked list node, MUST be first element.
+ iscsi_node node;
+
+ /**
+ * @union data
+ * @brief Invokes callback functions with arguments based on the execution queue type.
+ *
+ * This union contains the arguments needed
+ * for their respective callback functions
+ * of the completion process.
+ */
+ union {
+ /**
+ * @brief PDU write completion callback and arguments.
+ *
+ * For PDU write completion type, two arguments
+ * are passed.
+ */
+ struct {
+ /// Callback function to invoke after PDU write completion process has been completed.
+ iscsi_connection_write_complete_callback callback;
+
+ /// User data to be passed to the PDU write completion process callback function.
+ uint8_t *user_data;
+
+ /// Error code to be passed to the PDU write completion process callback function.
+ int err;
+ } pdu_write;
+
+ /**
+ * @brief I/O completion callback and arguments.
+ *
+ * For I/O completion type, three arguments
+ * are passed.
+ */
+ struct {
+ /// Callback function to invoke after I/O process has been completed.
+ iscsi_scsi_emu_io_complete_callback callback;
+
+ /// DNBD3 image to be passed to the I/O completion process callback function.
+ dnbd3_image_t *image;
+
+ /// User data to be passed to the I/O completion process callback function.
+ uint8_t *user_data;
+
+ /// Successful state passed to the I/O completion process callback function.
+ bool success;
+ } io;
+ } data;
+
+ /// Type of completion callback.
+ uint type;
+} iscsi_connection_exec_queue;
+
+
+typedef struct iscsi_task iscsi_task;
+
+
/// iSCSI PDU flags: Rejected.
-#define ISCSI_PDU_FLAGS_REJECTED (1 << 0L)
+#define ISCSI_PDU_FLAGS_REJECTED (1 << 0)
/**
@@ -6461,6 +11660,9 @@ typedef struct iscsi_connection {
* and filling the BHS, AHS and DS properly.
*/
typedef struct iscsi_pdu {
+ /// Doubly linked list node, MUST be first element.
+ iscsi_node node;
+
/// iSCSI Basic Header Segment (BHS) packet data.
iscsi_bhs_packet *bhs_pkt;
@@ -6471,52 +11673,183 @@ typedef struct iscsi_pdu {
iscsi_header_digest *header_digest;
/// iSCSI DataSegment (DS) packet data for fast access and is straight after BHS, AHS and header digest packet in memory.
- iscsi_ds_cmd_data *ds_cmd_data;
+ iscsi_scsi_ds_cmd_data *ds_cmd_data;
/// Data digest (CRC32C) packet data for fast access and is straight after BHS, AHS, header digest and DataSegment packet in memory.
iscsi_data_digest *data_digest;
- /// Key and value pairs to send to client.
- iscsi_hashmap *key_value_pairs;
+ /// iSCSI task handling this PDU.
+ iscsi_task *task;
+
+ /// Associated iSCSI connection.
+ iscsi_connection *conn;
+
+ /// Transfer complete callback function.
+ iscsi_connection_xfer_complete_callback xfer_complete_callback;
+
+ /// Transfer complete callback user data (arguments).
+ uint8_t *xfer_complete_user_data;
/// Flags.
int flags;
+ /// Reference counter.
+ uint32_t ref;
+
+ /// Bytes of Basic Header Segment (BHS) already read.
+ uint bhs_pos;
+
+ /// Bytes of Advanced Header Segment (AHS) already read.
+ uint ahs_pos;
+
+ /// AHSLength.
+ uint ahs_len;
+
+ /// Bytes of header digest (CRC32C) already read.
+ uint header_digest_pos;
+
/// Header digest size (always 0 or 4 for now).
int header_digest_size;
- /// Bytes of header digest (CRC32C) already read.
- uint header_digest_read_len;
+ /// DataSegmentLength.
+ uint32_t ds_len;
+
+ /// Position of DataSegment buffer for next operation.
+ uint32_t pos;
+
+ /// Allocated DataSegment buffer length.
+ uint32_t len;
+
+ /// Bytes of data digest (CRC32C) already read.
+ uint data_digest_pos;
/// Data digest size (always 0 or 4 for now).
int data_digest_size;
- /// Bytes of data digest (CRC32C) already read.
- uint data_digest_read_len;
+ /// Tasks referenced by this PDU counter.
+ uint task_ref_cnt;
- /// Bytes of Basic Header Segment (BHS) already read.
- uint bhs_read_len;
+ /// CmdSN.
+ uint32_t cmd_sn;
+} iscsi_pdu;
- /// AHSLength.
- uint ahs_len;
- /// Bytes of Advanced Header Segment (AHS) already read.
- uint ahs_read_len;
+/// iSCSI task flags: Ready To Transfer is active.
+#define ISCSI_TASK_FLAGS_R2T_ACTIVE (1 << 0)
- /// DataSegmentLength.
- uint ds_len;
+/// iSCSI task flags: Task is enqueued in SCSI layer.
+#define ISCSI_TASK_FLAGS_QUEUED (1 << 1)
- /// Position of DataSegment buffer for next read.
- uint pos;
+
+/**
+ * @brief This structure is used for iSCSI task management.
+ *
+ * This structure maintains the iSCSI task handling
+ * including the underlying SCSI layer.
+ */
+typedef struct iscsi_task {
+ /// Doubly linked list node, MUST be first element.
+ iscsi_node node;
+
+ /// Underlying SCSI task structure.
+ iscsi_scsi_task scsi_task;
+
+ /// Parent iSCSI task.
+ iscsi_task *parent;
+
+ /// Sub tasks doubly linked list for splitted data transfers.
+ iscsi_list sub_tasks;
/// Associated iSCSI connection.
iscsi_connection *conn;
- /// CmdSN.
- uint32_t cmd_sn;
-} iscsi_pdu;
+ /// Associated iSCSI PDU.
+ iscsi_pdu *pdu;
+
+ /// Buffer position in bytes.
+ uint32_t pos;
+
+ /// Buffer length in bytes.
+ uint32_t len;
+
+ /// Unique identifier for this task.
+ uint64_t id;
+
+ /// Flags.
+ int flags;
+
+ /// LUN identifier associated with this task (always MUST be between 0 and 7), used for hot removal tracking.
+ int lun_id;
+
+ /// Initiator Task Tag (ITT).
+ uint32_t init_task_tag;
+
+ /// Target Transfer Tag (TTT).
+ uint32_t target_xfer_tag;
+
+ /// Desired number of bytes completed.
+ uint32_t des_data_xfer_pos;
+
+ /// Desired data transfer length.
+ uint32_t des_data_xfer_len;
+
+ /// SCSI Data In Data Sequence Number (DataSN).
+ uint32_t data_sn;
+
+ /// SCSI Data Out count.
+ uint32_t scsi_data_out_cnt;
+
+ /// Length in bytes of R2T, used for ensuring that R2T burst does not exceed MaxBurstLength.
+ uint32_t r2t_len;
+
+ /// Ready To Transfer Sequence Number (R2TSN).
+ uint32_t r2t_sn;
+
+ /// Next expected Ready To Transfer offset is used for receiving the Data-OUT PDU.
+ uint32_t r2t_next_exp_pos;
+
+ /// Ready To Transfer DataSN, used for next sequence of a R2TSN.
+ uint32_t r2t_data_sn;
+
+ /// Next R2TSN to be acknowledged.
+ uint32_t r2t_sn_ack;
+
+ /// Outstanding Ready To Transfer (R2T) count.
+ uint32_t r2t_outstanding;
+} iscsi_task;
+
+
+iscsi_task *iscsi_task_create(iscsi_connection *conn, iscsi_task *parent, iscsi_scsi_task_xfer_complete_callback callback); // Allocates and initializes an iSCSI task structure
+void iscsi_task_destroy_callback(iscsi_scsi_task *scsi_task); // Deallocates all resources of the iSCSI task of an iSCSI SCSI task
+void iscsi_task_destroy(iscsi_task *task); // Deallocates resources acquired by iscsi_task_create
+
+void iscsi_task_queue(iscsi_connection *conn, iscsi_task *task); // Enqueues an iSCSI task
+
+void iscsi_task_xfer_complete_process_read(iscsi_connection *conn, iscsi_task *task, iscsi_task *primary_task); // Processes an iSCSI SCSI task which completed a read data transfer
+bool iscsi_task_xfer_del(iscsi_connection *conn, const uint32_t target_xfer_tag); // Deletes an iSCSI task from the active Ready To Transfer (R2T) doubly linked list by Target Transfer Tag (TTT)
+void iscsi_task_xfer_complete_process_other(iscsi_connection *conn, iscsi_task *task, iscsi_task *primary_task); // Processes an iSCSI SCSI task which completed a non-read data transfer
+
+void iscsi_task_response(iscsi_connection *conn, iscsi_task *task); // Creates, initializes and sends an iSCSI task reponse PDU.
+
+iscsi_device *iscsi_device_create(const uint8_t *name, const int lun_id, const uint8_t protocol_id); // Creates and initializes an iSCSI device with a maximum number of LUNs
+int iscsi_device_destroy_callback(uint8_t *key, const size_t key_size, uint8_t *value, uint8_t *user_data); // iSCSI device destructor callback for hash map
+void iscsi_device_destroy(iscsi_device *device); // Deallocates all resources acquired by iscsi_device_create
iscsi_port *iscsi_device_find_port_by_portal_group_tag(const iscsi_device *device, const uint64_t id); // Gets an iSCSI device being in use by portal group identifier
+iscsi_scsi_lun *iscsi_device_find_lun(iscsi_device *device, const int lun_id); // Searches an iSCSI LUN by LUN identifier
+
+int iscsi_device_port_add(iscsi_device *device, const uint8_t *name, const uint64_t id); // Creates, initializes and adds an iSCSI target port to an iSCSI device
+
+void iscsi_device_scsi_task_queue(iscsi_device *device, iscsi_scsi_task *scsi_task); // Enqueues an iSCSI SCSI task to the first LUN of an iSCSI device
+
+int iscsi_target_node_create_callback(uint8_t *key, const size_t key_size, uint8_t *value, uint8_t *user_data); // Creates, initializes and adds a portal group to an iSCSI target node
+iscsi_target_node *iscsi_target_node_create(uint8_t *name, const uint8_t *alias, const int index, const int lun_id, const uint queue_depth, const int flags, const int32_t chap_group, const int header_digest, const int data_digest); // Creates and initializes an iSCSI target node
+int iscsi_target_node_destroy_callback(uint8_t *key, const size_t key_size, uint8_t *value, uint8_t *user_data); // iSCSI target node destructor callback for hash map
+void iscsi_target_node_destroy(iscsi_target_node *target); // Deallocates all resources acquired by iscsi_target_node_create
+
+int32_t iscsi_target_node_send(iscsi_connection *conn, const uint8_t *dst_iqn, const uint8_t *src_iqn, uint8_t *buf, const uint32_t pos, const uint32_t len); // Sends a buffer from a source iSCSI IQN to target iSCSI IQNs
+uint64_t iscsi_target_node_wwn_get(const uint8_t *name); // Calculates the WWN using 64-bit IEEE Extended NAA for a name
+dnbd3_image_t *iscsi_target_node_image_get(uint8_t *iqn); // Extracts the DNBD3 image out of an iSCSI IQN string and opens the DNBD3 image
int iscsi_target_node_find_callback(uint8_t *key, const size_t key_size, uint8_t *value, uint8_t *user_data); // Finds an iSCSI target node by case insensitive name search
iscsi_target_node *iscsi_target_node_find(uint8_t *target_name); // Searches an iSCSI target node by name using case insensitive search
@@ -6524,6 +11857,7 @@ uint8_t *iscsi_target_node_get_redirect(iscsi_connection *conn, iscsi_target_nod
int iscsi_target_node_access(iscsi_connection *conn, iscsi_target_node *target, const uint8_t *iqn, const uint8_t *adr); // Checks if target node is accessible
iscsi_session *iscsi_session_create(iscsi_connection *conn, iscsi_target_node *target, const int type); // Creates and initializes an iSCSI session
+int iscsi_session_destroy_callback(uint8_t *key, const size_t key_size, uint8_t *value, uint8_t *user_data); // iSCSI session destructor callback for hash map
void iscsi_session_destroy(iscsi_session *session); // Deallocates all resources acquired by iscsi_session_create
int iscsi_session_init_key_value_pairs(iscsi_hashmap *key_value_pairs); // Initializes a key and value pair hash table with default values
@@ -6535,21 +11869,42 @@ void iscsi_connection_destroy(iscsi_connection *conn); // Deallocates all resour
int iscsi_connection_drop(iscsi_connection *conn, const uint8_t *conn_match, const int all); // Drops all connections based on matching pattern
void iscsi_connection_schedule(iscsi_connection *conn); // Schedules an iSCSI connection
-int iscsi_connection_read(const iscsi_connection *conn, uint8_t *buf, const uint len); // Reads data for the specified iSCSI connection from its TCP socket
-int iscsi_connection_write(const iscsi_connection *conn, uint8_t *buf, const uint len); // Writes data for the specified iSCSI connection to its TCP socket
+int32_t iscsi_connection_read(const iscsi_connection *conn, uint8_t *buf, const uint32_t len); // Reads data for the specified iSCSI connection from its TCP socket
+int32_t iscsi_connection_write(const iscsi_connection *conn, uint8_t *buf, const uint32_t len); // Writes data for the specified iSCSI connection to its TCP socket
+int iscsi_connection_handle_scsi_data_in_queued_tasks(iscsi_connection *conn); // This function handles all queued iSCSI SCSI Data In tasks
int iscsi_connection_init_key_value_pairs(iscsi_hashmap *key_value_pairs); // Initializes a key and value pair hash table with default values for an iSCSI connection
-int iscsi_negotiate_key_value_pairs(iscsi_connection *conn, iscsi_hashmap *key_value_pairs, uint8_t *buf, const uint pos, const uint len); // Negotiates all key and value pairs required for session authentication
+int32_t iscsi_negotiate_key_value_pairs(iscsi_connection *conn, iscsi_hashmap *key_value_pairs, uint8_t *buf, const uint32_t pos, const uint32_t len); // Negotiates all key and value pairs required for session authentication
int iscsi_connection_copy_key_value_pairs(iscsi_connection *conn); // Copies retrieved key and value pairs into SCSI connection and session structures
int iscsi_connection_save_incoming_key_value_pairs(iscsi_connection *conn, iscsi_hashmap *key_value_pairs, iscsi_pdu *login_response_pdu, const iscsi_pdu *pdu); // Saves incoming key / value pairs from the client of a login request PDU
+void iscsi_connection_login_response_reject(iscsi_pdu *login_response_pdu, const iscsi_pdu *pdu); // Initializes a rejecting login response packet
+iscsi_pdu *iscsi_connection_pdu_create(iscsi_connection *conn, const uint ahs_len, const int header_digest_size, const uint32_t ds_len, const int data_sigest_size ); // Creates an iSCSI PDU structure used by connections
+void iscsi_connection_pdu_destroy(iscsi_pdu *pdu); // Destroys an iSCSI PDU structure used by connections
+void iscsi_connection_pdu_free(iscsi_connection *conn, iscsi_pdu *pdu); // Frees an iSCSI PDU structure used by using connection callback function
-typedef void (*iscsi_connection_xfer_complete_callback)(uint8_t *user_data); // iSCSI transfer completed callback function.
+iscsi_bhs_packet *iscsi_connection_pdu_append(iscsi_pdu *pdu, const uint ahs_len, const int header_digest_size, const uint32_t ds_len, const int data_digest_size); // Appends packet data to an iSCSI PDU structure used by connections
+iscsi_ahs_packet *iscsi_connection_pdu_ahs_packet_get(const iscsi_pdu *pdu, const int index); // Retrieves the pointer to an specific AHS packet from an iSCSI PDU by index
+int iscsi_connection_pdu_ahs_packet_count(const iscsi_pdu *pdu); // Counts number of AHS packets of an iSCSI PDU
-iscsi_pdu *iscsi_connection_pdu_create(iscsi_connection *conn); // Creates an iSCSI PDU structure used by connections
-void iscsi_connection_pdu_destroy(iscsi_pdu *pdu); // Destroys an iSCSI PDU structure used by connections
+void iscsi_connection_pdu_digest_header_update(iscsi_header_digest *header_digest, const iscsi_bhs_packet *packet_data, const uint ahs_len); // Calculate and store iSCSI header digest (CRC32C)
+bool iscsi_connection_pdu_digest_header_verify(const iscsi_header_digest *header_digest, const iscsi_bhs_packet *packet_data, const uint ahs_len); // Validates a stored iSCSI header digest (CRC32C) with actual header data
+void iscsi_connection_pdu_digest_data_update(iscsi_data_digest *data_digest, const iscsi_scsi_ds_cmd_data *ds_cmd_data, const uint32_t ds_len); // Calculate iSCSI data digest (CRC32C)
+bool iscsi_connection_pdu_digest_data_verify(const iscsi_data_digest *data_digest, const iscsi_scsi_ds_cmd_data *ds_cmd_data, const uint32_t ds_len); // Validates a stored iSCSI data digest (CRC32C) with actual DataSegment
+
+void iscsi_connection_pdu_ack_remove(iscsi_connection *conn, const uint32_t exp_stat_sn); // Removes an acknowledged PDU from SNACK PDU doubly linked list by ExpStatSN
+
+iscsi_pdu *iscsi_r2t_find_pdu_bhs(iscsi_connection *conn, iscsi_pdu *pdu); // Searches an iSCSI PDU by Basic Header Segment (BHS) in the Ready To Transfer (R2T) active and queued task doubly linked list
+int iscsi_r2t_send(iscsi_connection *conn, iscsi_task *task, uint32_t *r2t_sn, const uint32_t pos, const uint32_t len, const uint32_t target_xfer_tag); // Sends an iSCSI Ready To Transfer Sequence Number (R2TSN) packet to the initiator
int iscsi_connection_read_data(iscsi_connection *conn, int len, void *buf);
int iscsi_connection_read_iov_data(iscsi_connection *conn, struct iovec *iov, int iov_count);
void iscsi_connection_pdu_write(iscsi_connection *conn, iscsi_pdu *pdu, iscsi_connection_xfer_complete_callback callback, uint8_t *user_data);
+int iscsi_connection_pdu_handle(iscsi_connection *conn); // Handles incoming PDU data, read up to 16 fragments at once
+void iscsi_connection_handle(dnbd3_client_t *client, const dnbd3_request_t *request, const int len); // Handles an iSCSI connection until connection is closed
+
+#ifdef __cplusplus
+}
+#endif
+
#endif /* DNBD3_ISCSI_H_ */
diff --git a/src/server/net.c b/src/server/net.c
index eb51d29..a279b02 100644
--- a/src/server/net.c
+++ b/src/server/net.c
@@ -20,6 +20,7 @@
#include "helper.h"
#include "image.h"
+#include "iscsi.h"
#include "uplink.h"
#include "locks.h"
#include "rpc.h"
@@ -178,6 +179,7 @@ void* net_handleNewConnection(void *clientPtr)
// Close enough...
rpc_sendStatsJson( client->sock, &client->host, &request, ret );
} else {
+ iscsi_connection_handle( client, &request, ret );
logadd( LOG_DEBUG1, "Magic in client handshake incorrect" );
}
goto fail_preadd;
diff --git a/src/server/server.c b/src/server/server.c
index d086930..0bbb417 100644
--- a/src/server/server.c
+++ b/src/server/server.c
@@ -27,6 +27,7 @@
#include "net.h"
#include "altservers.h"
#include "integrity.h"
+#include "iscsi.h"
#include "threadpool.h"
#include "rpc.h"
#include "fuse.h"
@@ -176,6 +177,10 @@ _Noreturn static void dnbd3_cleanup()
threadpool_waitEmpty();
+ // Destroy iSCSI global vector
+ iscsi_destroy();
+ pthread_rwlock_destroy( &iscsi_globvec_rwlock );
+
// Clean up images
retries = 5;
while ( !image_tryFreeAll() && --retries > 0 ) {
@@ -365,6 +370,12 @@ int main(int argc, char *argv[])
altservers_init();
integrity_init();
net_init();
+
+ if ( _iSCSIServer ) {
+ if ( (pthread_rwlock_init( &iscsi_globvec_rwlock, NULL ) != 0) || (iscsi_create() < 0) )
+ return EXIT_FAILURE;
+ }
+
uplink_globalsInit();
rpc_init();
if ( mountDir != NULL && !dfuse_init( "-oallow_other", mountDir ) ) {