diff options
| author | Sebastian Vater | 2025-08-19 16:53:14 +0200 |
|---|---|---|
| committer | Simon Rettberg | 2025-12-09 15:33:20 +0100 |
| commit | 197f25c9c913940c87774c15bded85aa920be14b (patch) | |
| tree | f3f9cd92be6532bdbc3064e2b1dc1d31e74ee28f /src/server | |
| parent | [SERVER] iscsi: Finish login handling, add NOP-In/Out handling (diff) | |
| download | dnbd3-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.c | 3 | ||||
| -rw-r--r-- | src/server/globals.h | 7 | ||||
| -rw-r--r-- | src/server/image.c | 74 | ||||
| -rw-r--r-- | src/server/image.h | 2 | ||||
| -rw-r--r-- | src/server/iscsi.c | 11489 | ||||
| -rw-r--r-- | src/server/iscsi.h | 6309 | ||||
| -rw-r--r-- | src/server/net.c | 2 | ||||
| -rw-r--r-- | src/server/server.c | 11 |
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, ¤t->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 *) ®_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 ) ) { |
